pax_global_header00006660000000000000000000000064141771723060014522gustar00rootroot0000000000000052 comment=49b3d2b9926a446b00970fadac9c52343a5b76bf gsmartcontrol-1.1.4/000077500000000000000000000000001417717230600144235ustar00rootroot00000000000000gsmartcontrol-1.1.4/.gitattributes000066400000000000000000000001251417717230600173140ustar00rootroot00000000000000 # Make sure github recognizes header files as C++, not C. *.h linguist-language=C++ gsmartcontrol-1.1.4/.github/000077500000000000000000000000001417717230600157635ustar00rootroot00000000000000gsmartcontrol-1.1.4/.github/workflows/000077500000000000000000000000001417717230600200205ustar00rootroot00000000000000gsmartcontrol-1.1.4/.github/workflows/autotools-tier1.yml000066400000000000000000000111771417717230600236250ustar00rootroot00000000000000name: Build - 1.1 Branch (Tier 1 platforms) on: # Triggers the workflow on push or pull request events but only for the branch push: branches: [ release/1.1 ] pull_request: branches: [ release/1.1 ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: env: SMARTMONTOOLS_INSTALLER: smartmontools-7.2-1.win32-setup.exe SMARTMONTOOLS_URL: https://nav.dl.sourceforge.net/project/smartmontools/smartmontools/7.2/smartmontools-7.2-1.win32-setup.exe jobs: linux-ubuntu: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 with: ref: release/1.1 - name: Install Dependencies run: sudo apt-get install libpcre3-dev libgtkmm-3.0-dev gettext autoconf automake - name: Run Autogen shell: bash working-directory: ${{runner.workspace}}/gsmartcontrol run: ./autogen.sh - name: Create Build Directory shell: bash run: mkdir -p ${{runner.workspace}}/build - name: Configure shell: bash working-directory: ${{runner.workspace}}/build run: > $GITHUB_WORKSPACE/configure --prefix=/usr --enable-tests - name: Build shell: bash working-directory: ${{runner.workspace}}/build run: make - name: Test working-directory: ${{runner.workspace}}/build shell: bash run: make check - name: Pack Source shell: bash working-directory: ${{runner.workspace}}/build run: make dist - name: Upload artifacts uses: actions/upload-artifact@v2 with: name: Source Packages (1.1 Branch) path: ${{runner.workspace}}/build/gsmartcontrol-*.tar.bz2 windows-msys2: # Disable for now, as the configure stage errors out with "The directory name is invalid." if: ${{ false }} runs-on: windows-latest strategy: matrix: include: [ { msystem: MINGW64, arch: x86_64, root: /mingw64 }, { msystem: MINGW32, arch: i686, root: /mingw32 } ] steps: - uses: actions/checkout@v2 with: ref: release/1.1 - uses: msys2/setup-msys2@v2 # p7zip is needed to extract smartmontools # fontconfig owns /etc/fonts with: msystem: ${{ matrix.msystem }} update: true install: >- mingw-w64-${{ matrix.arch }}-gtkmm3 mingw-w64-${{ matrix.arch }}-pkg-config autoconf automake mingw-w64-${{ matrix.arch }}-gcc mingw-w64-${{ matrix.arch }}-fontconfig make p7zip - name: Run Autogen shell: msys2 {0} working-directory: ${{runner.workspace}}/gsmartcontrol run: ./autogen.sh - name: Create Build Directory shell: cmd working-directory: ${{runner.workspace}} run: md build - name: Configure shell: msys2 {0} working-directory: ${{runner.workspace}}/build run: ../gsmartcontrol/configure --enable-tests - name: Build shell: msys2 {0} working-directory: ${{runner.workspace}}/build run: make - name: Test working-directory: ${{runner.workspace}}/build shell: msys2 {0} run: make check - name: Download Package Requirements shell: msys2 {0} working-directory: ${{runner.workspace}}/build run: | mkdir smartmontools cd smartmontools wget $SMARTMONTOOLS_URL - name: Extract Package Requirements (64-bit) if: ${{ matrix.msystem == 'MINGW64' }} shell: msys2 {0} working-directory: ${{runner.workspace}}/build/smartmontools run: > 7z -bb1 e $SMARTMONTOOLS_INSTALLER bin64/smartctl.exe bin64/smartctl-nc.exe bin/update-smart-drivedb.exe bin/drivedb.h - name: Extract Package Requirements (32-bit) if: ${{ matrix.msystem == 'MINGW32' }} shell: msys2 {0} working-directory: ${{runner.workspace}}/build/smartmontools run: > 7z -bb1 e $SMARTMONTOOLS_INSTALLER bin/smartctl.exe bin/smartctl-nc.exe bin/update-smart-drivedb.exe bin/drivedb.h - name: Package ZIP shell: msys2 {0} working-directory: ${{runner.workspace}}/build run: make win-zip-dist - name: Package NSIS shell: msys2 {0} working-directory: ${{runner.workspace}}/build run: make win-dist - name: Upload artifacts uses: actions/upload-artifact@v2 with: name: Windows Packages (1.1 Branch) path: ${{runner.workspace}}/build/gsmartcontrol-*-win*.* gsmartcontrol-1.1.4/.github/workflows/autotools-tier2.yml000066400000000000000000000040551417717230600236230ustar00rootroot00000000000000name: Build - 1.1 Branch (Tier 2 platforms) on: # Triggers the workflow on push or pull request events but only for the branch push: branches: [ release/1.1 ] pull_request: branches: [ release/1.1 ] # Allows you to run this workflow manually from the Actions tab. # This does not work for non-default branches, that's why we use the push / pull_request triggers above. workflow_dispatch: env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) BUILD_TYPE: RelWithDebInfo jobs: macos-homebrew: runs-on: macos-latest steps: - uses: actions/checkout@v2 with: ref: release/1.1 - name: Install Dependencies run: brew install pkg-config gtkmm3 pcre automake autoconf - name: Run Autogen shell: bash working-directory: ${{runner.workspace}}/gsmartcontrol run: ./autogen.sh - name: Create Build Directory shell: bash run: mkdir -p ${{runner.workspace}}/build - name: Configure shell: bash working-directory: ${{runner.workspace}}/build run: > $GITHUB_WORKSPACE/configure --prefix=/usr --enable-tests - name: Build shell: bash working-directory: ${{runner.workspace}}/build run: make - name: Test working-directory: ${{runner.workspace}}/build shell: bash run: make check freebsd: # Disable for now, make cannot find file2csource.sh for some reason. if: ${{ false }} # disable for now runs-on: macos-10.15 # env: # variables to pass to VM # MYTOKEN : ${{ secrets.MYTOKEN }} steps: - uses: actions/checkout@v2 with: ref: release/1.1 - name: Build and test in FreeBSD id: test uses: vmactions/freebsd-vm@v0.1.5 with: usesh: true mem: 2048 prepare: > pkg install -y pkgconf gtkmm30 gettext pcre libiconv autoconf automake run: > ./autogen.sh mkdir build && cd build ../configure --prefix=/usr --enable-tests make make check gsmartcontrol-1.1.4/.gitignore000066400000000000000000000017761417717230600164260ustar00rootroot00000000000000 # Builds /0* /doxygen_doc* /win32* /win64* /cmake-build-* # Build system artifacts /autom4te.cache /aclocal.m4 /ar-lib /compilation_flags /compile /config.guess /config.h /config.h.in /config.log /config.status /config.sub /configure /depcomp /gsmartcontrol.spec /install-sh Makefile Makefile.in /missing /stamp-h1 /version.txt *.glade.cpp *.ui.cpp *.txt.cpp /data/gsmartcontrol.appdata.xml /data/gsmartcontrol.desktop /data/gsmartcontrol-root /data/nsis/distribution.txt /data/nsis/gsmartcontrol.nsi /debian.dist/changelog /src/gsc_winres.rc /src/gsmartcontrol.exe.manifest .deps # Non-project files /TODO # Doxygen generated /doxygen_doc # OS-generated files .DS_Store .DS_Store? ._* .Spotlight-V100 .Trashes Icon? ehthumbs.db Thumbs.db # IDE /.vs /*.pcs /.idea /CMakeLists.txt.user /.kdev4 # Translations /po/boldquot.sed /po/en@boldquot.header /po/en@quot.header /po/insert-header.sin /po/Makevars.template /po/quot.sed /po/remove-potcdate.sin /po/Rules-quot /po/gsmartcontrol.pot /po/*.gmo # Backup files *~ gsmartcontrol-1.1.4/AUTHORS.txt000066400000000000000000000000541417717230600163100ustar00rootroot00000000000000Alexander Shaduri gsmartcontrol-1.1.4/COPYING000066400000000000000000000001121417717230600154500ustar00rootroot00000000000000See LICENSE.gsmartcontrol.txt file for copyright and license information. gsmartcontrol-1.1.4/ChangeLog000066400000000000000000000000231417717230600161700ustar00rootroot00000000000000See the NEWS file. gsmartcontrol-1.1.4/Doxyfile000066400000000000000000002153371417717230600161440ustar00rootroot00000000000000# Doxyfile 1.6.3 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = "GSmartControl" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = doxygen_doc # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it parses. # With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this tag. # The format is ext=language, where ext is a file extension, and language is one of # the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, # Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat # .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. Note that for custom extensions you also need to set # FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = YES # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by # doxygen. The layout file controls the global structure of the generated output files # in an output format independent way. The create the layout file that represents # doxygen's defaults, run doxygen with the -l option. You can optionally specify a # file name after the option, if omitted DoxygenLayout.xml will be used as the name # of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = . # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = *.c \ *.cc \ *.cxx \ *.cpp \ *.c++ \ *.d \ *.java \ *.ii \ *.ixx \ *.ipp \ *.i++ \ *.inl \ *.h \ *.hh \ *.hxx \ *.hpp \ *.h++ \ *.idl \ *.odl \ *.cs \ *.php \ *.php3 \ *.inc \ *.m \ *.mm \ *.dox \ *.py \ *.f90 \ *.f \ *.vhd \ *.vhdl \ *.C \ *.CC \ *.C++ \ *.II \ *.I++ \ *.H \ *.HH \ *.H++ \ *.CS \ *.PHP \ *.PHP3 \ *.M \ *.MM \ *.PY \ *.F90 \ *.F \ *.VHD \ *.VHDL \ *.C \ *.H \ *.tlh \ *.diff \ *.patch \ *.moc \ *.xpm \ *.dox # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = 0debug \ 0optimized \ 0win32 \ 0win32-debug \ 0win64-debug \ 0icc \ Debug \ Release \ MinSizeRel # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = */.svn/* EXCLUDE_PATTERNS += */.git/* EXCLUDE_PATTERNS += */doxygen_doc/* EXCLUDE_PATTERNS += */0*/* EXCLUDE_PATTERNS += */win32/* EXCLUDE_PATTERNS += */win64/* EXCLUDE_PATTERNS += */*.f # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. # This needs htmlhelp - # http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=00535334-c8a6-452f-9aa0-d597d16580cc GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. # For some reason it doesn't work, so look for doc\html\index.chm #CHM_FILE = "doc/api.chm" # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = "C:\Program Files\HTML Help Workshop\hhc.exe" # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = YES # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER # are set, an additional index file will be generated that can be used as input for # Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated # HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. # For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's # filter section matches. # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = YES # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvances is that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = YES # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = /usr/include \ /usr/include/gtkmm-2.4 \ /usr/include/glibmm-2.4 \ /usr/lib64/gtkmm-2.4/include \ /usr/include/atkmm-1.6 \ /usr/include/giomm-2.4 \ /usr/lib64/giomm-2.4/include \ /usr/include/pangomm-1.4 \ /usr/lib64/pangomm-1.4/include \ /usr/include/gtk-2.0 \ /usr/include/gtk-unix-print-2.0 \ /usr/include/gdkmm-2.4 \ /usr/lib64/gdkmm-2.4/include \ /usr/include/atk-1.0 \ /usr/lib64/glibmm-2.4/include \ /usr/include/glib-2.0 \ /usr/lib64/glib-2.0/include \ /usr/include/sigc++-2.0 \ /usr/lib64/sigc++-2.0/include \ /usr/include/cairomm-1.0 \ /usr/lib64/cairomm-1.0/include \ /usr/include/pango-1.0 \ /usr/include/cairo \ /usr/include/pixman-1 \ /usr/include/freetype2 \ /usr/include/libpng12 \ /usr/lib64/gtk-2.0/include \ /usr/include/gdk-pixbuf-2.0 \ # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. # Various per-platform macros are defined to not skip platform-specific functions. PREDEFINED = DOXYGEN_SHOULD_SKIP_THIS \ __GNUC__ \ _GNU_SOURCE \ HAVE_VERBOSE_TERMINATE_HANDLER=1 \ HAVE_GCC_ABI_DEMANGLE=1 \ HAVE_CXX___func__=1 \ HAVE_CXX___FUNCTION__=1 \ HAVE_POSIX_OFF_T_FUNCS=1 \ HAVE_WIN_LFS_FUNCS=1 \ HAVE_ISO_STDIO=1 \ HAVE__SNPRINTF=1 \ HAVE__VSNPRINTF=1 \ HAVE_VASPRINTF=1 \ HAVE_REENTRANT_LOCALTIME=1 \ HAVE_SETENV=1 \ HAVE_LONG_LONG_INT=1 \ HAVE_LONG_LONG=1 \ HAVE_UNSIGNED_LONG_LONG_INT=1 \ HAVE_UNSIGNED_LONG_LONG=1 \ HZ_ENABLE_COMPILED_RES_DATA=1 \ RMN_TYPE_TRACKING=1 \ CONFIG_HOST_KERNEL_DARWIN=1 \ CONFIG_HOST_KERNEL_DRAGONFLY=1 \ CONFIG_HOST_KERNEL_FAMILY_WINDOWS=1 \ CONFIG_HOST_KERNEL_FREEBSD=1 \ CONFIG_HOST_KERNEL_INTERIX=1 \ CONFIG_HOST_KERNEL_LINUX=1 \ CONFIG_HOST_KERNEL_NETBSD=1 \ CONFIG_HOST_KERNEL_OPENBSD=1 \ CONFIG_HOST_KERNEL_QNX=1 \ CONFIG_HOST_KERNEL_SOLARIS=1 \ CONFIG_HOST_KERNEL_UNKNOWN=1 \ CONFIG_HOST_KERNEL_WINDOWS32=1 \ CONFIG_HOST_KERNEL_WINDOWS64=1 \ CONFIG_KERNEL_DARWIN=1 \ CONFIG_KERNEL_DRAGONFLY=1 \ CONFIG_KERNEL_FAMILY_WINDOWS=1 \ CONFIG_KERNEL_FREEBSD=1 \ CONFIG_KERNEL_INTERIX=1 \ CONFIG_KERNEL_LINUX=1 \ CONFIG_KERNEL_NETBSD=1 \ CONFIG_KERNEL_OPENBSD=1 \ CONFIG_KERNEL_QNX=1 \ CONFIG_KERNEL_SOLARIS=1 \ CONFIG_KERNEL_UNKNOWN=1 \ CONFIG_KERNEL_WINDOWS32=1 \ CONFIG_KERNEL_WINDOWS64=1 \ CONFIG_KERNEL_LINUX=1 \ CONFIG_OS_ENV_CYGWIN=1 \ CONFIG_OS_ENV_DARWIN=1 \ CONFIG_OS_ENV_DRAGONFLY=1 \ CONFIG_OS_ENV_FREEBSD=1 \ CONFIG_OS_ENV_GNU=1 \ CONFIG_OS_ENV_INTERIX=1 \ CONFIG_OS_ENV_MINGW32=1 \ CONFIG_OS_ENV_MINGW64=1 \ CONFIG_OS_ENV_NETBSD=1 \ CONFIG_OS_ENV_OPENBSD=1 \ CONFIG_OS_ENV_QNX=1 \ CONFIG_OS_ENV_SOLARIS=1 \ CONFIG_OS_ENV_UNKNOWN=1 \ ENABLE_GTKBUILDER=1 \ ENABLE_GLIB=1 \ ENABLE_GLIBMM=1 \ HAVE_EXCEPTIONS=1 \ HAVE_RTTI=1 \ "HZ_FUNC_PRINTF_CHECK(a,b)=" \ __cplusplus \ _WIN32 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = /usr/share/doc/gtkmm-2.4/reference/gtkmm-2.4.tag=/usr/share/doc/gtkmm-2.4/reference/html/ \ /usr/share/doc/glibmm-2.4/reference/glibmm-2.4.tag=/usr/share/doc/glibmm-2.4/reference/html/ \ /usr/share/doc/atkmm-1.6/reference/atkmm-1.6.tag=/usr/share/doc/atkmm-1.6/reference/html/ \ /usr/share/doc/pangomm-1.4/reference/pangomm-1.4.tag=/usr/share/doc/pangomm-1.4/reference/html/ \ # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = doxygen_doc/gsmartcontrol.tag # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = NO # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = NO # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = NO # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 1000 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = YES # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES gsmartcontrol-1.1.4/INSTALL000066400000000000000000000000531417717230600154520ustar00rootroot00000000000000See README.txt file, Installation section. gsmartcontrol-1.1.4/LICENSE_boost_1_0.txt000066400000000000000000000024721417717230600201200ustar00rootroot00000000000000Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. gsmartcontrol-1.1.4/LICENSE_bsd-ac.txt000066400000000000000000000025621417717230600174640ustar00rootroot00000000000000Copyright (c) 2006-2008 Alexander Chemeris Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. gsmartcontrol-1.1.4/LICENSE_bsd-ucb.txt000066400000000000000000000027731417717230600176560ustar00rootroot00000000000000Copyright (c) 1987, 1989, 1990, 1991, 1992, 1993, 1994 The Regents of the University of California. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. gsmartcontrol-1.1.4/LICENSE_gpl2.txt000066400000000000000000000431031417717230600171730ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. gsmartcontrol-1.1.4/LICENSE_gpl3.txt000066400000000000000000001045131417717230600171770ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . gsmartcontrol-1.1.4/LICENSE_gsmartcontrol.txt000066400000000000000000000023671417717230600212340ustar00rootroot00000000000000 GSmartControl License This product is multi-licensed under GNU GPL versions 2 and 3. You are free to choose which one you use. This program is free software: you can redistribute it and/or modify it under the terms of either version 2 or (at your option) version 3 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licenses for more details. You should have received copies of these licenses along with this program. If not, see . See LICENSE_gpl2.txt and LICENSE_gpl3.txt for details. All files which don't have any copyright notices, as well as tests and examples are covered under the Unlicense. See LICENSE_unlicense.txt for details. This product includes the HZ library (or portions of it). See LICENSE_hz.txt for details. This product includes icons from Crystal Project, copyright 2006-2007 Everaldo Coelho. http://www.everaldo.com/crystal, http://www.yellowicon.com . Crystal Project icons are licensed under GNU LGPL. See http://www.everaldo.com/crystal/?action=license for details. gsmartcontrol-1.1.4/LICENSE_hz.txt000066400000000000000000000025701417717230600167530ustar00rootroot00000000000000 HZ Library License The HZ library contains software licensed under several different licenses. This is due to HZ including portions of other software. All original code (except for the tests) is licensed under the Zlib License. See LICENSE_zlib.txt for details. All tests and examples are covered under the Unlicense. See LICENSE_unlicense.txt for details. Portions of this software are copyright Regents of the University of California, see LICENSE_bsd-ucb.txt for details. Portions of this software are copyright Alexander Chemeris, see LICENSE_bsd-ac.txt for details. Portions of this software are licensed under Boost Software License, see LICENSE_boost_1_0.txt and individual source files for copyright information. Each source file contains its copyright information. If you intend to use HZ in your product, please note that you MUST include these licenses somewhere in your distribution (simply redistributing the license files or reproducing their texts in documentation and/or other supplied materials is OK): * LICENSE_zlib.txt (source distributions only); * LICENSE_bsd-ucb.txt (source and binary distributions); * LICENSE_boost_1_0.txt (source distributions only). If you use only portions of HZ, you may ignore this requirement for files you don't use. For example, if you don't use any of the Boost-licensed files, you are not required to distribute the Boost license. gsmartcontrol-1.1.4/LICENSE_unlicense.txt000066400000000000000000000022741417717230600203200ustar00rootroot00000000000000 This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to gsmartcontrol-1.1.4/LICENSE_zlib.txt000066400000000000000000000015011417717230600172630ustar00rootroot00000000000000Zlib License THIS SOFTWARE IS PROVIDED 'AS-IS', WITHOUT ANY EXPRESS OR IMPLIED WARRANTY. IN NO EVENT WILL THE AUTHORS BE HELD LIABLE FOR ANY DAMAGES ARISING FROM THE USE OF THIS SOFTWARE. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. gsmartcontrol-1.1.4/Makefile.am000066400000000000000000000361361417717230600164700ustar00rootroot00000000000000 # Add autoconf.m4 directory to local macro search path ACLOCAL_AMFLAGS = -I autoconf.m4 SUBDIRS = data debian.dist src # These files are actually needed for compilation. noinst_DATA = AUTHORS.txt LICENSE_gsmartcontrol.txt # For the files to be bundled with the distribution, specify them in # one of the following. # These will be installed into docdir. # Some of these files are actually needed for compilation (see src/res/Makefile.am). dist_doc_DATA = AUTHORS.txt ChangeLog NEWS README.txt \ LICENSE_boost_1_0.txt \ LICENSE_bsd-ac.txt \ LICENSE_bsd-ucb.txt \ LICENSE_gpl2.txt \ LICENSE_gpl3.txt \ LICENSE_gsmartcontrol.txt \ LICENSE_hz.txt \ LICENSE_unlicense.txt \ LICENSE_zlib.txt # nobase_ preserves their directory names. nobase_dist_doc_DATA = contrib/cron-based_noadmin/README \ contrib/cron-based_noadmin/cron_gather_smart.sh \ contrib/cron-based_noadmin/crontab.example \ contrib/cron-based_noadmin/smartctl_subst.sh # Extra files bundled with distribution. # file2csource.sh is needed for building but not installed. EXTRA_DIST = COPYING INSTALL configure autogen.sh \ gsmartcontrol.kdev4 \ file2csource.sh \ gsmartcontrol.spec # rpm support src-rpm: dist rpmbuild -ts $(distdir).tar.bz2 # NSIS installer support. # Requires installed NSIS, smartctl-nc.exe, smartctl.exe, update-smart-drivedb.exe. # Gtkmm and pcre dlls are required if linking against them. # dos2unix on build machine. # Execute only with win32 build present. WIN_ZIP_NAME = $(PACKAGE)-$(VERSION)-@WINDOWS_SUFFIX@ win-dist: win-dist-nocleanup win-dist-cleanup win-dist-nocleanup: win-dist-prepare nsis-dist-nocleanup win-zip-dist-nocleanup win-dist-cleanup: win-zip-dist-cleanup nsis-dist-cleanup rm -rf win-dist win-dist-clean: win-dist-cleanup rm -f gsmartcontrol-*.exe gsmartcontrol-*.zip win-dist-prepare-all: nsis-dist-prepare win-zip-dist-prepare # All of GTK+. # This includes both MSYS and OpenSUSE/mingw patterns. GTK_BIN_FILES = gdk-pixbuf-query-loaders.exe \ gspawn-win32-helper-console.exe \ gspawn-win32-helper.exe \ gspawn-win64-helper-console.exe \ gspawn-win64-helper.exe \ gtk-query-immodules-3.0.exe \ gtk-update-icon-cache-3.0.exe \ libatk-1.*.dll \ libatkmm-1.*.dll \ libbrotlidec.dll \ libbrotlicommon.dll \ libbz2-*.dll \ libcairo-*.dll \ libcairomm-1*.dll \ libdatrie-*.dll \ libepoxy-*.dll \ libffi-*.dll \ libexpat-*.dll \ libfontconfig-*.dll \ libfreetype-*.dll \ libfribidi-*.dll \ libgdk-3*.dll \ libgdk_pixbuf-2.*.dll \ libgdkmm-3.*.dll \ libgio-2.*.dll \ libgiomm-2.*.dll \ libglib-2.*.dll \ libglibmm*2.*.dll \ libgmodule-2.*.dll \ libgobject-2.*.dll \ libgraphite*.dll \ libgthread-2.*.dll \ libgtk-3*.dll \ libgtkmm-3*.dll \ libharfbuzz*.dll \ libiconv-*.dll \ libintl*.dll \ libjasper*.dll \ libjpeg*.dll \ liblzma*.dll \ libpango*-1.*.dll \ libpixman*.dll \ libpng16-*.dll \ libsigc-2.*.dll \ libssp-*.dll \ libstdc++-*.dll \ libthai-*.dll \ libtiff-*.dll \ libwinpthread-*.dll \ libxml2-*.dll \ zlib1.dll \ libz.dll # Don't strip smartctl (it will only change the header), and # don't strip update-smart-drivedb (it will break its CRC check). win-dist-prepare: all $(MKDIR_P) win-dist cp $(top_srcdir)/data/icon_*.png win-dist/ $(MKDIR_P) win-dist/doc cp data/nsis/distribution.txt win-dist/doc/ cp $(top_srcdir)/AUTHORS.txt $(top_srcdir)/LICENSE_* $(top_srcdir)/README.txt win-dist/doc/ cp $(top_srcdir)/NEWS win-dist/doc/NEWS.txt # The manifest is already embedded in exe # cp src/gsmartcontrol.exe src/gsmartcontrol.exe.manifest win-dist/ cp src/gsmartcontrol.exe win-dist/ unix2dos win-dist/doc/*.txt cp -p "@WINDOWS_SYSROOT@"/bin/drivedb.h win-dist/ cp -p "@WINDOWS_SYSROOT@"/bin/smartctl-nc.exe win-dist/ cp -p "@WINDOWS_SYSROOT@"/bin/smartctl.exe win-dist/ cp -p "@WINDOWS_SYSROOT@"/bin/update-smart-drivedb.exe win-dist/ for file in $(GTK_BIN_FILES); do for f in "@WINDOWS_SYSROOT@/bin/"$$file; do if test -f "$${f}"; then cp -p "$${f}" win-dist/; fi; done; done # /etc/gtk-3.0/ should contain settings.ini with a win32 theme. $(MKDIR_P) win-dist/etc $(MKDIR_P) win-dist/etc/fonts $(MKDIR_P) win-dist/etc/gtk-3.0 cp -p "@WINDOWS_SYSROOT@"/etc/fonts/fonts.conf win-dist/etc/fonts/ # cp -p "@WINDOWS_SYSROOT@"/etc/gtk-3.0/im-multipress.conf win-dist/etc/gtk-3.0/ # cp -p "@WINDOWS_SYSROOT@"/etc/gtk-3.0/settings.ini win-dist/etc/gtk-3.0/ # Without loaders, images and native file dialog do not work. $(MKDIR_P) win-dist/lib $(MKDIR_P) win-dist/lib/gdk-pixbuf-2.0 cp -p -r "@WINDOWS_SYSROOT@"/lib/gdk-pixbuf-2.0/2.10.0 win-dist/lib/gdk-pixbuf-2.0 $(MKDIR_P) win-dist/share cp -p -r "@WINDOWS_SYSROOT@"/share/themes win-dist/share # needed for file chooser $(MKDIR_P) win-dist/share/glib-2.0 cp -p -r "@WINDOWS_SYSROOT@"/share/glib-2.0/schemas win-dist/share/glib-2.0 # $(MKDIR_P) win-dist/share/icons # cp -p "@WINDOWS_SYSROOT@"/share/icons/hicolor win-dist/share/icons # Needed for window titlebar (if using client-side decorations), # tree sorting indicators, GUI icons. # Note that the current Adwaita uses "ui" directory, while win32 theme expects "actions". $(MKDIR_P) win-dist/share/icons/Adwaita cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/index.theme win-dist/share/icons/Adwaita $(MKDIR_P) win-dist/share/icons/Adwaita/16x16/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/ui/window-close-symbolic.symbolic.png win-dist/share/icons/Adwaita/16x16/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/ui/window-maximize-symbolic.symbolic.png win-dist/share/icons/Adwaita/16x16/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/ui/window-minimize-symbolic.symbolic.png win-dist/share/icons/Adwaita/16x16/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/ui/window-restore-symbolic.symbolic.png win-dist/share/icons/Adwaita/16x16/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/ui/pan-down-symbolic.symbolic.png win-dist/share/icons/Adwaita/16x16/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/ui/pan-up-symbolic.symbolic.png win-dist/share/icons/Adwaita/16x16/actions $(MKDIR_P) win-dist/share/icons/Adwaita/16x16/status cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/status/dialog-information-symbolic.symbolic.png win-dist/share/icons/Adwaita/16x16/status $(MKDIR_P) win-dist/share/icons/Adwaita/16x16/devices cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/devices/drive-harddisk.png win-dist/share/icons/Adwaita/16x16/devices cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/devices/media-optical.png win-dist/share/icons/Adwaita/16x16/devices # $(MKDIR_P) win-dist/share/icons/Adwaita/22x22/status # cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/22x22/status/dialog-information-symbolic.symbolic.png win-dist/share/icons/Adwaita/22x22/status $(MKDIR_P) win-dist/share/icons/Adwaita/24x24/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/ui/window-close-symbolic.symbolic.png win-dist/share/icons/Adwaita/24x24/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/ui/window-maximize-symbolic.symbolic.png win-dist/share/icons/Adwaita/24x24/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/ui/window-minimize-symbolic.symbolic.png win-dist/share/icons/Adwaita/24x24/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/ui/window-restore-symbolic.symbolic.png win-dist/share/icons/Adwaita/24x24/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/ui/pan-down-symbolic.symbolic.png win-dist/share/icons/Adwaita/24x24/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/ui/pan-up-symbolic.symbolic.png win-dist/share/icons/Adwaita/24x24/actions $(MKDIR_P) win-dist/share/icons/Adwaita/24x24/devices cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/devices/drive-harddisk.png win-dist/share/icons/Adwaita/24x24/devices cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/devices/media-optical.png win-dist/share/icons/Adwaita/24x24/devices $(MKDIR_P) win-dist/share/icons/Adwaita/24x24/status cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/status/dialog-information-symbolic.symbolic.png win-dist/share/icons/Adwaita/24x24/status $(MKDIR_P) win-dist/share/icons/Adwaita/32x32/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/ui/window-close-symbolic.symbolic.png win-dist/share/icons/Adwaita/32x32/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/ui/window-maximize-symbolic.symbolic.png win-dist/share/icons/Adwaita/32x32/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/ui/window-minimize-symbolic.symbolic.png win-dist/share/icons/Adwaita/32x32/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/ui/window-restore-symbolic.symbolic.png win-dist/share/icons/Adwaita/32x32/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/ui/pan-down-symbolic.symbolic.png win-dist/share/icons/Adwaita/32x32/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/ui/pan-up-symbolic.symbolic.png win-dist/share/icons/Adwaita/32x32/actions $(MKDIR_P) win-dist/share/icons/Adwaita/32x32/devices cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/devices/drive-harddisk.png win-dist/share/icons/Adwaita/32x32/devices cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/devices/media-optical.png win-dist/share/icons/Adwaita/32x32/devices $(MKDIR_P) win-dist/share/icons/Adwaita/32x32/status cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/status/dialog-information-symbolic.symbolic.png win-dist/share/icons/Adwaita/32x32/status $(MKDIR_P) win-dist/share/icons/Adwaita/48x48/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/ui/window-close-symbolic.symbolic.png win-dist/share/icons/Adwaita/48x48/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/ui/window-maximize-symbolic.symbolic.png win-dist/share/icons/Adwaita/48x48/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/ui/window-minimize-symbolic.symbolic.png win-dist/share/icons/Adwaita/48x48/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/ui/window-restore-symbolic.symbolic.png win-dist/share/icons/Adwaita/48x48/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/ui/pan-down-symbolic.symbolic.png win-dist/share/icons/Adwaita/48x48/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/ui/pan-up-symbolic.symbolic.png win-dist/share/icons/Adwaita/48x48/actions $(MKDIR_P) win-dist/share/icons/Adwaita/48x48/devices cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/devices/drive-harddisk.png win-dist/share/icons/Adwaita/48x48/devices cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/devices/media-optical.png win-dist/share/icons/Adwaita/48x48/devices $(MKDIR_P) win-dist/share/icons/Adwaita/48x48/status cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/status/dialog-information-symbolic.symbolic.png win-dist/share/icons/Adwaita/48x48/status $(MKDIR_P) win-dist/share/icons/Adwaita/64x64/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/ui/window-close-symbolic.symbolic.png win-dist/share/icons/Adwaita/64x64/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/ui/window-maximize-symbolic.symbolic.png win-dist/share/icons/Adwaita/64x64/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/ui/window-minimize-symbolic.symbolic.png win-dist/share/icons/Adwaita/64x64/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/ui/window-restore-symbolic.symbolic.png win-dist/share/icons/Adwaita/64x64/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/ui/pan-down-symbolic.symbolic.png win-dist/share/icons/Adwaita/64x64/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/ui/pan-up-symbolic.symbolic.png win-dist/share/icons/Adwaita/64x64/actions # $(MKDIR_P) win-dist/share/icons/Adwaita/64x64/devices # cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/devices/drive-harddisk.png win-dist/share/icons/Adwaita/64x64/devices # cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/devices/media-optical.png win-dist/share/icons/Adwaita/64x64/devices $(MKDIR_P) win-dist/share/icons/Adwaita/64x64/status cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/status/dialog-information-symbolic.symbolic.png win-dist/share/icons/Adwaita/64x64/status $(MKDIR_P) win-dist/share/icons/Adwaita/96x96/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/ui/window-close-symbolic.symbolic.png win-dist/share/icons/Adwaita/96x96/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/ui/window-maximize-symbolic.symbolic.png win-dist/share/icons/Adwaita/96x96/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/ui/window-minimize-symbolic.symbolic.png win-dist/share/icons/Adwaita/96x96/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/ui/window-restore-symbolic.symbolic.png win-dist/share/icons/Adwaita/96x96/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/ui/pan-down-symbolic.symbolic.png win-dist/share/icons/Adwaita/96x96/actions cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/ui/pan-up-symbolic.symbolic.png win-dist/share/icons/Adwaita/96x96/actions # $(MKDIR_P) win-dist/share/icons/Adwaita/96x96/devices # cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/devices/drive-harddisk.png win-dist/share/icons/Adwaita/96x96/devices # cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/devices/media-optical.png win-dist/share/icons/Adwaita/96x96/devices $(MKDIR_P) win-dist/share/icons/Adwaita/96x96/status cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/status/dialog-information-symbolic.symbolic.png win-dist/share/icons/Adwaita/96x96/status # $(MKDIR_P) win-dist/share/icons/Adwaita/256x256/devices # cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/256x256/devices/drive-harddisk.png win-dist/share/icons/Adwaita/256x256/devices # cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/256x256/devices/media-optical.png win-dist/share/icons/Adwaita/256x256/devices # $(MKDIR_P) win-dist/share/icons/Adwaita/256x256/status # cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/256x256/status/dialog-information-symbolic.symbolic.png win-dist/share/icons/Adwaita/256x256/status $(MKDIR_P) win-dist/share/icons/Adwaita/512x512/devices cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/512x512/devices/drive-harddisk.png win-dist/share/icons/Adwaita/512x512/devices cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/512x512/devices/media-optical.png win-dist/share/icons/Adwaita/512x512/devices # other cp -p "@WINDOWS_SYSROOT@"/bin/libgcc_s_*.dll win-dist/ cp -p "@WINDOWS_SYSROOT@"/bin/libpcre-1.dll win-dist/ cp -p "@WINDOWS_SYSROOT@"/bin/libpcrecpp-0.dll win-dist/ nsis-dist-prepare: win-dist-prepare cp -a win-dist nsis-dist cp $(top_srcdir)/data/gsmartcontrol.ico nsis-dist/ cp data/nsis/*.nsi $(top_srcdir)/data/nsis/nsi_* nsis-dist/ unix2dos nsis-dist/*.nsi nsis-dist-nocleanup: nsis-dist-prepare cd nsis-dist && @NSIS_EXEC@ gsmartcontrol.nsi mv nsis-dist/$(PACKAGE)-$(VERSION)-@WINDOWS_SUFFIX@.exe . nsis-dist-cleanup: rm -rf nsis-dist nsis-dist: nsis-dist-prepare nsis-dist-nocleanup nsis-dist-cleanup win-zip-dist-prepare: win-dist-prepare cp -a win-dist $(WIN_ZIP_NAME) win-zip-dist-nocleanup: win-zip-dist-prepare zip -9r $(WIN_ZIP_NAME).zip $(WIN_ZIP_NAME) # Only when cross-compiling: # zip -K -9r $(WIN_ZIP_NAME).zip $(WIN_ZIP_NAME) win-zip-dist-cleanup: rm -rf $(WIN_ZIP_NAME) win-zip-dist: win-zip-dist-prepare win-zip-dist-nocleanup win-zip-dist-cleanup distclean-local: win-dist-clean gsmartcontrol-1.1.4/NEWS000066400000000000000000000275621417717230600151360ustar00rootroot00000000000000Version 1.1.4, released on 2022-02-04 Fixed a crash when trying to render main window icons which have incomplete data; this fixes a rare crash when scanning devices (#13). Fixed rendering compatibility issues with newer GTK3 versions. Fixed build system conflict with C++20 version header (#14). Fixed "Running command ..." dialog floating above windows of other applications (#17). Fixed a crash when system locale is set to invalid locale. Support Finnish decimal separator (backported from the Debian package) (#23). Set LC_NUMERIC=C for smartctl process to avoid locale-dependent number formatting (#23). Fixed printing all GTK messages as warnings; the messages are sorted by severity now. Add current time to default filename in Save Output dialog. Support macos stat command in file2csource.sh. Fixed a few typos (backported from the Debian package). Windows port now uses Adwaita theme due to issues with win32 theme. Windows port is dpi-aware now. Version 1.1.3, released on 2017-11-12 Fixed gsmartcontrol-root not launching if GDK_* variables are not set. Version 1.1.2, released on 2017-11-11 Fixed gsmartcontrol-root not passing GDK_SCALE and GDK_DPI_SCALE variables to gsmartcontrol when using PolKit. Fixed blurriness of icons in the main window with GDK_SCALE=2. Tweaked the main window interface. Windows: Show volume labels beside drive letters in icon tooltips. Version 1.1.1, released on 2017-09-25 Windows: Use Adwaita GTK+ theme for systems which support Classic Windows theme, since the default win32 GTK+ theme is broken in it; this includes Windows 7 and Windows Server. Statistics entry values are formatted with commas for readability. Moved help information to website. Version 1.1.0, released on 2017-09-07 New Statistics, Temperature Log, Error Recovery, Physical and Directory tabs. General tab shows non-SMART device settings as well. Attributes tab shows entries in "brief" format. Error Log tab shows Extended error log by default (if supported). Self-Test Log tab now shows Extended self-test log by default (if supported). GSmartControl now uses "-x" equivalent for retrieving data (as upposed to "-a"); loading "-x" outputs as virtual drives is also supported. Implemented ability to copy rows in CSV format from Attribute, Statistics and Self-Test Log tables. Implemented "Update Drive Database" functionality. Windows: Drive letters are shown for each drive. Scan time is shown under virtual drive icons. Polkit is supported with gsmartcontrol-root script now. Pcrecpp is no longer bundled, use system-installed one instead. Smartmontools version 5.43 is required at runtime. Other minor improvements and fixes. Version 1.0.2, released on 2017-07-21 Fixed incomplete capturing of smartctl output under Windows. Added missing icons under Windows. Fixed being able to turn on AODC even if unsupported. Version 1.0.1, released on 2017-06-19 Fixed compilation under macOS. Fixed compilation under Fedora Rawhide. Don't use -mtune=generic for all targets. Version 1.0.0, released on 2017-06-16 Ported to GTK+ 3. Tweaked the user interface a bit. Dropped support for Windows XP, 2000 and 2003 (they are no longer supported by GTK+). Fixed detection of newer system-installed smartmontools under Windows. Version 0.9.0, released on 2017-05-11 Implemented (untested) support for Linux-based Areca controllers with enclosures. Implemented (untested) support for Windows-based Areca controllers (thanks to Richard Kagerer). Implemented (untested) support for Linux-based HP controllers with cciss and hpsa/hpahcisr drivers (thanks to Fabrice Bacchella). Changes in Preferences no longer fail silently until rescan/restart. Better drive detection under Windows after removable drives are detached. Windows version is no longer marked as "dpi aware" since it's not supported that well. Drive attribute descriptions have been updated (including clarifications for SSDs). Added support for SSD-only and HDD-only vendor attributes. Devices having only basic info can be displayed now in the info window. Fixed BDRW drive detection (it was detected as a HDD). Other minor improvements. A number of issues have been fixed (including a crash). Version 0.8.7, released on 2012-08-11 Implemented support for Adaptec RAID 5805 controller and possibly other Adaptec models as well (Linux). Implemented support for Areca RAID controller detection under Linux (untested). Implemented support for 3ware 3w-sas-supported (twl) RAID controller detection under Linux (untested). Implemented support for systems with several different 3ware controllers (twa/twe/twl). Fixed invalid parsing of tw_cli output which caused non-detection of drives and controllers with controller or port numbers greater than 9 if tw_cli was found (3ware linux, windows). The duplicate drives are no longer shown for some Intel matrix controllers under Windows; Intel RAID controllers are fully supported under Windows now. Added options to show device name and serial number under drive icons. Updated SMART attribute definitions and added warnings for SSD lifetime attributes. gsmartcontrol-root has a new argument "--desktop=..." which replaces the old positional argument; compatibility with the old invocation syntax has been retained. Completely documented the source using doxygen tags. Added support for Fedora's consolehelper. Made GSmartControl DPI-aware under Windows. Fixed compilation issues with clang and gcc 4.7, as well as glib 2.31.x and newer. Fixed minor issues in NSIS installer. Fixed other minor bugs and made minor improvements. Version 0.8.6, released on 2011-06-12 Support detecting drives behind 3ware controllers (Linux, Windows), including tw_cli/cx/px mode in Windows. Having tw_cli is recommended but not required. Added support for specifying -d option and extra parameters via command line, "Add Device" and "Preferences" dialogs. This change effectively adds full support for multiple drives behind a single device name. Beesu and su-to-root are supported by gsmartcontrol-root script now. Completely revamped the attribute database and its handling, should be a lot more usable and forward-compatible now; SSD attributes are also included. In-program help has been expanded considerably. General improvements to user interface have been implemented (better GNOME HIG compliance, better tooltips, dialogs, etc...). Added ability to show smartctl output for devices whose info could not be parsed fully. Attributes in "brief" format are supported now. The parser has been updated to reflect the recent changes in smartctl. Quit and rescan operations are no longer denied without confirmation when tests are running. The Windows NSIS installer has been vastly improved. Better support for Windows Vista and 7. Changed copyright notices for files with Whatever License to use Unlicense. Added other minor features and fixed quite a few bugs. Version 0.8.5, released on 2009-09-05 GSmartControl now uses XDG config directory for per-user configuration on UNIX and CSIDL_PROFILE directory on Windows. Existing configuration is migrated automatically. The names are shown correctly for unsupported devices even with the latest smartctl snapshots now. Smartctl SVN revision is shown (if available). The progress bars update properly when parallel tests are run. Windows: GSmartControl should be able to operate on any valid filesystem path (not just locale-representable ones). Windows: GSmartControl is now officially compilable on x86_64 via mingw64. Fixed compilation under very old gtkmm/libglademm, and with gcc 4.4. Fixed parsing of multiple error types in SMART error log. Added minor features and fixed miscellaneous bugs. Version 0.8.4, released on 2009-03-23 Linux Software RAID devices are blacklisted now. (backported from Debian). Attributes tab is before the capabilities tab now. A man page has been generously contributed by Giuseppe Iuculano . Smartctl version now includes the CVS snapshot date (if available). Windows: Look for "smartctl-nc.exe" instead of "smartctl.exe" by default. Windows: Use smartmontools-supplied smartctl-nc.exe by default (if found). Other minor changes (mainly Debian backports). Version 0.8.3, released on 2008-12-27 A random "Smartctl returned an empty output" error on Windows was fixed. Thanks to Zurab Khetsuriani for testing. Fixed a parser issue which prevented running self-tests in Windows. The supplied icon (hopefully) shows correctly in Windows 2000 now. This release adds an official support for Windows 2000 SP4. Added scripts to allow GSmartControl to read smartctl data from cron-generated files. This allows users to read somewhat recent smartctl information without having to run GSmartControl as root. Generously contributed by Alex Butcher . Configure script correctly aborts instead of printing a warning if gtkmm or libglademm (if needed) is not found. Configure script now accepts --enable-windows-console, --disable-abort-if-no-gtkmm, --disable-abort-if-no-glade-reader, as well as Windows-supporting "auto" for --enable-nsis-wine and --with-nsis. Configure's --with-win32-env has been renamed to --with-windows-dlls. The "About" dialog shows version information now. Minor bugs were fixed. Version 0.8.2, released on 2008-12-10 Fixed gsmartcontrol_root.sh script to support distributions with no /usr/sbin in their users' paths (thanks to Erwan Velu). Added desktop auto-detection to gsmartcontrol_root.sh script. This allows us to use only one desktop file (thanks to Erwan Velu). Added Debian package directory (named "debian.dist" for now to avoid control file conflicts with Build Service). Renamed gsmartcontrol_root.sh to gsmartcontrol-root, to make Debian happy. Added make targets for Windows packages (zip and NSIS). Fixed Windows-related issues (Vista is fully supported now). Fixed minor bugs: Smartctl parser is win32-locale-aware now. No more unnecessary parsing. No segfault on exit under Windows and Solaris. A friendlier message is displayed if smartctl was not found. No highlighted labels when switching tabs in Information window. Version 0.8.1, released on 2008-11-11 Disabled Linux "by-id" drive detection - it's unreliable on some broken systems. Added some more attribute descriptions. Our names for attributes override smartctls' now. Added a proper "Add Device" dialog for Windows. Added an icon and resource file for Windows. Fixed minor bugs. Version 0.8.0rc4, released on 2008-10-20 FreeBSD support (tested with DesktopBSD 1.6 (FreeBSD 6.3) / x86). NetBSD support (tested with NetBSD 4.0.1 / x86). OpenBSD support (tested with OpenBSD 4.3 / x86-64 / gcc-3.3.5). Solaris/gcc support (tested with Solaris 10 / x86 / gcc-3.4.3 / blastwave). Solaris/sunstudio support (tested with Solaris 10 / x86 / sunstudio12 / sunfreeware). Code to support Windows, Mac OS X, QNX. Support of older pcre versions (at least 4.5, maybe older too). Added gsmartcontrol_root.sh script to easily run gsmartcontrol as root. Improved .desktop files. License for tests and examples is legally more correct now. Minor bugfixes. Version 0.8.0rc3, released on 2008-10-08 Added support for udevless Linux distributions by providing a fallback /proc/partitions-based drive detection method. The new method adds support for Linux 2.4 and older systems. Thanks to Paul Marwick for reporting and testing. Fixed invalid error messages with directory-related operations. Fixed invalid verbosity levels of console output of debug messages in non-debug builds. Improved rpm spec file - now supports building on OpenSUSE build service with various distributions as targets. Version 0.8.0rc2, released on 2008-10-05 Fixed compilation under gcc 4.3. Thanks to Peter Linnell for reporting. Removed test data (there's a lot of it and it's unnecessary). Version 0.8.0rc1, released on 2008-10-01 Initial public release. gsmartcontrol-1.1.4/README.txt000066400000000000000000000065261417717230600161320ustar00rootroot00000000000000 About GSmartControl GSmartControl - Hard disk drive and SSD health inspection tool. GSmartControl is a graphical user interface for smartctl (from smartmontools package, see https://www.smartmontools.org/), which is a tool for querying and controlling SMART (Self-Monitoring, Analysis, and Reporting Technology) data on modern hard disk and solid-state drives. It allows you to inspect the drive's SMART data to determine its health, as well as run various tests on it. GSmartControl supports ATA drives (both PATA and SATA), various USB to ATA bridges and drives behind some RAID controllers: * Adaptec (Linux, some models only) * Areca (Linux, Windows) * HP CCISS (Linux) * HP hpsa / hpahcisr (Linux) * Intel Matrix Storage (CSMI) (Linux, Windows, FreeBSD) * LSI 3ware (Linux, Windows) * LSI MegaRAID (Windows) Note: Smartmontools supports even more RAID Controllers. The drives behind such controllers can be manually added to GSmartControl using "Add Device..." functionality or --add-device command-line option. See https://www.smartmontools.org/wiki/Supported_RAID-Controllers . https://gsmartcontrol.shaduri.dev Software Requirements Note: If using the official Windows package, no additional software is required. Build requirements: * pcre 1 - http://www.pcre.org . * GTK+, version 3.4 or higher - see http://www.gtk.org . * Gtkmm, version 3.4 or higher - see http://www.gtkmm.org . Runtime requirements: * smartmontools - see https://www.smartmontools.org/ . * xterm The following operating systems are supported: * Linux - All the popular configurations should work. * FreeBSD - Tested with DesktopBSD / x86. * NetBSD - Tested with NetBSD / x86. * OpenBSD - Tested with OpenBSD / x86-64. * DragonFlyBSD - Code written but no testing has been performed yet. Expected to work without any issues. * Windows Vista SP2 (32-bit and 64-bit), Windows 7 SP1, Windows Server 2008, Windows 8.1, Windows 10. The Windows port uses pd0, pd1, etc... for physical drives 0, 1, etc... . * Mac OS X. * Solaris. * QNX - Code written but no testing has been performed yet. Command Line Options GSmartControl inherits options from GTK+ and other libraries, so be sure to run it with --help option to get a full list of accepted parameters. Note: The Windows version may not have a text output at all, so --help and similar arguments won't have any effect. The most important parameters are: -?, --help - Show help options. -l, --no-locale - Don't use system locale. -V, --version - Display version information. --no-scan - Don't scan devices on startup. --no-hide-tabs - Don't hide non-identity tabs when SMART is disabled. Useful for debugging. --add-virtual - Load smartctl data from file, creating a virtual drive. You can specify this option multiple times. --add-device - Add this device to device list. The format of the device is "::::", where type and extra_args are optional. This option is useful with --no-scan to list certain drives only. You can specify this option multiple times. Example: --add-device /dev/sda --add-device /dev/twa0::3ware,2 --add-device '/dev/sdb::::-T permissive'. -v, --verbose - Enable verbose logging; same as --verbosity-level 5. -q, --quiet - Disable logging; same as --verbosity-level 0. -b, --verbosity-level - Set verbosity level [0-5]. License and Copyright For license information, see LICENSE_gsmartcontrol.txt file. gsmartcontrol-1.1.4/TODO000066400000000000000000000022561417717230600151200ustar00rootroot00000000000000 GTKMM Notes: To transfer an ownership from your local code to a container, use Gtk::Button* pButton = Gtk::manage(new Gtk::Button("Test")); container->add(*pButton); (add() takes an object by reference). otherwise, you will need to delete() it. local scope destruction also works if using stack variables. { Gtk::Button aButton; // destroyed here } -------------------------------------------------------- TODO: -l defects support? (smartctl 6.6) Don't rely on smartctl return code (2), parse the output instead. This will allow us to show the Info page. Need usage cases. Testing: If ETA time has elapsed, but it's still only at 10% completion, ETA 0 is displayed. Fix. Detect running tests on launch (maybe ask the user too? some tests may be stuck due to bad firmware, e.g. 3ware/windows). If smartctl outputs gibberish while testing, the GUI hangs. Support RAID for these controllers (supported by smartctl 6.5): https://www.smartmontools.org/wiki/Supported_RAID-Controllers Adaptec (Windows, Linux using "-d aacraid") HighPoint RocketRAID (Linux, FreeBSD (analyze sent info)) LSI MegaRAID (Linux, FreeBSD) 3ware (FreeBSD) Areca (FreeBSD) HP CCISS (FreeBSD) gsmartcontrol-1.1.4/autoconf.m4/000077500000000000000000000000001417717230600165605ustar00rootroot00000000000000gsmartcontrol-1.1.4/autoconf.m4/ac_cxx_exceptions.m4000066400000000000000000000024221417717230600225300ustar00rootroot00000000000000# =========================================================================== # http://autoconf-archive.cryp.to/ac_cxx_exceptions.html # =========================================================================== # # SYNOPSIS # # AC_CXX_EXCEPTIONS # # DESCRIPTION # # If the C++ compiler supports exceptions handling (try, throw and catch), # define HAVE_EXCEPTIONS. # # LAST MODIFICATION # # 2008-04-12 # # COPYLEFT # # Copyright (c) 2008 Todd Veldhuizen # Copyright (c) 2008 Luc Maisonobe # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. AC_DEFUN([AC_CXX_EXCEPTIONS], [AC_CACHE_CHECK(whether the compiler supports exceptions, ac_cv_cxx_exceptions, [AC_LANG_PUSH([C++]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[try { throw 1; } catch (int i) { return i; }]])], [ac_cv_cxx_exceptions=yes], [ac_cv_cxx_exceptions=no]) AC_LANG_POP([]) ]) if test "$ac_cv_cxx_exceptions" = yes; then AC_DEFINE(HAVE_EXCEPTIONS, 1, [defined to 1 if the compiler supports exceptions, 0 otherwise]) else AC_DEFINE(HAVE_EXCEPTIONS, 0, [defined to 1 if the compiler supports exceptions, 0 otherwise]) fi ]) gsmartcontrol-1.1.4/autoconf.m4/ac_cxx_namespaces.m4000066400000000000000000000024701417717230600224710ustar00rootroot00000000000000# =========================================================================== # http://autoconf-archive.cryp.to/ac_cxx_namespaces.html # =========================================================================== # # SYNOPSIS # # AC_CXX_NAMESPACES # # DESCRIPTION # # If the compiler can prevent names clashes using namespaces, define # HAVE_NAMESPACES. # # LAST MODIFICATION # # 2008-04-12 # # COPYLEFT # # Copyright (c) 2008 Todd Veldhuizen # Copyright (c) 2008 Luc Maisonobe # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. AC_DEFUN([AC_CXX_NAMESPACES], [AC_CACHE_CHECK(whether the compiler implements namespaces, ac_cv_cxx_namespaces, [AC_LANG_PUSH([C++]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM( [[namespace Outer { namespace Inner { int i = 0; }}]], [[using namespace Outer::Inner; return i;]])], [ac_cv_cxx_namespaces=yes], [ac_cv_cxx_namespaces=no]) AC_LANG_POP([]) ]) if test "$ac_cv_cxx_namespaces" = yes; then AC_DEFINE(HAVE_NAMESPACES, 1, [defined to 1 if the compiler implements namespaces, 0 otherwise]) else AC_DEFINE(HAVE_NAMESPACES, 0, [defined to 1 if the compiler implements namespaces, 0 otherwise]) fi ]) gsmartcontrol-1.1.4/autoconf.m4/ac_cxx_rtti.m4000066400000000000000000000030271417717230600213330ustar00rootroot00000000000000# =========================================================================== # http://autoconf-archive.cryp.to/ac_cxx_rtti.html # =========================================================================== # # SYNOPSIS # # AC_CXX_RTTI # # DESCRIPTION # # If the compiler supports Run-Time Type Identification (typeinfo header # and typeid keyword), define HAVE_RTTI. # # LAST MODIFICATION # # 2008-04-12 # # COPYLEFT # # Copyright (c) 2008 Todd Veldhuizen # Copyright (c) 2008 Luc Maisonobe # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. AC_DEFUN([AC_CXX_RTTI], [AC_CACHE_CHECK(whether the compiler supports Run-Time Type Identification, ac_cv_cxx_rtti, [AC_LANG_PUSH([C++]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include class Base { public: Base () {} virtual int f () { return 0; } }; class Derived : public Base { public : Derived () {} virtual int f () { return 1; } }; ]], [[ Derived d; Base* ptr = &d; return typeid (*ptr) == typeid (Derived); ]])], [ac_cv_cxx_rtti=yes], [ac_cv_cxx_rtti=no]) AC_LANG_POP([]) ]) if test "$ac_cv_cxx_rtti" = yes; then AC_DEFINE(HAVE_RTTI, 1, [defined to 1 if the compiler supports Run-Time Type Identification, 0 otherwise]) else AC_DEFINE(HAVE_RTTI, 0, [defined to 1 if the compiler supports Run-Time Type Identification, 0 otherwise]) fi ]) gsmartcontrol-1.1.4/autoconf.m4/ac_cxx_verbose_terminate_handler.m4000066400000000000000000000030031417717230600255550ustar00rootroot00000000000000# =========================================================================== # http://autoconf-archive.cryp.to/ac_cxx_verbose_terminate_handler.html # =========================================================================== # # SYNOPSIS # # AC_CXX_VERBOSE_TERMINATE_HANDLER # # DESCRIPTION # # If the compiler does have the verbose terminate handler, define # HAVE_VERBOSE_TERMINATE_HANDLER. # # LAST MODIFICATION # # 2008-04-12 # # COPYLEFT # # Copyright (c) 2008 Lapo Luchini # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. AC_DEFUN([AC_CXX_VERBOSE_TERMINATE_HANDLER], [AC_CACHE_CHECK(whether the compiler has __gnu_cxx::__verbose_terminate_handler, ac_cv_verbose_terminate_handler, [ AC_REQUIRE([AC_CXX_EXCEPTIONS]) AC_REQUIRE([AC_CXX_NAMESPACES]) AC_LANG_PUSH([C++]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM( [[#include ]], [[std::set_terminate(__gnu_cxx::__verbose_terminate_handler);]])], [ac_cv_verbose_terminate_handler=yes], [ac_cv_verbose_terminate_handler=no]) AC_LANG_POP([]) ]) if test "$ac_cv_verbose_terminate_handler" = yes; then AC_DEFINE(HAVE_VERBOSE_TERMINATE_HANDLER, 1, [defined to 1 if the compiler has __gnu_cxx::__verbose_terminate_handler, 0 otherwise]) else AC_DEFINE(HAVE_VERBOSE_TERMINATE_HANDLER, 0, [defined to 1 if the compiler has __gnu_cxx::__verbose_terminate_handler, 0 otherwise]) fi ]) gsmartcontrol-1.1.4/autoconf.m4/app_auto_clear_flags.m4000066400000000000000000000017401417717230600231560ustar00rootroot00000000000000 ############################################################################ # Copyright: # (C) 2008 - 2009 Alexander Shaduri # License: See LICENSE_zlib.txt file ############################################################################ # APP_AUTO_CLEAR_FLAGS() # Clear user-specified CFLAGS / CXXFLAGS / LIBS / LDFLAGS if requested. # You should call this macro before any of AC_PROG_C(XX) checks to # avoid checking with user-specified options. AC_DEFUN([APP_AUTO_CLEAR_FLAGS], [ AC_ARG_ENABLE(user-flags, AS_HELP_STRING([--disable-user-flags], [ignore user-supplied compiler flags (default: user flags are enabled)]), [app_cv_compiler_user_flags=${enableval}], [app_cv_compiler_user_flags=yes]) AC_MSG_NOTICE([Enable user-specified compiler flags: $app_cv_compiler_user_flags]) # Reset user-supplied flags if requested. if test "x$app_cv_compiler_user_flags" = "xno"; then CFLAGS=""; CXXFLAGS=""; LIBS=""; LDFLAGS=""; fi ]) gsmartcontrol-1.1.4/autoconf.m4/app_compiler_options.m4000066400000000000000000000322331417717230600232520ustar00rootroot00000000000000 ############################################################################ # Copyright: # (C) 2008 - 2009 Alexander Shaduri # License: See LICENSE_zlib.txt file ############################################################################ # APP_COMPILER_OPTIONS([flags_prefix]) # This macro enables various compiler flags (common warnings, optimization, # debug, etc...), controllable through configure options. # flags_prefixCFLAGS, flags_prefixCXXFLAGS and flags_prefixLDFLAGS # will be initialized (or appended to) with the results. # DEBUG and DEBUG_BUILD will be exported to config.h in case of debug builds. # You must call AX_COMPILER_VENDOR, APP_DETECT_OS_KERNEL([target], ...) # and APP_DETECT_OS_ENV([target], ...) before using this macro. AC_DEFUN([APP_COMPILER_OPTIONS], [ # These requirements check compile-time presence, not that they were called AC_REQUIRE([AX_COMPILER_VENDOR]) AC_REQUIRE([APP_DETECT_OS_ENV]) app_cv_compiler_options_cflags=""; app_cv_compiler_options_cxxflags=""; app_cv_compiler_options_ldflags=""; # ---- Common compiler options (warnings, etc...) AC_ARG_ENABLE(common-options, AS_HELP_STRING([--enable-common-options=|auto|none], [enable useful compilation options (warnings, mostly). Accepted values are auto, none, gnu, clang, intel, sun. (Default: auto)]), [app_cv_compiler_common_options=${enableval}], [app_cv_compiler_common_options=none]) # the default value is "yes" (if no value given after =), treat it as auto. if test "x$app_cv_compiler_common_options" = "xyes" || test "x$app_cv_compiler_common_options" = "xauto"; then app_cv_compiler_common_options="$ax_cv_cxx_compiler_vendor"; fi if test "x$app_cv_compiler_common_options" = "xno"; then # for pretty message app_cv_compiler_common_options="none"; fi AC_MSG_NOTICE([Enable common compiler flags for: $app_cv_compiler_common_options]) if test "x$app_cv_compiler_common_options" != "xnone"; then # gcc (or clang) / gnu environment, mingw, cygwin. if test "x$app_cv_compiler_common_options" = "xgnu" || test "x$app_cv_compiler_common_options" = "xclang"; then if test "x$app_cv_target_os_env" = "xmingw32" || test "x$app_cv_target_os_env" = "xmingw64" \ || test "x$app_cv_target_os_env" = "xcygwin"; then # mingw gcc options: # -mno-cygwin - generate non-cygwin executables in cygwin's mingw. # -mms-bitfields - make structures compatible with msvc. recommended default for non-cygwin. Default for mingw. app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -mms-bitfields" app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -mms-bitfields" app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -mms-bitfields" fi app_cv_compiler_tmp_var="-Wall -Wcast-align -Wcast-qual -Wconversion \ -Wfloat-equal -Wnon-virtual-dtor -Woverloaded-virtual \ -Wpointer-arith -Wshadow -Wsign-compare -Wsign-promo -Wundef -Wwrite-strings"; if test "x$app_cv_compiler_common_options" = "xclang"; then # these are really annoying app_cv_compiler_tmp_var="$app_cv_compiler_tmp_var -Wno-sign-conversion -Wno-format-security" fi app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags $app_cv_compiler_tmp_var"; app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags $app_cv_compiler_tmp_var"; # intel / gnu environment elif test "x$app_cv_compiler_common_options" = "xintel" && test "x$app_cv_target_os_env" = "xgnu"; then app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -w1"; app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags \ -ftrapuv -fstack-security-check -early-template-check -static-libgcc -static-intel \ -Wcomment -Wdeprecated -Wextra-tokens -Wformat -Wmissing-prototypes -Wnon-virtual-dtor \ -Wpointer-arith -Wreturn-type -Wshadow -Wtrigraphs -Wuninitialized -w1"; app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -static-libgcc -static-intel"; # suncc elif test "x$app_cv_compiler_common_options" = "xsun"; then # Enable useful language extensions (e.g. __func__ in C++). Details at # http://docs.sun.com/app/docs/doc/820-7599/bkapy?a=view # Additional options: "-staticlib=Crun -staticlib=Cstd" - link statically to # C++ runtime and standard libraries. # http://docs.sun.com/app/docs/doc/820-7599/bkaws?a=view # STLport seems to be more standards-compliant, but incompatible # with default Cstd. Also, it's not guaranteed to keep compatibility # between versions. Select it with "-library=stlport4". # http://docs.sun.com/app/docs/doc/820-7599/bkakg?a=view # http://developers.sun.com/solaris/articles/cmp_stlport_libCstd.html app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags \ +w -errtags -erroff=notemsource,notused -features=extensions"; app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags \ +w -errtags -erroff=notemsource,notused -features=extensions"; fi fi # ---- Compiler options for debug builds AC_ARG_ENABLE(debug-options, AS_HELP_STRING([--enable-debug-options=|auto|none], [enable debug build options. Accepted values are auto, none, gnu, clang, intel, sun. (Default: none)]), [app_cv_compiler_debug_options=${enableval}], [app_cv_compiler_debug_options=none]) # the default value is "yes" (if no value given after =), treat it as auto. if test "x$app_cv_compiler_debug_options" = "xyes" || test "x$app_cv_compiler_debug_options" = "xauto"; then app_cv_compiler_debug_options="$ax_cv_cxx_compiler_vendor"; fi if test "x$app_cv_compiler_debug_options" = "xno"; then # for pretty message app_cv_compiler_debug_options="none"; fi AC_MSG_NOTICE([Enable debug build flags for: $app_cv_compiler_debug_options]) if test "x$app_cv_compiler_debug_options" != "xnone"; then # Define DEBUG and DEBUG_BUILD for debug builds (through config.h). AC_DEFINE([DEBUG], [1], [Defined for debug builds]) AC_DEFINE([DEBUG_BUILD], [1], [Defined for debug builds]) # gcc, mingw if test "x$app_cv_compiler_debug_options" = "xgnu" || test "x$app_cv_compiler_debug_options" = "xclang"; then # We could put libstdc++ debug options here, but it generates binary-incompatible # C++ code, which leads to runtime errors with e.g. libsigc++. app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -g3 -O0"; app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -g3 -O0"; app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -g3 -O0"; # intel / gnu elif test "x$app_cv_compiler_debug_options" = "xintel" && test "x$app_cv_target_os_env" = "xgnu"; then app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -g3 -O0"; app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -g3 -O0"; app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -g3 -O0"; # suncc elif test "x$app_cv_compiler_debug_options" = "xsun"; then # There's no -O0, it's the default. Debug format is stabs by default, but can be switched # to dwarf by -xdebugformat=dwarf. app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -xs -g"; app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -xs -g"; app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -xs -g"; fi fi # ---- Compiler options for optimized builds AC_ARG_ENABLE(optimize-options, AS_HELP_STRING([--enable-optimize-options=|auto|none], [enable optimized build options. Accepted values are auto, none, gnu, clang, intel, sun. (Default: none)]), [app_cv_compiler_optimize_options=${enableval}], [app_cv_compiler_optimize_options=none]) # the default value is "yes" (if no value given after =), treat it as auto. if test "x$app_cv_compiler_optimize_options" = "xyes" || test "x$app_cv_compiler_optimize_options" = "xauto"; then app_cv_compiler_optimize_options="$ax_cv_cxx_compiler_vendor"; fi if test "x$app_cv_compiler_optimize_options" = "xno"; then # for pretty message app_cv_compiler_optimize_options="none"; fi AC_MSG_NOTICE([Enable optimized build flags for: $app_cv_compiler_optimize_options]) if test "x$app_cv_compiler_optimize_options" != "xnone"; then # gcc, mingw if test "x$app_cv_compiler_optimize_options" = "xgnu" || test "x$app_cv_compiler_optimize_options" = "xclang"; then # -mtune=generic is since gcc 4.0 iirc if test "x$app_cv_target_os_env" = "xmingw32"; then app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -g0 -O3 -s -march=i686"; app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -g0 -O3 -s -march=i686"; app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -g0 -O3 -s -march=i686"; else # mingw64, cygwin (they have new gcc), others. app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -g0 -O3 -s"; app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -g0 -O3 -s"; app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -g0 -O3 -s"; fi # -mtune=generic is only for x86* case "$target" in i686-*) app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -mtune=generic"; app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -mtune=generic"; app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -mtune=generic"; ;; x86_64-*) app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -mtune=generic"; app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -mtune=generic"; app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -mtune=generic"; ;; esac # intel / gnu elif test "x$app_cv_compiler_optimize_options" = "xintel" && test "x$app_cv_target_os_env" = "xgnu"; then app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -g0 -O3 -s"; app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -g0 -O3 -s"; app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -g0 -O3 -s"; # suncc elif test "x$app_cv_compiler_optimize_options" = "xsun"; then # There's no -O0, it's the default. Debug format is stabs by default, but can be switched # to dwarf by -xdebugformat=dwarf. app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -xO5"; app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -xO5"; app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -s"; fi fi # ---- Compiler options for GCC pch support AC_ARG_ENABLE(gcc-pch, AS_HELP_STRING([--enable-gcc-pch],[enable precompiled header support (pch make target) (GCC only) (default: disabled) (use "make gch" to speed up the compilation, but don't enable it if you don't intend to use it - it will slow things down)]), [app_cv_compiler_gcc_pch=${enableval}], [app_cv_compiler_gcc_pch=no]) AC_MSG_NOTICE([Enable GCC precompiled header usage: $app_cv_compiler_gcc_pch]) # Don't check the vendor here - it's disabled by default, so it's on the user. if test "x$app_cv_compiler_gcc_pch" = "xyes"; then app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -Winvalid-pch -DENABLE_PCH" app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -Winvalid-pch -DENABLE_PCH" fi # ---- Compiler options for Mingw's mwindows/mconsole # -mwindows - hide console window. possibly suppresses any std output / error. # -mconsole - opposite of -mwindows, default. AC_ARG_ENABLE(windows-console, AS_HELP_STRING([--enable-windows-console=yes|no|auto], [enable windows console (MinGW and Cygwin only). Accepted values are yes, no, auto. ] [Auto means disabled for optimized builds, enabled for all others. (Default: auto)]), [app_cv_compiler_windows_console=${enableval}], [app_cv_compiler_windows_console=auto]) if test "x$app_cv_compiler_windows_console" = "xauto"; then # disable console for optimized builds. if test "x$app_cv_compiler_optimize_options" != "xnone"; then app_cv_compiler_windows_console="no"; else app_cv_compiler_windows_console="yes"; fi fi if test "x$app_cv_target_os_env" = "xmingw32" || test "x$app_cv_target_os_env" = "xmingw64" \ || test "x$app_cv_target_os_env" = "xcygwin"; then if test "x$app_cv_compiler_windows_console" = "xno"; then app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -mwindows"; else # specify -mconsole even if it's the default - it should override the user-supplied setting app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -mconsole"; fi AC_MSG_NOTICE([Enable windows console: $app_cv_compiler_windows_console]) fi # ---- Export our variables if test "x$$1[]CFLAGS" = "x"; then $1[]CFLAGS="$app_cv_compiler_options_cflags"; else $1[]CFLAGS="$$1[]CFLAGS $app_cv_compiler_options_cflags"; fi if test "x$$1[]CXXFLAGS" = "x"; then $1[]CXXFLAGS="$app_cv_compiler_options_cxxflags"; else $1[]CXXFLAGS="$$1[]CXXFLAGS $app_cv_compiler_options_cxxflags"; fi if test "x$$1[]LDFLAGS" = "x"; then $1[]LDFLAGS="$app_cv_compiler_options_ldflags"; else $1[]LDFLAGS="$$1[]LDFLAGS $app_cv_compiler_options_ldflags"; fi AC_MSG_NOTICE([Compiler-specific build options:]) AC_MSG_NOTICE([CFLAGS: $app_cv_compiler_options_cflags]) AC_MSG_NOTICE([CXXFLAGS: $app_cv_compiler_options_cxxflags]) AC_MSG_NOTICE([LDFLAGS: $app_cv_compiler_options_ldflags]) ]) gsmartcontrol-1.1.4/autoconf.m4/app_cxx_extern_c_overload.m4000066400000000000000000000035251417717230600242530ustar00rootroot00000000000000 ############################################################################ # Copyright: # (C) 2008 - 2009 Alexander Shaduri # License: See LICENSE_zlib.txt file ############################################################################ # Check whether C++ compiler supports overloading of functions # with function pointer argument differing only in extern "C" specification, # that is, extern "C" function pointer and C++ function pointer. GCC and # Intel treat them equally, so no overloads are needed (or accepted) # there. # SunCC accepts C++-to-C function pointer assignment in function # parameters, but not in constructor parameters. To make such # code work an additional C-function-pointer constructor must be # specified. Then the constructor itself can make the conversion # (with a compiler warning though). # SunCC also disallows C-to-C++ function pointer specification # for template parameters (I don't remember the exact details). # If such overloading support is found, # HAVE_CXX_EXTERN_C_OVERLOAD is defined. AC_DEFUN([APP_CXX_EXTERN_C_OVERLOAD], [ AC_CACHE_CHECK(whether $CXX accepts extern C function pointer overload, ac_cv_cxx_extern_c_overload, [ AC_LANG_PUSH([C++]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ extern "C" { typedef void (*c_func_t)(void); } typedef void (*cpp_func_t)(void); void f1(c_func_t f) { } void f1(cpp_func_t f) { } ]], [[]])], [ac_cv_cxx_extern_c_overload=yes], [ac_cv_cxx_extern_c_overload=no]) AC_LANG_POP([]) ]) if test "x$ac_cv_cxx_extern_c_overload" = "xyes"; then AC_DEFINE(HAVE_CXX_EXTERN_C_OVERLOAD, 1, [Defined to 1 if the C++ complier accepts extern C function pointer overload, 0 otherwise]) else AC_DEFINE(HAVE_CXX_EXTERN_C_OVERLOAD, 0, [Defined to 1 if the C++ complier accepts extern C function pointer overload, 0 otherwise]) fi ]) gsmartcontrol-1.1.4/autoconf.m4/app_cxx_func.m4000066400000000000000000000027751417717230600215120ustar00rootroot00000000000000 ############################################################################ # Copyright: # (C) 2008 - 2009 Alexander Shaduri # License: See LICENSE_zlib.txt file ############################################################################ # Check whether C++ compiler supports C99 __func__. AC_DEFUN([APP_CXX___func__], [ AC_CACHE_CHECK(whether $CXX recognizes __func__, app_cv_cxx___func__, [ AC_LANG_PUSH([C++]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[const char* s = __func__;]])], [app_cv_cxx___func__=yes], [app_cv_cxx___func__=no]) AC_LANG_POP([]) ]) if test "x$app_cv_cxx___func__" = "xyes"; then AC_DEFINE(HAVE_CXX___func__, 1, [Defined to 1 if the C++ complier supports __func__, 0 otherwise]) else AC_DEFINE(HAVE_CXX___func__, 0, [Defined to 1 if the C++ complier supports __func__, 0 otherwise]) fi ]) # Check whether C++ compiler supports __FUNCTION__. AC_DEFUN([APP_CXX___FUNCTION__], [ AC_CACHE_CHECK(whether $CXX recognizes __FUNCTION__, app_cv_cxx___FUNCTION__, [ AC_LANG_PUSH([C++]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[const char* s = __FUNCTION__;]])], [app_cv_cxx___FUNCTION__=yes], [app_cv_cxx___FUNCTION__=no]) AC_LANG_POP([]) ]) if test "x$app_cv_cxx___FUNCTION__" = "xyes"; then AC_DEFINE(HAVE_CXX___FUNCTION__, 1, [Defined to 1 if the C++ complier supports __FUNCTION__, 0 otherwise]) else AC_DEFINE(HAVE_CXX___FUNCTION__, 0, [Defined to 1 if the C++ complier supports __FUNCTION__, 0 otherwise]) fi ]) gsmartcontrol-1.1.4/autoconf.m4/app_detect_os.m4000066400000000000000000000325041417717230600216370ustar00rootroot00000000000000 ############################################################################ # Copyright: # (C) 2008 - 2009 Alexander Shaduri # License: See LICENSE_zlib.txt file ############################################################################ # APP_DETECT_OS_KERNEL(triplet_name, define_macro_prefix) # Check target OS kernel. # triplet_name is target, host or build. # You must call AX_COMPILER_VENDOR before using this macro. # If you call # APP_DETECT_OS_KERNEL([target], [CONFIG_KERNEL]) # you will get CONFIG_KERNEL_LINUX C macro exported to config.h # (on gcc/linux). # If no define_macro_prefix is specified, no C macro prefix will be used. # app_cv_target_os_kernel will be set to kernel name (lowercase). # app_cv_target_os_kernel_macro will be set to kernel name (all caps). # TARGET_OS_KERNEL_LINUX automake conditional will be set to true. # Note: Some operating systems / compilers have built-in defines. # For gcc, check with # $ gcc -dM -E - < /dev/null AC_DEFUN([APP_DETECT_OS_KERNEL], [ # These requirements check compile-time presence, not that they were called AC_REQUIRE([AX_COMPILER_VENDOR]) AC_MSG_CHECKING([for $1 OS kernel]) app_cv_[]$1[]_os_kernel=""; app_cv_[]$1[]_os_kernel_macro=""; # If vendor is not recognized, it's set to "unknown". # NOTE: This works only on target compilers! if test "x${ax_cv_cxx_compiler_vendor}" = "x"; then # msg_result is needed to print the checking... message. AC_MSG_RESULT([error]) AC_MSG_ERROR([[ax_cv_cxx_compiler_vendor] not set.]) fi # "build" - the type of system on which the package is being configured and compiled. # "host" - the type of system on which the package runs. Specifying it enables the # cross-compilation mode. "target" defaults to this too. if test "x$1" != "xbuild" && test "x$1" != "xhost" && test "x$1" != "xtarget"; then # msg_result is needed to print the checking... message. AC_MSG_RESULT([error]) AC_MSG_ERROR([[Triplet name must be build, host or target.]]) fi case "$$1" in *linux*) # gcc defines __linux__ on linux. Other defines include: __linux, __unix__, __gnu_linux__, # linux, unix, __i386__, i386. app_cv_[]$1[]_os_kernel="linux"; app_cv_[]$1[]_os_kernel_macro="LINUX"; ;; i*86-*mingw* | i*86-*cygwin*) # mingw defines _WIN32. msvc defines _WIN32 (built-in), and WIN32 via windows.h. # Other mingw defines include: __WINNT, __WINNT__, __WIN32__, __i386, _X86_, # i386, __i386__, WIN32, __MINGW32__, WINNT (all equal to 1). app_cv_[]$1[]_os_kernel="windows32"; app_cv_[]$1[]_os_kernel_macro="WINDOWS32"; ;; x86_64-*-mingw* | x86_64-*-cygwin*) # mingw64 defines the same stuff as 32-bit one, plus _WIN64, __MINGW64__, etc... . # Keep in mind that if you're generating a 32-bit application, the kernel will # be windows32 even if you run it on 64-bit Windows. app_cv_[]$1[]_os_kernel="windows64"; app_cv_[]$1[]_os_kernel_macro="WINDOWS64"; ;; *interix*) # Interix is a kernel on top of windows, so we treat it separately. app_cv_[]$1[]_os_kernel="interix"; app_cv_[]$1[]_os_kernel_macro="INTERIX"; ;; # This includes debian gnu/kfreebsd. Dragonfly is checked separately. *freebsd*) # gcc built-in defines (to check for freebsd kernel): # defined(__FreeBSD__) || defined(__FreeBSD_kernel__). # See http://glibc-bsd.alioth.debian.org/porting/PORTING . app_cv_[]$1[]_os_kernel="freebsd"; app_cv_[]$1[]_os_kernel_macro="FREEBSD"; ;; *dragonfly*) app_cv_[]$1[]_os_kernel="dragonfly"; app_cv_[]$1[]_os_kernel_macro="DRAGONFLY"; ;; *openbsd*) app_cv_[]$1[]_os_kernel="openbsd"; app_cv_[]$1[]_os_kernel_macro="OPENBSD"; ;; # This includes debian gnu/netbsd. *netbsd*) app_cv_[]$1[]_os_kernel="netbsd"; app_cv_[]$1[]_os_kernel_macro="NETBSD"; ;; *solaris*) app_cv_[]$1[]_os_kernel="solaris"; app_cv_[]$1[]_os_kernel_macro="SOLARIS"; ;; *darwin*) app_cv_[]$1[]_os_kernel="darwin"; app_cv_[]$1[]_os_kernel_macro="DARWIN"; ;; *qnx*) app_cv_[]$1[]_os_kernel="qnx"; app_cv_[]$1[]_os_kernel_macro="QNX"; ;; *) app_cv_[]$1[]_os_kernel="unknown"; app_cv_[]$1[]_os_kernel_macro="UNKNOWN"; ;; esac # Assign this - we may use this in configure itself. if test "x$2" != "x"; then app_cv_[]$1[]_os_kernel_macro="$2[]_${app_cv_[]$1[]_os_kernel_macro}"; fi # config.h macros (yes, we need this many. otherwise config.h will be incorrect). if test "x${app_cv_[]$1[]_os_kernel}" = "xlinux"; then AC_DEFINE($2[]LINUX, 1, [$1 OS kernel]) fi if test "x${app_cv_[]$1[]_os_kernel}" = "xwindows32"; then AC_DEFINE($2[]WINDOWS32, 1, [$1 OS kernel]) fi if test "x${app_cv_[]$1[]_os_kernel}" = "xwindows64"; then AC_DEFINE($2[]WINDOWS64, 1, [$1 OS kernel]) fi if test "x${app_cv_[]$1[]_os_kernel}" = "xinterix"; then AC_DEFINE($2[]INTERIX, 1, [$1 OS kernel]) fi if test "x${app_cv_[]$1[]_os_kernel}" = "xfreebsd"; then AC_DEFINE($2[]FREEBSD, 1, [$1 OS kernel]) fi if test "x${app_cv_[]$1[]_os_kernel}" = "xdragonfly"; then AC_DEFINE($2[]DRAGONFLY, 1, [$1 OS kernel]) fi if test "x${app_cv_[]$1[]_os_kernel}" = "xopenbsd"; then AC_DEFINE($2[]OPENBSD, 1, [$1 OS kernel]) fi if test "x${app_cv_[]$1[]_os_kernel}" = "xnetbsd"; then AC_DEFINE($2[]NETBSD, 1, [$1 OS kernel]) fi if test "x${app_cv_[]$1[]_os_kernel}" = "xsolaris"; then AC_DEFINE($2[]SOLARIS, 1, [$1 OS kernel]) fi if test "x${app_cv_[]$1[]_os_kernel}" = "xdarwin"; then AC_DEFINE($2[]DARWIN, 1, [$1 OS kernel]) fi if test "x${app_cv_[]$1[]_os_kernel}" = "xqnx"; then AC_DEFINE($2[]QNX, 1, [$1 OS kernel]) fi if test "x${app_cv_[]$1[]_os_kernel}" = "xunknown"; then AC_DEFINE($2[]UNKNOWN, 1, [$1 OS kernel]) fi # Export to Makefile.am's. AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_LINUX, [test "x${app_cv_[]$1[]_os_kernel}" = "xlinux"]) AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_WINDOWS32, [test "x${app_cv_[]$1[]_os_kernel}" = "xwindows32"]) AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_WINDOWS64, [test "x${app_cv_[]$1[]_os_kernel}" = "xwindows64"]) AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_INTERIX, [test "x${app_cv_[]$1[]_os_kernel}" = "xinterix"]) AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_FREEBSD, [test "x${app_cv_[]$1[]_os_kernel}" = "xfreebsd"]) AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_DRAGONFLY, [test "x${app_cv_[]$1[]_os_kernel}" = "xdragonfly"]) AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_OPENBSD, [test "x${app_cv_[]$1[]_os_kernel}" = "xopenbsd"]) AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_NETBSD, [test "x${app_cv_[]$1[]_os_kernel}" = "xnetbsd"]) AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_SOLARIS, [test "x${app_cv_[]$1[]_os_kernel}" = "xsolaris"]) AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_DARWIN, [test "x${app_cv_[]$1[]_os_kernel}" = "xdarwin"]) AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_QNX, [test "x${app_cv_[]$1[]_os_kernel}" = "xqnx"]) AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_UNKNOWN, [test "x${app_cv_[]$1[]_os_kernel}" = "xunknown"]) # composite ones, for easy checking AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_FAMILY_WINDOWS, \ [test "x${app_cv_[]$1[]_os_kernel}" = "xwindows32" || test "x${app_cv_[]$1[]_os_kernel}" = "xwindows64" \ || test "x${app_cv_[]$1[]_os_kernel}" = "xinterix"]) if test "x${app_cv_[]$1[]_os_kernel}" = "xwindows32" || test "x${app_cv_[]$1[]_os_kernel}" = "xwindows64" \ || test "x${app_cv_[]$1[]_os_kernel}" = "xinterix"; then AC_DEFINE($2[]FAMILY_WINDOWS, 1, [$1 OS kernel family]) fi AC_MSG_RESULT([${app_cv_[]$1[]_os_kernel}]) ]) # APP_DETECT_OS_ENV(triplet_name, define_macro_prefix) # Check target OS userland environment (mainly glibc). # triplet_name is target, host or build. # You must call AX_COMPILER_VENDOR before using this macro. # If you call # APP_DETECT_OS_ENV([target], [COMPILER], [CONFIG_OS_ENV]) # you will get CONFIG_OS_ENV_LINUX C macro exported to config.h # (on gcc/linux). # If no define_macro_prefix is specified, no C macro prefix will be used. # app_cv_target_os_env will be set to env name (lowercase). # app_cv_target_os_env_macro will be set to env name (all caps). # TARGET_OS_ENV_LINUX automake conditional will be set to true. AC_DEFUN([APP_DETECT_OS_ENV], [ # These requirements check compile-time presence, not that they were called AC_REQUIRE([AX_COMPILER_VENDOR]) AC_MSG_CHECKING([for $1 OS userland environment]) app_cv_[]$1[]_os_env=""; app_cv_[]$1[]_os_env_macro=""; # If vendor is not recognized, it's set to "unknown". # NOTE: This works only on target compilers! if test "x${ax_cv_cxx_compiler_vendor}" = "x"; then # msg_result is needed to print the checking... message. AC_MSG_RESULT([error]) AC_MSG_ERROR([[ax_cv_cxx_compiler_vendor] not set.]) fi # "build" - the type of system on which the package is being configured and compiled. # "host" - the type of system on which the package runs. Specifying it enables the # cross-compilation mode. "target" defaults to this too. if test "x$1" != "xbuild" && test "x$1" != "xhost" && test "x$1" != "xtarget"; then # msg_result is needed to print the checking... message. AC_MSG_RESULT([error]) AC_MSG_ERROR([[Triplet name must be build, host or target.]]) fi case "$$1" in *cygwin*) app_cv_[]$1[]_os_env="cygwin"; app_cv_[]$1[]_os_env_macro="CYGWIN"; ;; i*86-*mingw*) app_cv_[]$1[]_os_env="mingw32"; app_cv_[]$1[]_os_env_macro="MINGW32"; ;; x86_64-*-mingw*) app_cv_[]$1[]_os_env="mingw64"; app_cv_[]$1[]_os_env_macro="MINGW64"; ;; *interix*) app_cv_[]$1[]_os_env="interix"; app_cv_[]$1[]_os_env_macro="INTERIX"; ;; # This includes gnu/linux, debian gnu/kfreebsd, debian gnu/netbsd... *-gnu | gnu*) app_cv_[]$1[]_os_env="gnu"; app_cv_[]$1[]_os_env_macro="GNU"; ;; # Freebsd libc *freebsd*) app_cv_[]$1[]_os_env="freebsd"; app_cv_[]$1[]_os_env_macro="FREEBSD"; ;; *dragonfly*) app_cv_[]$1[]_os_env="dragonfly"; app_cv_[]$1[]_os_env_macro="DRAGONFLY"; ;; *openbsd*) app_cv_[]$1[]_os_env="openbsd"; app_cv_[]$1[]_os_env_macro="OPENBSD"; ;; *netbsd*) app_cv_[]$1[]_os_env="netbsd"; app_cv_[]$1[]_os_env_macro="NETBSD"; ;; *solaris*) app_cv_[]$1[]_os_env="solaris"; app_cv_[]$1[]_os_env_macro="SOLARIS"; ;; *darwin*) app_cv_[]$1[]_os_env="darwin"; app_cv_[]$1[]_os_env_macro="DARWIN"; ;; *qnx*) app_cv_[]$1[]_os_env="qnx"; app_cv_[]$1[]_os_env_macro="QNX"; ;; *) app_cv_[]$1[]_os_env="unknown"; app_cv_[]$1[]_os_env_macro="UNKNOWN"; ;; esac # Assign this even if not using flags - we may use this in configure itself. if test "x$2" != "x"; then app_cv_[]$1[]_os_env_macro="$2[]_${app_cv_[]$1[]_os_env_macro}"; fi # config.h macros (yes, we need this many. otherwise config.h will be incorrect). if test "x${app_cv_[]$1[]_os_env}" = "xcygwin"; then AC_DEFINE($2[]CYGWIN, 1, [$1 OS userspace environment]) fi if test "x${app_cv_[]$1[]_os_env}" = "xmingw32"; then AC_DEFINE($2[]MINGW32, 1, [$1 OS userspace environment]) fi if test "x${app_cv_[]$1[]_os_env}" = "xmingw64"; then AC_DEFINE($2[]MINGW64, 1, [$1 OS userspace environment]) fi if test "x${app_cv_[]$1[]_os_env}" = "xinterix"; then AC_DEFINE($2[]INTERIX, 1, [$1 OS userspace environment]) fi if test "x${app_cv_[]$1[]_os_env}" = "xgnu"; then AC_DEFINE($2[]GNU, 1, [$1 OS userspace environment]) fi if test "x${app_cv_[]$1[]_os_env}" = "xfreebsd"; then AC_DEFINE($2[]FREEBSD, 1, [$1 OS userspace environment]) fi if test "x${app_cv_[]$1[]_os_env}" = "xdragonfly"; then AC_DEFINE($2[]DRAGONFLY, 1, [$1 OS userspace environment]) fi if test "x${app_cv_[]$1[]_os_env}" = "xopenbsd"; then AC_DEFINE($2[]OPENBSD, 1, [$1 OS userspace environment]) fi if test "x${app_cv_[]$1[]_os_env}" = "xnetbsd"; then AC_DEFINE($2[]NETBSD, 1, [$1 OS userspace environment]) fi if test "x${app_cv_[]$1[]_os_env}" = "xsolaris"; then AC_DEFINE($2[]SOLARIS, 1, [$1 OS userspace environment]) fi if test "x${app_cv_[]$1[]_os_env}" = "xdarwin"; then AC_DEFINE($2[]DARWIN, 1, [$1 OS userspace environment]) fi if test "x${app_cv_[]$1[]_os_env}" = "xqnx"; then AC_DEFINE($2[]QNX, 1, [$1 OS userspace environment]) fi if test "x${app_cv_[]$1[]_os_env}" = "xunknown"; then AC_DEFINE($2[]UNKNOWN, 1, [$1 OS userspace environment]) fi # Export to Makefile.am's. AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_CYGWIN, [test "x${app_cv_[]$1[]_os_env}" = "xcygwin"]) AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_MINGW32, [test "x${app_cv_[]$1[]_os_env}" = "xmingw32"]) AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_MINGW64, [test "x${app_cv_[]$1[]_os_env}" = "xmingw64"]) AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_INTERIX, [test "x${app_cv_[]$1[]_os_env}" = "xinterix"]) AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_GNU, [test "x${app_cv_[]$1[]_os_env}" = "xgnu"]) AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_FREEBSD, [test "x${app_cv_[]$1[]_os_env}" = "xfreebsd"]) AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_DRAGONFLY, [test "x${app_cv_[]$1[]_os_env}" = "xdragonfly"]) AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_OPENBSD, [test "x${app_cv_[]$1[]_os_env}" = "xopenbsd"]) AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_NETBSD, [test "x${app_cv_[]$1[]_os_env}" = "xnetbsd"]) AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_SOLARIS, [test "x${app_cv_[]$1[]_os_env}" = "xsolaris"]) AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_DARWIN, [test "x${app_cv_[]$1[]_os_env}" = "xdarwin"]) AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_QNX, [test "x${app_cv_[]$1[]_os_env}" = "xqnx"]) AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_UNKNOWN, [test "x${app_cv_[]$1[]_os_env}" = "xunknown"]) AC_MSG_RESULT([${app_cv_[]$1[]_os_env}]) ]) gsmartcontrol-1.1.4/autoconf.m4/app_get_mt_flags.m4000066400000000000000000000173011417717230600223170ustar00rootroot00000000000000 ############################################################################ # Copyright: # (C) 2008 - 2009 Alexander Shaduri # License: See LICENSE_zlib.txt file ############################################################################ # APP_GET_MT_FLAGS(flags_prefix[, action-if-not-found]) # This macro detects compiler flags which are needed to compile a multi-threaded # (preferably pthreads-based) applications on target OS. flags_prefixCFLAGS, # flags_prefixCXXFLAGS and flags_prefixLIBS will be initialized (or appended to) # with the correct flags. # You must call AX_COMPILER_VENDOR, APP_DETECT_OS_KERNEL([target], ...) # and APP_DETECT_OS_ENV([target], ...) before using this macro. AC_DEFUN([APP_GET_MT_FLAGS], [ # These requirements check compile-time presence, not that they were called AC_REQUIRE([AX_COMPILER_VENDOR]) AC_REQUIRE([APP_DETECT_OS_KERNEL]) AC_REQUIRE([APP_DETECT_OS_ENV]) AC_MSG_CHECKING([for target thread support flags]) app_cv_target_thread_cflags=""; app_cv_target_thread_cxxflags=""; app_cv_target_thread_libs=""; app_cv_target_thread_found="no"; # If vendor is not recognized, it's set to "unknown". # NOTE: This works only on target compilers! if test "x${ax_cv_cxx_compiler_vendor}" = "x"; then # msg_result is needed to print the checking... message. AC_MSG_RESULT([error]) AC_MSG_ERROR([[ax_cv_cxx_compiler_vendor] not set.]) fi if test "x${app_cv_target_os_kernel}" = "x"; then # msg_result is needed to print the checking... message. AC_MSG_RESULT([error]) AC_MSG_ERROR([[app_cv_target_os_kernel] not set.]) fi if test "x${app_cv_target_os_env}" = "x"; then # msg_result is needed to print the checking... message. AC_MSG_RESULT([error]) AC_MSG_ERROR([[app_cv_target_os_env] not set.]) fi # Compilers use at least 3 defines to enable thread-safe support: # _THREAD_SAFE, _MT, _REENTRANT. # We use the ones that are required for detected platform. case "$app_cv_target_os_kernel" in linux) # glibc manual says that defining either _REENTRANT or _THREAD_SAFE # enables some thread-safe implementations of glibc functions. if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "clang" \ || test "$ax_cv_cxx_compiler_vendor" = "intel" || test "$ax_cv_cxx_compiler_vendor" = "pathscale"; then app_cv_target_thread_cflags="-pthread -D_MT -D_THREAD_SAFE" app_cv_target_thread_cxxflags="-pthread -D_MT -D_THREAD_SAFE" app_cv_target_thread_libs="-pthread" app_cv_target_thread_found="yes"; elif test "$ax_cv_cxx_compiler_vendor" = "sun"; then # suncc's -mt expands to: # "-D_REENTRANT -lpthread" on Linux, uses pthreads (pthread.h); # "-D_REENTRANT -lthread" on Solaris, uses Solaris threads (thread.h). # To use pthreads on Solaris, use "-mt -lpthread". # http://docs.sun.com/app/docs/doc/820-7598/bjapp?a=view app_cv_target_thread_cflags="-mt" app_cv_target_thread_cxxflags="-mt" app_cv_target_thread_libs="-mt" app_cv_target_thread_found="yes"; fi ;; windows*) # NOTE: Not sure about cygwin or clang. # NOTE: This only enables multithreaded code generation. For linking # with pthreads-win32 you need additional flags. # gcc man page: -mthreads: Support thread-safe exception handling on Mingw32. # -mthreads defines -D_MT, and links with -lmingwthrd. if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "clang"; then app_cv_target_thread_cflags="-mthreads -D_THREAD_SAFE" app_cv_target_thread_cxxflags="-mthreads -D_THREAD_SAFE" app_cv_target_thread_libs="-mthreads" app_cv_target_thread_found="yes" fi ;; interix) # Interix kernel is implemented on top of windows kernel. # Interix uses -D_REENTRANT for pthreads, automatically enabling -lpthread. if test "$ax_cv_cxx_compiler_vendor" = "gnu"; then app_cv_target_thread_cflags="-D_REENTRANT" app_cv_target_thread_cxxflags="-D_REENTRANT" app_cv_target_thread_found="yes" fi ;; freebsd | dragonfly) # NOTE: Not sure about debian gnu/kfreebsd and gnu/netbsd. # Freebsd had -kthread, but it has been discontinued (afaik). # For freebsd -pthread replaces libc with libc_r. if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "clang"; then app_cv_target_thread_cflags="-pthread -D_MT -D_THREAD_SAFE" app_cv_target_thread_cxxflags="-pthread -D_MT -D_THREAD_SAFE" app_cv_target_thread_libs="-pthread" app_cv_target_thread_found="yes"; fi ;; openbsd) if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "clang"; then app_cv_target_thread_cflags="-pthread -D_REENTRANT" app_cv_target_thread_cxxflags="-pthread -D_REENTRANT" app_cv_target_thread_libs="-pthread" app_cv_target_thread_found="yes"; fi ;; netbsd) # See sys/featuretest.h if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "clang"; then app_cv_target_thread_cflags="-pthread -D_REENTRANT" app_cv_target_thread_cxxflags="-pthread -D_REENTRANT" app_cv_target_thread_libs="-pthread" app_cv_target_thread_found="yes"; fi ;; solaris) if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "clang"; then # gnu documentation mentions -threads (and its aliases, -pthreads and -pthread). # _REENTRANT is needed to enable some thread-safe equivalents of standard # functions. # http://docs.sun.com/app/docs/doc/805-8005-04/6j7hcvqs4?l=ko&a=view app_cv_target_thread_cflags="-pthreads -D_MT -D_THREAD_SAFE -D_REENTRANT" app_cv_target_thread_cxxflags="-pthreads -D_MT -D_THREAD_SAFE -D_REENTRANT" app_cv_target_thread_libs="-pthreads" app_cv_target_thread_found="yes" elif test "$ax_cv_cxx_compiler_vendor" = "sun"; then # suncc's -mt expands to: # "-D_REENTRANT -lpthread" on Linux, uses pthreads (pthread.h); # "-D_REENTRANT -lthread" on Solaris, uses Solaris threads (thread.h). # To use pthreads on Solaris, use "-mt -lpthread". # http://docs.sun.com/app/docs/doc/820-7598/bjapp?a=view # http://docs.sun.com/app/docs/doc/816-5175/pthreads-5?a=view # http://docs.sun.com/app/docs/doc/816-5137/compile-74765?a=view # We use POSIX threads, so append -lpthread. app_cv_target_thread_cflags="-mt -lpthread" app_cv_target_thread_cxxflags="-mt -lpthread" app_cv_target_thread_libs="-mt -lpthread" app_cv_target_thread_found="yes" fi ;; darwin) if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "clang"; then # Darwin produces mt code by default, and -pthread is an error. app_cv_target_thread_found="yes" fi ;; qnx) if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "clang"; then # afaik, gcc/qnx produces mt code by default (at least on newer qnx releases), # so no flags are needed. app_cv_target_thread_found="yes" fi ;; esac if test "$app_cv_target_thread_found" = "no"; then AC_MSG_WARN([Cannot detect compiler thread support. Set CFLAGS, CXXFLAGS and LIBS manually.]) m4_ifvaln([$2],[$2])dnl else AC_MSG_RESULT([CFLAGS: $app_cv_target_thread_cflags; CXXFLAGS: $app_cv_target_thread_cxxflags; LIBS: $app_cv_target_thread_libs]) if test "x$$1[]CFLAGS" = "x"; then $1[]CFLAGS="$app_cv_target_thread_cflags"; else $1[]CFLAGS="$$1[]CFLAGS $app_cv_target_thread_cflags"; fi if test "x$$1[]CXXFLAGS" = "x"; then $1[]CXXFLAGS="$app_cv_target_thread_cxxflags"; else $1[]CXXFLAGS="$$1[]CXXFLAGS $app_cv_target_thread_cxxflags"; fi if test "x$$1[]LIBS" = "x"; then $1[]LIBS="$app_cv_target_thread_libs"; else $1[]LIBS="$$1[]LIBS $app_cv_target_thread_libs"; fi fi ]) gsmartcontrol-1.1.4/autoconf.m4/app_use_system_extensions.m4000066400000000000000000000117011417717230600243410ustar00rootroot00000000000000 ############################################################################ # Copyright: # (C) 2008 - 2009 Alexander Shaduri # License: See LICENSE_zlib.txt file ############################################################################ # APP_USE_SYSTEM_EXTENSIONS([flags_prefix]) # This macro detects compiler flags which are needed to enable # system-specific library extensions. For example, on GNU platforms # it's -D_GNU_SOURCE, on solaris it's -D__EXTENSIONS__, etc... # Basically, it's the same as AC_USE_SYSTEM_EXTENSIONS, # but uses compiler flag approach instead of config.h. # The extensions used bring the system closer to the GNU, # X/Open (a superset of POSIX) and POSIX systems. # You must call AX_COMPILER_VENDOR # and APP_DETECT_OS_ENV([target], ...) before using this macro. # More info: # http://www.gnu.org/software/autoconf/manual/autoconf.html#Posix-Variants # http://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html # http://gcc.gnu.org/onlinedocs/libstdc++/faq.html#faq.predefined # http://docs.sun.com/app/docs/doc/817-3946/6mjgmt4o8?l=en&a=view AC_DEFUN([APP_USE_SYSTEM_EXTENSIONS], [ # These requirements check compile-time presence, not that they were called AC_REQUIRE([AX_COMPILER_VENDOR]) AC_REQUIRE([APP_DETECT_OS_ENV]) AC_MSG_CHECKING([for target system extension flags]) app_cv_target_extension_cflags=""; app_cv_target_extension_cxxflags=""; app_cv_target_extension_libs=""; # If vendor is not recognized, it's set to "unknown". # NOTE: This works only on target compilers! if test "x${ax_cv_cxx_compiler_vendor}" = "x"; then # msg_result is needed to print the checking... message. AC_MSG_RESULT([error]) AC_MSG_ERROR([[ax_cv_cxx_compiler_vendor] not set.]) fi if test "x${app_cv_target_os_kernel}" = "x"; then # msg_result is needed to print the checking... message. AC_MSG_RESULT([error]) AC_MSG_ERROR([[app_cv_target_os_kernel] not set.]) fi if test "x${app_cv_target_os_env}" = "x"; then # msg_result is needed to print the checking... message. AC_MSG_RESULT([error]) AC_MSG_ERROR([[app_cv_target_os_env] not set.]) fi case "$app_cv_target_os_env" in gnu) # glibc all-extensions macro: _GNU_SOURCE. # gcc automatically defines it in later versions. # suncc needs the flag explicitly. # http://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html app_cv_target_extension_cflags="-D_GNU_SOURCE" app_cv_target_extension_cxxflags="-D_GNU_SOURCE" ;; solaris) # The problem with solaris is that it has too many incompatible # feature test macros, and there is no enable-all macro. Autoconf # assumes that __EXTENSIONS__ is the one, but it should be defined # _in addition_ to feature test macros. Note that on its own, __EXTENSIONS__ # still enables some useful stuff like _LARGEFILE64_SOURCE. # To support some posix macros, additional link objects are also required. # Some *_SOURCE feature test macros may be incompatible with C++. # http://docs.sun.com/app/docs/doc/816-5175/standards-5?a=view app_cv_target_extension_cflags="-D__EXTENSIONS__" app_cv_target_extension_cxxflags="-D__EXTENSIONS__" ;; netbsd) # There is no enable-all macro. # (_XOPEN_SOURCE >= 500 || _NETBSD_SOURCE) is recommended. # The default is _NETBSD_SOURCE, _XOPEN_SOURCE overrides it. # The manual says that rename() will behave posix-like if _POSIX_C_SOURCE # is defined. However, stdio.h uses (_XOPEN_SOURCE <= 2) condition, # which is really bogus. # See sys/featuretest.h ;; mingw*) # This enables standards-compliant stdio behaviour (regarding printf and # friends), as opposed to msvc-compatible one. This is usually enabled # by default if one of the usual macros are encountered (_XOPEN_SOURCE, # _GNU_SOURCE, etc...). # Also, we make the win2k API available (includes console functions, etc...). # Other things to consider: _NO_OLDNAMES (disable "deprecated" # non-underscored names like chdir (use _chdir), etc...). # See _mingw.h for details. app_cv_target_extension_cflags="-D__USE_MINGW_ANSI_STDIO=1 -DWINVER=0x0600" # Since Vista app_cv_target_extension_cxxflags="-D__USE_MINGW_ANSI_STDIO=1 -DWINVER=0x0600" ;; interix) # As per AC_USE_SYSTEM_EXTENSIONS. app_cv_target_extension_cflags="-D_ALL_SOURCE" app_cv_target_extension_cxxflags="-D_ALL_SOURCE" ;; esac AC_MSG_RESULT([CFLAGS: $app_cv_target_extension_cflags; CXXFLAGS: $app_cv_target_extension_cxxflags; LIBS: $app_cv_target_extension_libs]) if test "x$$1[]CFLAGS" = "x"; then $1[]CFLAGS="$app_cv_target_extension_cflags"; else $1[]CFLAGS="$$1[]CFLAGS $app_cv_target_extension_cflags"; fi if test "x$$1[]CXXFLAGS" = "x"; then $1[]CXXFLAGS="$app_cv_target_extension_cxxflags"; else $1[]CXXFLAGS="$$1[]CXXFLAGS $app_cv_target_extension_cxxflags"; fi if test "x$$1[]LIBS" = "x"; then $1[]LIBS="$app_cv_target_extension_libs"; else $1[]LIBS="$$1[]LIBS $app_cv_target_extension_libs"; fi ]) gsmartcontrol-1.1.4/autoconf.m4/ax_compiler_vendor.m4000066400000000000000000000061031417717230600227010ustar00rootroot00000000000000# =========================================================================== # http://autoconf-archive.cryp.to/ax_compiler_vendor.html # =========================================================================== # # SYNOPSIS # # AX_COMPILER_VENDOR # # DESCRIPTION # # Determine the vendor of the C/C++ compiler, e.g., gnu, intel, ibm, sun, # hp, borland, comeau, dec, cray, kai, lcc, metrowerks, sgi, microsoft, # watcom, etc. The vendor is returned in the cache variable # $ax_cv_c_compiler_vendor for C and $ax_cv_cxx_compiler_vendor for C++. # # LAST MODIFICATION # # 2008-04-12 # # COPYLEFT # # Copyright (c) 2008 Steven G. Johnson # Copyright (c) 2008 Matteo Frigo # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Macro Archive. When you make and # distribute a modified version of the Autoconf Macro, you may extend this # special exception to the GPL to apply to your modified version as well. AC_DEFUN([AX_COMPILER_VENDOR], [ AC_CACHE_CHECK([for _AC_LANG compiler vendor], ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor, [ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor=unknown # note: don't check for gcc first since some other compilers define __GNUC__ for ventest in clang:__clang__ intel:__ICC,__ECC,__INTEL_COMPILER ibm:__xlc__,__xlC__,__IBMC__,__IBMCPP__ pathscale:__PATHCC__,__PATHSCALE__ gnu:__GNUC__ sun:__SUNPRO_C,__SUNPRO_CC hp:__HP_cc,__HP_aCC dec:__DECC,__DECCXX,__DECC_VER,__DECCXX_VER borland:__BORLANDC__,__TURBOC__ comeau:__COMO__ cray:_CRAYC kai:__KCC lcc:__LCC__ metrowerks:__MWERKS__ sgi:__sgi,sgi microsoft:_MSC_VER watcom:__WATCOMC__ portland:__PGI; do vencpp="defined("`echo $ventest | cut -d: -f2 | sed 's/,/) || defined(/g'`")" AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,[ #if !($vencpp) thisisanerror; #endif ])], [ax_cv_]_AC_LANG_ABBREV[_compiler_vendor=`echo $ventest | cut -d: -f1`; break]) done ]) ]) gsmartcontrol-1.1.4/autoconf.m4/ax_cxx_compile_stdcxx.m4000066400000000000000000000474241417717230600234340ustar00rootroot00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html # =========================================================================== # # SYNOPSIS # # AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) # # DESCRIPTION # # Check for baseline language coverage in the compiler for the specified # version of the C++ standard. If necessary, add switches to CXX and # CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) # or '14' (for the C++14 standard). # # The second argument, if specified, indicates whether you insist on an # extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. # -std=c++11). If neither is specified, you get whatever works, with # preference for an extended mode. # # The third argument, if specified 'mandatory' or if left unspecified, # indicates that baseline support for the specified C++ standard is # required and that the macro should error out if no mode with that # support is found. If specified 'optional', then configuration proceeds # regardless, after defining HAVE_CXX${VERSION} if and only if a # supporting mode is found. # # LICENSE # # Copyright (c) 2008 Benjamin Kosnik # Copyright (c) 2012 Zack Weinberg # Copyright (c) 2013 Roy Stogner # Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov # Copyright (c) 2015 Paul Norman # Copyright (c) 2015 Moritz Klammler # Copyright (c) 2016 Krzesimir Nowak # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 7 dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro dnl (serial version number 13). AX_REQUIRE_DEFINED([AC_MSG_WARN]) AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], [$1], [14], [ax_cxx_compile_alternatives="14 1y"], [$1], [17], [ax_cxx_compile_alternatives="17 1z"], [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl m4_if([$2], [], [], [$2], [ext], [], [$2], [noext], [], [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], [$3], [optional], [ax_cxx_compile_cxx$1_required=false], [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) AC_LANG_PUSH([C++])dnl ac_success=no AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, ax_cv_cxx_compile_cxx$1, [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [ax_cv_cxx_compile_cxx$1=yes], [ax_cv_cxx_compile_cxx$1=no])]) if test x$ax_cv_cxx_compile_cxx$1 = xyes; then ac_success=yes fi m4_if([$2], [noext], [], [dnl if test x$ac_success = xno; then for alternative in ${ax_cxx_compile_alternatives}; do switch="-std=gnu++${alternative}" cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, $cachevar, [ac_save_CXX="$CXX" CXX="$CXX $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [eval $cachevar=yes], [eval $cachevar=no]) CXX="$ac_save_CXX"]) if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done fi]) m4_if([$2], [ext], [], [dnl if test x$ac_success = xno; then dnl HP's aCC needs +std=c++11 according to: dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf dnl Cray's crayCC needs "-h std=c++11" for alternative in ${ax_cxx_compile_alternatives}; do for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, $cachevar, [ac_save_CXX="$CXX" CXX="$CXX $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [eval $cachevar=yes], [eval $cachevar=no]) CXX="$ac_save_CXX"]) if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done if test x$ac_success = xyes; then break fi done fi]) AC_LANG_POP([C++]) if test x$ax_cxx_compile_cxx$1_required = xtrue; then if test x$ac_success = xno; then AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) fi fi if test x$ac_success = xno; then HAVE_CXX$1=0 AC_MSG_NOTICE([No compiler with C++$1 support was found]) else HAVE_CXX$1=1 AC_DEFINE(HAVE_CXX$1,1, [define if the compiler supports basic C++$1 syntax]) fi AC_SUBST(HAVE_CXX$1) m4_if([$1], [17], [AC_MSG_WARN([C++17 is not yet standardized, so the checks may change in incompatible ways anytime])]) ]) dnl Test body for checking C++11 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 ) dnl Test body for checking C++14 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 ) m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 ) dnl Tests for new features in C++11 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ // If the compiler admits that it is not ready for C++11, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201103L #error "This is not a C++11 compiler" #else namespace cxx11 { namespace test_static_assert { template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; } namespace test_final_override { struct Base { virtual void f() {} }; struct Derived : public Base { virtual void f() override {} }; } namespace test_double_right_angle_brackets { template < typename T > struct check {}; typedef check single_type; typedef check> double_type; typedef check>> triple_type; typedef check>>> quadruple_type; } namespace test_decltype { int f() { int a = 1; decltype(a) b = 2; return a + b; } } namespace test_type_deduction { template < typename T1, typename T2 > struct is_same { static const bool value = false; }; template < typename T > struct is_same { static const bool value = true; }; template < typename T1, typename T2 > auto add(T1 a1, T2 a2) -> decltype(a1 + a2) { return a1 + a2; } int test(const int c, volatile int v) { static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == false, ""); auto ac = c; auto av = v; auto sumi = ac + av + 'x'; auto sumf = ac + av + 1.0; static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == true, ""); return (sumf > 0.0) ? sumi : add(c, v); } } namespace test_noexcept { int f() { return 0; } int g() noexcept { return 0; } static_assert(noexcept(f()) == false, ""); static_assert(noexcept(g()) == true, ""); } namespace test_constexpr { template < typename CharT > unsigned long constexpr strlen_c_r(const CharT *const s, const unsigned long acc) noexcept { return *s ? strlen_c_r(s + 1, acc + 1) : acc; } template < typename CharT > unsigned long constexpr strlen_c(const CharT *const s) noexcept { return strlen_c_r(s, 0UL); } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("1") == 1UL, ""); static_assert(strlen_c("example") == 7UL, ""); static_assert(strlen_c("another\0example") == 7UL, ""); } namespace test_rvalue_references { template < int N > struct answer { static constexpr int value = N; }; answer<1> f(int&) { return answer<1>(); } answer<2> f(const int&) { return answer<2>(); } answer<3> f(int&&) { return answer<3>(); } void test() { int i = 0; const int c = 0; static_assert(decltype(f(i))::value == 1, ""); static_assert(decltype(f(c))::value == 2, ""); static_assert(decltype(f(0))::value == 3, ""); } } namespace test_uniform_initialization { struct test { static const int zero {}; static const int one {1}; }; static_assert(test::zero == 0, ""); static_assert(test::one == 1, ""); } namespace test_lambdas { void test1() { auto lambda1 = [](){}; auto lambda2 = lambda1; lambda1(); lambda2(); } int test2() { auto a = [](int i, int j){ return i + j; }(1, 2); auto b = []() -> int { return '0'; }(); auto c = [=](){ return a + b; }(); auto d = [&](){ return c; }(); auto e = [a, &b](int x) mutable { const auto identity = [](int y){ return y; }; for (auto i = 0; i < a; ++i) a += b--; return x + identity(a + b); }(0); return a + b + c + d + e; } int test3() { const auto nullary = [](){ return 0; }; const auto unary = [](int x){ return x; }; using nullary_t = decltype(nullary); using unary_t = decltype(unary); const auto higher1st = [](nullary_t f){ return f(); }; const auto higher2nd = [unary](nullary_t f1){ return [unary, f1](unary_t f2){ return f2(unary(f1())); }; }; return higher1st(nullary) + higher2nd(nullary)(unary); } } namespace test_variadic_templates { template struct sum; template struct sum { static constexpr auto value = N0 + sum::value; }; template <> struct sum<> { static constexpr auto value = 0; }; static_assert(sum<>::value == 0, ""); static_assert(sum<1>::value == 1, ""); static_assert(sum<23>::value == 23, ""); static_assert(sum<1, 2>::value == 3, ""); static_assert(sum<5, 5, 11>::value == 21, ""); static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); } // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function // because of this. namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } } // namespace cxx11 #endif // __cplusplus >= 201103L ]]) dnl Tests for new features in C++14 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ // If the compiler admits that it is not ready for C++14, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201402L #error "This is not a C++14 compiler" #else namespace cxx14 { namespace test_polymorphic_lambdas { int test() { const auto lambda = [](auto&&... args){ const auto istiny = [](auto x){ return (sizeof(x) == 1UL) ? 1 : 0; }; const int aretiny[] = { istiny(args)... }; return aretiny[0]; }; return lambda(1, 1L, 1.0f, '1'); } } namespace test_binary_literals { constexpr auto ivii = 0b0000000000101010; static_assert(ivii == 42, "wrong value"); } namespace test_generalized_constexpr { template < typename CharT > constexpr unsigned long strlen_c(const CharT *const s) noexcept { auto length = 0UL; for (auto p = s; *p; ++p) ++length; return length; } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("x") == 1UL, ""); static_assert(strlen_c("test") == 4UL, ""); static_assert(strlen_c("another\0test") == 7UL, ""); } namespace test_lambda_init_capture { int test() { auto x = 0; const auto lambda1 = [a = x](int b){ return a + b; }; const auto lambda2 = [a = lambda1(x)](){ return a; }; return lambda2(); } } namespace test_digit_separators { constexpr auto ten_million = 100'000'000; static_assert(ten_million == 100000000, ""); } namespace test_return_type_deduction { auto f(int& x) { return x; } decltype(auto) g(int& x) { return x; } template < typename T1, typename T2 > struct is_same { static constexpr auto value = false; }; template < typename T > struct is_same { static constexpr auto value = true; }; int test() { auto x = 0; static_assert(is_same::value, ""); static_assert(is_same::value, ""); return x; } } } // namespace cxx14 #endif // __cplusplus >= 201402L ]]) dnl Tests for new features in C++17 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ // If the compiler admits that it is not ready for C++17, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus <= 201402L #error "This is not a C++17 compiler" #else #if defined(__clang__) #define REALLY_CLANG #else #if defined(__GNUC__) #define REALLY_GCC #endif #endif #include #include #include namespace cxx17 { #if !defined(REALLY_CLANG) namespace test_constexpr_lambdas { // TODO: test it with clang++ from git constexpr int foo = [](){return 42;}(); } #endif // !defined(REALLY_CLANG) namespace test::nested_namespace::definitions { } namespace test_fold_expression { template int multiply(Args... args) { return (args * ... * 1); } template bool all(Args... args) { return (args && ...); } } namespace test_extended_static_assert { static_assert (true); } namespace test_auto_brace_init_list { auto foo = {5}; auto bar {5}; static_assert(std::is_same, decltype(foo)>::value); static_assert(std::is_same::value); } namespace test_typename_in_template_template_parameter { template typename X> struct D; } namespace test_fallthrough_nodiscard_maybe_unused_attributes { int f1() { return 42; } [[nodiscard]] int f2() { [[maybe_unused]] auto unused = f1(); switch (f1()) { case 17: f1(); [[fallthrough]]; case 42: f1(); } return f1(); } } namespace test_extended_aggregate_initialization { struct base1 { int b1, b2 = 42; }; struct base2 { base2() { b3 = 42; } int b3; }; struct derived : base1, base2 { int d; }; derived d1 {{1, 2}, {}, 4}; // full initialization derived d2 {{}, {}, 4}; // value-initialized bases } namespace test_general_range_based_for_loop { struct iter { int i; int& operator* () { return i; } const int& operator* () const { return i; } iter& operator++() { ++i; return *this; } }; struct sentinel { int i; }; bool operator== (const iter& i, const sentinel& s) { return i.i == s.i; } bool operator!= (const iter& i, const sentinel& s) { return !(i == s); } struct range { iter begin() const { return {0}; } sentinel end() const { return {5}; } }; void f() { range r {}; for (auto i : r) { [[maybe_unused]] auto v = i; } } } namespace test_lambda_capture_asterisk_this_by_value { struct t { int i; int foo() { return [*this]() { return i; }(); } }; } namespace test_enum_class_construction { enum class byte : unsigned char {}; byte foo {42}; } namespace test_constexpr_if { template int f () { if constexpr(cond) { return 13; } else { return 42; } } } namespace test_selection_statement_with_initializer { int f() { return 13; } int f2() { if (auto i = f(); i > 0) { return 3; } switch (auto i = f(); i + 4) { case 17: return 2; default: return 1; } } } #if !defined(REALLY_CLANG) namespace test_template_argument_deduction_for_class_templates { // TODO: test it with clang++ from git template struct pair { pair (T1 p1, T2 p2) : m1 {p1}, m2 {p2} {} T1 m1; T2 m2; }; void f() { [[maybe_unused]] auto p = pair{13, 42u}; } } #endif // !defined(REALLY_CLANG) namespace test_non_type_auto_template_parameters { template struct B {}; B<5> b1; B<'a'> b2; } #if !defined(REALLY_CLANG) namespace test_structured_bindings { // TODO: test it with clang++ from git int arr[2] = { 1, 2 }; std::pair pr = { 1, 2 }; auto f1() -> int(&)[2] { return arr; } auto f2() -> std::pair& { return pr; } struct S { int x1 : 2; volatile double y1; }; S f3() { return {}; } auto [ x1, y1 ] = f1(); auto& [ xr1, yr1 ] = f1(); auto [ x2, y2 ] = f2(); auto& [ xr2, yr2 ] = f2(); const auto [ x3, y3 ] = f3(); } #endif // !defined(REALLY_CLANG) #if !defined(REALLY_CLANG) namespace test_exception_spec_type_system { // TODO: test it with clang++ from git struct Good {}; struct Bad {}; void g1() noexcept; void g2(); template Bad f(T*, T*); template Good f(T1*, T2*); static_assert (std::is_same_v); } #endif // !defined(REALLY_CLANG) namespace test_inline_variables { template void f(T) {} template inline T g(T) { return T{}; } template<> inline void f<>(int) {} template<> int g<>(int) { return 5; } } } // namespace cxx17 #endif // __cplusplus <= 201402L ]]) gsmartcontrol-1.1.4/autoconf.m4/ax_cxx_gcc_abi_demangle.m4000066400000000000000000000034031417717230600235770ustar00rootroot00000000000000# =========================================================================== # http://autoconf-archive.cryp.to/ax_cxx_gcc_abi_demangle.html # =========================================================================== # # SYNOPSIS # # AX_CXX_GCC_ABI_DEMANGLE # # DESCRIPTION # # If the compiler supports GCC C++ ABI name demangling (has header # cxxabi.h and abi::__cxa_demangle() function), define # HAVE_GCC_ABI_DEMANGLE # # Adapted from AC_CXX_RTTI by Luc Maisonobe # # LAST MODIFICATION # # 2008-04-12 # # COPYLEFT # # Copyright (c) 2008 Neil Ferguson # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. AC_DEFUN([AX_CXX_GCC_ABI_DEMANGLE], [AC_CACHE_CHECK(whether the compiler supports GCC C++ ABI name demangling, ac_cv_cxx_gcc_abi_demangle, [AC_LANG_PUSH([C++]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #include #include /* not cstdlib */ template class A {}; ]], [[ A instance; int status = 0; char* c_name = 0; c_name = abi::__cxa_demangle(typeid(instance).name(), 0, 0, &status); std::string name(c_name); free(c_name); /* not std::free() */ return name == "A"; ]])], [ac_cv_cxx_gcc_abi_demangle=yes], [ac_cv_cxx_gcc_abi_demangle=no]) AC_LANG_POP([]) ]) if test "$ac_cv_cxx_gcc_abi_demangle" = yes; then AC_DEFINE(HAVE_GCC_ABI_DEMANGLE, 1, [defined to 1 if the compiler supports GCC C++ ABI name demangling, 0 otherwise]) else AC_DEFINE(HAVE_GCC_ABI_DEMANGLE, 0, [defined to 1 if the compiler supports GCC C++ ABI name demangling, 0 otherwise]) fi ]) gsmartcontrol-1.1.4/autogen.sh000077500000000000000000000007071417717230600164300ustar00rootroot00000000000000#! /bin/sh #echo "Adding libtools..." #libtoolize --automake --copy # echo "Running aclocal (builds aclocal.m4)..." # aclocal -I ./autoconf_m4 # echo "Running autoheader (generates config.h.in)..." # autoheader # echo "Running automake..." # automake --add-missing --copy --foreign # echo "Running autoconf..." # autoconf autoreconf --verbose --install -W all --force rm -f config.cache echo echo 'run "./configure ; make ; make install"' echo gsmartcontrol-1.1.4/compilation_flags.in000066400000000000000000000024601417717230600204470ustar00rootroot00000000000000srcdir = @srcdir@ top_srcdir = @top_srcdir@ INSTALL = @INSTALL@ host_triplet = @host@ AMDEP_FALSE = @AMDEP_FALSE@ AMDEP_TRUE = @AMDEP_TRUE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ PACKAGE = @PACKAGE@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ RANLIB = @RANLIB@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ am__include = @am__include@ am__quote = @am__quote@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ install_sh = @install_sh@ oldincludedir = @oldincludedir@ prefix = @prefix@ program_transform_name = @program_transform_name@ target_alias = @target_alias@ gsmartcontrol-1.1.4/configure-dev000077500000000000000000000065721417717230600171200ustar00rootroot00000000000000#!/bin/bash # Configure a build directory according to developer flags type="$1"; configure_script="$2"; if [ "$type" == "" ]; then echo "Usage: $0 []" echo "where is one of:" echo "none debug optimized gcc-debug clang-debug icc-debug sun win32-cross win32-cross-debug \ win64-cross win64-cross-debug win32-msys win32-msys-debug win64-msys win64-msys-debug"; echo " is ../configure by default" exit 1; fi if [ "$configure_script" == "" ]; then configure_script="../configure"; fi flags=""; case $type in none) flags="--enable-tests" ;; debug) flags="--enable-debug-options --enable-tests" ;; optimized) flags="--enable-optimize-options --enable-tests" ;; gcc-debug) flags="CC=gcc CXX=g++ --enable-common-options=gnu --enable-debug-options --enable-tests" ;; clang-debug) flags="CC=clang CXX=clang++ --enable-common-options=clang --enable-debug-options --enable-tests" ;; icc-debug) flags="CC=icc64 CXX=icpc64 --enable-common-options=intel --enable-debug-options --enable-tests" ;; sun) flags="CC=suncc CXX=sunCC --enable-common-options=sun CFLAGS=\"-errwarn=%all -I/opt/sun/target64/include\" \ CXXFLAGS=\"-I/opt/sun/target64/include\" PATH=\"/opt/sun/sunstudio/bin:$PATH\" \ LDFLAGS=\"-R/opt/sun/target64/lib64 -L/opt/sun/target64/lib64\" \ PKG_CONFIG_PATH=\"/opt/sun/target64/lib64/pkgconfig:/usr/lib64/pkgconfig\" \ PKG_CONFIG_LIBDIR=\"\" --enable-tests" ;; win32-cross) flags="PATH=\"/usr/i686-w64-mingw32/bin:/usr/i686-w64-mingw32/sys-root/mingw/bin:$PATH\" \ --with-nsis=/usr/bin/makensis --with-windows-sysroot=/usr/i686-w64-mingw32/sys-root/mingw \ --enable-optimize-options --enable-tests --host=i686-w64-mingw32" ;; win32-cross-debug) flags="PATH=\"/usr/i686-w64-mingw32/bin:/usr/i686-w64-mingw32/sys-root/mingw/bin:$PATH\" \ --with-windows-sysroot=/usr/i686-w64-mingw32/sys-root/mingw \ --enable-debug-options --enable-tests --host=i686-w64-mingw32" ;; win64-cross) flags="PATH=\"/usr/x86_64-w64-mingw32/bin:/usr/x86_64-w64-mingw32/sys-root/mingw/bin:$PATH\" \ --with-nsis=/usr/bin/makensis --with-windows-sysroot=/usr/x86_64-w64-mingw32/sys-root/mingw \ --enable-optimize-options --enable-tests --host=x86_64-w64-mingw32" ;; win64-cross-debug) flags="PATH=\"/usr/x86_64-w64-mingw32/bin:/usr/x86_64-w64-mingw32/sys-root/mingw/bin:$PATH\" \ --with-windows-sysroot=/usr/x86_64-w64-mingw32/sys-root/mingw \ --enable-debug-options --enable-tests --host=x86_64-w64-mingw32" ;; win32-msys) flags="PATH=\"/mingw32/i686-w64-mingw32:/mingw32/bin:$PATH\" \ --with-nsis=/mingw32/bin/makensis.exe --with-windows-sysroot=/mingw32 \ --enable-optimize-options --enable-tests --host=i686-w64-mingw32" ;; win32-msys-debug) flags="PATH=\"/mingw32/i686-w64-mingw32:/mingw32/bin:$PATH\" \ --with-windows-sysroot=/mingw32 \ --enable-debug-options --enable-tests --host=i686-w64-mingw32" ;; win64-msys) flags="PATH=\"/mingw64/x86_64-w64-mingw32:/mingw64/bin:$PATH\" \ --with-nsis=/mingw64/bin/makensis.exe --with-windows-sysroot=/mingw64 \ --enable-optimize-options --enable-tests --host=x86_64-w64-mingw32" ;; win64-msys-debug) flags="PATH=\"/mingw64/x86_64-w64-mingw32:/mingw64/bin:$PATH\" \ --with-windows-sysroot=/mingw64 \ --enable-debug-options --enable-tests --host=x86_64-w64-mingw32" ;; esac echo "Running:" echo $configure_script $flags; $configure_script $flags exit $? gsmartcontrol-1.1.4/configure.ac000066400000000000000000000360651417717230600167230ustar00rootroot00000000000000 AC_INIT([GSmartControl],[1.1.4],[],[gsmartcontrol]) AC_CONFIG_SRCDIR([configure.ac]) AC_CONFIG_HEADERS([config.h]) AC_CANONICAL_TARGET AM_INIT_AUTOMAKE([foreign no-dist-gzip dist-bzip2 -Wall]) # Clear *FLAGS, LIBS, LDFLAGS if requested through configure option. APP_AUTO_CLEAR_FLAGS() # C compiler, C preprocessor AC_PROG_CC #AM_PROG_CC_STDC #AC_PROG_CPP # C++ compiler AC_PROG_CXX #AC_PROG_CXXCPP #AM_PROG_LIBTOOL AM_PROG_AR # C++ support in checks #AC_LANG([C]) AC_LANG([C++]) # Needed for generating static libs AC_CHECK_TOOL(RANLIB, ranlib) # Win32 resource file compilation AC_CHECK_TOOL(WINDRES, windres) # Win32 external dll stripping (already detected by autoconf) # AC_CHECK_TOOL(STRIP, strip) # Defines LN_S, needed for man page installation. AC_PROG_LN_S # ------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------- # ------------- Detect compiler, OS, environment. Enable system features. # detect compiler vendor (sets $ax_cv_cxx_compiler_vendor) AX_COMPILER_VENDOR # Detect target OS kernel and userspace and export to config.h # (also define automake conditionals). APP_DETECT_OS_KERNEL([target], [CONFIG_KERNEL_]) APP_DETECT_OS_ENV([target], [CONFIG_OS_ENV_]) APP_DETECT_OS_KERNEL([build], [CONFIG_HOST_KERNEL_]) # Detect common (warning, debug, optimize) compiler flags and # export them to C(XX)FLAGS, LDFLAGS. # We don't use prefix because this way the flags will be used for tests, # which is important for some compatibility flags (e.g. __func__ visibility, etc...). APP_COMPILER_OPTIONS([]) # Enable C++11 support. This is implied by gtkmm3, but CentOS7 fails to add it # to gtkmm flags, so we have to enable it manually. Make it optional for Debian 7 (gcc 4.7). AX_CXX_COMPILE_STDCXX([11], [], [optional]) # Thread support for known configurations. # Sun compiler (at least on linux) has some runtime issues with stringstream # unless we use the thread flags. Also, Linux-based gdb fails to debug # a gcc-compiled binary for some reason. # Do this for all systems except mingw. if test "$app_cv_target_os_env" != "mingw32" && test "$app_cv_target_os_env" != "mingw64"; then APP_GET_MT_FLAGS([]) fi # Enable _GNU_SOURCE, etc... APP_USE_SYSTEM_EXTENSIONS([]) # Arrange large file support (define _FILE_OFFSET_BITS 64, etc...) AC_SYS_LARGEFILE # ------------- Correct incomplete standard libraries # define int8_t and friends if the standard doesn't have them AC_TYPE_INT8_T AC_TYPE_UINT8_T AC_TYPE_INT16_T AC_TYPE_UINT16_T AC_TYPE_INT32_T AC_TYPE_UINT32_T AC_TYPE_INT64_T AC_TYPE_UINT64_T AC_TYPE_INTMAX_T AC_TYPE_UINTMAX_T AC_TYPE_LONG_LONG_INT AC_TYPE_UNSIGNED_LONG_LONG_INT # ------------- Language / Standard Library checks # detect RTTI (defines HAVE_RTTI) AC_CXX_RTTI # detect exception support (defines HAVE_EXCEPTIONS) AC_CXX_EXCEPTIONS # detect abi::__cxa_demangle (defines HAVE_GCC_ABI_DEMANGLE) AX_CXX_GCC_ABI_DEMANGLE # detect __gnu_cxx::__verbose_terminate_handler (defines HAVE_VERBOSE_TERMINATE_HANDLER.) AC_CXX_VERBOSE_TERMINATE_HANDLER # detect __func__ support (defines HAVE_CXX___func__) APP_CXX___func__ # detect __FUNCTION__ support (defines HAVE_CXX___FUNCTION__) APP_CXX___FUNCTION__ # detect if the compiler accepts extern "C" function pointer overloads # (defines HAVE_CXX_EXTERN_C_OVERLOAD) APP_CXX_EXTERN_C_OVERLOAD # This may be needed for pch support AC_CHECK_HEADERS([stdc++.h]) # Non-C99 systems don't have strtof and strtold. some have strtodf for strtof, # but let's ignore it. Note: They may be supported by system libc, # but not enabled by default (e.g. by glibc feature macros). In that case, # defining them still leads to undefined symbols at link time, so we don't do it. # Header check is the best way to determine availability. # This defines HAVE_DECL_ to 1 or 0. AC_CHECK_DECLS([strtof, strtold], [], [], [[#include ]]) # link check only: # AC_CHECK_FUNCS([strtof strtold]) # ------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------- # Non-standard feature checks # getrawpartition() for netbsd and openbsd in -lutil. AC_CHECK_LIB(util, getrawpartition, [ LIBS="$LIBS -lutil" ]) # ------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------- # enable / disable compilation of tests AC_ARG_ENABLE([tests], AS_HELP_STRING([--enable-tests], [enable compilation of test programs (default: no)]), [with_tests=${enableval}], [with_tests=no]) # export to automake.am-s AM_CONDITIONAL([ENABLE_TESTS], [test "$with_tests" = "yes"]) # ------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------- AC_ARG_ENABLE(abort-if-no-gtkmm, AS_HELP_STRING([--disable-abort-if-no-gtkmm], [disable abort if no gtkmm is found (default: configure aborts when gtkmm is not found)]), [app_cv_abort_if_no_gtkmm=${enableval}], [app_cv_abort_if_no_gtkmm=yes]) AC_MSG_NOTICE([Abort if no gtkmm is found: $app_cv_abort_if_no_gtkmm]) pkg_modules="gtkmm-3.0 >= 3.4.0" # define ENABLE_GLIB and friends if it's found. gtkmm_found="no" PKG_CHECK_MODULES([GTKMM], [$pkg_modules], [gtkmm_found="yes"], [gtkmm_found="no"]) if test "x$gtkmm_found" = "xyes"; then AC_DEFINE([ENABLE_GLIB], [1], [Defined to 1 if GLib is installed, 0 otherwise]) AC_DEFINE([ENABLE_GLIBMM], [1], [Defined to 1 if glibmm is installed, 0 otherwise]) else AC_DEFINE([ENABLE_GLIB], [0], [Defined to 1 if GLib is installed, 0 otherwise]) AC_DEFINE([ENABLE_GLIBMM], [0], [Defined to 1 if glibmm is installed, 0 otherwise]) fi if test "x$gtkmm_found" = "xno"; then if test "x$app_cv_abort_if_no_gtkmm" = "xno"; then AC_MSG_WARN([$GTKMM_PKG_ERRORS]) AC_MSG_WARN([The program will be unable to compile fully.]) else AC_MSG_ERROR([$GTKMM_PKG_ERRORS]) fi fi #CFLAGS="$GTKMM_CFLAGS $CFLAGS" CXXFLAGS="$GTKMM_CFLAGS $CXXFLAGS" LIBS="$GTKMM_LIBS $LIBS" # ------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------- PCRECPP_CFLAGS=""; PCRECPP_LIBS=""; # try pkg-config first PKG_CHECK_MODULES(PCRECPP, [libpcrecpp], [pcrecpp_found=yes], [pcrecpp_found=no]) # if not found, try pcre-config (solaris had it but not pkg-config). if test "x$pcrecpp_found" = "xno"; then AC_PATH_PROG(pcre_config_binary, [pcre-config]) if test "x$pcre_config_binary" != "x"; then PCRE_CFLAGS="`pcre-config --cflags`" PCRE_LIBS="`pcre-config --libs-cpp`" else # AC_MSG_ERROR([Could not find pcrecpp through pkg-config and pcre-config. Make sure the pcre and pcrecpp libraries are installed]) AC_MSG_ERROR([$PCRECPP_PKG_ERRORS]) fi fi CFLAGS="$CFLAGS $PCRECPP_CFLAGS" CXXFLAGS="$CXXFLAGS $PCRECPP_CFLAGS" LIBS="$LIBS $PCRECPP_LIBS" # ------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------- # Win32 distribution support (NSIS, zip) AC_ARG_WITH([nsis], [AS_HELP_STRING([--with-nsis=|auto], [specify makensis.exe location to support making NSIS packages] [ (default: "C:\Program Files\NSIS\makensis.exe" for windows, ] ["~/.wine/drive_c/Program Files/NSIS/Unicode/makensis.exe" for others)])], [with_nsis=${withval}], [with_nsis=auto]) AC_ARG_ENABLE([nsis-wine], [AS_HELP_STRING([--enable-nsis-wine=yes|no], [use wine to run makensis.exe when making NSIS packages (default: no)])], [with_nsis_wine=${enableval}], [with_nsis_wine=no]) AC_ARG_WITH([windows-sysroot], [AS_HELP_STRING([--with-windows-sysroot], [specify location of Windows system root directory when making Windows packages (default: /mingw32)])], [with_windows_sysroot=${withval}], [with_windows_sysroot=auto]) if test "x$with_nsis" = "xauto" || test "x$with_nsis" = "x"; then # if building on windows, use native paths if test "x$app_cv_build_os_kernel" = "xwindows32" || test "x$app_cv_build_os_kernel" = "xwindows64"; then with_nsis="\"C:\\Program Files\\NSIS\\makensis.exe\""; else with_nsis="~/\".wine/drive_c/Program Files/NSIS/Unicode/makensis.exe\""; fi fi NSIS_EXEC="$with_nsis"; if test "x$with_nsis_wine" = "xyes"; then NSIS_EXEC="wine $with_nsis"; fi if test "x$with_windows_sysroot" = "xauto" || test "x$with_windows_sysroot" = "x"; then with_windows_sysroot="/mingw32" fi WINDOWS_SYSROOT="$with_windows_sysroot"; WINDOWS_SUFFIX=""; if test "x$app_cv_target_os_kernel" = "xwindows32"; then WINDOWS_SUFFIX="win32" elif test "x$app_cv_target_os_kernel" = "xwindows64"; then WINDOWS_SUFFIX="win64" fi AC_SUBST(NSIS_EXEC) AC_SUBST(WINDOWS_SYSROOT) AC_SUBST(WINDOWS_SUFFIX) # ------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------- # Win32 security manifest support WINDOWS_ARCH="" if test "x$app_cv_target_os_kernel" = "xwindows32"; then WINDOWS_ARCH="x86" elif test "x$app_cv_target_os_kernel" = "xwindows64"; then WINDOWS_ARCH="amd64" fi AC_SUBST(WINDOWS_ARCH) # ------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------- RES_FILES="src/res/gsc_about_dialog.ui \ src/res/gsc_add_device_window.ui \ src/res/gsc_executor_log_window.ui \ src/res/gsc_info_window.ui \ src/res/gsc_main_window.ui \ src/res/gsc_preferences_window.ui \ src/res/gsc_text_window.ui \ AUTHORS.txt LICENSE_gsmartcontrol.txt" # Unfortunately, _SOURCES can't contain substitutions, so we use LIBADD. RES_LIBADD="" # .o file to put into .a, libres_a_LIBADD, libres_a_DEPENDENCIES. RES_DIST="" # only files in "res" directory, EXTRA_DIST RES_TARGETS="" # entry for each file in makefile for RES_FILE in $RES_FILES; do # on separate line for portability RES_BASE=`echo "$RES_FILE" | sed 's/.*\///'` # "a/b/c" -> "c", "c" -> "c" RES_DIR=`AS_DIRNAME(["$RES_FILE"])` # "a/b/c" -> "a/b" RES_BASE_NOEXT=`expr "$RES_BASE" : '\(.*\)\..*'` # "a.b.c" -> "a.b" RES_BASE_EXT=`expr "$RES_BASE" : '.*\.\(.*\)'` # "a.b.c" -> "c" # Add to EXTRA_DIST only if it's in the same directory. Other files should # be added in their own directories. RES_FILE_CANON="\$(top_srcdir)/$RES_FILE" if test "$RES_DIR" = "src/res"; then RES_DIST="$RES_DIST $RES_BASE" RES_FILE_CANON="\$(srcdir)/$RES_BASE" # shorter path, better make behaviour fi if test "$RES_BASE_EXT" = "ui"; then RES_LIBADD="$RES_LIBADD $RES_BASE_NOEXT.ui.$ac_objext" # file.ui.o RES_TARGETS="$RES_TARGETS $RES_BASE_NOEXT.ui.cpp: $RES_FILE_CANON \"\$(top_srcdir)/file2csource.sh\" \"$RES_FILE_CANON\" \"$RES_BASE_NOEXT.ui.cpp\" \"$RES_BASE_NOEXT\"_ui " else # txt, etc... SRC_FILE="$RES_BASE.cpp" RES_LIBADD="$RES_LIBADD $RES_BASE.$ac_objext" # file.txt.o RES_TARGETS="$RES_TARGETS $SRC_FILE: $RES_FILE_CANON \"\$(top_srcdir)/file2csource.sh\" \"$RES_FILE_CANON\" \"$SRC_FILE\" \"$RES_BASE_NOEXT\"_$RES_BASE_EXT " fi done # export to makefile.am AC_SUBST(RES_LIBADD) AC_SUBST(RES_DIST) AC_SUBST(RES_TARGETS) # Don't put this one into Makefile.in by default - it's multiline and causes havoc. _AM_SUBST_NOTMAKE([RES_TARGETS]) # ------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------- # Export some configure variables as defines for the source code. # Note: Autoconf manual says that AC_DEFINE won't work here. ADDITIONAL_FLAGS="-DPACKAGE_PKGDATA_DIR=\"\\\"\$(pkgdatadir)\\\"\" \ -DPACKAGE_SYSCONF_DIR=\"\\\"\$(sysconfdir)\\\"\" \ -DTOP_SRC_DIR=\"\\\"\$(top_srcdir)\\\"\" \ -DHZ_USE_GLOBAL_MACROS=1" #CFLAGS="$CFLAGS $ADDITIONAL_FLAGS" CXXFLAGS="$CXXFLAGS $ADDITIONAL_FLAGS" # Top src dir should always be in include path, before other includes. # Add global configuration file. Note: It must be searched in top_builddir # first (that's why -I is the first flag), for precompiled headers to work. ADDITIONAL_INCLUDES="-I\$(top_builddir) -I\$(top_srcdir)/src" # -include works with gcc, intel, pathscale and sunstudio since 12u1, but doesn't work # with pgi and older sunstudio (the one in solaris10, for example). # Note: some gcc-frontend-based compilers may have to be added here # (e.g. gccfss (gcc frontend / sun backend), etc...). # We use -include only when using pch. # if test "x$app_cv_compiler_gcc_pch" = "xyes"; then # if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "clang" \ # || test "$ax_cv_cxx_compiler_vendor" = "intel" || test "$ax_cv_cxx_compiler_vendor" = "pathscale"; then # # This auto-includes this file when compiling any file. Benefits include pch support. # ADDITIONAL_INCLUDES="$ADDITIONAL_INCLUDES -include global_macros.h" # fi # fi # our flags must be before all the others #CFLAGS="$ADDITIONAL_INCLUDES $CFLAGS" CXXFLAGS="$ADDITIONAL_INCLUDES $CXXFLAGS" # ------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------- # final setup # This macro should be defined by ./configure automatically. # However, sometimes it's not (e.g. win32, why?), so we define it manually. CXXFLAGS="$CXXFLAGS -DHAVE_CONFIG_H" #CFLAGS="$CFLAGS $USER_INCLUDES" #CXXFLAGS="$CXXFLAGS $USER_INCLUDES" #LIBS="$LIBS $USER_LDFLAGS" # make them available in Makefile.am AC_SUBST(CFLAGS) AC_SUBST(CXXFLAGS) AC_SUBST(LIBS) AC_SUBST(LDFLAGS) # ------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------- # all_includes="$all_includes $ALL_CXXFLAGS" # all_libraries="$all_libraries $ALL_LIBS" #all_includes="$all_includes $USER_INCLUDES" #all_libraries="$all_libraries $LIBS" # substitude them in makefile.am-s #AC_SUBST(all_includes) #AC_SUBST(all_libraries) # print them out for debug purposes AC_MSG_NOTICE([ CXX: $CXX CXXFLAGS: $CXXFLAGS LDFLAGS: $LDFLAGS LIBS: $LIBS ]) # substitude autotools' predefined dir names in makefile.am-s AC_SUBST(AUTODIRS) # ------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------- AC_CONFIG_FILES([data/gsmartcontrol.desktop data/gsmartcontrol.appdata.xml data/nsis/distribution.txt data/nsis/gsmartcontrol.nsi debian.dist/changelog src/gsc_winres.rc src/gsmartcontrol.exe.manifest gsmartcontrol.spec version.txt compilation_flags]) AC_CONFIG_FILES([data/gsmartcontrol-root], [chmod +x data/gsmartcontrol-root]) # these are all the makefiles to generate AC_CONFIG_FILES([Makefile src/Makefile src/applib/Makefile src/res/Makefile src/hz/Makefile src/libdebug/Makefile src/rconfig/Makefile src/rmn/Makefile data/Makefile data/16/Makefile data/22/Makefile data/24/Makefile data/32/Makefile data/48/Makefile data/64/Makefile data/128/Makefile data/256/Makefile data/nsis/Makefile debian.dist/Makefile]) AC_OUTPUT gsmartcontrol-1.1.4/contrib/000077500000000000000000000000001417717230600160635ustar00rootroot00000000000000gsmartcontrol-1.1.4/contrib/cron-based_noadmin/000077500000000000000000000000001417717230600216055ustar00rootroot00000000000000gsmartcontrol-1.1.4/contrib/cron-based_noadmin/README000066400000000000000000000015251417717230600224700ustar00rootroot00000000000000 This directory contains scripts and an example crontab to allow GSmartControl to read smartctl data from cron-generated files. This allows users to read somewhat recent smartctl information without having to run GSmartControl as root. Put the scripts into /usr/local/bin, run GSmartControl and set smartctl path to "/usr/local/bin/smartctl_subst.sh" (without quotes). An example crontab runs cron_gather_smart.sh script every 10 minutes, storing its output to "/var/run/smart-$devname". The smartctl_subst.sh script prints the contents of this file instead of running smartctl, thus eliminating the need to run GSmartControl as root when reading SMART values. Note: These scripts provide read-only capabilities only, they don't allow changing SMART settings or running self-tests. Contributed by Alex Butcher . gsmartcontrol-1.1.4/contrib/cron-based_noadmin/cron_gather_smart.sh000077500000000000000000000011631417717230600256460ustar00rootroot00000000000000#!/bin/bash ############################################################################ # Copyright: # (C) 2008 - 2010 Alex Butcher # (C) 2008 - 2010 Alexander Shaduri # License: See LICENSE_unlicense.txt file ############################################################################ device="$1"; if [ "$device" = "" ]; then echo "Usage: $0 " exit 1; fi dev_base="`basename \"$device\"`" out_file=/var/run/smart-"$dev_base" # Change the path to smartctl if necessary. smartctl -a "$device" 2>&1 > "$out_file" chmod 644 "$out_file" gsmartcontrol-1.1.4/contrib/cron-based_noadmin/crontab.example000066400000000000000000000001601417717230600246070ustar00rootroot00000000000000*/10 * * * * root /usr/local/bin/cron-smart.sh /dev/sda */10 * * * * root /usr/local/bin/cron-smart.sh /dev/sdb gsmartcontrol-1.1.4/contrib/cron-based_noadmin/smartctl_subst.sh000077500000000000000000000011571417717230600252210ustar00rootroot00000000000000#!/bin/bash ############################################################################ # Copyright: # (C) 2008 - 2010 Alex Butcher # (C) 2008 - 2010 Alexander Shaduri # License: See LICENSE_unlicense.txt file ############################################################################ if [ "$1" = "-V" ]; then smartctl -V 2>&1 exit fi while [ $# -ge 1 ]; do device="$1" shift done if [ "$device" = "" ]; then echo "Usage: $0 " exit 1; fi dev_base="`basename \"$device\"`" out_file=/var/run/smart-"$dev_base" cat "$out_file" gsmartcontrol-1.1.4/data/000077500000000000000000000000001417717230600153345ustar00rootroot00000000000000gsmartcontrol-1.1.4/data/128/000077500000000000000000000000001417717230600156465ustar00rootroot00000000000000gsmartcontrol-1.1.4/data/128/Makefile.am000066400000000000000000000001621417717230600177010ustar00rootroot00000000000000appicondir = $(datadir)/icons/hicolor/128x128/apps appicon_DATA = gsmartcontrol.png EXTRA_DIST = $(appicon_DATA) gsmartcontrol-1.1.4/data/128/gsmartcontrol.png000066400000000000000000000421421417717230600212550ustar00rootroot00000000000000‰PNG  IHDR€€Ã>aË IDATxœí½IIv&ö=wå_sC&2d&ªTlVW“Í¥{†ìnŒ—¡d2‘²1-£1™IlŠgšÌ4§1]hÆ“‰¤(Î…]¨ƒ¤Ñð2lކ63bSÖ\z©îªB µaO ׋ÍÝup÷øãÏLÐ$mØ düîþž¿÷ümNZk|¯üÍ-ì¯ú¾WþjË÷àoxùü /âE5ôËÿí/Ò4½²¾¾þŸÿÈýh—q®Î’1c OŸ9ÆÁÁ^Z^Vý^Zëzkö1†ét*þükúþý{wÿÎù¨­Ÿÿí÷~ï}Ç C€éx)å¥Ý«ôÓ?÷³7VVW¡•‚T œs8D ¢Ú‡ºë7^yDYȲ 8Æ«¯¾ŠÑ™à„^xŸpÞÍß9Ÿ€L¶WÛ¸Öå{ºí]H)¡”Âöîv1)%´rcc×JqV~ #ÂÎÎþŸ¯|å¿yû­·~šs~rŽ;Wya ”€ÿþO}ÿ­K—¥ .¬¬`2™”uò<ó[YÌwG·ÛHC)-•„Æ€¢DÍR³‚¸’Ò ¶Ðy&>c¬†mHÒ,>Ê4ß—ˆÀci–šûœC3]Õ'ÂÒÒÆã±ÁÄÙ¸x¯ÿÐ}þÑ£GŸð‡çy—ó”†DDØíõ(Šy‘cmu½^<Àx<Æöö6¦Ó)„°Ýj ƒáJkôº]„aˆ¢€. ¹¼ýKö?7 %€Éý±õêà&¦Ö Y–#ÏsH¥æªµ!‚ ¨Þ´\Ÿ8””BÚÖ2—æ)!Š"„a€ñxlžÕZkH­±´´„8ŽqÿÞ=;f;˜% D€3)õ¶{ÏQ^0Á „Ø£J)€élX__GExòä 8眗ä°^pN§ƒ<Ë‘f)Š<‡òFù<ŸKD y€l€0 E!²,C’¤52†!â8†àRÈÒ…”sýh­=8ŸÅ'çYž¡×ía8b2™ IS‹ÂÉlÎ9Ö76Föžì¡×ëáøðñçŽÑx´Á¹ú¥¡µ„Ãl6Cžçèõz. 1‘£@†Ãü1>xÿ}pÎÑë÷Á8Û~‘‹÷† tâ8Ú‚À"€7g-‡GNÍ}84H’i:C!¥¥ ¨Û_èúlssÔ/úËD `6›!MScÐJä´m»eÙ\ûZ£&px×}µ>4¦GÓ–iUK !S¤RèÄ1úÃf³²4Eše¸ýî»xô葉E!¸WµF‡³Ó¿ð|åÅÉŒ/ÇîcÌòÈjè*d¨ É8GàvægY)‹Ö¥—!év&A+]çyö -¢”RFyŸÊ'Œ)•¬Ì‚²ŸªV½}ÿ‚nÅ(G)Ú‰SÉú@åyQ˜L§(Šý~Po¿õ6ö¡¥š„8ŽÁ¿¨”Ñ_/àœïý.A)mÉ(À˜CS1!r\9”j¼¹f>ßÛÒm“Ü›¯.d€«ÉÎg'°“®¡|>ï)‰P×Zo ‹®jû’ Q}R‚fÆ+Ír<ýð#dÉ [[[(ŠÇ'Ç™5„!‚ ˆ³,»BD{§uÞò¸‰àìÊ ?`<@ƒ13ÓÃ0DFˆ¢œsHe²$Mìú¼zÆQ’’h eÿšsXìÚQZ—r„27L;p×5”Ven™XQ/@CA+e5îZãpï¡1÷Þµöeµû÷ɵƽ»ãÝwÞÁã½'ˆ;n¼rJ*»üÕàŒ#Ž¢€€]FÿøÄpûÄO6 Ñv§Û±kÖÉQb0þ*%²µFZ¨Uó]ÛÙߤ/~î:g yQàã?Æ“½=„a"àèè—/_FäynTæœ!ìÄÀx´K쯙 Œ¢Í0Š •.%ý @k#le’$1<¨IÙ%Ÿv3•VÌ T ¥FϵA @)CêKÄñˆoS ô‘„t ñLMŸUòûv‘³|÷ƒÐ¥<Ð,ƦÓ)>üàCœŒNÀ9Ak"ÏÁˆ!ŠbäE Ã>ã(1v™Zl$Ÿ¤¼0ˆ£x7Œ"(­!¥„qc2[í[a£$¯~YÀMu{­R”ÓÊ›½ ´¿h_6úèçI Âò‘jk×v j@Ñ<™!²+ï2ˆáøðô’4Aài5`&ŠÖèv:˜N'ˆ!Žbèêù”Ùg—‚¿òK_æëëWâ0„²úö0ŒdPZ–<°‚““¼Û–lT›GX5 ,496@ðQÄÞ¬/—i¨®¡ù\ó². 4%Ú¹Ží¹öo¼Y¾Ò ÀÞÞ#<¼ÿJ)áköÌ3RJH­ÐívA¬l3 C.vˆH(š#÷¬å… @Ç(Œ·¹0:)¢(€ÒÚ¥”„Òª6ËtýÕÀ-.%ð’”0Ÿã¶†{@òÛU^ÝÚl²øX’ï:­@Ûú®^Ów"0n¬<ÄÓ§O@ŒÀ¯µE–íi¥Qzý>4´ôˆâ\ˆm­U‡ˆZMÃÏR^ Zãh‰qf%u… -&‹“àÔy> Ëá-_÷®Äÿ7<œqÅ—öšŠ"_ðjá6ÚF{¬˜Sh9!Ï‘}}µÎ(4ç˜% Ü¿Ñhƹ÷šUMfÙ™‚F–çˆãœ18RŸ`K…T üõ@©än·Ûér"H)ADK ñ pË4hÛ’%ö5ñX×€m¨j æDyµgžœQ#ðÔ¬‡Z½ª!jÓšv°&4¶R»°&àñh„û÷ï#˲º•±Ù7œŠ ȳN .„K‚1„aØQ‰Ú!¢;-¯ùLå… €`|«Óé0 gß'aˆBF)¤tµžv@pÂZ}ä1Ç+&ìñ`ÿoô‰jrc±Pƒ4j”k‘&R4HO…LT¿å¯J¬æÌÜ;ØßÇÞã=()!Xä7ߤbA i’`ey eYéO‡¥³dûyÖÿ®¼ˆ£h;Ž;FÉ¡”‘VÃišV ¥K ½¼:½õ¥åjÄ+ÉÑ,åPn™X¯ª|DÇj¼®ÚÇg3u–Q˜æC—}WT©zfUÖ{OãààD.xóš_ë}²KÆE!ò<ƒ1›Gq ŸìÐ ° ¾(`Ëñ|­5ˆD`:–HQjà€Š÷»27«ÝŲƒò‘nÈa‹…2µE¤zþ²G3jÍê–úäwá-E œò<Çã½ÇÆ`Œ—Ôa~ÒÎ# 5Y tâÓÉF–`ˆŒ.àʋмà‚o‡a`€¬4¢02 µ°9õgL6¡^‡8ùÒ´$ÇtýÁÆR¯ÖS‹@Yžûd¾ ©¨¼t­-× #Ât6ÅÞÞÒ4µ³¾QJYd '¤QÈZ+t{=ìïï—Âe…`œ®¾·€çF€ÿþW• ‡Ã+a•¯VB³®-o•ó½ÒïUª×†PWÃ*ý æŠoo‘ª íî2æxªí,¯ÄýZ="†ã“<}òÊ:ÞYJ÷6ï«ä c*î@ƒ¬Ç“FEœïˆ$gw²¸<7¨†á®Âa¤DEаJ åHP‰ÏsRÖüÌó•,5¾[VhÒ šƒÚ\VvãµÕÐÚÕ– Òád‡<O÷q|t­À÷Q‹‘h©Ó·*`çÖÆc²(Ç‘çF„Æ ¶¥¤êÑ_-0bÃ0—9gÊÌò( òGV,@[èÖ€¬ÛaQǪ' îûçW'ov¶-aÕµ-ý¶Ön. K 3[eQ`ÓÉÄx7[¯MogB„¡s<%(gÁTÆB)„@š¥twÀ˜€Ò  aAˆ ›ë|‡ˆöçGâüåù)gÛauˆ¸Õ0ã[' #Ø«ÏôEܯ”ÃQªðæ¬4Þ$õ&¼›ØÕ­æ™­á€wžÔ’ÕQˆi’à`ÿY–‚sáé“Z„;"c#‰côz]t:]Dq‚øŠ¢€Ò fÅ „€„AÆ ²0ƒCEA^;ÄØ7Îñ% Ës#çl§Ûé»x…d!áÖ×¾Ût“J×Çø”™¨ÕÃfbR] ƒ?a}B^ÙM9gK¿9K¾'“)ŽŽŽ ¥4$ß{ß¶5:çý^KË˸páÖ76Ðï÷‘ç9ëž–ÆT®lIQœ@) ÆŒEЬB̦“íçµ ??0v5Œ¢Úº5îÄÆà\€1^J¯ðrYš¯;€m¬¬»À!³ oS\†gÕ¤ÏzsÎ\{rr‚ÑÈhb9oÇ}U/YŸ¾^¯‡K—/ãòåËÈÒo½ù& ðó²(Êou²“ÌÍ5!ãQŹ1 m³óš§”@Äz „‘ò•Cˆ 1£.¢y–Ù01é‘ï®ÛÐ|R?g6•+pÖ%øª]G<@M$ªž+ñÎNwŸY‘Uuc6›Y³îâ)Xš­É<ˈ°´¼ŒË—/ãáÇxã›ß2õÙñ±TÈ­.´†‚A°8Š•1ãOE bÛÏk~nA°-‚°KÆ8cX]YÅáÑ‘P¥0!Bšf†´iÕ€s]¸r¥”ØÛƒj'¦ ·b°Ïør[öèŠ-¤H%×2}0"äYŽ£ã#ÞÖPéÖ‰Æ<"!9Ë2|ôá‡øú׿áp%%’4-]ÈJD¯!•>®£0ŠÀ8ß0½rÎò\ª„ÿñý#ÁÕ  51ŽñhŒ·Þz R+¬¬,£ßë£7`0¢Óé@±ÒÎ+ü-ÍÝê±<|êßò^¥ë9úG½K­KIec0ƒ?Kf8<:DQàŒY¥;ˆ|=ã kᬔÄãÇqïÞ=Ü|õU„À,™A)ézˆÄJêRÜ€ÎÉ6 #pÎv¡ÃžCx. Àˆ:à»à 5e 9k¨™”Ï({hãÿN“¨•Âl– MSÀî´a­‘݆nÖñîP}ÍXko–L‘å)ú½>–—–$ îܾã“pàÎûï[vÚÇd2çì¥ç y.B\uÆ h(‘Ìf˜N'èõzøÂ¿ˆNC+…~¿~¿YH™ø{óé­zù¹õ¹·ZhªN+5xÚÅRÒ·Êd6CQ`ä¤|ï¹E3¬EÚ¯õÛ *jrÊûZMãh1üÏÿä ‚ ¸ˆ ÔÕÀòÊ .\¸( F ƒ•%ÄQ„4Iq|r\ VÞgÏéy¬Ê½VœŸ½[ž¹Ár3Šü*«Œ{ØÿcΉPä9f³Ji0ò”; Xê¼Q¶×¸èÆ»N%fÐ<£úiQÇöøüç>‡×_û4=~ŒÔúFaÎøŽRª‹¿l F1ü„Y=ÛïÑJ#W…ÅÐÃþ" 0™Î0l°£=ì@Øõmê󲵯vèËønàÛ´i^ör–åÆoqW—ªûh ˜nTóFeþOÜ;l ”Ä@×ëÔ”8cfŤ’4Å,™Ys¸QsÁ»ºÀ€£¹×9Gùä@¬ÃïùŽÙ6‰ú½cƘN§4êZ«hŽ“w¿OmŒ= P[m éâ¼jR›Šf~‰ç!%µÅ“3[¿£ºRÇ?òï)Ö|¼Ñ–ëó42šGŽ0ˆB%“mo̽Ä9Ê'FÆèe!‚缦DÕ¢0À ßp2!IOZvµ¼2G Û×ÝMÞ¶ðëø=ʾµÖÈ £… ‚z<T¹T-M}7_žG¬ŸVXUqŒÊØ%¥4¾€VI¤¡!GHÓäêY#±¨< ¸‚3"»f×Ù Á9·¡YùÒ¾¶wžÁS¹/Õ§hÒÔï×VóõÀ vo-nYžƒ•£\Ú‹Ò·Ê÷š./€r÷ÆéÂ`“•Q“þÛÓÑyAJ)"()“{! ÂåÅ/wzyŽUmŠ ‘I°ä|ÿÖÖ. °aPi–™ü;Þz¿UgVš“ZêxW*X4‚3 Å ³fSf•5EnVq#k˜ Mî=eòE<Ëç9ò,G¡ h)mXÊØÆ6‚ÐN*r&Kð‡ ! j ÈB"ì„`Œ—)c8g&Ft¥­÷ó”ç`|É¥yÑÒ2®®­¢Óéb2c–$¥+xTQ3üxE{v÷WZ¥¬Å—­æ1çŒ3@/œN'Æph0†a€<Ï‘¦™YŠ*e4‡ŒsÑèJGŒ,ÍfÆYCYd(3‹4_ 7„»:ñs×=v×›2,.š: $YjÇŠ„8ç»ÿôýöK¿üå.Ó‹Ë'gЯº„…RIpΰ¹qI’ ( “çO9ëZsV{¤¾f°uÈÔ!Z4ÿØ:ÖÛ¢$ë‚sh!°¶¶†••h'''xüøNŽOPH‰¢(JsŸs3ÎÀˆƒ˜ñßB 0ΑgYÍÕ]ÏÅ'6™øülvgµsjÔ¨ñ•¤4J'›4MË*aB±  `‚g,Ÿ~û·~+ ‚à.8” cÞÜÜBENŒ[“'BùÝyINä,Ýô«”:[q# !À…q^]YÆÖ¥KH“ß½‹Ãƒdi M”A›µyYê 4$ ý>Ï«˜x`ŒH¥ë–oPòÏ@cÏkï Úüjm D½nœ ì K3p!L; Ú.ZÞ1WoArĵ†–f%À,[ ”þ‚k¿ÊÕ µÖ9…c[Þ0 1ñèÑ#\X[E§Ó©¹Ÿµ¶ÀN«whоaC  +h‚0@¯×ûDà± ¬ a< „Ø~81çÍj¿¢Áÿ+r׫kë—úù’¸û¶Kª³Ü[^^ÆèäÄ,=-Omë×÷¶õï5ûtÈY'ݦOæõ nyQàd<ðXIóœÛoÜ gñ{:°z/ CùŒö,,ÈäÌóí_ø…_xæ>XYZù´á§v&„!`M|œ¦l\_mvëÆœ£ÚŸ¹{%å¬ßA¹õ‹å½¬-Ÿæž­·Â*"ÛBþ}P"rË»U¸i´ŒEžƒÛ¬ãs,§íÊ©|¢:7.æž+ @ö[IqC)½óÿâ_t·µ¼¨<3øÅÿä:ƒþઉ"›ÖD¬pn–cF÷_w•ò•>¥ÀçfPÖ/?ŸPýšZUù|- Ã\ˆÒ‹¡á§Oxfa­Â {wóül2C–e6ƒ¹_¯Aɳ¼öoÁs¾;š‹®&2º{¨¡Á¹À`0èýã_ÿÇ[Ï ÏgF€ÉtrÁW„0®`R*ÌfS›:01‚Yß9x¤ÜIñmƒÙ.7ÆÎ4ãÑŒj’N¯¬® ×ëU°#ÕÔî­Q¨v-p]j÷Ŭü¡µÂÓ§O t{½RÁäÚš—ìëm”GYÐ *1…mK:6J0º‰åååøÿýã?¾ú]G€í5¥UGÂØ©•Äl6CE‚Àl¶Ä¤Ú u e Y Ý’¼6%àœ:6/ ƒ1†­­-³&çÐ¥ÀÕD(Û†ãÃ%ýz~V}‡£Bp3(¥¯„AÎYRö‰8É2„aˆÔN¤Ý#È’Ö†N9ù 0ôÈ3õ¥ØPÑœyÑ&¡Ê²>Äö•+¸xqD ûOM–mçØáe)qíS½«Úº0hg57I0>þøc¼ÿþû˜M§ØÞÞÁööޱÖQ£]4Ú¥Ú¯–Âʇ„*P†q#o)kfgÌD ½þéןYðÌpr|<Œ®_'‚¶ ûOŸb:¢Ûí"ž&PR!Ê2¨B¢Ð6Õ¹ûGíÍW™Os®¾†Ó­49•'(Efç»wïa}cKËKà‚ãäøišzij\ ÷J€ò‰SÓ|E+³Vwtxˆ»ßÅ“§OvvvqóæMëlR=µcWyež Ô©Qóv)0êjו,Ïáä¦8Žqrr²ÓëõØd29·Yø™`ssó #C9€0Ê IDAT(Š0>áîÇwñêÍWÑôQ(‰ŽÍtÌl×Vû <©ññj$ί9_Læ0¥€ét‚‡îc8\B ‡“ÔìJ’eªš’+ø*K¯uç´*¥Bže8>>Æ“§Opppˆ$I†nܸ3átË&”ó«Çê[ÎÜ9(ÇC—¦ê¢ÈaÂÒ¬‹8cÛ0™Ã¦-ÕZž þßÿyñ_ÿƒði&ÌROk0wb¼wû]\}é*¢(B¯Ó’…És U›HÌ#þBÉЈO;[êk“‡ì¾ƒˆb#”jh#4im¼{¤¿£™RëÍ$•D‘çȳ Išb4c<c6"Ës!pyû v··1 ÊÔøm }¾]÷Ô:¾ÑÛ†ÒÇ0©×¶†KƒW_ye ß-`ŒÅ" /sk€aZC„!úý>öööpë­·ñú~^y‘#/ ã¢tQ,^ø…æg‰”´wÞVh‰Ý~ÍE.KëR…Ù•TJÎ ¬0©Yò̼sš¦Æùê¶ûƒ–WV°~áz½žE§u<9Ñœä ¢ŒçÆ IQN“ ÀÄV‚JùF¥å•μöÚ6€‡­´”gB€“Ñ(dŒõ3„Á|BÔé`0\­wnáâæElnm¡×ïc2¿<›5ät·‰Ú×¶_n>_’Öªh¤443+åÈ3Q-w_©ÀÀ ,[‹87þ‚B ¶;{ÆQ ˆjoAØ\Àþk¸3šŸáó‚ïY^þçÙ Q.++W7#:™Öã„y–]ð§­ØRž þÖç?¿«¥<ƒ1º€B`8 IføÚ×þûÇÿ¶Ù0I ‹ÂØ®‰jJœ…Càµt&tƒ0Wq 5}UZçj‡ò@ãÍ£& x®åßFÑd؃’à]ÕvAÊë®}VCÔJ$^„̵Ö}8&b¬‚Î[ÂxGà\<ÓJà™àò¥ËŸâ‚"0,À}!@„«««8:<Â_üÙŸcmuýÁÀ쪚´þ½naãòâ̧/Qc0Z$îv¦  4K”ž ðÞÇÙ}rîÒ¶ q½ì¯sk¥ÝðWi÷v’ìãD&ii†qãžöïÿï™tÏ„ÓÙt3ŒB7 öòň!¬¬¬`eiÙlŠàœ9k« ›K°{Aÿšæk‘GVÈ* ª`S¯N[{^µ9ä«4XõWož·ÈmßÒ|÷ò\ßú´ïóØ€}’Y_“‡¬vr0~÷(@žç«QÛt¨nnÒ•„A / à&Ôê³ý¨ò•1 ¬o«Þò|í*µÈ ÀœàÕʆ|ºm×\§!îÜ;" žç@-zù¹ï«„N"†€sdÞêÊ¤Ž£Ý~¿ŽÇãs‹>ìîî^'€Œ¿Eˆã€ÝÖÛ2æ4 yBœYçϵ>߯çÃò¨:ž÷=Ï íÖ^·†d 8c`B€òjã0ë"¾-„8w´ð¹A÷ç~®3 o:/UH„aˆõ ÆÌr¯0;b—fW4ÌŸþ¹ÿoî:Ðüå_ahXÐÊkðÎMÍ2;Gy>Ÿ±£´zi_Î|϶:tF7ƒ×™DõM󎪕/$ƒ`Üf ¥`ä‚‹ÿÁOýÔ¹ÍÂç¦ß»‚°&„€!Í2¬o\4N&pA"EA6ëµöŒ8^Cí*>sË;#ªöÿñ… S©BIV[fpóZû}Æ÷¤æmR½OBó[<ªÓF9¢[0ã BMËÐ;Î9¢(Œã8ÞðvûÛ×˹`8,w»Ý>ã ReŠ7§ ÔdŒ%ª€Ýn!ÊK»ˆ§ºsG’Ë H \¢ékWkwqÍSßaQÛ/@ôïK±ÅéP˜{‚Ðroý5Ke†ÐÀ¹ýÏ?ù“?yZ7Ñ@J)³#¸ ¶”J¡Ès¤I‚\Z•jžÛ01YÿØ–Aq÷œÓ£s³vOKmséÃíLvJ9E’¡s! ŸCœûÝD."Àª—c‹°~Qo f6œvÑÎ"mö& Íóçö 87äiúJÜé€ icÓºÝ.mç&ê¶Û5iW’Ĥ@Ï2äYVî…[wÿnèÉÐëštêPäŠ"G!%dž—TÅo«FœÞȳòyÙó¹“yÖr9žûq*ÓÒÖ©ǘ‰xâ wcÄQ\ÞŸMg8<8Àþþ>f³™Ù^– ÂH)ñ³?ó3/žÌ’t¹Å&œšshnß¾~¯‡¥¥%,¯¬”r€ôm´›LAD(s~€ño‡XZ^®Ò³u €sGM²…ÌQ.ó¸ K7Z?û·T¢ìËŒª?Ðs'Õ­Se…°ÍáVsFÏ?°˜}X…ã‚# Bt{Ýr£ÉÉxŒ‡áÉÞNF'Èóý^œ%µD€^§·= øh4:3‰ô¹`wwçqÎ9‚0D·ÓÁÞã=|<‚ìVfkkkØÙÝÅúú:ˆq'¶Þ»@Fd"r/9£ZEX^^ˆð)ã£#\ÙÞÆÖÖ&:Â0D†Ð]]²”<ÏQH³òÐnkZeC·ÍXÚö•í³ Ô €ÒÐN cè[R³ÁÑ©£ý“w¿F l”s†èv»ÆñFJ<¸ïÝyûû¢8Æ`0ÀÒÒâNTîI„ˆa]àì­eÏ…ý~ŸÿæoüÆMέ? tz]\à Yšb6K0›ÍðÁàý÷ßÇõë×ñ©O½†0 Çqé+WÆ:›¼âNŒn§‹ýý§øö·¿ƒ{÷î!Ifxãoass/½| ;;;X^^FˆÒï0Ž rI›@IE©v.…OåúQ‹å†öà–lné¢:»©Q arÔǃõýDÙU“ ZAŽ‹Àìl=n¿û.n½} ÓÙƒ~k60.¡Ç.UœÍ¼j¬„ƒáðâWn ñ¢àúµëÝ‹/îºA!­Á£Q¢ƒúèC,- ñ©O}ª4 — Ž”BA|ˆGâ[oãÍï|abum7qaý.nnbueNÝŽq ‡ˆsíÛ÷׸|øÁxãoÃõk×páÂ:â(J)Ìf3(¥0 ŒÉ3B­õ¹Ás!ÀÆÆÆæ ?22ëÌ¥åeüþÿýÏðµ¯ý)~ñÿS(õ—¶¶DZIÔpé”T¸}û=L§S„véâ6A*ŠI’`ÿé>‚0Àõë×±²²Z•¡”3¶67‘|ß÷ãøø=“'{xòä îÞ»k©G§ciyKÃ!úƒƒ†Ã!º½ºQdÊ&Tªù:§¥4’df²†&3L&SL&«}­´ƒÌJçiš^ „UذÒÍ[³df\Ä]À Q݃Hk¤i‚4Í=J`¨äÓ§O¡¤ÂõWoàâÆ†%÷ÜÎtßüÍß@’&ø¹Ÿý9Ü¿w7oÞç‚0‚Ö8—Uð\Ðív_ ð FèÄ1þðþ·o¿‡oë[øæ×¿_þï~‡‡ø[Ÿûøà\}é%\¹|ÃáWwwqëwÐë÷ñ…Ÿø ðœA€~¿ÝÝ“,Ên†h„aÃ)ã(ÂpiÉh]8™“–iÞ>oKƒ‹q'Æêê*®]»†¢(å9’$Áx4ÂÉÉ£ñã‘ñç›N͆I’@J‰ñtbvç’f›[ƒ±]‰DQŒ~¿Wz1F‚ýÁ ŠjrÒ~³gJäV+€Õî²¹@F AšmsÜ8fåJv\ îI?Š"¸@œ$KñäÁS|æ3¯ãæÍW±¾¾Ž|ûÛßAEø×^´Æg臶‡Ã!;999ÕCø\àÆ+¯lpÎÑëõðàáCÜzûmL§fƒäo~ó[ȲÿÃ?ü‡ÆMÙò_g&^ZZj¢•¼½)½lÜôoèÍëׯVýÙëõ°~áB9€Zk´i@ÚÚ9r6 ã6ãU¡eNɈ¬jʬúÊP×Ųžÿ¾^¿µ]LæF5váØ,//ãÝÛ·qç½÷prr‚'OžâkögX[]Åg?ûYäyîXîŽÖº àÔídÎD"¢ßþíÿe×u|ßÍ›èõzøÎw¾©¾ïû¿_úÒ—ÐítÊY&ºåÃm—ªO{át=ù)µn°ÍòÊ(TÂ0Dà¬oöÚôBín#¦FÒ”Ê8Fœn¬*õ õvç.µó:Î0 ŸŠAˆ/~ñ‹z<?¹|åÊ–KQZøœ³§é½$‰Íß¾PWS±ú×=­[I®]}ÿº/3T_>gFm*yŒ˜qHu¬ÈKFÛ€z*ˆµ>£Ž®RÊ««|uu%GÔþÚçœEÔ]÷µÌ¥Ì`4áÊåË7N{]à "Š¢HH¥ØñÉÉ#ÎØ§ïß¿ã£#ìïïãøøRÊÒNíÖðå¹§ü)“6xÉà¼s¼:¶ã²-FÆÇ€µ\ŒóêÜ!Gó°ÏØo™;3S‚ (£œŠ¢(3Ž*¥JJç´ZkäY™h#˜°·Â& fvG4i'ˆ{NY³­ÖPºô‘p‚©sžÑPÐÒè&4ÜuUé,”'ÐÚ¹ t‘J" úa€n·‡N§SüÚ¯ýZïw÷wgï¾ûnëjàT  ”bI’dÓé·nÝÂ{·oãÍ7ßÂíÛ·1ŒKã…3_–IšK“&÷&We ©f¹QœE¯ãȲ ‘Íãì+`Ln@ûÛºF™ô®ñ˜M!ÇK`²²ž{Î:X{÷î¡( ÄqŒ—vwq|r‚‡B)…õu¬]¸€?ü³Ù B¼üòËȲwïÞ…” Kƒ!®l_ÁÞÞïíaks ƒáïݹƒ¢(E®îîV‰³]9…e4ÃךC¨µF’Ì,™AJ…µµ5¼zó&^¾ú~ð?ƒ ‹ :Ýn7YÔÏ™«­5;:::.ŠÂ¸}¯®`eyKáÍÒ…8 Ä98™çŒcž{®ÜÖð’Q†ÉdR*zübAFÜîD*,Ëþ9‡°ijLQæfV5ìŽ 0³èÝwßE–eôû¸öÒËxüø1Þ¾u €ÉBº±¾÷îÜÁØnywýåkHg)Þ¾õ´Ö¸|é^¾v û¸ýÞ{`ü$E€÷îÜ1&Û~×^¾!DÍvÐ&äÖ—'óÌ«’U4ÜfQUVVV°±¾ŽÍ­M0΋Ùt:999R.¶ Ÿ¥‚ôt:dYŽ(І¢Øxyn]¿ÝÀºÊÁYo_gQÕ·û ¶}¬`‚yÀÖjVWpQ"ö¼vƒ4ÖÛ(ô|ë ×ëáÂÚ8¯†¤Ûëbii¡õs`ŒaeeÒz>k­qáÂ:–KȲ¬¬³º¼Œ~¯[²š(ŠÊ=Ú–“ Çÿ´ÝÀ´†`¤+¶¶4\Â`8D¯×ƒ”rv2M-vz*H)U’$r4’4QŒs¶ø8î MR#ùä–1C–9³zôÓ=ÏzT(Šj׉ Oç¥ÞõSÍv!87ë…ð BŽa<$ ‚£q'ÆÒpP{ß~¿NǨƒ # ú}E^Ö‰¢Ýn\ |aa{{ô Kz£8FÜ­û¼…Jͨ¥.ƒŠtal‘8íŠ#·'Bq’¥Ùt2™”ÈÚVÎq||,G'£ãÙt–a‡A€(Œ ?®YµP 5ŽÅëÊ;bn}îÉÍç}­]ý9PÕtù"@“ß§Òë¨~ÑkÂ6…!‚(ªÍÐ(Šj–;!„qÀô ¹”˜XE˜³ÉûSn8BØýž©&µCСYîR¢©¥°[áD Š"»A'féQ^ä“<Ï•]®“n!?§!i­uš¦j–ÌŽ'ÓÉl9XŽGgÜ꾕ÁP’ (²³WÊä˜b€m]<@¸õ$9pÿ‚ûÔTÂjÀmt°JJL§“£Éd2=:::Ucwhšs®•”Y’$3,é·Èì¡£´i­ÍÖfÒúá‘’†deë_—¾!¥|ƒjSç"@AƒA) "ó¬4ø˜zJ+0­ 4Ai¦´–Њ Ùw¯€mÝ©KE agw³Ñ¹%ùÜFãäY=‹PIäÎCwâ­dœÄiîì­×Iõ>^~ ?ý¥ûø…‹¿ þ¯¿ƒýûKèî/—Ž1“50©mÒ$=ÇižçÒ ]»ð±AÛ´”RRfiš&…ͺUU²@Rp”›qV‘!Uu¨ÅY»¹÷¦m0ÜG”ˆ`xS–¸ý|JKy½ÚÔJ¡ª'=päÓíÏÇÃÒÒ Uápˆ(Žp2ª²±¦i‚$Mj«˜4MjÛáv:J–H§,5óÙ<ìÚŸ1þþÞÅ×±Œÿ+øñó—®á~ï0-r(2ïç+ߤÉÊr ¥ÌÞ•3WI’“Éd6›Í&Y–•K ·Ç®VšL‚ 5sTƒޝS©þ´, aY•q®è’¿›äp¢â ¶ÿJ] U½“f¨îs»ïŽ,÷€öv­æ66PÛwtÖDåßææ¦Ý2¦B’‹›[H³ ³Ù €A’8Ž1:9©}‹,ŠÒdì>û¬¢¤Br8Æ»ïeøþŸÃ—AñgúÿÄÛwÆØœeFÞbT0Ƒ晔R>N§EžçÚ®´žM K¯³,S“Éd6OŽ“44L"È `#wœ:V™\y¤ ) n„(TY:š#à®7“-¨‚¶4íx>ÓPʦ¤gvÖ+¥ˆ˜6õ•RFUÌìLSdôîD1¼Ùçò4-_Ì©}µ¬^TJ‰,Í˯ÖßaïÑã²ÎÅ‹ë›]ÄqŒõ 0›Î*ê9§ûig *“X=ÿþ*þ³¥'ø©Ÿÿ§Hrà_ý3Ž+o•£<5Èg@#M³d2™O&“ìñãÇÊú)´âÜy4z2™äÓéô8Ïr0nÄXâøø*7²u“&Þ(˲iš¦’ˆNíê<AÐJéB‰RÒäþsÖ+GfQ-Ṵ̀b$HN§&! +8:CR³H¥ ²Z+DaTÙ,ðc×Ê-ù σ÷ްÀÈp©ì,Ë&* ÌVsÞð™ebå†!VVV±÷¸RµA`SS‹3†( ‘ùªî&HÍen™M Žd2Ål<1f`­LT0f'qÎmª»ü$Ëóñt:•Y–=è$IT’$E’$ã4M!•Bš™¿ÚÅçYáŠ4™Y¥Ò&€Ê—£ç‹Ó²qÍ åKHóY^ ŒÆ°c6A¼ •²¤ž2_¹r»eª)‚d dF‡³8Ji$³aPœt»]diVÖq¦_Ÿãñ³i‚‰ÕhÀì8ªÃa‘ àk°d1§Ì ÀB€@Èó ³Ùìd<Me›Å/gR€4MÕx<ÎŽŽŽŽF''9c<˜Ìf(dQíh+¥4@ÊPu-ЯæM™~QNHÔ§¡‰ûh,Ï‘å9&“ ¢(D'ê Óí fÈî÷î²gÙÄlf¥¤óÙ“( Å ©â¾i’"™%pƒîTÇÒó|šÍf˜M§e)%Æã1NÆcB½nP©ÝÞÕl†˜M“Êa¦1vA3W*ŠØ¸IÎÅ^U"dØêަÓi–¦©œ¸^ÎÃÔd2É=zôps}cÐêh|‚ñÉÒ’Q‚—ÍÃ@ÀœÙ%J…4÷ñî'C•ƒ/A¹¹•Æ7ÀÛ—€&B–™Ðñ8ŠLâfâ02\ Cϧ¿2%3.°½½édŠõõuä2ÇÒò2677Ñïõ0ôq|r‚+W®`6›aii R ØÚÚB¿×g„8 °³³ƒ$I°¼¼ M /^4asƒ´–ଠøSÌÀÕØÛ6w]+CD(ŠOŸ>`<š£Nüh2™L§Óé™ÑÁtšž€LÖäxiiióúõë?ôÚüÀµ<\ú»OŸ>Ń1žŒ¥='³ké⇃yíV]z)ÐÃÁÁ!là¬ÛYs0LfO£;¨È§†Ñ€ÆMÍl)ÇËå%·ÎeÝìøaŒ@qܬ2‡E†ìËB"ÍR¨i0MÁ¹Q»‘ [Ïu:cPE4IFa™¸!%(T ,*ÎQ!‡žŸ²uܨ’]§Y†Y’@+…Í‹¸qã\ºté–&üÎW¿úÕù'ò'ŽÇãDëÅäõL€šN§³‡>’Rþa¿ßÏ õ§§ãÉ…ãããPÊ‚T!r[µƒì®hHêòFS5lÉÄšzB€|qÍœÚÀh\*Z”†T¹ë™tÅ?É.3 6· ò|ì¬jvó+ãŠå}‡}/­=#SD‘«ã’·8T×å†Ú(ûp_E¶[Û¯Ê-†QjÁ”¤’šj8\*Ö.\8დñè«{{{w>|8šÍfÏÇìR°Èó|òäÉ“G³Ùì€,˲[RÊ‹yž÷•R‘m‹QÅèÈ9)Vn]¬T0¹sΘ¶>}dë(F¤‰1Å×6‡ˆœ÷‘邈Èù’C8ØaYÅ’]…˜¶Ik·Õ‚\DÁ"y¬¶„«4zºÒAÔWFZkÒZ3­4ih*#£­ÆRk­•RÚ¨¨¥S§›Éêü­þFAé²M¥µTR+S2b4 ²tr8:yòÞ;÷cw>º{÷îXJ)ÏRŸÊ-p{D´BD´ÖkZë%]˜mËC"âæÄ8çÄ#ι8çfÏçï' ‚@»g„å3îàœãŒ`¦8–SÖqÈáÞ9–d—ˆxšH­KØå¤°¿•îÖZJIö`EQ,$Ic ÐJ*(¥´”RK)µ=‡RJÙ05-¥Tö/, u!¥¶Î¦ZJ©³,“EQ¤J©€IGNgÀÞd2ÙßßßfWŸÓà<à4µ@³!A ´¿ qΙ¾(GIDATVEÔëõ¨ßï³^¯Ç¢(baR†Ç1 Ã…aHQ± X†,B¸z‚€qΙ‚ÙsxfÂý%‹DDî=0K)àÉUtŸç#‚÷ÍØ@Ét `´ÖZ9$°³Z¹Ù]…°bQ(ï\E¡µÖ*Ïs•ç¹r€/ŠBæy®ó<¯çy.³,ÓI’¨ÑhTL§Ób<ËÙl–E‘æy>˲l†a:™Lò½½=}ÿ?´ k "= h8âÊ•+ìâÅ‹lccƒ666ø`0à½^u»]Ç1EQÄà…< C&„àB&„`Dă àœs O QKacµßœsP""ADÜž3r¼£J J¨Ø”ü\ÂPm)@£hwﯲ”ÂÍ2åŸÛCÚkJJ©´ÖÒC©µ–vöK‹<Ò"…´£¤”²( e@fY¦¦Ó©œN§rooON&9™LŠýýýbyy9OÓT½öÚkòË_þò™À=wŽ ÷áVµXiƒ~åW~…nܸA›››táÂ6yÇ5[*Ƙcڞê (õFÜö¥ýÉéýõ’7þÖ‡ Þµ¶vüöÛŠ{/í –ÄÚñ‘Þuéõ¥¼kþódÄ,j²Ge”ׇTJ•H¢”*¤”E𦅔²È²L©µµ5Å9W/^<åSªòÌ{%TxÐ(jüæ ½ëîæ7×´ýmÞ‡÷»Ù~ð~i~ùá»n†ùÀU¨ã>ÈCÚ{u²É1OÒyã¾îÿõ_ãÿ˜Ÿõ‹€ßD–úך@ñÉ|söK¯ ðõ±j£Í:ÍgÛ~&"¼Hpù ì&év€wÀ˜¦ð6`6ۼ̽)è üïòÏuã\µÜkÎØ6öàhg ‹ªòøïp®òÝ¢Ž urîx»¾ŒÞœ¥‹fïiõШïÚi›çî{šçm¬Í?? A!†Ïš÷ÛŽEò@Û»·–ïuAÎý¦Æõ6ᘗÔýkÍÙÛäåçr›‹Âi+WÞ¿·Hö9 Q:mÏŸ&œZ^44ïÎá· ]mRx4ë,z¾y¿í=ž·4º èmrD¨§Q•E÷Îóü¹Êws°èš/#œˆE÷Nöy¯ûïó¬ÈpžçàY®ŸU·¹¬}¦™ïʹ5ß+ÿn–Ó#7¿Wþ/ßC€¿áå{ð7¼üÿÚWÉØh2ŸIEND®B`‚gsmartcontrol-1.1.4/data/16/000077500000000000000000000000001417717230600155625ustar00rootroot00000000000000gsmartcontrol-1.1.4/data/16/Makefile.am000066400000000000000000000001601417717230600176130ustar00rootroot00000000000000appicondir = $(datadir)/icons/hicolor/16x16/apps appicon_DATA = gsmartcontrol.png EXTRA_DIST = $(appicon_DATA) gsmartcontrol-1.1.4/data/16/gsmartcontrol.png000066400000000000000000000066271417717230600212010ustar00rootroot00000000000000‰PNG  IHDRóÿa pHYs  šœ OiCCPPhotoshop ICC profilexÚSgTSé=÷ÞôBKˆ€”KoR RB‹€‘&*! Jˆ!¡ÙQÁEEÈ ˆŽŽ€ŒQ, Š Øä!¢Žƒ£ˆŠÊûá{£kÖ¼÷æÍþµ×>ç¬ó³ÏÀ –H3Q5€ ©BàƒÇÄÆáä.@ $p³d!sý#ø~<<+"À¾xÓ ÀM›À0‡ÿêB™\€„Àt‘8K€@zŽB¦@F€˜&S `ËcbãP-`'æÓ€ø™{[”! ‘ eˆDh;¬ÏVŠEX0fKÄ9Ø-0IWfH°·ÀÎ ² 0Qˆ…){`È##x„™FòW<ñ+®ç*x™²<¹$9E[-qWW.(ÎI+6aaš@.Ây™24àóÌ ‘àƒóýxήÎÎ6޶_-ê¿ÿ"bbãþåÏ«p@át~Ñþ,/³€;€mþ¢%îh^  u÷‹f²@µ éÚWópø~<ß5°j>{‘-¨]cöK'XtÀâ÷ò»oÁÔ(€hƒáÏwÿï?ýG %€fI’q^D$.Tʳ?ÇD *°AôÁ,ÀÁÜÁ ü`6„B$ÄÂBB d€r`)¬‚B(†Í°*`/Ô@4ÀQh†“p.ÂU¸=púažÁ(¼ AÈa!ÚˆbŠX#Ž™…ø!ÁH‹$ ɈQ"K‘5H1RŠT UHò=r9‡\Fº‘;È2‚ü†¼G1”²Q=Ô µC¹¨7„F¢ Ðdt1š ›Ðr´=Œ6¡çЫhÚ>CÇ0Àè3Äl0.ÆÃB±8, “c˱"¬ «Æ°V¬»‰õcϱwEÀ 6wB aAHXLXNØH¨ $4Ú 7 „QÂ'"“¨K´&ºùÄb21‡XH,#Ö/{ˆCÄ7$‰C2'¹I±¤TÒÒFÒnR#é,©›4H#“ÉÚdk²9”, +È…ääÃä3ää!ò[ b@q¤øSâ(RÊjJåå4åe˜2AU£šRݨ¡T5ZB­¡¶R¯Q‡¨4uš9̓IK¥­¢•Óhh÷i¯ètºÝ•N—ÐWÒËéGè—èôw †ƒÇˆg(›gw¯˜L¦Ó‹ÇT071ë˜ç™™oUX*¶*|‘Ê •J•&•*/T©ª¦ªÞª UóUËT©^S}®FU3Sã© Ô–«UªPëSSg©;¨‡ªg¨oT?¤~Yý‰YÃLÃOC¤Q ±_ã¼Æ c³x,!k «†u5Ä&±ÍÙ|v*»˜ý»‹=ª©¡9C3J3W³Ró”f?ã˜qøœtN ç(§—ó~ŠÞï)â)¦4L¹1e\kª–—–X«H«Q«Gë½6®í§¦½E»YûAÇJ'\'GgÎçSÙSݧ §M=:õ®.ªk¥¡»Dw¿n§î˜ž¾^€žLo§Þy½çú}/ýTýmú§õG X³ $Û Î<Å5qo</ÇÛñQC]Ã@C¥a•a—á„‘¹Ñ<£ÕFFŒiÆ\ã$ãmÆmÆ£&&!&KMêMîšRM¹¦)¦;L;LÇÍÌÍ¢ÍÖ™5›=1×2ç›ç›×›ß·`ZxZ,¶¨¶¸eI²äZ¦Yî¶¼n…Z9Y¥XUZ]³F­­%Ö»­»§§¹N“N«žÖgðñ¶É¶©·°åØÛ®¶m¶}agbg·Å®Ã“}º}ý= ‡Ù«Z~s´r:V:ޚΜî?}Åô–é/gXÏÏØ3ã¶Ë)ÄiS›ÓGgg¹sƒóˆ‹‰K‚Ë.—>.›ÆÝȽäJtõq]ázÒõ›³›Âí¨Û¯î6îiî‡ÜŸÌ4Ÿ)žY3sÐÃÈCàQåÑ? Ÿ•0k߬~OCOgµç#/c/‘W­×°·¥wª÷aï>ö>rŸã>ã<7Þ2ÞY_Ì7À·È·ËOÃož_…ßC#ÿdÿzÿѧ€%g‰A[ûøz|!¿Ž?:Ûeö²ÙíAŒ ¹AA‚­‚åÁ­!hÈì­!÷ç˜Î‘Îi…P~èÖÐaæa‹Ã~ '…‡…W†?ŽpˆXÑ1—5wÑÜCsßDúD–DÞ›g1O9¯-J5*>ª.j<Ú7º4º?Æ.fYÌÕXXIlK9.*®6nl¾ßüíó‡ââ ã{˜/È]py¡ÎÂô…§©.,:–@LˆN8”ðA*¨Œ%òw%Ž yÂÂg"/Ñ6шØC\*NòH*Mz’쑼5y$Å3¥,幄'©¼L LÝ›:žšv m2=:½1ƒ’‘qBª!M“¶gêgæfvˬe…²þÅn‹·/•Ék³¬Y- ¶B¦èTZ(×*²geWf¿Í‰Ê9–«ž+Íí̳ÊÛ7œïŸÿíÂá’¶¥†KW-X潬j9²‰Š®Û—Ø(Üxå‡oÊ¿™Ü”´©«Ä¹dÏfÒféæÞ-ž[–ª—æ—n ÙÚ´ ßV´íõöEÛ/—Í(Û»ƒ¶C¹£¿<¸¼e§ÉÎÍ;?T¤TôTúT6îÒݵa×ønÑî{¼ö4ìÕÛ[¼÷ý>ɾÛUUMÕfÕeûIû³÷?®‰ªéø–ûm]­NmqíÇÒý#¶×¹ÔÕÒ=TRÖ+ëGǾþïw- 6 UœÆâ#pDyäé÷ ß÷ :ÚvŒ{¬áÓvg/jBšòšF›Sšû[b[ºOÌ>ÑÖêÞzüGÛœ499â?rýéü§CÏdÏ&žþ¢þË®/~øÕë×Îјѡ—ò—“¿m|¥ýêÀë¯ÛÆÂƾÉx31^ôVûíÁwÜwï£ßOä| (ÿhù±õSЧû“““ÿ˜óüc3-ÛgAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅF²IDATxÚT“ÍnEÇÕÝ»ó¹3³Ø^ïF¶°LŒ D }ÂÀpÊð$\È! ¼9DŽ )ÅÛŒ³³3Óݲ‘’ªJ*éÿSU©J¼÷|sçë/?ýü³/ŒV•µŽ«º o‚÷>ñžà—G~º³÷톅­¯¯}µ¾¾v£®[œ³‚ˆ ”à½G+…éiªyC?ˆ¶€·Ãa1jê–‹ós”z#FD„^ÔçŸÃ\\üKš&Ã+]ÈYRÏ*ê¦Æhý–XÍ_Ï™ÏV–—±ÖFW:ðÃwßÇ(„yÞã¬]¸£m[N^Q×s†Ã!:ì¡”2÷îÞÝêE‘ßTJdz|‚ÁxÁC[·  yVà¼ÅÍ-ãÉ*Ù ¸Õâ$¹5hë9Z)”Q¬,­“¥"' "²t@VÇc¬¸;ÀÇ>|wccú½>½^H’§€ÃèÕ¼Æ9K˜DDQÊ0Ï8Øv£[¢Öêñ§ â¡õ–òü’tSÍfDQD–e ‚Ça‚ˆ 4›`ëý­÷úAŸ4ࣼS䃥À{µ¸¥ÙÞÞ^ëFX]¯<ñœ$‰ã¬Åz…KU½¦u– è™ïÓÓ&“ky×R*¾ÿó}iÊétJ3¯‰²4å¢,QÞÑXKQ„AÈ|Þ ´˜°¿ÿ´Úò$XZz‡ß~ÿƒƒƒ§lnl2¹6az6¥™7xà“Û·)†éÍ ¢ãë®<~üçË“£ã¼,KÚ¦!Ž"”V8kéé:T€pvöŠË²dvXRdÍ+ñÞ#"Ë{{{íì쌲,›Äq26F/i­s£u߃µÖΪª:«ªê¨,/ÿžNOÝÝÝ=”«·ýŸõH€E½^—‹ìþuIÃddt×IEND®B`‚gsmartcontrol-1.1.4/data/22/000077500000000000000000000000001417717230600155575ustar00rootroot00000000000000gsmartcontrol-1.1.4/data/22/Makefile.am000066400000000000000000000001601417717230600176100ustar00rootroot00000000000000appicondir = $(datadir)/icons/hicolor/22x22/apps appicon_DATA = gsmartcontrol.png EXTRA_DIST = $(appicon_DATA) gsmartcontrol-1.1.4/data/22/gsmartcontrol.png000066400000000000000000000077371417717230600212010ustar00rootroot00000000000000‰PNG  IHDRÄ´l; pHYs  šœ OiCCPPhotoshop ICC profilexÚSgTSé=÷ÞôBKˆ€”KoR RB‹€‘&*! Jˆ!¡ÙQÁEEÈ ˆŽŽ€ŒQ, Š Øä!¢Žƒ£ˆŠÊûá{£kÖ¼÷æÍþµ×>ç¬ó³ÏÀ –H3Q5€ ©BàƒÇÄÆáä.@ $p³d!sý#ø~<<+"À¾xÓ ÀM›À0‡ÿêB™\€„Àt‘8K€@zŽB¦@F€˜&S `ËcbãP-`'æÓ€ø™{[”! ‘ eˆDh;¬ÏVŠEX0fKÄ9Ø-0IWfH°·ÀÎ ² 0Qˆ…){`È##x„™FòW<ñ+®ç*x™²<¹$9E[-qWW.(ÎI+6aaš@.Ây™24àóÌ ‘àƒóýxήÎÎ6޶_-ê¿ÿ"bbãþåÏ«p@át~Ñþ,/³€;€mþ¢%îh^  u÷‹f²@µ éÚWópø~<ß5°j>{‘-¨]cöK'XtÀâ÷ò»oÁÔ(€hƒáÏwÿï?ýG %€fI’q^D$.Tʳ?ÇD *°AôÁ,ÀÁÜÁ ü`6„B$ÄÂBB d€r`)¬‚B(†Í°*`/Ô@4ÀQh†“p.ÂU¸=púažÁ(¼ AÈa!ÚˆbŠX#Ž™…ø!ÁH‹$ ɈQ"K‘5H1RŠT UHò=r9‡\Fº‘;È2‚ü†¼G1”²Q=Ô µC¹¨7„F¢ Ðdt1š ›Ðr´=Œ6¡çЫhÚ>CÇ0Àè3Äl0.ÆÃB±8, “c˱"¬ «Æ°V¬»‰õcϱwEÀ 6wB aAHXLXNØH¨ $4Ú 7 „QÂ'"“¨K´&ºùÄb21‡XH,#Ö/{ˆCÄ7$‰C2'¹I±¤TÒÒFÒnR#é,©›4H#“ÉÚdk²9”, +È…ääÃä3ää!ò[ b@q¤øSâ(RÊjJåå4åe˜2AU£šRݨ¡T5ZB­¡¶R¯Q‡¨4uš9̓IK¥­¢•Óhh÷i¯ètºÝ•N—ÐWÒËéGè—èôw †ƒÇˆg(›gw¯˜L¦Ó‹ÇT071ë˜ç™™oUX*¶*|‘Ê •J•&•*/T©ª¦ªÞª UóUËT©^S}®FU3Sã© Ô–«UªPëSSg©;¨‡ªg¨oT?¤~Yý‰YÃLÃOC¤Q ±_ã¼Æ c³x,!k «†u5Ä&±ÍÙ|v*»˜ý»‹=ª©¡9C3J3W³Ró”f?ã˜qøœtN ç(§—ó~ŠÞï)â)¦4L¹1e\kª–—–X«H«Q«Gë½6®í§¦½E»YûAÇJ'\'GgÎçSÙSݧ §M=:õ®.ªk¥¡»Dw¿n§î˜ž¾^€žLo§Þy½çú}/ýTýmú§õG X³ $Û Î<Å5qo</ÇÛñQC]Ã@C¥a•a—á„‘¹Ñ<£ÕFFŒiÆ\ã$ãmÆmÆ£&&!&KMêMîšRM¹¦)¦;L;LÇÍÌÍ¢ÍÖ™5›=1×2ç›ç›×›ß·`ZxZ,¶¨¶¸eI²äZ¦Yî¶¼n…Z9Y¥XUZ]³F­­%Ö»­»§§¹N“N«žÖgðñ¶É¶©·°åØÛ®¶m¶}agbg·Å®Ã“}º}ý= ‡Ù«Z~s´r:V:ޚΜî?}Åô–é/gXÏÏØ3ã¶Ë)ÄiS›ÓGgg¹sƒóˆ‹‰K‚Ë.—>.›ÆÝȽäJtõq]ázÒõ›³›Âí¨Û¯î6îiî‡ÜŸÌ4Ÿ)žY3sÐÃÈCàQåÑ? Ÿ•0k߬~OCOgµç#/c/‘W­×°·¥wª÷aï>ö>rŸã>ã<7Þ2ÞY_Ì7À·È·ËOÃož_…ßC#ÿdÿzÿѧ€%g‰A[ûøz|!¿Ž?:Ûeö²ÙíAŒ ¹AA‚­‚åÁ­!hÈì­!÷ç˜Î‘Îi…P~èÖÐaæa‹Ã~ '…‡…W†?ŽpˆXÑ1—5wÑÜCsßDúD–DÞ›g1O9¯-J5*>ª.j<Ú7º4º?Æ.fYÌÕXXIlK9.*®6nl¾ßüíó‡ââ ã{˜/È]py¡ÎÂô…§©.,:–@LˆN8”ðA*¨Œ%òw%Ž yÂÂg"/Ñ6шØC\*NòH*Mz’쑼5y$Å3¥,幄'©¼L LÝ›:žšv m2=:½1ƒ’‘qBª!M“¶gêgæfvˬe…²þÅn‹·/•Ék³¬Y- ¶B¦èTZ(×*²geWf¿Í‰Ê9–«ž+Íí̳ÊÛ7œïŸÿíÂá’¶¥†KW-X潬j9²‰Š®Û—Ø(Üxå‡oÊ¿™Ü”´©«Ä¹dÏfÒféæÞ-ž[–ª—æ—n ÙÚ´ ßV´íõöEÛ/—Í(Û»ƒ¶C¹£¿<¸¼e§ÉÎÍ;?T¤TôTúT6îÒݵa×ønÑî{¼ö4ìÕÛ[¼÷ý>ɾÛUUMÕfÕeûIû³÷?®‰ªéø–ûm]­NmqíÇÒý#¶×¹ÔÕÒ=TRÖ+ëGǾþïw- 6 UœÆâ#pDyäé÷ ß÷ :ÚvŒ{¬áÓvg/jBšòšF›Sšû[b[ºOÌ>ÑÖêÞzüGÛœ499â?rýéü§CÏdÏ&žþ¢þË®/~øÕë×Îјѡ—ò—“¿m|¥ýêÀë¯ÛÆÂƾÉx31^ôVûíÁwÜwï£ßOä| (ÿhù±õSЧû“““ÿ˜óüc3-ÛgAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFúIDATxÚd“ßn$W‡¿sN÷éÿ¦gì¯XÙãÍ& E‚X\±ÀcpHHp âÄpÇy„  y„\)ˆdÐîjwm'ž‰=žñØ3ížî>‡‹±gQ:R•TGŸªêW%¬µüì'?}û{?øþ/¢8VÖZ’8f6›Y¥X°X„Ö‚v5Aà‹Éd`?zðáïÿðþ8\[G?º»w÷ÇMÛ µÆXKšeV qXX‘¥E¯Çl6#N’øùÿ€;EqÏ|æóKš¦¡iÚ›B¬ BJ¬1¼2ÁRU/Ÿ¿ n›oßdÖà8Žn'qÄx|†µfUˆkÀõƒ×:p”âô«3^<{ÎF“¢(ÿN¢lsY7TMƒÂ¾xº‰…H©ŽFe¹Q½û:x7 C6{Ç_³–Ì‚µ¥\×AJïûø~€ÖOk|/ÀKÅìÜyƒ0 ßY‹§=o7Ësšå‹EI¹ÞÙ$É“‹É%ЉbÎË a¬ka•’ÄqˆÖÞÞºâòª¼›§uÓâ:.ÊqPJáiŸî-Ÿ½ ,/hkâ©ñŸËrAùhíáiå:t‹‚óÉtg]ñËÃ;y7å|6e¹¬R‰%ŒC6úêºFJ°‡Ã—¸F¡ýY/Wë%$qÓZ³µoíöétÊæf—áð©ÄzGÏ ÃyU2_”,.ÏI“œ[½>5-< …@XÉ`ÐÏ×uü)’†Ãtà …ƒxu4u뺙¦×í‚Û\$BI¤¼¹{ÅÞÞ^eÙm' í¢×ÕÅÆYšrqyAcaà¡‡å²ÆÚaRIV¬µ+á¬E pØöjA·×“EQÜsŠNçëÆùùgŸ³}gc`tô’‹r‰1KŠÎ~èc›%‹Êà p—ÈP®ÄQ.­5ÌOÇL¦glv ƒ=g°µõV’$H¡ñàw¿…£4uÛÐ.fó SÏɰÄ~ÂR5-ÂZ,i$[[Ò$b±(ÙÝÝý¦“çyòèÑ#>ùË'¤i‚ö|Žxqp@'‰ˆ“‚N§ƒxLÎÎPJ’ÔUÀÎöRJêeͲj)z);;;}g<ŸŒOÇ|qtÄU·K†<~ü˜Ï>Äó|6{ý-’8á?OŸ’w2”RÌ/ Ás„UxÛ-ñ^Ëä_-ÇÃѱœN§#!ÊqV‚Yš¯®Î÷‰â”0IÒ„4MÈÓŒÀéQ’u2‚(¦)[FÇ'¤iÆp4<Y–Ýûî{ï=|úä‰ï:Z{œœžpøÅ—¸Râhßw uÀ|1Ç=l#0ÖP·Knmn¡\…šªlxãÞ¶ùô¯ŸþЙN§ÿBìïïï¿uÿþý¤ßï§yžçy'‹=$®v×Q¡TJ»ŽëZhmÛXSÕeU].ÊÅUUÎóÅùt:žÇ{ðç?þï*Ò hF8nIEND®B`‚gsmartcontrol-1.1.4/data/24/000077500000000000000000000000001417717230600155615ustar00rootroot00000000000000gsmartcontrol-1.1.4/data/24/Makefile.am000066400000000000000000000001601417717230600176120ustar00rootroot00000000000000appicondir = $(datadir)/icons/hicolor/24x24/apps appicon_DATA = gsmartcontrol.png EXTRA_DIST = $(appicon_DATA) gsmartcontrol-1.1.4/data/24/gsmartcontrol.png000066400000000000000000000026121417717230600211660ustar00rootroot00000000000000‰PNG  IHDRàw=øQIDATH‰u–Oo$GÆUÕ]ý¿§§ÇöŒ—5öb¼Ù¤H‹/ÀÂ×àHHpqñr˾C'{BJrJ6 ‹P²Z¯mðøÏxÆcÏ´{º»ŠÃسÞ8yÕ¯êUÿyžzëyTÕÂZËuüâg?ë‡?þѯ¢8VÖZ’8f<[¥Ôü ‹!ÀZЮ&|1ì_ÞÿàÝ÷þôÇgט7"Š£ŸÜߺÿÓº©ÑZc¬%mµÌÄUaaÎ(¥È—–ÇÄIR¿üJ‚vž?ðŸÉ䂺®©ëfñLˆ9¸k̯KY–¼|±CÕÔß¿‰ùAGkI1œb­™ÏVÄÐÕ7:r”âäè”ç/Xî®çyïk ’¨µ2«jʺFao½¼®…H©èòùçÏq”$ }²,kßÄ”¯iFÉe]Ó”SLÓ`š†¦1¯Fc0¦ÁC]7ôû}Žyë;o°ýƒm´EqøÛ_ÿFß"øÃï~ßõ´×%MÛ4Æ`¬ÅZƒ±×ଥ®*N'EA'~ÌølÌp8$Š"7ƒµ[Q½¡”RÿÇSâ4ž»Ë+[°PÎ*ÆççH ŠB|ßg8<ÁƒçùÅTFQôÎWl†aÈÊRÎÁÿXHkÁZƒR®ë ¥À÷}|?@k§5¾`Œ%Žb6î}‹0 ß¾%²ö¼ÍV–QÏfX,Jʅ瓤E˜„œ/ð€vsV”[c…\@)I‡hímÝê ¸,îgiNU7¸Ž‹r”RxÚg¹s‡½ç;Å9Me˜Ö~âsQL‰#­=<í¡\‡Nžs6mÜêàåÞ“r61›•)ÄÆ!ËÝUU!¥@X‡½þK\£Ð~„¬fóÙ IœÄ4Ö¬Þ"X¿»vo4±²Ò¡ß?F*±X¢Ýý„a„¼,˜L ¦g¤IÆ¥.-< …@XI¯×Í^["×uµ»k‘DÐï£íyh­ç)êªÂu]òV‹o®¯s÷Î7Jâ*׋íj B±µµ´Z­µEa®æK//ÓJSÎ/Ω <”ã0›UXÛ ¬@*‰ÂÁJƒµv.°µnÓ\Né,-É<Ï{@ÞnÛ#ú˜õ{‡û/9/f3#o/ã‡>¶ž1- 7p‰üåJåÒXÃädÀptÊJ§G¯×Û9½ÕÕ7“$A EèhŒo¿ó=¥©ššfV3ž”˜jB KìG ,eÝ ¬Åb‘F²ºÚ#M"¦Ó‚ÍÍÍï.–(˲äÉ“'|üÑǤi‚ö|ö÷vÙÙÝ¥DÄIN»ÝÆ <†§§(¥@Iª²`c})%Õ¬bV6äK)ÝÁ`08œ øïþ>—aòìÙ3>ýì3<Ïge©M¯»J'üç‹/ÈÚ-”RL.¦ Ás„Uxë ñVÃð_ ýÃ…‹F£Ñ¡å8sá€V:wZâûDqJ˜$iBš&di‹Àiç9QÒj·¢˜ºh8<8&M[ôû{‹ŽŽŽžøÑ‡—;;/|×qÐÚãøä­=вäààáð”PL¦&cl-0ÖP53ýõÊU(¡)‹šÓŸ˜>}úxA0þ-„ØÞÞÞ~óáÇI·ÛM³,˲v+öt¸ÚM\G…R)í:®kh ±MmMYey1-¦—e1žN¦g£Ñht:|òþŸ?øÛ|³¼ñWq#2 äWu ˆ¸>L*`L€3`œÞë¯#W}9_:s•ÍàuήîóD3«n–Y±IEND®B`‚gsmartcontrol-1.1.4/data/256/000077500000000000000000000000001417717230600156505ustar00rootroot00000000000000gsmartcontrol-1.1.4/data/256/Makefile.am000066400000000000000000000001621417717230600177030ustar00rootroot00000000000000appicondir = $(datadir)/icons/hicolor/256x256/apps appicon_DATA = gsmartcontrol.png EXTRA_DIST = $(appicon_DATA) gsmartcontrol-1.1.4/data/256/gsmartcontrol.png000066400000000000000000001324571417717230600212700ustar00rootroot00000000000000‰PNG  IHDR\r¨f pHYs  šœ OiCCPPhotoshop ICC profilexÚSgTSé=÷ÞôBKˆ€”KoR RB‹€‘&*! Jˆ!¡ÙQÁEEÈ ˆŽŽ€ŒQ, Š Øä!¢Žƒ£ˆŠÊûá{£kÖ¼÷æÍþµ×>ç¬ó³ÏÀ –H3Q5€ ©BàƒÇÄÆáä.@ $p³d!sý#ø~<<+"À¾xÓ ÀM›À0‡ÿêB™\€„Àt‘8K€@zŽB¦@F€˜&S `ËcbãP-`'æÓ€ø™{[”! ‘ eˆDh;¬ÏVŠEX0fKÄ9Ø-0IWfH°·ÀÎ ² 0Qˆ…){`È##x„™FòW<ñ+®ç*x™²<¹$9E[-qWW.(ÎI+6aaš@.Ây™24àóÌ ‘àƒóýxήÎÎ6޶_-ê¿ÿ"bbãþåÏ«p@át~Ñþ,/³€;€mþ¢%îh^  u÷‹f²@µ éÚWópø~<ß5°j>{‘-¨]cöK'XtÀâ÷ò»oÁÔ(€hƒáÏwÿï?ýG %€fI’q^D$.Tʳ?ÇD *°AôÁ,ÀÁÜÁ ü`6„B$ÄÂBB d€r`)¬‚B(†Í°*`/Ô@4ÀQh†“p.ÂU¸=púažÁ(¼ AÈa!ÚˆbŠX#Ž™…ø!ÁH‹$ ɈQ"K‘5H1RŠT UHò=r9‡\Fº‘;È2‚ü†¼G1”²Q=Ô µC¹¨7„F¢ Ðdt1š ›Ðr´=Œ6¡çЫhÚ>CÇ0Àè3Äl0.ÆÃB±8, “c˱"¬ «Æ°V¬»‰õcϱwEÀ 6wB aAHXLXNØH¨ $4Ú 7 „QÂ'"“¨K´&ºùÄb21‡XH,#Ö/{ˆCÄ7$‰C2'¹I±¤TÒÒFÒnR#é,©›4H#“ÉÚdk²9”, +È…ääÃä3ää!ò[ b@q¤øSâ(RÊjJåå4åe˜2AU£šRݨ¡T5ZB­¡¶R¯Q‡¨4uš9̓IK¥­¢•Óhh÷i¯ètºÝ•N—ÐWÒËéGè—èôw †ƒÇˆg(›gw¯˜L¦Ó‹ÇT071ë˜ç™™oUX*¶*|‘Ê •J•&•*/T©ª¦ªÞª UóUËT©^S}®FU3Sã© Ô–«UªPëSSg©;¨‡ªg¨oT?¤~Yý‰YÃLÃOC¤Q ±_ã¼Æ c³x,!k «†u5Ä&±ÍÙ|v*»˜ý»‹=ª©¡9C3J3W³Ró”f?ã˜qøœtN ç(§—ó~ŠÞï)â)¦4L¹1e\kª–—–X«H«Q«Gë½6®í§¦½E»YûAÇJ'\'GgÎçSÙSݧ §M=:õ®.ªk¥¡»Dw¿n§î˜ž¾^€žLo§Þy½çú}/ýTýmú§õG X³ $Û Î<Å5qo</ÇÛñQC]Ã@C¥a•a—á„‘¹Ñ<£ÕFFŒiÆ\ã$ãmÆmÆ£&&!&KMêMîšRM¹¦)¦;L;LÇÍÌÍ¢ÍÖ™5›=1×2ç›ç›×›ß·`ZxZ,¶¨¶¸eI²äZ¦Yî¶¼n…Z9Y¥XUZ]³F­­%Ö»­»§§¹N“N«žÖgðñ¶É¶©·°åØÛ®¶m¶}agbg·Å®Ã“}º}ý= ‡Ù«Z~s´r:V:ޚΜî?}Åô–é/gXÏÏØ3ã¶Ë)ÄiS›ÓGgg¹sƒóˆ‹‰K‚Ë.—>.›ÆÝȽäJtõq]ázÒõ›³›Âí¨Û¯î6îiî‡ÜŸÌ4Ÿ)žY3sÐÃÈCàQåÑ? Ÿ•0k߬~OCOgµç#/c/‘W­×°·¥wª÷aï>ö>rŸã>ã<7Þ2ÞY_Ì7À·È·ËOÃož_…ßC#ÿdÿzÿѧ€%g‰A[ûøz|!¿Ž?:Ûeö²ÙíAŒ ¹AA‚­‚åÁ­!hÈì­!÷ç˜Î‘Îi…P~èÖÐaæa‹Ã~ '…‡…W†?ŽpˆXÑ1—5wÑÜCsßDúD–DÞ›g1O9¯-J5*>ª.j<Ú7º4º?Æ.fYÌÕXXIlK9.*®6nl¾ßüíó‡ââ ã{˜/È]py¡ÎÂô…§©.,:–@LˆN8”ðA*¨Œ%òw%Ž yÂÂg"/Ñ6шØC\*NòH*Mz’쑼5y$Å3¥,幄'©¼L LÝ›:žšv m2=:½1ƒ’‘qBª!M“¶gêgæfvˬe…²þÅn‹·/•Ék³¬Y- ¶B¦èTZ(×*²geWf¿Í‰Ê9–«ž+Íí̳ÊÛ7œïŸÿíÂá’¶¥†KW-X潬j9²‰Š®Û—Ø(Üxå‡oÊ¿™Ü”´©«Ä¹dÏfÒféæÞ-ž[–ª—æ—n ÙÚ´ ßV´íõöEÛ/—Í(Û»ƒ¶C¹£¿<¸¼e§ÉÎÍ;?T¤TôTúT6îÒݵa×ønÑî{¼ö4ìÕÛ[¼÷ý>ɾÛUUMÕfÕeûIû³÷?®‰ªéø–ûm]­NmqíÇÒý#¶×¹ÔÕÒ=TRÖ+ëGǾþïw- 6 UœÆâ#pDyäé÷ ß÷ :ÚvŒ{¬áÓvg/jBšòšF›Sšû[b[ºOÌ>ÑÖêÞzüGÛœ499â?rýéü§CÏdÏ&žþ¢þË®/~øÕë×Îјѡ—ò—“¿m|¥ýêÀë¯ÛÆÂƾÉx31^ôVûíÁwÜwï£ßOä| (ÿhù±õSЧû“““ÿ˜óüc3-ÛgAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFªJIDATxÚìý{Ü%YZˆ>ïZ±¯ß=¯•UYY•Õuëîªê†îiè/ãeFeô â8Š0€"ŒüŽuŽ£þœ‘#:sæxPÑu怢 #G Aº›¾Tw]³*ïùåwÝ÷ˆXë=¬µ"VÄŽØßþ®™YddíÚûÛ;vìˆë}×{yÞç%fÆÃíáöpûí¹ÑCðp{¸=T·‡ÛÃí¡x¸=ÜnÀÃíáöp{¨n·‡ÛCðp{¸=Ü*€‡ÛÃíáöPT„èž^Äq)±¯þªÑßþ|/À™Ó§ñÄåËÙOÚÇÃí˜ãößúô§1²þã¯ü ÿùïþnþí<ÿ*€šíÏüWZœä;Ýî?ž/?uO^¾ÌÀƒ¥ú;;´µ³C¯¼ò lnnÑÎÎ6ÇIÂWß¹ªŸzÏS8Œ"øm¥¾ý[ÿë©÷þÁþÀ±^à·}Ë·R<™üî ~ð‘ .>õô{Ðl4 ´Ž`ð©îm{l€H˜5ÀÐæéOî׎pÎÐ~®jŸƒpœËòQþ(UK&ˆˆ@nÜ Dl473´½öo¦ÒQh??F•¦Øíõpå7·nÞúƒúg?ò/¯÷X~Û·|ëÔ{ßÿÿp®ï‚ ôz?|êôéoúÀ—| Þóô{Ðj·!„ØSó‘g¯—ïÛ¿¹b?_X3ýbæQö›Ìîkœ}ž)÷º¬œ¼sí]˜C…s«øj𔯆kféôHd¿iO¤j5cæ=W9¶?I3m¯;ã¿?†µ÷™¼óŸ’Çì„húb‹÷šù%Vœ òcîqyˆÍxá…ºW®\ù+ŸùÔ§¿ë¿ü/þ‹?÷Ãÿâ_ü“cU¦‡Ð£ÁIþØA¶oúcìë» ÝoúÒïá…ÂDœš”´÷ZH3Ì(òŽË‘|oq¾‚L™_þï»×{ É:H5Ïò_7F¥÷OƬ¥Ù×A4÷XÒ¬Q1–@¥ÉÌÌ`òÖþLÀEµ•Rwî}Ièt:xöÙgÑi·;ƒAÿÓûcÿþÿó/ß8€“÷w>°¶¶†S§Ne‚‰iÝ ¡ÕjA+¥’4­\ ëÖ’‚¾÷Vo§ ûY%ÀÌùdª—šÕkžsÚÏý šõ´ðÙ>hn×°n .[Eû8÷²µÃ5cIU¿U¶Ä샭EǬ3ë±pÜòyÚcUÙTfJ)¨4E’¦¤œ²¤”XZYÁ¥'ž@¯ßÿ#¾ï~4î€èc+««è..ææwI`Ïœ=‹ååeL&|þsŸÃùGÁ¹¥%ìììàîÝ»ˆÂ°Þds7Ú_슠µFš¦h[—žÐ›Ç^âK%£€Jžüšk8¨ò7|3=MS¤i ÖzJÁÔ™å¼×«ri¬‚¬³Qîªù–›¯h1ÃíažR <Ãbr÷¨ s®Iœ NÏÇ/Yº3}èÒÊkŸ…‚aBYTÇ\¥TŠorUܤðÄÞß\­¨fļáÌÜ7"Âd<ÁÙ³çÐô±¹±)¥9.ýŽ·Ð=ÇåÕU,//cggׯ_ǵ«W ó¬Ýé`4"Ã,@†!Áš^8Þ5òà `ßÒ¼¸¸8õ8æí£ívÒ™mÞ`t$ ^{í5tÚm¼ïýïG§ÓÁÖÖ†Ãz½^&ìœ=#ÿ»Bz“$ÆÒâ"xÖ{q€êþDeo"SI¸¹è¤ìÏ`_ x:zÀTµz;A•h4XXX@«Õ´«qQvª¬_ÿ}ÿ†!Úí6:¦]qKcSÔðã¥ìâñA3×íÙv™{@…ñ#4Ó\q#oç‰7:J+lmm¢ÙhàܹóB"Më%PeLɹzNišâµW_E·ÓÁ{ß÷¾â<‹"ìÚyÏ5ÜÝÝEoÐëܯö¾-€ÝÝÝ“µ$™M{ŠÌÀ`0@§ÝÁsÏ=¥”1Å¢O?óŒIËìö 3!wlÓ‡\”/"H (¥±¼²‚±I•B’$ÐJ]‰y¾0ÞÑì2ߘ @J„aˆ(ŒQ! #ÄIŒñdVzf,Ä?©(ŠÐh42ëÁpÑ­©J¡µžVX¥ ÒZÛ1›Ûù˜¬¡jwÎ7çÉ­Ö ƒ½^Q£sçÎacã.úƒAæ&3=¹¥2èÐétðÜóÏ›y¶½Ï3¥pwcc*¸xýú |üÌü±ûÕNòÇö»ýÉoú//,, Óî@¨4)×ïÜÁxanZ)4[-\|üq¤I‚ÛwîàÊ•+xîÙg§üMÿoÊüD!âIŒµµ5¤qœýTš¤HÒĺlž .2[Ÿ Çä’‰»×Ü®ïydÄO-jDk$I‚†!šM³bGQ„FÔÀx2Æh<®*Ú‚ÀÄ?*îù$ޑıÁbœ3X÷n¦Ü)ÐVqf34€8޳{8™L°²²‚N§ƒ»wï‚BB hÏz`ÆÆæFãºnž5›ù<»}W®\Áûßÿ˜¥R¼öêkxó7p¿Cíïk€ÈvHä&£óëœ–ßØÜÄ•+o#U)¢(Âöö6Ö××Ñj¶ðÌ3ÏL P&°žÿLD‚ •B£¡Õjb2™€íʕؠ»UÌÄ”37³`¸—&c}¼¾b‰£’«P±Ê•ß®òá53&qŒI# 4›MA€†}ÆHÓÄ·ÃADh¶šh4…c*•b2‰ÇLçËsV7G|¬Ad¢p=T—*¤CaÄ™Á¥1eÖyöÈÞ4M¡´F†ØÙÙE³ÙĹóç±¾¾Žx2AF`æ<6ÆÎöÞyû$i‚(а³½ƒ;ëwÐjµðüóσÁÇxåsŸG¯×C§ÛÍ”çŸúÿåËÿøŸþðÏ?ðÀIn‚èkVWVLœ}WÒ 1#++& $ –—–ñøÅǽIQœH¬íjí’C^ªO)…ÓgÎdÂÖHcâ²vACžÒìš«Eìù±\“Ÿžì•«¾Ã*pñHu>|Y<“$A’$ÂíV °ÐífAN¥5„ „AX2ÝÃ!Ò$õsì(yŽb`®‰/2[D•º‘kžkÇ´ÔSD€"‚`ÆDk´3âx‚³gΠ×ëa{ga€2°ƒµ‰¬ZÃxaDZ³½ÏþóH’$;ÐJÀòC`ÿ¿E‚RŠZ¹ |áBÔh˜TŒ7M‰œÏjvô'\63™LpîÜyÄ“I6‘•R˜$Ælå‘E¸&ÖÅåOÉ9bþþ…ÑLçIy‡¥ŠóK“»Éš&Zí„R'ƒý¡Ñx„Ñh\y¡™ráêÜ=Ñ>}}ß§¯‡²¹Sø›¦Ù? qfá¸ø Ç1Tšk`{n­V wïÞ…Jd˜#¬»Ól6Ñl6 ÁÅ;wîàµW_:ÝN»ƒ^o^ð¯xàD1ㄯZZYF§ÛаD¥v–“mÖT·x~&‚ ‚#qúÌ$qœ}AkIÓßN Î%Ü·ÁK+—%À­BPïCÄg߮οÍ<ôhùl ÐÔØLå#¹^Ø+ö§ ‰Ÿr83i²ãi¬ÆÌÃý~Q£³gÏbksÃÑA€­‰ç²lS‡išâ·ßÆ7|Ë5{µ@°tÂá‘ßÈøÜ?ïa”£²” j>iXk$IŠ3gNC%‰€Rn¨4õ¢×<‡.˜ŽlGïcV­BuDžª¯½¤Sû¾w\¥úý>úý>„” [ÔR{%|n@Q 3‚5cWùºêwKJÙv)òQ‚̈™¡¤3#M,-/£Ùޱµ¹i‚ÄBx÷1ñꫯbgg§v7¢6ŽõÒ»Â8I  ñÑï7ë"®¾ÏßY]]1~Bh°â$6h:/ÝÇup׺‰]Î:x¯yê¨Î±ßëöz¿FP•ñUk÷¯+7š«i¯hxÍç¼gAP~­ÅÕžçð4Š÷Hh”¬5Â(B¯×C«ÕÆÙ³g±±±$IŒ’„I‡¿úÅ/b<+²â÷H4ó@ê¥wEàd—û¯Õ-ÕjVOÏÔ6x¦ý`¤”ƒiš ‚!Ž(­2Óß OEã}´ ªèlO6ÌŒ½¦'í%XäûàŒ*¯™jEµÆï®±"Ø4˜Š.ºïz—³Œ¢üç «vu®²”¦*újÆ€ý€e]fBXÈ/3=™ CðhA„S§Naw·‡^¯‡m\yë tª e­4$táš]öʹVÇ©÷«ðÿ™?õ§¿zaqÍfc Á¦íªì"´>†Œ)Ÿ¨Ùª]z&"Da„0°úOÃFø“ñijÀ>î78ƒ—Š€¼G¥è‚…ÙߨØ';ŽçLWœ/2D›‡9œ:Æô±¸€~ô^W˜:^·ÐÙ]¸>F”§\ pée!ùyç0×îSWU™ìÓ˜Mÿ¡í=ÓÞýŽã“ñJ)ôv{h·šˆ‚WßyÇdŸ¢A˜ µ¨64‚Ùît3”á·üéo>%àŽï?ø,€BH! 8GÊÒÔâÒjItÖÐ×Q@Úãù°·h­¼ï³—-¨†‰Vûñ䥯jVa´‹0Ë ¨Ê›ûÀ/Æ{¹ŒúµÔþ@±zÐç>©«}(gF¸2h?u %`7ppö›i@Õ´µÆäYŒD`ky²Í ݼ~  ï?®\yw77 ­Rk iCc/& "v À•Ú8)@ÀËKK‹èt:žùlÊ9³*;W"rò'ø`‚ DàIA6©Rˆ“d^jwâŽ3WÊ)W†ÌÈ_•¸¾l×߯ õZNgµ¢xÈw9 å²—kAMùÊ_&*áj÷‹sS»±á •óô! ×JÕÀ'çÒÐCsf¨Ê%)WBåwº@2žÓÛÅ×!@XXèB3ã™çžEëʼyåŠ)ŠÒ€ráφ`4›ðn‘ H+ôNpò‚²«ºƒñæóŬXRL<†½imjE˜Ñh4H‰$IjïÁââ"nݼ è]R"6UÇ%tR§N­áÜÙ³ØÙÙÉR…s“â45åªÖ¿ŸeöMïÔÔÌ2]Ë‚R·r—ªùª‚€³Ý‰ü}áá#ödHöWô:Ó½ÖÖ®¹¶ªÏöPG¢K9u_8¹‚‡+"Bµ9ü²Ð–óênUó'wL]ÂdÉ Ïõ ÒwP2} çRAˆ™qz+;*ÈN ç\º>öÇ«ôòXréšæ ë¨0~¥ß›y?*Αg(WpëÖ-¼õÖ[SÂ/f0O»ÅeyyZkHO&üZ”îÂBöþw|Û·½|À‰áNJ1Ea˜qÏù7W[@3£Ùlf•]BO `G—@2e“vž•¢æ=òÓS3t~0jHý¨f•q+uNŒI3NòÐ¥g(Ÿ÷ÌvÕîIY˜ògšÃªP¨³,«Ú:á}ñJ%Óu= ÜûišâÆõëØÜÜ„¨á_ô­&öbK£ÁÍÕÂ00÷W-wB(I¤„RÇCžr¢¤ tQ@½Ül6…aaåÉhžˆ …@($i’õ°6‚oö×sšƒûäê"ª̪Ðã™aêŠ1fÿ5ÏwÚ3¯29Ÿrqh¶[D•á³½±ùu®‚Ÿà9¿V»RPµ WyìR6c<ãÚµkôû3§A u{¥S)Ñév€Û¦N†@`òŸÖÆî´;†3xÀÏ?°1€“ "jD 4šÍ †Êžv'†­†ÚFô EVMf“¯Ö<]6^ ’ªˆ —SaåIä§ÂÈã¡«ò± 5- æ:Š —ÊÌD)¸^Ãt^œü¼‡fc;=ÓoŸjm䘑ªBå1ð232'\­¹6+@à©ó¦Êƒ±7h;ÛÛ¸yãâ8.‚w*œä¬˜Ì—Úûîø"›ÍtšB6›PB‚•™d Á‚ ƒÀ¹G^|²AÀ“É|e£!”AAøÙKç5›-Sºª”‰ö³Îo³Ô²‡]÷i\(>¡J“<'ý(–¤ri5-æá½×•¥»5BQÎSÍéŽTÁsyŽ$7ÏÉÍuw‹’^+´uÖUŒÁ^¿K3EUVi%ÑnëwîàÎ;&v$æË›H m|4™€ˆÐî´!ÃÀÌAÛ?’A$MŠÐ¢wMÙðK¸¶ûÒ "Š¢AzÈ>á‡Q# BÇ&Cëˆ:yš:ÃÌOÒB¤Á{» sº¼‡ËQ—`”k÷ÊÌñcsìVoZW ˜R RÕ â” ÿ€÷È$ø'§µÆ­›7±¹¹¹/WЯ>e°¥b7rX\X€¡T uh’Ò ˜ÊS«m–hp"‰¯”A”ãûµ7ØL‚v«mVmÄA{]p|ÿ)PË”]Leôã~ ¯¶h…k\‘Û«s7j'_¡˜®¸:³~óÙ‰ÊçE¼gN“öÔ:iò1ÿb/×&״ʼnŠç'j°å“8 ½z<‰qýúu ƒêè묣2&THZ CAˆF«a¸¤„ ë°0uÐîf­ŽÞï¢ràÿÛ_üK„aX³Ê£á²4LÌŒ(в´Ÿ6_yé®Cú­±È+4¡|21ë¢oW.µõ]†©(uM™lé³ÊÒaøé©½øÊ­Pº[w^Ue»¥ó-ŸWUym±ÔÖ‘>KïT:n*XÊ60–?›ñçü3®yìiUUíü+îõz¸òö ‡xî|õc*¥fö„©q4íãHPV´fÚÈØ‘áÆø®ïüÎKG4ß»,úÈ,qÌ*c4¾lšP¾>ÒxæâRÖˆQ1Ÿ# RÞ U7ÃçÎVÝ&ç¬æ–S+S¯]ùö…ÑT­ÎMà}¬–û-µ-?ýs/`$ücì¦Ä,PÒçèã(666 Á§Rsu®'.-[à…0´qͦ¡PW¬‘·ø8þ‰(òÝçK¸OÊ‚ƒƒé›ãõÿÛí6‚0Ì0E6vÛ˜ÿij€ZMvtxSœZ!¨XMÜ>ºê;寢uìVD¿ÞÔ#§,xÌ3ë ªró™9çêüç*ÁÙ+ßVB´ñ åRèÚ\bæ¡òïÍjbP…žô‹–jºõ–ÇÉá›7±³³S|<@À{ -I@’˜îÀÍ(2¨U"ƒ˜Îl!Š"ÄqìÀÑy‡X•ï»b "z9ÓrÙ ´T;¤5ÍL¦h¦W þž+\iEçò}:«Â$,÷²¯òÑ+üd.¯6%sŽæ~®"ÇAXA`B³HM¸&%X¡DýßeÏ*((½YÓhÞº‡r?„ŠûÚó›7oZk±—™íý`{ÚÝ.Ò45¦¾”™ò%gÉh6ixÄ.ÀI’‚Ø€Â(Ê[-—à® Bga$„5ëX§Å•±¦»§é[ÎáW¬Z•pRßEØ+…W^ÝK|x…yÅqgaù¹´Zú¿Ye~×™äìsÖøf™ËuûîáJx}ñÆk4âæÍ›ˆã8GöÝêd~N)°] †C£d:­VF1—§­¨hfôðø›8î4 ½íÊ뚀z%/X•&Iþ9[ÄÅ P…M4%ô¨«×51‚¹R~{kæªâ\ Ãõl/_t¦]‰Ù´ãu†ti²×šëõ2R HšíÚTç$¼=¨"NR²°¶·¶°ywJ©C ÿT €ã˜ÐÂTkK¦Ðjw „„N•Ý?oK.,gE5ýÀƒ©Ž}ù'Zk6[ˆ€2%·¡[XX0Ê@»H¬Î²^š‹¬ASS¿Ü‡«ÍÈò×¨Îæ¦zß¡.ÅFÕ¨œY6råî\Æãs…RõSuÈþ=Vù=-š*ç¯dúÕùéùÉÀ[¥ûvçÎÜ߯,»8¬åjÛ±˜‰ €x2 $Úí†Ã¡~í_«Át;m÷Ý—ØÀqZí¯þ·4 ^–é§0©]n¦G›°€ß~‹Y{ ¼”Q‰MIm%…×øƒ{Äõ«¸±§–Ä9Á6¢:³P¯„öX†gîF…ݸ..H5Y›©¯¡Psaž_ñ̓QÐJãæ›‡îWf;ó‡ðŸs£ƒ¡µ%Ñ@¢ZAˆF£ñxlÜSNmð0Ð v¼|¿,¸÷@Ê`!BÓ¯ÝÞ¢ºk^·[-Ь§¨¬)³4Èö±'”rÜ\ó@ñ‘çèË%¥ä­¾öÅ_³¬²fQÌ„ý\xádP Ï)¾‹½¦0 U²TIÒ_!ª%Œ€ï^åÁÀ" Ue¨.»%O 3|EÖÁþè9ôÚì9qõ«Vøíl³g5•þ$Ô Ù«¦¢PB»Õ2™)¢âiØÀa»ÕÊŽõýo¾ç­€ƒ#î«Z€Ñxü0 !a£ù:;9„”€˜L&FÈ”*tìõs´óF÷6Ç©†3pVr©NëfÖzQ5If]€*’c{öó¨ÎÀSk|¡þ¡*ÖW5VÌõEM…®&>ß#Ü–b–‚-ѧyô{X__7%¶{ áæ›²4×Üw…Cä©Éáp„•åeDQ„@~JMŠsŽC®Gf¦YWpÉ?,5š H!MW–€–@h5›^· |ò(f«™m*¼ ^݈յÞ1pƒ™LAÕŽA‘8ÓïA8wP-£Äžþ©ÀZ•Çá+Y¹~®ÿ³L3VW5Y%>…8OÿUNbF‰s©7A93³¹±‘ùû‡™ûugÝîB˜sÒÖ`mVý……%(­Ùq“%ÐXXè¢ßïGXü®¡#Âi*¨2mÖZ‹!3p<‰A[hMT°þTj­RŠŠçªŽçЀUœþ¾ _Xkª@:Œéôc V ªÏ S2´W¼¡&K@%ÅÁ%‚ÒJŽ@¿‘HÖ9™j9당ÛÁ•–ƒV¦ ÷h4:\ƒCl 5—™ƒh;Hh¶š†øÃ*ð,èXzˆp¿m÷Uˆ–šÍ¦AúqÌQJÁÁ„ë¼ížEþéyçL0fp×Íè 75!§È+Kd”Æ¢¥JbÊ=ùûªÀ<µ~~}“ñYD¡uÆo˜hæ.°áN+µ2Ìw¯Ž…UãO&¸}ûv²ë(çâ<Ç®C0|D¦ù;MS4 „a„T+CU¯T=JaiqÑ”>v’çþ@(ð*pEWÞf³ !c0”V…‰ëËTËo}·ÙZOeÿú9ˆ4g ð ¬[!EY_Ç­_Y)Șï,ˆï\”*+f¦ëá’|ˢʒ±ç3qw}=ã~<î9êºýú|€ä¥Ú„¿¢[Â¥ˆZ­–!©  c§.ÙãÞ™€ûÊD§›FQ€íkÅŒF³i‰@M?:Ç ®à¬ ïì§Íwe«B™x“yî ¸Z‘hÚLž‡,¤bì¨".à[1{ñ,Á¿Þašf¸îõöζ¶¶ŽTèH(cÓǘîà,H í)äâ8F«i\! ^–ݯyçÖívÝË#Ëœhw`ªøw„ò>)džò¦_›†)ÂHcCªµ İ/.¦÷P÷( MUü PCL¥ïÓtŸ0ëûîõsóé‡yº Ïw}uÇ*UæÎ<ÍT8TÈTPyŒÊé©=ÆIkÆúulomWιyYp…W÷$³N?BšÖs¡ Lsû¤D %¤}!L×))ÈÐ|OHHdžUª ˆ°°¸V‚üD`"’A]Ã_ù‹éH¬€wMw`)eWJaèÀméeaÕPÚðÚ‰«áSkæ ‘}VsMqãóôµ9þÌwgg™È€= æZ^}Q»{¶pŠÙ&ú´­PEíY‚D×¥êPjñµ7®õI×êÆ SÙ8%I¬ß½‹$Ž _eEZs]m%ç„ÛDÖýGûLÉŒBs·@HÈ@BBH ¤I„îK ÿD©¥ ´ÛŸƒð%1Aèñ»Ç¤þê_úË/IXØdnZj‹½B Ýí@H&“BPöâS“’‹<•s¦†Rzvkl®Î,Ì0ù«½Ýš#—+e+,²b@rúÚ³ü¹}¿Ðꌫ#Ì%i÷s{T7RUJÓS$¾¾ô«LÆcܽ{7#s9ŒÐûþ;¹Þ‘$!@VˆHAŸ5 ¡sBZ¥Í‚b¯[’þ0ŠÈRJ(K,5£b8ÄžœO„2×wé c`%j6 ƒA™‰¢uf«H(­AÄÈÊ‚m…ÍT«j:¤ï3Ó[Ó»”—¹ö}Áå^XßgÔqPIù¡ êåf¬îé? %²L5Â9£¦™™Ñï÷±»½=%0õ^Iäæ±Ât›’ÒpM„mjÌü £‚c6mÑ5+ ÷ÍcJ®ÎŸì< ‚ÀXRB+3í–!«MTš[šDæd„ ¶Úm‡8€“TB› ð’!NtZ8§s+G»Ý6€4µ€m•ƒ¶=Ûòïø´UU³ñPå¨4§b i”!W«è1€ãÊgÂ(BÓFùu‰õVƒ˜ÔÑNY8âGŸ“­.{k ØÉ¦†ê›¨ÒåF¬â¼§*?+‡°“=Ÿ:ÂZd];ï*ê³òù•!Ĺæp8ÄÎöö‘6˜¥¯ë0…!š–eªÑlâñK—²®ÓnÛÙÙÁÍ›7ÑëõpóÆ °Ö )Ñ·mçð‡*îo¾ˆI(•"ISai«…w‚ÍFÃý} ÷x»Ò€ÌÏQ‰ñ‡K”`Ë+˘‰Þ 0`rµV ­¡*&ê,üùLjcö3‘ƒŒ"aˆV«…§žzª°ïöÖ~ãŸÀÕë×ÑívÑít°´¸ØyÖiuP¤ƒõþ*a*ÒTAke\‡ Ĩ lFQ”eþú_ûk/ýµ¿þ×?õÛ> ƒ -¤ ÔËϽ®À”­½&2¥ÁÒ*)MjE ƒÂÒ¶kK-Þ /s ̓UŸ){‘‚–â\C}%*Xqg­ä3WpGqUw~q ®¹†y'`š¦ØÙÙq̸Ǻ¸Íï; O#Ц„ÿŸø>óéOãô™3xúé§¡µFb[Ë+¿½\Ý­v­ê}E µ­Uá)áwBßh6ýÃ,úZôîÀã¯ýw/yóD˽V\E- ˜»¹dŸ ŒËàü*f(×iFëÊ–`Ìõ š#Ì´'s0͹JOÕúÏøOiÕuôÛ+ûšÇr@eÆ ò¼ftñ™.扱³³“õrœ{0çs>`ÈWZVˆOŸ9SPD?ýS?~—Ÿz)‘Œ'˜$ñTojŒJnM–™É²ÍFÈeøX¢íj·ZÆÀ=Ý/@ å 0  (Š,8 M&´2Üÿ"pîìylnmJ0  µEjS%MÐ*ÿ[yÔÔDÕ éJfªb  œ»‹:³#%­jš1%o\í øíÄjùH˽ó¨>$_(ÄGržó’?‚™Ü¡eA-ƒÀh4Äîîî¾æÏáI<ÚÃj­1²¬AÃá?ú#?‚f«…'/?­5Æ“1Ò4E’¤K3WW”W,&S¸ A€¥LFñ\]£°ðÀ‰^ ‚d}ú"˜ÅhØv·kR1 R`um ;Û;ˆã ´ ¶ 0Y®ö$AšuãŒÄ¡ÎÛå*᪊ð—M~Æ¡PP^TË­À ~¤˜‚ÊW¡‹Ë‘rF²¦Bò9b5ˆ÷2sh>, ‚fÆ`0À`0Üs©§ýôêÛsöxæžÄqŒkׯ£Ñlâ×ý×!„ÄãG<‰'1Ò$…ÒªPsb¬•­ÜZéÊ•|Úüp+>ͼ¾V»^ÿhÊ‚ø‚ÍFÓ+}%(ׯO3®½}`à=O¿J™àÎÒòz½Æ£±! µE”Bb‚‰­ÔÐÊ¥o*R{Tä¬c¿qæÏTD˜6ƒ©4 Eá‰tÚ> à8>,¥„°Ž1CSnV ®_¿†x2ÁÓÏ>c\­°Ðé C ú}O¦rÑ‚& ©&ØB£*¢Ï4.'øª(§-Õ­Ü «Ž=WžK•1b좜äJR•;³Ç >7`v†#IìîîšÆ-S}D)¿¹¨°Å§µB’˜|~¸²‚(j 4¢Öï®ç¤¢tø×¼BÝn·œr{ùz÷+° :Ò2ç>•°ÍRšøÀÆÆ]üÖ§?á`€$UÍ꿼²bR>A}E†yŽ"Q`2¡s9].uÉͲ…Éåå$„ï'çu´®_<»¿Ë\Âî;~zÌ2O[Úõu¹/¡\º¼Ww_Úw,yÈ•*ǩΙÇØÞÞ.¿kôzXáwüù…ßÄ€Ò45$Áúú:Æ£43Â(ÄùsçqöìYRXàR†+½ßûïÿæß\>¬ÒyÐËŸre™\@æM”}?Œ øÍO~ï{ßûÐiw ÔfKËËèõz &X¸=v’$Æ%pyÚº®@óæð«ÞóÛŒÕ|‡æZ­÷?Îu)+:¢cÍ¢# î‡ÎáWðÃÏI?«ŒF#¼sõ,//cye£Ój¡}ñ"¶·¶°½»{`¾ÁýŒy·Ûõ÷9T&àGQ ‚b åü÷,¿`Š,Â(4ã·>ýi\~ê)œZ[Ãh¤%°¸ˆþ`ˆ8ž€";aåÇŠcë˜èpF%VŠŒW2ûìgbÔ•Ï£Xöã¯^]¹~`Éߟæ¡«».Oá)­1O&‡ìãäùcYjcs;»»8uê:í6 ååtº]¬ß½khçá¼jhÆ–q¶{üþÖßz) Cd\€¥™h ëdfGú/Bâõ×_ÇîÎü”RH“­v A 1,ÄX$%ÈÞ„5ý½Üvu §'ù…Aµ¨Ã*pM8¨ü{õ˜€=~Ï_Á«˜Šë€C5VJªƒÒ4½/…~Ö–¦)nݺ…V³‰Ó§Ï@J0pîì9 }lmmMÍRèýmaa=5~ À¿z@,€£ Ѳ L=v†E„š/$~ÐË¡«„@SHlÜÝ@<Žqéòà@CõRtº],..¢×ë!ð˜\ušÂG¥L-A ¶¹× RÝ Y* Ú Fì_KUAÐüE8´/Ë„ëŠDû³VL& ‡ÃŒ¼ã¨ú¤Ãx2ÁÕkW±²²‚åÅE0:.ZÍ6¶·7ÑwAÂ# bfóä—ßiÀ—L/‘w`-mKËËXYY1 ÑÃÑÃÁXZ^F£a·ßÃ+Ÿÿ<ž|òI4›-¨]´Û-,.,`0‚™j4Š ˜!•Ál»úí“RÖõ¨Xéë˜g´Þ¬ WÍ‘¥’b©~1ã·xº^ µx­ÂÆ—FûTšKŸÜqŠÇÚÚÚÆ`0ÀêÊ*¢F! +«kh·»ØÚÙ‚šiå˜Á\^YÂéÓ§11ŒÐô1 ±¾~gÏœ-`9ºÝ.ú½£ 9ARУ÷ÿÅE"(Œòà¶'8O?ó žþy¬­®âçþÝÏá[¾õ[±»»‹G}ï}ï{±½µÍ@«Õ„V ¯¿ö677‘¦ úýúƒÚívÆ&äŠD •q~µÚ<$ ÈSqU„–~+-×>Â@÷¯Ü»Ðß¿˜G°ÿqù ¼øÒKX[[ÃÏþìÏâ#_þüÔOý$.=þ8^xáý¸~ã:z @䨂K‡“¡8 @D  ¦Zh{äñéO}ŸýÜg±qwïyòIüð?ù'PiŠ?üŸÿçxüâE|îó¯à±GCÔh U ï\}ƒÁçÏŸGš¦ÐJ#c÷,tùPz/ï¹°V®» —ŠtœZ{X\¿™w3\³œˆf. \ãÜÏË ”Æp8´þ>Ýw+x½¾ÿµ°×ëa8byiÍfÃøìÝE´Zmìöv§‚„ ‹‹xôÑGqãÆ ü/?ôCØÜØÄG>ø%ø©ŸüIü¿ÿ÷øŠ¯üJ|íúCøÌo}Ï>¿² W÷º,Xœ¤¶©ÙN O˜uùq«œñd‚·Þz Ýn¿ÿüüÁ?ô‡pîìYœ?ÿ‚ Àx<Á+¯|Þî˜:í¶·¶qå­+ˆ÷È@>•ʺ gS¹ÐÉÏOwòè–\3.RUg«°[‰í³ŸSß«51U&ìm-:W¹ì—ù3Ï‹ðiÝ«þy'I‚Á oÈXÊçQ˜ õÇʨyì}S`¨yˆìQ\©ó‡+ ©‡Ö ›[›ØÚÜ4¸2ÀÊÒ2–—–í îFøüç?0ðüñíßùçðÁ—>€Ó§ÏàôéÓÆØØÚÈ®/Avçoï¥Â8rx\ZÖÖ²2¿{÷.º¾üË¿ I’àæÍ›ÐÌxôâE|é—~)Ò8ÁæÖ¦©-·Ë†b†$ Ómc4áÖÍ›¸páQHkúÇq’YJiËçkCžîOWø[ÔTðå+¸4çâ´ÏöÔu¾ù<‡©ì<ûŒÜ%ÇcL&/€xR=_Ákí4c£É“»ëXè.Ê:ÍF„0\Åh4Ä`8ÄÖö6v{=<÷Ì3xâÒ%lnl`ÐïC„¾û/ü7ˆÇ1F£!^yå|åW~% Õnúct À•ÆO2 pÄ&ˆ & °Tëõzøä§>…K/ t»]Da„sçÎa2žàæÍøÁú!|Ç·{aƲÅ~w: úôú=Á2d M­·J¡µÛÆ¢ žH§9ÇŸ:órøÀZ~óÑ âc.g#fp Ôþ13F£â8>Ræž“…kËÌØííb4aaa¡ @˜ñ(Âöî.þ?÷sxï{߇00lÃO¿çi\|üqŒ†ClllâïüÝïÃÿÆo,·Ñh8wâ%܃²à{ø¾ïýÞ—›Ìš.zÂ…!Æã1þõOÿ4"Û.l8`w·‡/|ñ ¸qãþìw|N:5½3£Ùj¡Ùj¡Õhd(@Ö q’Ú0?~ë¢ý¯àU´à4ÏïT”òòŒãúøª˜fª*üÓZc4!MÓÙwŽ˜Äã$„ù0ÇJÓ[[[è´:hµ[æúƒkkk~ìû1€Qa{{Û[;øßüÞ¹zßý]ß…/û²/+/j6!êò½¸Þ{ª„+ޱ¥ÌÑæ~giqùÈGðÎÕ«¸qýºakÕ _ÿu_‡K—.4´/ KKËhDQfŽk¥‘ªckÊú„û~îÙtN3~_ã8«÷_R™JÍ­Tšb4Må÷äü¨çñp<Äx2B§ÓE³ œ:u ò›þ¾øÚ«øßüMÓ; ”ø†oøüίùšJpYEš¿_| Àg¾LZ #PDM÷Þ‹>Š‹>Z;iÜ Rbqq2²׊‘ª1667 Á?ª0•Qc£fåž5W…&,¶ « Öª#𨰠ŠM?ÊDü|TÏ 4‰cÄ“IFAön[Áò8†ï 4m Ýî ´‹ØóÏ?gŸ~ÃÑãñZ)C1æÝs7¶œ!xù^Èà=¥$—"@ÔhØ Z)ÍUß™•¤kD t»<¿jó^;ý[¤R¬Û­'¤½ù0æˆóQ WvùýyÈ6©ŠµcÖ{\Bª;çñh”ñóÓUÅ‹aZ¿--ìSjŽf*â'ä<6× ~qY:2U)†ã1º8uêv¶w¤If-0›žƒ‚4›TeEûa=9’XÔtxéÇʃ› )F ƒrÞstô¶µö‹‹ h¶š^kBš*lnma2žLuþæšô{¸»ìÙ[¬ÙGÄyÏ4å‹O ¿ ôÛEûÏÂší‚¨È 0•Å7Üß~ÛþM$À(=˜À¦U.R­1 ‹mmÊÜK°Ï‘çŸ DðçÍÇš)¨Ç ô…>‚’‚¢ ej í£ÑJkL’“ñÄЈ—”¸°·«ÙjeïýOï|ù`òÿ€" 8#rÖš1ƒVIJ,t»RXn,³jÆqŒÞnFֹߕbªÌ–¦gå~}þsA䳕¸¢b($¦ÕAš·á˜>$NÇqf…ì׌>IlÿQë¨pæÖå?HRä•«(Ân¯—íÇ éÎÑ(i)?Örâq€{\€(KQééî¾dWgK)ÐítMkgdã‰áhˆÁ`׈D¡]xeà­(~ÎU >©ÐåîÑŒˆ½&šÕK‹×ož½Ç“IVÜBDËþ~ï Šöä„y?½Ïs¢²%aæjš¤6 ˜¢3ÊOÁ­þnì[í6Æ9Eø¿º¯³GÕøÿõ?ýÏ—LGÛE†Ûéî¾9Ñ(9K6;‰¥ƒÈR]ºö¾¶Hಠ;°&“?Ù©‚Š›¬êt;^ãÊPZ©íPÅá/ö4Ž«onÕŸ~º«º ùQhnΨ­kwóù}JâR‚Ò‰MñÉ=¤(GV':õÎ…Wàâä‹„ †aˆ¨Ù„sæi2Aé}¿Ûí:bÒ—4~âÌÑ—¸b×’¹œ÷{ ËæÓétz„Ь5v{½"#+&:Jܲ¥@¦þ¦=|oöΩ PV€…1¨j4RA]Æù{!eFŠéº*Í´¢Ì-*]«ÖU×z@.Ããè#¯•­&Áê’*…(Š ¥4¯Ãì/®i¨+M?DUà‰$¢„”XiÅ*Ov‚Á]»BæsÁÐ Ö„•-¿ªš€Î¼†:0’_=Xa×ÎÓ>¬ªN lñPyèÌ÷¼¿]ATl’$ Tš˜hë€ Ý9–¨Ó³>Ræã¡uAPùæè½DÆ:! b3”uìš ¢ ‚tÊÛ+ŸÊ½Óíëë‡PâäÀn—¥…ûC¬ËwfK÷¢Ûé MUvS†“©J§VɃ†ÊÊBu˜Õì(&`ÿ ¨˜|YúÇ"÷^Ç‚0ÌÑ3øï…¯ìe `AgE èòjy˜ ïó'®IË³Ï ÒÆ„í{`CÕ'üÉ:%üUŠEG‡d1°È`H °”€­¨t Á§ëæ\=ïâ,ÚXÙít EG‰<ô¦c¨ê.,ø×r û, þÌg>s’.ÀQ™i´Ø•‰HF{ý3òêêê*¨~¯gL}­1*„ ß-mf®*eÌ|¡æ¿:¼Ü(5Qè- ,¶Çì,…4d"O‘º@“Ö¦™ª°1í]×B· XX\3#4›MC‘Î ­´!LÑ ÖŒ8c<‰›=H•Âd<¤K}J0 ÎR¦.ÈÅZCû ×ø~ú£É;›³â8Éõ hÖˆ é¹#ƒÀ¥e÷íüÝÿñïM½÷}ïïp„‡Ý¤mÓñG‚\+ïÌôÔYÀ ðè…GqõêUˆ0O²¶ÏY­{UþÈWœ9 ªˆ¦O}L…ƒÓÓSx‘㼫NæÇÛÕ# ¤Úý•R6-$°°¸€••tºhµZ…@6MµÎZblj…a•šãDÍm«´6íµ˜Ù‚ˆ&˜Œ&HÒÄXÒ¸dM9%)wášyc‰£îÁý-S&ÌÂÝMkE1#Fd”hææš¹Í6e@B Õlbhð+—p‚Û¾À¯þú¯úGàûÿᓎÒ*² Zçx}\¾|»»»„þ8N ›s¦4Ê]]§o$ï)ôõ«Ól¢Šý¸zoQÂPÍDvÈ18Ú/ë ëï × Ù¯ S«~úÔ)œ>s-b VŸŒ†Cììì€5°ÛÛ™åÕd[£Ñ€„0l€d F‚ „R J+$qŒ$1Ê€™!Y`aº€°Êiv"ñ„røG­`J÷pÂpà{$—0+›lM>vÀ"<õÔS¸{w2D˜Ä X34´ÍSSM«nžŠ°îUo„núsß²‚jwþk*ØþU­ÈËHÞßSŸòß(Ü*€ååe\|übal¯]½Š[·neÒ<’ïgZô¾„‚ ‘~;ÙÙ¤Ó45”VA`ÎÓú¸"€2V÷€Œ›) ñ‰&ÐWUsáÂÜk!„I½6›C !ÈÚ=lÕÆ3™(¾ô®WÞk.Zz9d¬ÀDÀòâ2VN­áæg>Ь¡TjÖK¦RоŒÇ¯^Õ Â<ՃЋz“g¦ÓŒ@¡}]H Õ МQÊ…Ý5LÍzÓ[ÁZ^* þx<Æ¿ð m”\ÐÙZÚ:³˜f%çtÍGÎl'ᬈ `&!å¯fW«‘* IB:p—ÈE|ð€ÌÂ|r¸ò´6 ¥5„”fþj†D¿qç窛­Œ ôÝoA AÌ&”ßD¸|ùIlomC„OÆ9…•Ý_O )ͱ=‚‹(4ó¨Z#ö I:d ËÙ;d˜|QI…aˆÇ/]B»m±ý|áÕW³Š¾<m¾¤¬2“ÛŒ àNµàè‚ÁûùŠAÁëí¨5X+Lt¤„f™†ÍèP¹“ò} ôób|ìùeÏB˜x€(j@HÛùJi“.µµd³F#;Öýà¾üÍßò-?ÿ.Vâ<e<€nbºIÜŽ"¬¬­áÊ›oÙv QÞ\sŽ­–­÷ “¦¦í£Ç¡åw 'ü~þ~iy/æ«þ§?ýi ƒ¼C'Èz?×9ǵëa–à—¯ÝTÄ™jÏTk¨4E/ŽÑl4 Ž«AŠð>DV‡(çO*E¦ij²W0TM|€ ™ mE95؉n÷(@ËÓ.@îG?j'z<‰!$N*…¸v:ç¤ÙW»îC‡«%`ª …!2ŽDn{ᬬ­nß¾7ß|3z¶«¸ö”ÒaÎi¯¹<î$DΤäcâ‚&BERb0@3£? ŒcÓn˳(øˆÎû$ï±ïÕsn0Y¬„©Åh7[˜$kEYÀTipÃ(rà­—qBá÷DÈ h‚ȘA0ýÚÙ®4R\|üqÓì#&pÂyΟmÞº°Ê‰ùR4eŒ[9QH¥ QY™ˆÓ¿gMŒÒwܳôV{'üîÁÌxæ¹çL×d¯¼ò ¶·¶ò±(ÿ“uÞ ·9] ÂØyhÉrqÙ%!%°³» NSŒ†CÄI‚Õås½¶qƒ@PéÃpˆl3@F¨$°Y•*H! “§$œuET’, >5~ÒÂÿßÿž—d8¿²UŽÙ€WX^Z‚ƒ~å¡ùŒ,öŸLX«„Ÿ§ëxK(c”Ãa mûÈ*Žý†™\Dú¹³™øvÝkb2¥’  ú.)¤ ¢¢ðþó¹ð3ƒ• ðéBbVZ¨cý*þ›«•*ˆ´Jã䚉²­™w˜•¥4¢†µò&X_¿›·YóÛ­ù4W¨%«iqV ÐsÒ©ÊÎ?‘aˆ¨A#«Õ…ž εjåiÜËœ¸ ´þr3áMwTözr+««€Éx"­â<ÕÇÚD¢•—F©âÊœÕ$“æÓîÇ›Z²ýñ²<¿RHƒ»ðÈ#xÂÿ ¶·w²,I¾|Ôçɢ⪅ªl=¹ô®ùᥥeÄIŒd²O•üe³µŽ¤ÁÔ뛚 ·Ú ),ºÏìÓn·±²²xýõ×±½½-©ì¥õökÈ•Ðḋ YgH¸µµS¸yó† özÃËËËS)”£DiHˆD¾šg–ªuYC¢…ް !‚¢‹(­fË¥_ÂìDÒƒë!ÞCÂLzžpf"3£³¸˜­ÌÚÞ Ÿ¡Wœ›nì#KQöBW_÷ ’ ™õ%(¾&so, ΞݣÌÊZÅDÌìjDÆ&+i={lf<ñÄ€­­-lÞÝœr]æélë¬ ÿqÜ{§Ùi‹f3Ày uakk§²xÎÆÝ Ã¥WºsYì‡4íëX§ÅEÔˆ e®Q®¤•Ö€ ˜wlÊS³½?ž6 rj0üÐþà¥w¥ =N„Qh€Úˆ73––å£lº„KÁ>Ê&¿‚ÐUæfÍúíSg•c´Ç"qĸG á§Ž2¥à´µ€à­7ß„få!$yÏUü~ìªS/ù=j·[h6›öûHµÆ»wðè#ÔV ÌgÄÜ V"Î8(¹dñ)•v »¨‘Mm; Yš€V»•z†xë]¨Ä?*ï÷èkÛ²QfeKZ³—ã®WÇuçŸÄ‡€|…ô+úìXœ>}€é&“º.=TÛàÛ¿ýÛ¥ ‚žù«mÞ×—8+!$DDy…of9ã Ðϧ_ªëÌy.™˜åVÛ3–çé¿]ú’(kæIBÌÿÞînåùMŒNì8u«ÌT—ÜFÃÑÌŒíÂXì;x¯ÆÀŸƒÞb ÁR" #$“8§s¯:V>÷×)€÷>ÿüGÜÍ¢ÈxAdLB¥gΞµ+B[[[Æü÷€3Y¹€RF²Ô<<¬PP‰À“¼Dh{À¬lÄÌs":¸àÕxγږQF##'ØfšG*¨' \BO”¥dÚþ i±ŠÅ©Óé`d(Âß}€âËœÏ+…ÈØS¹Ô7-Ž"e¥Dšš‰¥u†,;va>¦•‚È`Á3·¢Â¬Ì²ÚŸÜo+øaƼP†lŦKlÄ÷³ÐÏŒ7yÖ¡(Bßÿd±’Uêm/áR'ª¤ÏˆMe[’$Ù ÍzarZCJ Ö )$t† 0º¶PÞ+=³¾¢ö¿|¹*P3eÑQ-_FìUA2Z8V‰^ÜUÇ š \5€KŒF˜s<(]ujêé•ÊXžÊï|¤Øž}b"D©ÌÌR•jiدY)È ru¬9‰(›¾‚ Ýl¬o':Ñ   ñ¢¼yâÔgåªÈ}æóR†¸ó.Ó‚ÈEö q%¼³#(vôþä©:ÿX^ Ò­p°%°d'UœŸp@!á„¶:÷^€új"{Ìó=f\A! f¿Óëíššf öªÆì‰Q¨ ¾`Å5ÿè°˜áͧ²5l#f4-/#‰"P,aË†Íøþ‹öÏìd]).0E{ŸÉ)U!ˆ B)) ’œ‹‰ÁL‡±jqòPQ.f0²)RÑ9ÀÚé5¬ßY7:Q„f‹¢;:Ý~¯esW¥ ;;»fŸöÆìU@ô(ﱩî©B¢»F¬BÐZÕ¿Ù(p9.¿[`)å)gä)8eéÀoß¾…îÒS PH±6M(ˆ $AhÃ(£™ Uò×Õ$=JÅ`¬L­eçd<gMÖÖÖ°¹±€ ±„ HwèàÂ|²Š¯ê8U)´­í¼ÈIi¥Å%Tô• ]Dõ‘ا’§¼t†ƒ“س3÷NdŒB”Ž#óesYð‰Za¶Ís`íqd×^ÙÚhÍlØp½Ü±q«ç¹C;NæÛJ)ØGî{ñ\p¸–>¼ø<àV ÿ½B„¡üŸÕˆ™3$+cš¢Ÿ|»øè£†¬À0?Ý€vp„ {,i‘þvõÚ5ŒÇ#hæ ö|úô©=…ÞfFàL”…G¼~’Z1D˜Ro×ÐÍ#ƒÆ¡m㢽kbËËË_ã@aš(d˜¬ÉkOÌ‚À–בfpFüµûâ xFñy¹i©vÜÖ pˆÈ¨ÙÄÙsçpûÖ­,ŸŒŠ®ÂGy}G™>$Ì*¾3ðÎÕ«FømÝÇÙ³gÑ(õº;q,ÇüZ­ò= ˜&¡ÞùhV¤åA„È.Ìg"ký¾Ûb‚àKÝkiMzFZÇlnl]† @l­òÜG•í§逓ô¨sÜ{ d©ñ(k ¶´ÛÌŒ»ë뙀3gÎ`2™`{{Û¤•‚v©H—¢<>èqDÅØ€Á`€Ï}îsYfÄ1==qé’¹'÷#–cN¥'|¨³]à´Ö¦ù‡exv1›bþÀ<†³n^:n¹< €¬0?ç̶°Ñ0«»”†‡ ð'Ic³ÊÙÍñÅI)!¥@`)–¥ "вù6íK9v³¯l>ò^ðΊóʘ{mþ;MS\/ÕKËKX¿s‚@ʬCh¡ AA‚*™dçéM_UXlʇš4sOf6$c¬•cûÃí[·¡Ïht»]ÃŒ F«ÕBpæ úý>z½¤5ŸuY ‹ÖÐû¬­ó ̼׶µ¹‰·Þ~“Ñ(~­Á¬ÑítñÁ~Ф†÷!¨‡ )$zz±«ÜK+IÒ(L5=èä±%5›MÓrÍ(€rßÑJðŸÿsßÙ‚ 4 †ÃæÀ¥P„Lˆ'cܾu gÏËÐj´1‰'@„šÎ³:4“^i(­jR|86Ü÷aW“)È€6QÍB+ÛbP`ýÎ:Ò$Ew¡›µç€N»ƒ0 1 1‰'~ŠÒurÏT~‡½¾Ê½^W¯]3݈•²&¿éÄšqúÔ<÷üsYƒ˜c_Á¸ ð¯ù¿(¦mE ¦yê4-eÖ”™w¬eÁ'b,//ŸB4òáµ$ åœñ"@ ¼ùæ[Ð]ê"Þ˜@ ‰€-q„}$H I¯¼—!9Žú‰Lˆ4\©°Immma<cqa®KäÚ6&“1â$±ŠÄ´\“° +Éï Xª%àˆŒLì›0£¬ØëÆÇ1’8F†YãM7ºú¬ÐF©œTŦF} N! jS‹ãñ`Æ$ŽO&FH•‚VÊ·Í<¸RÞòë(ŠpæÌ<öØc¥<Òq:ŠûwléCŸö­'1Õ°Èî…Ëòd&šu‰Ýïþ«ÿñ¿öë¿þS¢À0“àé:+$E`»úiŠ( 1œLð©ßø |ìk¾¦hM,.bww7ë»®´F`'>,jPi}p%p’´X5°ìƘ^ fÍ”â8Fœ$YOÁ,†à)Fe'˜¶±NS#´q åPxÌPJeJ$S&ij„Ù º²-š9rf(wL¥²c.,,àÌ™38çÅrŽ[ï+ÅPÁéW°ˆ0„v(W2=ƒ5‹ ÜÖi·}Ëfù¸âÇ­a¸JBz ”€ª ZàKœ$¸yûÞ|óM<þøã¦€Øl61M‘Pd ;ì(Ĭi|¤0Çà œy,+ØÂ®,ìEïÒ4-ÒlY!ô-¤ në *{Âïÿ\VΊȔ‚µ\AS³Õ©S§°º¶Vˆß<+ø!ÏIxÁ@¶É­­çÐ9«ô ·è%¿è/ª‚ÈN´Óé,R JΙGRˆÌ PJ#Š"ŒÆc|ꓟÄy‹‚s¼ê­VËäH“¤€½öMh±œô#EÇ>±Ê‹3ÿÑ 9…àÜi;k­‘¦i–{¯RÚ¥è¬"ð_û<}lW~÷ºÝí¢ÕjaqaÝ…³Lüm?ì±<´Ÿ3÷… ªLglŒìß&³kÉ9EÐi·1œð@ù{þÂ_X¶Z³Ñ˜07±…=v-Ãu Iš"ŽcüÒ/ý>öòËÐI’5Ì\^^ÆÍë×ó‚ ï>—ÐßKznfS]n­$@¢¦þÀ€,¥cÝe¥$ ŠT¢¤ X¾E4«¬:Ï5#Ãc×ít!¤@»ÕF^.}¼™’ûH{À(ãû 0´6ã)mGèÂl-D 2pGøØƒ¨·Z­Kj˜7êÍM¶K®$ÊÍf ý~wïÞÅ+Ÿÿ<žyæY“C³ª±IjçóOµ;IÉ“Kj®™|6ð6meÙÞ ˆ „ ÂY[M,¢šÈóÞ ý½JÙÎ%¢ÂqÙS!‹|$áÔ„(¥È€F³‰ž%„qõEdW±¸¸ø¥Îüo6•!4°é.ÂÀ t«a#Za8â•Ï¿‚……¬:…»wALFøm~YkÓ97k%vØ xŠá Àæï-U¬FE¡ž¿X®¸F°Þ+ø~B¾Õ@"±Ú’»2l— FŸcE97‘¡ òVa/?°ÀÒâÒGÊ#U5`$X›î½BJHf„A„V˹’$Åg?ûY,//ƒ•FwaK‹KÐlÛfWSæ¥ÿ9ÿN{wšÅ'×Þ'å¯ÎÓBSO•æ¯Ì€|?›ö÷%2ÐU¾feÑy ß¹²yj@/†f3³˜àüÃåoý¶ÿz먭€c !sÑh6öx¡Í€AŽ >^Ð"âÉ2Œ'h4›h6`­PCÀü#™XGªŽŠ©÷ð¶Kà~5íŃ2æÌEdˆ¯YÁY¹ž5`SFyP•€Ç{ôý0™€#M—0ð<@7›'JîÐì!"ÃC„aˆ0Š.,!I ú}4ZM€"`0›cH*d;ßUéã>žÄ±°6oû:Vv>6 €@ÈК¹åÊWf£Ùð¾ü ¹Úív×4f˜OÂl0­Væ‡J¢Õ2ÇH’qšds1Äì5±î>¹ßfŠAœ¤0ß‹1·)½¼õ—©½ÐJJ‰8ŽmC¯}º÷ý(ŠYîK~êAqü‰oú¦v£Ùìf­»©¾á“Ÿˆ¢È@!a9ð@ ÀðªÅUxÆ Tš$÷%‰äÃüä…þ¤Ç¼ªtI Éfj„08 )Tž"(ô $KŸ—÷XÊ‚Õ8{öìãBˆ€ˆÐl4*ŠQ(›d鯖10˜rAa±èiš*ƒŒB™òXÃý'Wðw¶ÿÇÊÀY@ËÌ20žLl°šÇ0 C¸PVü@ÄLЯÑx”ÈbJl¸Tš$Q£(,æ§ÉÂM'q íqà‘7ùö¬{ùäî…b¸ýëw…bpãå 63# # ¢Â=pÅl"E‘“KŠ)»Å¥¥øýß…­nó5¤­f3Ëy:ŸŸ-C®Ã¹û ³÷Ó„¸_ÛTßEM÷¯ Ò>uð¬D5]˜EfÐic謅\¾¸5›M‡¼äòÈ,€ã ÏNnyqéK4ÕýÖ§ïR"C´Z-ÓÈã8†VÚƒTúǙնn¯½3u¬ÃgÞGô{Å.Á„ú>Àù¿¹:Ïy…Ç>žû<ÎìcíÉŽyÈË]˜«•'e™%IÃüd¹P@¼NYäíÜcOÿÔÿþ“/ÄÜ×eŸŠ!Ãø—M}»ÊB€‚(ÊX›‹¡Y¨R­¡”Æ`0@»Ý†ðzW oîuÚmlåçXžN÷e KKKÏ•Íig;&Ù`ŽÉº–ùdÁE˜gkµZD˜ˆ ìÀ¥JºåVuþ-ÞP‘··%™þØJa0ÝÅE“ž")¤Ìovl‹•´ÖPiΨËڣή97r«…%é¨nÙ•³=Äœ,Þø²†¦+§ú&d~~£ÑÈxþ ‚Ÿ$ØÝÝ5­Ýâ ƒ!úý>FÃ&q @JKj81¢^œ!¦qŽòEÿGÄxT  Pè÷Ò‹/-7›ÍÈ­þL RHH@ŠWÞz ¯¿ú*˜­f +§Öpæôi¬®­¡ÝnïéB`H„$I,¬ÖäSµR f(¥3v•:º`Ó¦‰³ýˆòíîšF–Ɖ'Ó§¢›mEY¶#I$¶Q'+e¬+ü¦KýE°4Lò•Ÿ…9—LI€–*ì=ïoˆyn&ÍuÇiO—ƒö?{yNó Ø÷8•Óz®=:Ýδ¹¯¶··Ñï0°µµ^¯•&føóX<€›[f¾ øä¢F#C€s_[ ——N…aÔÕh9N[’%Bfm½Uª01º~wnß‘@³ÕĹ³çpîÜY,-/ך£ G5î1+§p/§sóå,‰Ææw[Jãë·;]“φrìæ­[ôÃÝnËËKY¾:WŘ~ q<)8ùm»´Öyko羸sÖ°ðiÎ"’Äe7À²¨0"û°2ÚmÊ ÌVNÍúð‚*ŽÀÐ9¬Uáj,¬ð‡ahtiá ‡ØÞÚÂ`8Äúú]lnnÚt¶¶ WD`Ü)$Â(2MnBcQ0Ú•<"!)³Ax yáßo1žZ[»,$…D@…j>·zº O¤RA%)«L@âx‚Þî.^íU¬:…Ë—/cuu5˳ækš‰øa{a;°(mð\2ç|ŠfÿÄe ÓkoB¯7Ào¼†·¯¼µ¼:wþôa°ÖYŠÄåéÝäcÍHUÑßv“'U `@LÞž_Ǹzõ*Þyç G£,²E»xý^·nÝÂ0I‘ªÔåwðÎÛoãÚ;× $áܹó8uú4Î?ò:¶±,ZtÚ<4ÏM  €(·¹N¼©-–*»슨ÈÃÞUo’û«¿_—ïš•NÙ¡ÎêòR±röàS”q3dÖGÜÝëmcc[››xå•/`<› a`qqÑ4KµAbaƒ}~ݬLøJuêVxçÅÅÅǼs?e ÷¬Õn/ù+\U4•™AA`Òv„—Kg¥¤¾v»íímŒFc¨4ÁÖÖ>ûÙÏà…_ƒBžÔMHÒ2Y¡WOà\ 0cýÎ\¹ò6·7ÍJoÝ‘sçÎá‘óçÁÌX]^Æ…GÅÆÝ»¸uëÖ×׳<¿‚Ràê;oãÚµ«øÌo}KËËX[;…G.<‚µS§:Í:Z­62e@VŒk¥Él›zÎ#tÓÝi¬Àêù³¿·íímloná _øÆ£¡)ò5"œ?wvQà C^! ¤((Ò½j*|  Óé¼À/ÝoY€ÞÿþöêÊJÞ Ø›åUå“nåršÐ g TBY÷@z»»ØÙÙAš¦¸qó&–——ñ裂„¨Èû'ä Åx4Æ;WßÁÍ›71 )‰Rh6[xîñ‹†“ÍÁ~- éü#àÌÙ³‡X__Ç7ÐëõL•£f M‘€0ßÂ[·ñ…W^ àÔ™³8}ú Μ9m‚0éN³…f»…fé䥀éáœå‡[yôèíîâÕ×_Ã`0°mê: ]œ?­fa!²uäÓØ—‚|ó }†Pi !åRÉê¾çAÀÌüæ™gδZ͆«€*_¤C@±-üFA€V£ÓgÏBºb!hTh‹Ã­ïµµµ…4IðÚë¯cíÔ)(¥LtUˆ‚©”™¢ ƒÊsÏZ#Iîܹ·Þz ýÁ Æ1ã‘óçqæìÙ °$"óÒŠJ)4¢KKKxâ‰'ÐëõpãÚ5ÜÝÜÄîÎNöÑ*+Û¸qí*n\»škyygNŸÁâÊ V–—±²²Ld¸ÑD#j ÙŒ*q·“ß²véJ#Ilo›•{k Ìf[^YÆ#çÏ’Û(2 @¤ÈàÂnÕßÝÝ…Ö½^“É”ØÞÙÁSO=…Ån·6^dy2^,ÉÝ}£ôÂââÙ »þªî¦ÛíâìÙ³ˆ'¼úÚkxý7ðÂûßv§ƒ­­-¼ùúë8}æ,– ¨-¬` •%"S@¤567·‘Ä1®¼õ._¾Œ­ÍMkFY`¶¥Ç¬¡SPÓ6ˆ—¦)ƒ666슯‘¦)Úí.^¼ˆ(jd)ý5ðOd~­2+AVJ!\]ÅÊò2”R ‡¸uû6nݺ»úÏÛ¬LK³õõu¬¯¯ç=㈰ººŠN§ƒ¥¥¬.¯ l„8{Ö@¦E`ÒGB4-0€v»‘ó^>öQÀx4GG…DT®ë©à 2Qp!„ ò*eÒ]RZŒ@`bÒ LpÌ×2umÊäéoÞ¼i22ÐÐÅ-#Cvj­±³³‹õõu(mÐ|KËK8î<š&‚(²©=Yˆƒ 4š-œ;{“8Æ'~ýב¤)>úÑbqqo½ù&6înâk¾æk¦Î±Ñhb2š€™Wpµº²zY’”B„R­±ººŠ(Š0‘Ä1¾ó;¾¿ö‰OàOýÉ?…?ÿÝß…@J,,,à“ŸüM|àÀòò2„Ö™"pøkf--c§è÷û¸zín\»¤€ CÑwÄ ÖG÷ã ÚÅ´+ ’/^Äòò’YñÉLJ8Qìa¹.–ÜAJ™M­4š—–ñÔSïA<‰±~wwqóÆ-ô}£?¢v_s/oߺöJýx·SgΜ³F§ÓA§Óèt»æµv©CV»)¼öS`°EGš·Ü8¸ó1¸­Ìç£ñƒÁÀ¢Ò€Áp€Ñ``&òpˆÁhÊ`ÔDŽèÔäËý˜A D€»ëJ¼ÖW¶B8(l¿5Õ—¦¿ž#S$°³»Œ}Ç&¥ùžÝuQö2-=ûJ/òN6ú/-±§ãö+f“9+n·:¸ðÈD­Â@BÈÀ+Fã Μ;‡8Ž1ð3?û³ø¦?þÇÁþ×ñ/ðüóÏciÉÌ~ü_ý8¾þë¿~:¶%`¼4|a>¡å}V¡Uø-®z1ÿÕ¿òWÿÒüƒðoÙ`:ݤ”Xèv' ¾ÿû¿àÓŸúÞ~ó-líî`iq ›[Ûø§?üØ$|þó¯àë¾îë¼UĘ]J+Ó,Ž1póæm$iœ¥¾LvAæñäÚ[ge»žP1caa>ö˜ ²Ø›)-dÙM šë¶‚”CY34+¯ä™í{y-À`0ÀÖÖîÞ]ÇÖö6îܺm !òU†ó÷Êi>äŠ! ð¹kâü¼Â0ÄÊÊ ”b0+s\ke…J¬¡”I%çŠ ÿ|4b0«#a&s–ÿ÷¬Tk¸í…U "WÙû”7x)ñ.‚c®ÇNÊ•€;N––´ €ðüí©cV…¯Ù¥ÿl¹¯t+·U&ð:X9(p Ùhàò“O¢ÕîdðÀæô…Už£ñ ]hf¼öÅ/âÇâ'ÐnwðïîçpûÖM¤J£Ûíàñ'žÀw~çwbscív þ²/Ëcƒnߺ øðG>Ü…½&UÙ€yåú¨-Ýl·>èÆHJñxŒfÔÄd2Á¯þê¯á3¿õ[øú¯ýZ¼÷½ïÅk¯¾Š_þ¥_Ưþʯ`yuãÉqã?ü‡_Æ×ý×-`;àNc¯¬­`kk *UÆùúV¼â_ˆ¢0Â… `qaÁäa-BK CÝœ7¥R^¸xÉ™b` Ñ|ì)"Í——°´¸„‹ËnÒ;w°µ¹‰þpˆ­ÍMܼq+Ç*” ÂÚ/,bÎ@@Îduç’$ ƒµz83gË0d¿.!u$«ˆ|átÁ+¦ÌÙF»ÉÂcÁ949‹pƒÀ¤A(\»“8_ÝÙÃ( syqv–Ÿ½ BÛ–ò`²À$+äÚ+ív›vÕ˜B€ØóWlϾB½ˆ£ wŒ¿»z;…”¸ðè£hµÛˆÆì—´ÅBy>kËtœ$øþøñÌSOãégŸÆSO>Oê·ð‹¿ø xûÊ\zâIL&DQ„kׯã#¾`; À¿ý7ÿ棿ï÷ÿþ_<¬p¤1)¥xìÂ…KÙÁƒ“ÉÛ£-¹Šçßû<¾íÛ¿çΜÁ``òì_øâñ¿þè ISL&cÄI‚«×®M§„ÈNÖçVVVLo5Ëȹäg:Ññ«h‡³Ö¹$Nð_yµÞ{ÙX°†7QüÌFvì¡Å\xôQŠÀ œZ[Ãh<Æõ×ñ‹¿ð ¸xñ"Æ“ Ò8AœÄøü+¯˜·_p‚œò+ 0Št”g|¸o™ˆÞ u|mNðɃ_ÎcÌ+µAóÏ‹½ª ÄãgV–—±¼´„G/\ÈcÞ¾w×ïb4!žLpw}=ûë×®MQ”ݾ}ÛëA8Z¶jXÊ @˜rëó–«ÁÅ^VVV°l)ÜZ­Μ>óÜK™¹­V §OŸ™Òœ\1%‡ºèÎxY=焜+ð7Ãd®»ûÂKo»¹ãÌ}×ÝʆøÛâÒ>ùÉOâÙgžE«ÙD’$èíîâçápõ«xþùç1O$ÿÏþÙ?ÇW|ÅØmiié£GŽ`åÏžŸ¼|yemmít¦)múêÂ… ø™ÿãÿÀ³O?µµ5$qŠ¥¥E|í×~]Æò“Ä þÙ?ÿçø×?ý¯ññ¼š¤Ãµ·-ÆÏzaåg. ¯gúA)x~eæ_Š= EÈ~#«¡]÷…ƒ=j(x'~Ð#œÏÆÓO?“ýxUÞÿ(I5ÜØÇw‚kóÇwê`eu‹šgIÍ{ÂÊêÉ9 ¤øAüËùôû}œ>}:cHéõzxý7ð›¿ù›xì±Çð“?ù“¸téR½Ÿa«ù¤ öpmÀ£¾«DÆÙ¹øý ULfgÊÖNðšO×íW¨=8¾Çƒ TaèµáŸçß»Ê/g`>Ê{3ïqrW Xµ—åøòË/ãg~ægðCÿø£Õjaeeý^ý‘ƒO}òSxëÊ[øÃøãGôG«eÖúƒG‘8J iDÑ’ ¼dÒmO^¾Œ¿ü—ÿ2~ù—ŸøÄ'ðÎ;ï`gg»»»¸|ù2¾ëÏÿùÊÜ'ê,ä´YõØñ½ŽW^1Oœ‚ªBÉpIáAˆ‚#¼kçCNæ}]gª3 ž»@Ý~-€£TVǨ¸÷Œ͹ýÞßû{ñ¡}¿ò+¿‚_ùÿ×®_G¯×Ãîî.þ“ßûŸà›¿ù›ñä“OÖ~¿E˜ŒF`æµR à@VÀaÓ€d•H@Ï_ø ÿÕ7~ã7þÏDÀÚÚZ¾B”MóÃÞðß®}çKî æ¼ë4ÇñŠô$›ŸÐ½8Ö=Ü®_»f²5^úÀÚÖ Lí3ï7 xX¤·k*èÅÅÅgˆ(ty[‡^ÉcÝIø™Ïãå«u°wC‡yZÄîûXsçÐÇ*å̋ǯè8ì5½‡¨øWÙ-XT<¦®o¾®¼s=æÏ9~‹ŽðX÷ÁCkÆÆÆ&R¥³÷~â'~â1ø¼a÷Êèt:Áw÷wÚíγ9lV@+ÆÎö.͆ Øù¹ë„gîrÂúû(©¶ÞÍçt„ç.~›V&iÖè÷û%‹±¶ºvIq]k}O@æŽÇcõñüñïùžï¹è#è\Î7M“¼«ÎQo·‡ÛÃmN—Û½3¥B»Õ¼ôâ‹/þúg?ûÙ$Iª#» ¢(")%>øÁž?uêTÛÑ'¹.)Îq¤2%ðÛþ¦>Ün³„ËÔç 1#Ž“?òÜsÏýø… ‚_üÅ_T»»»ûªC»AÐã?Þ¼víÚÝõ;wÖž|ò’´m‘Ê ^ÃŒÔXJe ®Ì3‡jʪÀVÕ{{ÇÚóøå¦¢û/¿öá˜eJx ³¢Ëöïû~­Ê¥­¾FÆt—XÿØY)ª—†ó{Ë•‰5ýßóÛMû³òùM½_a.V•èÖ½/üOyù¤ªSÕˆ¨écàÚ]QR©ÿª*5mSV¢\ú Ú´Ôv|Îl6Ü œ}檹ô™á„DÆ}à~Ï|†Ü:uœ ®Ø©â3W…ɬs¾wlû{n?ÍȾ Fæ«4Eœ¤HSË:*¤:Øt§Š¢ÝΚ­&ZÍ&dà¹gŸAœ¦d°³°°ÐÇAEòÖ­[Â,sƒ‚íØNDôûý^’$é8ŽAB`g{[[[FØØØÀÆÝ»¸yó–m™ÔC'жyUa®°„Èv*L~S§í“4‡Í> Sùö¾ðôz=¶õ—åqÏ] [Ï^á¸s¡ œv컅 ²k,¬$^e²šú s‰{±ê8® ²°=Ç©^ Çøœ“9|TõøÎ÷ŒéªÞŠZi¤*E’¤–n>E’¦ë³ ‰v§Å….Μ=‹å•,/."j6Ñj5±²º Ô—R6…aò™gž¡üãÇ,yZá§ $¤Z'Z)°6,5ãÉÄöþk¢ÑhZ„a5pQ ¤(>X"ÒNZKØ „ŠºýCF\ýíúõëûWzšQ>ûÌ4îœEŽ“Áì#iÊ‚;Žoŵ·\‰~ HÜ1])lᘔ°Àò,þ›ûo§®gmm ùð‡³Qý¿ökØØØ˜Úïü¾ß— á¯þê¯ânÕ>¿ÿ÷g¯o\¿ŽO~úÓSû|àÅqáÑG³¿ÿõOÿôÔ>§N—}ä#3÷Y\XÀÇ>ö±ûÊå¨RPZèR)gpvm)ç¢F„f«…v§ƒn§ƒ…ÅE,,,d=(AÔOÓ4JÓTÚ¦5û†Dø°{¶f BwïÞÝ ‡q+Š X!Daˆ†eI‰"óÚ5Ãð'ÂÖf›‰ŠŒÙÇ4ë4~w& ¢ÀÜst‹í_ø}!´n…p¬5Þy:¡"_zÖŽ( >‘å* Û¥Æu«ÒjïxTd´ÉÏ!¯ï—V „ATÜEE`;Oë‚—Ù÷E±0¨°Ga.jNe ܃üteeeO:ôf«µçqŽÌŠ8ô»ª%¼o»K)ÑŒ"4›M´¬hw:hµÛAVZë]¥”Ç"Iêt:'Ÿ´E ÌÌr<&£ÑX-.‚µF³ÑDÔh@šòE–$Ñõ@ÓRBÃu8sßMÔ‚ÐX×” ¢°ÖÀAUôa¬'üBøJIæ+¸O[5µ²[a÷8âÝuó΂^°Ðà$¤Ì-Yn_xR̳Ò£§rJ"ûmçnxØ "»ZV [³v»…^¯Þä‚ívÃAßXe5‚ÛnwÛ€·¯¼]­$ºè÷w­©ÔX%Í,0냀'¤¦áé{‘>õ¡¥`—¬³ÎÕ …¹wahd% #Aˆ0@R¢Õj"Ib(•îh­U’$éîî.¶··O”,3Z&“ Æã±ÒZ«ñxœN&“‘ÒZ§©­VÛPp»®¨B ´ÝQC€EC] °ª²ÑnÀt"ÊÉ@¥‡”¢*´ßhsªá×+úý6¸'BšVeÒ¿–²›`ýVYCMM$¯ÖêÚ‚9üêSŽ2 kÝŒc³Üí¥×¢”ãÓùk"ÁB+-2–a¶cJ+ ‚À¸ÎDh6"®åo3sDZÀA°·0{9p¦i¤”œ¦)F£‘Gz4m¥q’D •¦v’YÚd!ˆ`º5rÞpËž>ŸÈ@å¢$Nch@ G·ÿ\•3°¢¤ÙæU¢Žï}È›R˜¾>[Ú?²‡Ó HKhœ¡%íw˜òSÖ®b\c€É\G^”™“'+–ÔVMŒÐ´´Ö5Ä£YÊ-Šòž UûQÀ|ÔZQ”í3¬1ïQ¯;Zäçd´€—VV¢¤}ö•€ò¶—S—¶Àa3µ¥ÙF¸Ò6­!)! ÒJ)€°eÝn¦© ú §è,@Ǽ±±¡WWW•JUš¤IªY§qª²¦‰Bzæ¬ÌW(ΰÑÚòãK€Lƒ ‚if̈@ÒãÒ7R@•)*áf|-·^• ïµÏþ<$Ö6»H~®‡Š¶  ùJĦF3éu,Jä>'ŠeŠyÖ`ý]M€d{lûÓ¬ä~Jq­î £aͤm0AD³†+Ÿ¶dfŸi®‹'äû\½z­ÆåˆœÙYÛ à½Ï¿o½ùÆÉ¿sUÜŠ•N °ü €Ôag486s€)ïb¤¯\úÜÒ£ @’¦H“ãñxCk›Œ â(Šø 3öÐ @kMÚT‚h)%íììÜÇ“V“ RCbòš¦á¦2£ôÎê§l[-³ê»þ¾V(µ0ó—VÒ®lhÁ­¥PÈè}¬ââíwÃõe›5@n·0'bÎZ_™>…" biWw¶ °0Klå\,qdþÐ0Á#hvfåLÁ` ÖDù¸²ð"Î,=%S=N­Ì$n „0@"»Ïææf¥’ƒÐ;W[ÃB6¢îœ‚Ü‚©ÑÆÚÀÉZø6€@] M¸N,†¶s` °´E‰ÇŽÀ^{.³È[Ö9oºY«Qš¦q’$Éd2Q»»»êÆ'jü ‹q’$#¥õDiÍ ­FÎ0=øDñÍÌ+=ÒÚµl×v¿¶Q=â 6J‚]L•f•š‚Ì2â«/’gØòv6 k¶k0K#|~²Š=‹ óvظ hˆ5$H³z6½õ˜ŒàVzIYU1gßµ é)Eåü _i*b+£@kLé@µŸ›Õ=0°T«„·¶·*Ü„ •ç:7ÁS6\Ÿupû¼þÚëµJâØ5û"´¿`¼ lÉ-ô×î :ÝC2RwÅc½'oÇ€½˜/Ù„ŒŒŒBÚÌáÓ”A€Ô¸½8Ž“8Ž“Éd’ŒÇc-„¸gA˜ˆ\0PI)'Ã(¡µé>[ :°Õs\™™´ ” «É:È®id@F°øÂ³NnãšOuÞÇ+ó0rå`‹T­ò“Ù;Æ:`Èlo×’\ƒAÐL¤-Íêï¬áÊtMÀ@ì,%Ê>cMæjõÙ@Ýçð¥úÒL¸ë…€¾˜k•ª&Ò=Ëwä‘Gz1 ]sÏǣѾ¬¿å÷³ãë\Š"³ú'íÿ‘þw|óïÛÄ*¿kø*¸…_Â?Ýü~¬ÿÆÄR| Hè[+ŒTîæ±‡1É0ÈÊ¡ÓTZo[ J’D !tÅÂ|b DÄRJmO“xÒos:MMdÛEeKÝoXëBAëàJÌEoYk£\ó ¥€ ÈÚ~“ÖµQãC¦8fˆ¿w†Nv½÷ØõßÓ”ÕÉ%7Éxª‘}uêÅ¥¸Vy†Qˆ0šO?súŒ­êÔµicNAŒ‡ãj%áíS©$„ÀææV¡ûT*±ÓÁ ?Ü3¸FDï‡Sß™© þùÝÿ?õm>ˆ?ƒ.¾ ÀeÀþâêÿ€ùÝ ß÷·/ãcK_ 0´sœLÀýT³›G¦)ó.3'Zë$MSÕëõ\`ÿÊîË`áV03«T¥J)B ¶…$¦soÉßô{ùð´O$ᔂg%€ªÜÙ¶ôwá¡õþäÌ•—.´&t/tÅ{åìW9$n1ÈH-lw_xQc×Í×­…AGN½î¬.†"ÉG¹†!xO%€3S(ŒÂО§ÆÛ7+•ÄÝõõlŸ:E'“ì|ªÎiuuu.|>Ïš3ûxhfhbhaùg^ûYü¾ß¡pïA/dŸ¥Vñø]x“ç¿€[½[`Á±åWômšÖ-HfÁÔT+(“ÞÖZ§D¤ˆHTø«²ù›$ FÿÃÌ<žL6Ò4Õiª¤)´RÓ )ö~^—¤A—£ ï¹” à1ÇÊÏZW*€zõ‡=Z#ÓBÉ–m^„^'à–ç¯3“+‹Â©êÜÒbŸå¦&àLn=#PèR…uÊäÙgŸµ®Dý>BL,o3ã†ßÝÛGQk•eÖ†ë?qRÿì9ÇqŒ·Þ¼‚'Þ)Rl¸VRí×$xòð¾øË%;ÎA Ha³gÒ5²•ЩBª!wÒ4M³iÏ¢á½óßGë°Ðq«Éd’ÆqÐ*‰E³Ù0~K3A&Tž­vy¨[3AØè? _fó¼¿"“$›Ô¥Kv¸ÀÚ^usêËÓ§Nãµ7Þ˜ß*à ¼Œƒóô.ð3€û}/-¨}½e–=Ô”{)ì¥%ë„2°yèYV‚SÙ=¨Øž{î9ܼys¦Y}öÌ™§sÇ“I¥p`"ÖºVI”3•Ä‘mf>*áßÞÚv/ôzË_@„Ÿ‡€@„÷h ÅëØÁOc_À[»Àpg N¬œò67MKŠŒGÓ)·T+$I‚°ÑØ`æ$IÍÌ<|ÁGÑœâ8æáp˜&I’¤išŽÇãJÇ̈âZ+›uòÈ.$e™OÇÙVõØT–vqàÓ!ØšJ‚‚2±‚G­|ˆxP]¥\&d¾`ù~?Û†Mo’Ük2‹þSžÒwDZ㠥ÛÏYJ^6€É”DzLô–yiF€,‡ž4¿Çd°Y{‰tê'Œý+À’"™ŒF3÷ùèW~em!‘¯H„§ª”„”ׯ]; 6±)h À)»À¿ýi}#ðÕø·á7âBŒ«¸uüD ¼úsÀÒàD¢¡…œªÍ€œ*!MS¤i l¥iš(¥Ôp8Tišr£ÑðW>Q Ànp°àÑh´¦‰Ò,CWNšÌRù"¦‰)ÀKöUvÕ§âÊÉÌPN`ÈùâT©ô!2³³ \À0P&ÝdÐzU¸ÛÖÚåöÍW|¬€ÿ^žÔLæÐ–D†a²¾Bp©8 4æ½M­zÖ‘IfÙ€šè½JS$q23 ˜$I¾˜¥HxþÙT³=ÿüó>cŸ¯ýÚ¯Eokûøóþ¾HM:o)X† L~Pãû>°ˆ×žÜÅûšw°‚;`·|²üد´€ÿï/}ð%“Œ8ÍÝ,fåϘ´e޳HãZé‘FÇqœ¤ ¨·¶¶ôíÛ·¹” 8vÁ-8O&5Õx<”¤IÚLS•åý³‹²  ƒ $ˆ Ùfrè¾mêØa„2t1A#àt$7uŽùèÇúÈÍêØ“+„²OA‚]0Ôû¸T¡ï˜ˆ8lP GPx,/Ãá’¼ÒSaSŒ¨Mó%I‚$IPoÜ[PJ’ÌLª4…²­áf)¤3÷IÓŸýìg÷^˜=X19b äzäCøŸþU\ÿ»øþo ñèWw±Åи=Špç§·áÅç_ÀåÕ'1ò¾¾0x²õ% È’¤{’DA{éd’N&“d0¤“É„šÎf¢ÈŽ@9Ù—,g Sœ$3Išâ·ß±÷°Î’ˆmx¶%Ñï÷fî“$³ÝwœyR€GÈn^jP_þÞàÌÒl ·1FŒa¹Äk'¿Ùظ aÞ Ë {ø ÚjU*–Ú@:s]I’ÄP‚ÏØ'NS¨TÏÜçw|ù—ïéƒ IS-ɪ\€£BùÍõ(£H•ÎÇUW,(Ú[ù™ 'ƒã cµ K›o€óñ4M$È ØNÓ4afÅÌ:> 08" ÇžŽ"'ÉN”¤,IPiÓ˜UÖG-›°VˆYs!çÏ@€ §¿0¦sŤŠ Æ`4,v¼<¾—*,cüK`!C“†´Ì ,\žŸ=ì€5ˆ9Ïõ»€Ir–tðX¶Y–¼V@ÿ².`Ñy³|n¥ôÌ}Ο=Ÿ‚ë¶ñx²§ñzõ«{îc,–c6û÷JÌ B*bÊ­[–39–%Ó?ÀX“8†Rj;I’4I’4ŽcÕëõøÊ•+sœÙ1…HÓTF#miš¦ZR'I,›Ü2«…ÖP¬¡C+í5`të›°lì¥üd(“O~ç‚yÌ@Üê¦ç™3g5Wv¦¯`Ó6t({ì|òªÉ¥ïÈFý-(H¸4›Ã¹³—[(ä½ë]z¤¡í /u¡áê«OâqšÔ*ç»»Æ!¨ÃÓX¿{w¦’ÇsEøu°½µU¹Ï ß?¹4à‘Dμ þKÛRNÊRHl Õ^ÿØ–Oâ8VƒÁ@1³>¨ÿdAÀÑh¤‰H7 Çq:™LÔh0ÜJº :IbF‘ÅzÛUƒ•Y´§GÐEXá`S^÷ŸÇúìúåÒÝdY8ô!4À1÷í' ÆÛÛ€…v­v'«Óg«à2´Ÿ­ÛD(ÈtPÌ•…´9Q&Ê[&-Mz0C æ„ ¾ÂäËǬî1â86Ç#Y«$R¥f*‰ñxŒÍõ™ã3™Œ÷,éN“8;×ͰÏÚÚ)loï8¸ï4àa2™+‘ÇÜ wÜʺϚ5â8˜{Ö÷W£Ñ(M’„wwwËU2÷$ €8Ž9MSe#”éh<¦i¢'ã1ÆQÃ"¬+àÚBöÖÚæõó¢Ù¼ržèç×’:pü·ð,¼ãm(ÑÑAšÍ&:­š­V–Ðó|)ø  Èr‘¯ 9çÁK r2ƒrpÕìÉ'IÖͧÖJHbè4­ý&ãÉÌÏÍ>c¸]nÞ©fûm4Z`µãE>§·V³yb)ÀÃÎ8ö*[ó8P!W­©Òˆ”F’ÄBô’$I´Öi’$j4©ù“ãQe b­µæáp¨Fãq2zJ©d45¢¨ITë=ÌûÑž‚wgýn¦$F£jBÐ'ž¸„Û7oÍ´Âã8žiùvôi^dƒ‚Î"æ¼gÀd<‚ÒŒ4 0™L!{Ìk­fVB‡¿aÜC `!+¥x<§ƒ~?ÙØÚºqjmm2èº$¤™ìIl|ËIŒ8N 8/w‚“{ˆV”‚ã~Of>[?jê i¾ ©#ñûÚ“oFùXè‚ÿ˜QW»¿KÏÂûNÖÓ€‰RH{=ô{½¬ƒòòÒ@„F™†*R‚•FœÄy¶ÁÏó•LÈ~g 4¤5HÔ³)%iŒ8M f̧ÉdŒ0 gî3žLf~ãÁÀ d½åÔëeJ¢›`€êä€ÚŸð¹ ØÒ±eÄ f…”5Rb8"žÄh4 0FÃ1ÂF4HÓtÇñd4%“ɤŒ¸w.€µ’Ñh4Çã~¯7ŽÆëãÉd ½>˜5úƒ>Æã1&“ØÄRã?jÖ9„JR ×Aa ¢Ê80>Ê3+[íL±k;ŒMΖvAä\qÂ)¤ŒWß}—ïf^L7W0;½ž§dÌu./¯ tZ-È °]“ ÝN‚`Ü,¥²æ«–cѵ`sŠŠúÒ/ÅÐf.Üöä¥',Ø à{ßûÞ©±év;æ{{ìÓ뙎?ýÁ rŸG/>†kW¯`œZ]­Ügkk3SV§ÖÖ*÷G'L§JÓMî¹êû&~ž0®°Ò ¤ØÙÚBo·‡•Õ°f$q‚Á ‡'yêµ­­í± ¦ƒÁ õŠ€îm0Žc=ÓV«•îîîŽÚíöèÚõk¿vöô™Ç¶w¶;ab{{ãáÃñƒAi’f¾~N¾A™ÏÊ"gÑ+”£T-(¨ÅN»]r_˜…/ÌÂ#ä¤i¯T™!@Ù³(ü-ì¦@ĽNS£<'“Iµ "¬ß½ ‚a`n6›‘µ$Ü54¢Ò*RVãÑO]¾Œ¥…ÅBI.³F¿ßÏ”ê—}è#h6¾I„TiLFq¶Ïïø²/G£yãJxîò¾ê+¾"oñmÕïõ3%²´°€ßù±Mõ{ÜÞÚÊöYY^Æïzùål²Ö×Ý;ëÇKº‡KÉûLF;0QªR c'1–—°¼²b P*E»Ó}u0Þé÷ûƒÑh4NÓ4¶\€ú0ËÜ‘4µ'¡m*pÜjµ†½^oxóÖ­×Ó4ý?O¯®þžÁ ß&ƒ!†ã!ÆÃ1&“±)Qʃæ=ë¤co®Y¹\£Ñ|s­Â}k¸m© Øp ŽOEíl?{üâÅÚ<Ðx<¶¥®ú·€§ÓNVGÅñ Ÿ¤ÊÍ!¯ç ¨<özµ*ŽJ§Óιý¸Ž‘g´P˜]—?«2û»ºæ>ÎØÓ5‚Ë=uvºØuhóäž§f†¶qeë¢F„N³mª‰N§m¬žZ{çÔ™SßÙÙÙF½Ñh4éõzzgg'=¬{T€{N´ÖI¯×Ñ€îx4úÂÍ›7§Oþp å¥0 Œ@qc2ž@«ÜC4}ã {ôÌWv¯¡Œ)«s²DMÅÖ^\£UÔ‚Šƒg(ŽPp½5u¥Rñ «­,ÏRçFlÇçʯ›p52uj/S0 Cgí½¯”¥·y@µNŸÏuë·‚ô¡Ï³ö°—ÐÚ›ÿŒ&Å;ÀÝc÷ü£>-¥|{}}ýÎîîîf¯×ÛÙÝÝÅqœXÿÿžâ|àcš$IÒëõÆJ©í*¥Ö¥”ÑÕ«WG£Ñè“­Vë|§Õ:£™SVÁ8“fä7R"‘ÇÍ ¦´gr[°Œ–}ƒhÏ|H¡k¢@ÆÊãŠw™ ûíAp¤[~®ŒÝeâ.šZ¯9 raõ®,%âšØ†ðw…ŽAYþ¾TC@TL¥æÁÔ*ÿˆ«»MÚméÉÕdQŠ©MOÊtž&K­V\ I挔¡J¸$ ¾ï=}\bo€2kвùQõ=2Ÿ•×! MQò¤Ÿµ¥„`AÐAr§ÝN[öhaii«½¸pS Þ¼ys›™ï¦izs8Þî÷ûÛ;;;£;wîÄ›››e:ð{bøŠ@¥iƒ…J©@J)l…à$MÓÁîîînÇï(¥ZJ©Pk2sîGÒNié¥5¨J~ÉÊŽa÷i”Kï‘+·ôƒ`F§ˆœ‡ÝÑŽ›Íƒ½c13»ÊG3 ‹Wai¦x§ËÞy›×žr#ï¼Ëß±ç‘_s†$žÀçËuR97¢ úp»?–ö:3ÌŽåMnÿ=_žÃ ðùËø~ûÚ‚>Ík­5¹L3ˆ„É"߇;Tù<Ü{Y;º¼î„ݪêï±^Úv¹ªúÜ=+­ «¼ÿ¬µfοĔÖ:B$2é8‰“Nš$bÌ„Aǽ(жˆh#MÓ;ƒÁ`s<÷wvv&Ìœ¢H‹{O\*i @¬”öû}¡”’B 0LÓt‡™»J©Ž|'ü‘U}8"ïµ|Ê[p •JÀµ÷§ô¹R*)rV<àR.BhïX®+Ùt¨¾ì ²žTq޾R#︙bñ¯E”R¹LæÜ@Bäάw¤µ®ü~Y¸™9Û¯¬œ@¸sñ…Ãã†È¸JÀ}A²Ïä –w<래ã13Ù}…aRξç>ã*EàÛS,…ý´éÑíŽÅî:«èÔ«€òÂø0³ûvŸ3³¶ cb±øŒwwwG“ɤ×h4v„[išnF£íÁ`0ØØØomm%(ö>P΋ö‹ŽªS’·z‡öÑÐÃp™ÛºÌ¼ ÉÌ­µÛ/‚i¬äzªJû;~oUr“ßP·mŸD(}Æž€ 'hN Ük÷ïsò…Ã-\óûìWƒJ )XOaù²nZ^åC¶›ü¥k÷y×IÊ„|!öϧt?™ênìŒÈ·¯üU¶â3.Í3'ÖÐ ö„þgVHÙ]‹[õ- †0d#F!¸cxÏ\î*¬SþgÌÌPJùçÍž²áÒy´ké<<ÞLVJi©¥ÒOLˆ(‘RÆQ ƒ ¶Z­A}­õNýÑh4ÚÞÞlmmõÎRzž˜Ëq(ßDžˆ<áî kž”½zfˆ¼Í¢•A!¼Õž¬Ð¥#'Ä–d$ûÌ ±ûž¥"+ '9¥!„ {<A@¾U`ÇöwXJÉAd.€couëŽE9³k¶{ÊÅý–µÈÉ)0§”È;ž¿²»s&_)úÖŒo¸ß)¿®2ܵû+»“)©þjAвରÌ)i²Rðÿvß-Y°B˜í£”"­513¥–2Ìþ]0¹לÆlV8Ë e¢òî÷´ý¼Ï3ó=MSÒZgŠÄíçþvÊÂ=Û}Ùý†míjS*™¦iš2s"„HL˜yØl6cfI)‡ý~Øh4âÁ`‡Ã8MSUJÞsà‡¤¤·¢ž‰y«¼/ø…G¢Ùl:Sµ Xa’”2þ ( Crã„Ø ¾pû !(á .Ù¤”ÂW.vò³ýeLFR²[±í9 _AyÊ&;ŸÒªïάr «`„¿OÉJð-òWñÒkò›/ä¾"ò¤—=Kk*vR'(» ž?ÌU“Ï{ÖðÊÅK¦¸¯ œ%À¾ùí¯È®DÖ,סšK+¾ûÛ ûm¶˜)çÂÚ•9[©­È,Ëu¡}eáVt§p”R°ßÉÎG)ÅišfÇO’DY Çã13sÇ1’$‰]\ MÓX)•Qš¦éD‘&I/..ªÉd¢nÞ¼™¢ˆǽRU“ET<|Á§Š@û+–;/'°Fƒ„hµZEµÛm„aHÍfSv»]"" ‚ S¡Í’ý®pï33¢(¢(ŠHAa 'èöRJá ´SAÀ>K_ÐýÏü÷½8†k´!ý÷Ýoä|¾9á´qrÊ,"A%¦²kã|cò÷U¸¢|ëÌ~ï}ò‚„T¡ ­Ù#ûó…Ýý팷º{A²‚‚ðß· [u.ýÙîÞŠ^¾ùê) WÊžY ö=ßÄgS‹£’p ‚»ã:÷_'I‚4My0¨Ñh¤™YO&ÄqÌãñ˜Çã±–Rê4M•Ö:Uf@ô`0ˆÃ0Ô£ÑH---)"â7ÞxCmÛ Óê,çÉ*€*wÀw |÷€<%AU+ÍòòröÞâ⢀ÕÕUj·Û™pŸ:uŠ¢(¢f³)Z­A@ÍfS¸ÕÚîçV[Š¢HØFî}·ª kQ¸‡R +ÐðVsðVÔLXÃ0¤*!&"áeœSnúJÀ|Úgáí+JJžE ¼ûY©J”vÅg•Už™ONÐÜëšÌŸ®°¸BL)Ï ( <ÁÐÎù =á×¾ÂHÓÔ’k§JJA»ÀœZç-ø Èjm-ÍÌÚ6PaϽpBŸ)4Mu’$ÌÌ<™L8I=ÀÌz4©~¿Ïý~_YÎ DZÇÚvÖQéÕÕUµµµ¥‡Ã¡þèG?Š~¿Ïßû½ßˇUGY PµqZZ”V{ª33 Úí6–––¨ÕjáñÇN‡˜«««´´´$šÍ&…aHFC4›M€F£AV!'˜RJ·Â 'è¾`;I­÷‡]v‰™µs5ê„Ô ¼ð3ÎÏ÷bäMò¢_ˆóŽW6ÍE•¢õWi%à)ÔŒ=aÿe¥Tsß¹æsö]þ=>ûÿ³w5«Q,Qøœšk @"®BÌ"+ŸC@AQn}²,Qð-|‚K ‚š™ô9wáôÜšãùëNçÆ UÒÓS]Õßw¾óUMw¯0HÎz`æÕ3MzERÕ¡JÞ¯FûªÔe0*¢¢åº®"ŠÞ±_M¸é#}EÔ§Ëçbö&\]]QOËÏ]¯‹Íçsº¸¸à_¿~Ñ|>§eWß¾}ãïß¿s×uÝÞÞ=xð€¿~ýÊçççðìÙ3.¥ð§OŸ®=ïyjक़¨¥xòä ""Eh­¾v]0PrC‰Ÿ ˆÚçµ_³Uλ4¸ÖƒxÎÚˤ‘*u°6_¾ÚnõŸ™©"eªRúoªÀZ]ETTïS­J ›Ïç+?¡J9ºÅbÑõáòò²[,´X,hkk‹~þüÉÌÌôãǾÿ>ÓçÏŸy>ŸóãÇÙ ¸D^z>|@D„££#DDØÝÝE€ýýýÒçç"ŠÏJ)ÀÌ}Ä^­T5ªPƒ¶_Wúqå  ³ œP×)”é ü ù¹XEôed+Fú¤¾(ÑYHÅÀN´Gƒ´YkýÄõ$ã:ko²a%%?q%… H#€eŠ ½[šà’h{mY© ˆ(Û¡Ja²_]­f–ãÿµÚ ž<–y?ÿ~ ðo…PJƒƒçÔ›BaÛOŸ>…?¢ã)Ì Dà(F;5@­v,`×íÎo£8û*ÿKóTª€¾³µ„œ‰ü5‰D/O²€•1" f¬—É“ ˆµH®ìC'AÛ¾3ȧÿŽÄ,Y¹ ` D°ùPåkðQ„ØL‡H­üè™Åè/Z-Õ"=0¦R[ëÀ!LBv”µ<Àg¿÷@¯FA¤ŒõZ}¯ÿNéK«CÑdÏ˵Ê?ð•çÏŸƒçB‹—rVF·~Y»y‹±¥+ )4À€âBÉzõ¾iï8"iâç„Äñ{2Ÿ³®ù|½Ñ#êëdy)€¢0 ©L88f Hö+ZÀhÛR>X‘€öêîk¾x]MÇ6W¼xñÞ¿ÉÑâHiKšƒR/#Õeš ÕÀ̈ÐÅèSk/Šî%ˆøèœ§â˜|%1 E~@AƒôÀ6‰íYHgT@NƲõÁQZPï«¶]§DçÎ2'È~¥¨rÒ¾sÀË—/áÝ»wÚWEùìåÿ`¹ã8³9;¾C¬%h·8û¯¹úô™˜½_ÁÕ¶lœÓ±†Ê‰¼+ú²A 40màF")@ä#XE1qЯE"àœƒk“À­Àëׯáôô8Ò8!,€›äåÞh€3j#ŠÚ™uÙœˆñ—~´B ƒÙI+¢€²È€]Sð $¸µQNnË)T¯U÷›DZr·àÕ«WpvvÝdÖÿb€È›(=pG ï¥lDvXØ?+í{€Ï‚=s‘½ü3ò¼:”0)A Vä$áMh2‚h‘¥^´zÓ9¾É$#·FoÞ¼““HDxó³C`¸å^ÄdY¥}ˆ‡î0ˆðÞñ²B>`˜˜™ [œPȵtÁ2õ<¿Àšo@FÔ o]†Œ¬a< È5Ù|è׊\ ¹YnòÅ%3#½ˆ@*e=$ IYŽÁ¾s°?VTÏ,[`Ï@Æ…æäç¬cï™`ŒxJ!z„ÛyËÆ;Á ÀÍ6ÏÏÏáàà`( p 8ˆœ‘§ PB2߯ÛfúÍÊü)?ŠãGŒ$„(eà€3ºFz2¦?ŒMp”¤à¸¥yoß¾…ÝÝÝìMäýà“%AMBʳBQ¾¬Í¢cEV°gÛe"=€?W¿²=šÊ ©z¥$®G–à† @þ8‘}(à­uZº"½TÌ?ë†ô‰ÿ‘Œ©ÏeÊýŽÎ ßð7™2’Ê»±§Ê¥ð†z[Ñz¯ÿߤRÆ\?¾¡cšÞh¥•VîNiÐJ+Zi¥•F­´ÒJ#€VZi¥@+­´Ò •VZ¹«åß Û¢íß™eIEND®B`‚gsmartcontrol-1.1.4/data/32/000077500000000000000000000000001417717230600155605ustar00rootroot00000000000000gsmartcontrol-1.1.4/data/32/Makefile.am000066400000000000000000000001601417717230600176110ustar00rootroot00000000000000appicondir = $(datadir)/icons/hicolor/32x32/apps appicon_DATA = gsmartcontrol.png EXTRA_DIST = $(appicon_DATA) gsmartcontrol-1.1.4/data/32/gsmartcontrol.png000066400000000000000000000037601417717230600211720ustar00rootroot00000000000000‰PNG  IHDR szzô·IDATX…½—KÇÇýœŸû¥¥Äu²–"É’K¾çøž[äS$òø ¾Äßs5‚b@N0àClc%í ’¸.Krfzºs˜YŠ«µì[ltÍ Yõ¯ªU5EËë7¿ü•|ô“ÿúî½{›eYr1›qÿþ}ŽŽŽ! „@!Ô¬=”Þ³¹ÑÅXËá³C:ÝOfûôÓ>þä“%o\zýÆ9w«ßïÿ©×ë¥ÙrI·ÓA)ÅÎÎN¥^TFàKO ¥\!`E*Én¿O’Äø²tIš~üç;ÄqüÓ½ÁÞnÇ­i4š8W •B®ŒT@¤”H©!à}Y¯ƒâC@+ÅÆÆó9‹åmìϾ@«Ó~€€à=3œs°æáJ¢òü2…képR2N™N§œŸ`­yø¦ñkšÍÖƒ$Iˆ¢˜ÑèŒW<¯?¯Á¼å’ŠñxÌ7_Ãζ¶¶HÓÆ½ïÐj5Zkò<Ã9‡”’rݯ5#ßj\TÆGç#Ž ‚÷D‘%MÓÝï¤ém¥‹Åï=!„*Ô¯]þvB‚€“ÓNNORÅ!@’$4Ío ×oÒ4ÝTJ‘ç9¥÷¥Ç{÷¡Þß”+¾®àÕ«WŒÎGXcØßßçþƒ‡ÜèÝ@)M£ÙLÿÛßÅoðç?üQÇQÜBpãF¥>T¥B „Ð2Bå9§§§EAEØ(ÂZËÙÙ)ÃáÉtJw£«md¯ñ`ÀFöN«Ý2ÿýê+‹9½=BðøVD'„zg“ñÀZ‹Ñ)/^¼@JIš¦ïɲL$qòî[$qrWk-òeÆèlÄx|NÝv¨-‚¨¤KûÙ"c>¿@+1­ ÚŒ1mȳ¥I’`!I’÷Þ°"aœ$¥”ìöwÉóœÙì)ÅÙPµckmÕš¥@ks®NÇëNy¹—eÉÖÖÆf³‹·0F¿×l6ÑZ3›ÍPJº2ÀC·ÛŹ’år‰T lEÈ,ËVß«šÔkÐ.#ð÷ÐZ¿Ûét˜Ïçu›•µ.±òf{g‡ÉxÂt:©ž×KJ‰’мÈI“”@¸2¤”DQŒ¾ÿV“é´ßh4X.—(¥BVçVT)‰X.—XkWÆ„” a4>§Óé|¸Ò2¥’­1Æ´ß°"¡”rKJI»Ý®?'Ës””kIÓW8Š"Ç9WõÿÚˆ”²"áe¤j—²"¢(âåË—4Ò”½½½ë)ØÝÝ}àË/¿¤Ûí²Ûï€Ó“S&Ó „@£Ù¤Ùl‘$1ÖF@Uÿeé Þ!„¨ªg­|CEÁ“ƒ'q±¿¿¿ À`0¸wxxÈd2a0ØãðÙ3„t»níÞb¹X0N9;=a¾Xey^EC+E\·Z¥5Ö„¬Êî²Tû·ûQ–%¾>AeÙ’—»ÏƒÒú³kž|tðäà›f³¹ÿÁăÁ év»i§ÓI6ºQ’&qE‘µ6ÖZGJ©HJ©”RêR‘÷¾,Ë2wΕËåÒ-—Ë,˲ùb±XÌçóÅÙÙÙüøøø«‹‹‹¿®ëN×®6°l®­Ъß%@T;P:À°fÀ8N p¼rÀ]/[練- ÖÞ ®ë=PÖ»ŠÚX^?Ï×Öwø¿^ÿ¸)[ØoZ‡IEND®B`‚gsmartcontrol-1.1.4/data/48/000077500000000000000000000000001417717230600155675ustar00rootroot00000000000000gsmartcontrol-1.1.4/data/48/Makefile.am000066400000000000000000000001601417717230600176200ustar00rootroot00000000000000appicondir = $(datadir)/icons/hicolor/48x48/apps appicon_DATA = gsmartcontrol.png EXTRA_DIST = $(appicon_DATA) gsmartcontrol-1.1.4/data/48/gsmartcontrol.png000066400000000000000000000074371417717230600212060ustar00rootroot00000000000000‰PNG  IHDR00Wù‡æIDAThÕšÉo$ÉuƱä^U\ªHö6ÝÍéE挤6Ø3šƒÆdË:Öݶ_ دð¿à‹àƒ/†/:ùhÉ#òÁÆ@Ó£FšîYÔj²»IV±IVeU.áCfe-$gznvÁÌÈÊå}/¾÷½‘Î9þ?oú¢þôO¾þÖ믽Úíõ¤µŒÓ1aˆ/Ý»ÇýwÞqRJ„ ÷‰é_!˜þ4ï$1÷·þ‘W¿ôEñàƒ8>>v­V)ƒÁÀýç[?üyYéôÿò½ï½8€ÍÍÍ¿ùí×^ûë Çàžç‘e;;;•‘°Bµc Î:¤(¥®qõ³¦[Q¼|ëeY¢¤Bi…sŽÃƒÃx÷þ;æûþEf~ €K[wã(¢4g-J)¾ï_òƒàªÖš8N ú”eÙp~Þ•Ë4ZêY$/_Û G)Eže|òÉ'=?b{{›(ŒH’$ò|oUy‘ýçˆãøf±Öš²,Èó¢¶eêwÂ5ª"we׸Š&‹Lñ§§§üúÑ#&“ ¾ç!„$NbÂ(AöŒ1gói’$Ùô}ß÷ÇXk*ïÏ[P†kºg]ãð)Uª;fJ:‡”’çÏØÛÛÃïù8ÖZI‚ïùAp7˲ÿø\¢8îzžÖš¢(pÖ‚8Äœó£Qõë]C›ª?—j0Óá888 ß „ÀÓ^=p޲(Hâß÷ðÿËeY"åù4:@ø›Z+¬µE‰­›yržÿn!ˆ§à8ã±ZB¥À”%ýÃ>ÃÑ­UeˆÒ„QˆsŽ ÑZ£µGà_ȳüóÂè BH¬s”¦ÄZÇâýánÞÆšþbv n)È¥äYÆ`0 Ïs<ÏI+á•W^ÁÃ`0 О&i%ëé8•J)ûBþþoÿN\¿qã¶s–V«Åd<&ÏòF>g_ˆ Óøy58)`<óüùs¬µ•ñ€³Žv«Mžç|øá‡E%ÖXÂ0¤Õj­G¡R*}!ÚÓ+QÝ,Ë’Çsý¥ëø¾ÏÓ§O«‡ Aã׆)n!h]cyÕÎ1¥ GC¤(OÍ +ÁÑñsв ‰N‡§”e‰ö4žç‘´Z]ÏÓëRÊsœ!–çyka¶Ûí6<àÝwïEN§RHçj?»)†™±,üB`ãt8$§(¥QZ!¥B©ªI¥B’¦)Y–±²²J»Õ&Žbÿú×à\øÁ-¥çmgž¿Aøüùs†Ã!E^pxpÈh”"D#‚Sûšb†£B XcH‡CʲÄÓ]­•l(¥ÐZ¡µÆXÃp8Äó=¤”ŒF)G_à …ü \Óž'¤Rܽ{—8Š99=¡( „‹Qéh,kNY&“1Ö9”Ò3ðÓë›Òhz0«§&ã ëÝuÞxã ô·œ[~Ã…ü/H)+þ% ''§³¨Ã‹IE,(¦Öº)Ÿ“V‚1†"Ïk9¶3£çˆ¥s pŽÒ´RÄq´1^ €§õŽs°ÚY!Ë2¬5µñbIÚ½¨µ¦³Ò!Œ"p¦)eY ”& #ʲd2cL Tš5£Ÿ˜Ñq–]š\'­^Q—3Ÿ @*¹Ój·HZ '§'•ŠL3ëÒÄeº÷}Ÿ ²<çÉÞ^•½«eW ¤@iÔŠ¢(ÐJ!•\(CæŸ;MÞAà“*E×?>(ª"÷bÿøÝïŠv»Ók·ZXkq®ªW*'‰³žU¼±±AQ”<ÙÝCi…ÖÞ‚wgcžïs||Lw}möâ¦ô˜£Q]^h­ñ´wóÙþþ°wÆáóO~õ(ô|oMk”rÖ”BJ‰RU_H‰TÓ½"Œ"NOOð|¯Žê|¥2­TíuEEdYVñÛóPuÌ(UÐT™„H%ÑZÓj·Ú'''ÛËÆŸðƒü ÕnµWu$IR«Gm¨”KÇ=œuH!X[_¯’”TÞ7Z¯+ã}Ï£, †ÃSü ¨2“V=×—R!(%‰Âˆãã“ËçX Ðµk×¶ÛöÊ$›Ðj·‰Âtœ"¦4ª‡{YÐúý>W®\ÁÃéÉIìjŠ )Ãá>xÀæÖ&QUeúÒ3g²Z?BHÂȧ·ÑÛøL½ÞN’$bÐïSä9—._fw÷1¦4U<ÿ"1{åx<àèè9+«+\¾|™ÍÍ-¶¶¶PªšCã\3‘o ¯[³¹ÜÂÊx3±hOóÎOÊáá!7nÜ Ûí·ïܹz1€n÷Z«Õ’?ùñ9ùÚ×ÞäÉ“=ƒ#´VL² )%7onsë–äé³g|üÑG¼÷³÷0Öàyžï£D•¥1EIQäTë:¿®ó=­«k¥bº`¬´®V?€yž³ººŠ5ëÎÚzï°Îâ¬c8 !\•œJJ!¤@ ‰¢^7uÓ¸q®.7¬µÒZ+œ³5%›óuÞ°EéÂ(ÿÎ׿þ?÷ß}÷/îß¿ÿà\¢ª™{ÀËÀ*àmnnªK—.é••Õn·U«ÕÒ­VKµZ-¢(R¾ïë tJk=mR*¥¤RÖ3­ƒœsÎZk1Öc‹¢°yž“ç¹Íó¼Ì²¬œL&åd2)Ó4-G£‘é÷ûùáááððàà`wÿü vÎY`¿n¼ýöÛz}}=ˆãØ÷}?ð×&õ~zï s†,ïÕ9ç§M,µé6¿6oÏi%³Q2KÇåܽ/` â¢ãeã.:¾hsç¿è¾Ù.üWƒOyÉÿ©ís_ÔÙ߯ÌÉIEND®B`‚gsmartcontrol-1.1.4/data/64/000077500000000000000000000000001417717230600155655ustar00rootroot00000000000000gsmartcontrol-1.1.4/data/64/Makefile.am000066400000000000000000000001601417717230600176160ustar00rootroot00000000000000appicondir = $(datadir)/icons/hicolor/64x64/apps appicon_DATA = gsmartcontrol.png EXTRA_DIST = $(appicon_DATA) gsmartcontrol-1.1.4/data/64/gsmartcontrol.png000066400000000000000000000135401417717230600211740ustar00rootroot00000000000000‰PNG  IHDR@@ªiqÞ'IDATxœå{I$Éuæ÷ló%¶ÌŒŒÌÊ¥ª²–fõÆEl²‡Ó¤t’Zºˆ 0é +9Âü(Bw$Q€æ8Ž.Ã뀂¨ CvOKê®nu×Ò[-¬ÜcßÌLs÷ððŒÌ*‰Ì8Ò2<ÌÝÍí}öv{AÖZüÿ|°ï ü{âYnú¯ßý/½×~ùëLƒ*ÇX‹O?ù„~ó[ߢþ``î|ø!„pCÑ™q¨ò¿~zöÞ³u–áæsÏaem•ýÕ_þ%„¦ÓéÀ b q’°Ÿüø/~þƒÿögýg¡ èi"ð;¿õ[Þó/¼ø‡_ûúk_°D Y‹ò k1Nisó²,µÓéÄØ¹´[Àpñî^ªwÀZ‹  ¥¤££#!¬Öœ3hcÔßýÍßüí;ÿð¿÷ßÿüÏõS©Ç3p€Ö拯¼úêwöö®a<•«k¬…§¸ÐY†nwÝF•D÷Œ±È² Zgå;8çR.l+ÿ܇ˆ`­…ÝnQÁXƒV«ÑpÏóð_ù•¯üñ'?ðÿ&ƶç)LgSÀZ ‡#ø¾b Œ1H©¦)fQäˆ,Ÿ¤âoá`ŒA !²LCJÆ8Ò$1f€µ€ìNIÓïCJ‰Ù,Âd<Æ,Š0ðÞí÷h:îü›Ðl6®û¾$Ž‘ež§F~­3L§˜Š(-åî‚2$I!‡çyÐZc2ÂZ;®&TGk,Máû>qœ`<ãþÝ{èúPJ^⟠€F£¹Ë‡ÖDŽ+ ¥²,] ¾˜h *.”¹»Ò4E’¦9M5È–è¥â D¶¼e:›Aƒ$ŽqtpˆÙt†FØ€RÞ•g¢Ï@«Õ¼^PÖh4 µ†ÖIšBgY.ï•IÒI-Ó³Õçr‚m­qwfÝ €ˆÆàî;F¸qýúƒ>â8†çy7žFWq<€°ÙØÒ:ÃJgaâèèI—„.X… ¨é„êsUЪ¢P;Š~ÆÎøô“Oq||Œ°ÂÂÂ÷|èLÃü§ÑU:B¿ûï6›Ö–ÑJ)$IŒ4Ma­…5ÖX×l¥c¬kyŸû4ùµ¢¹~c-Œ1ùxféó¶21†Éx‚{wïb0èC)Áƒ0 ÀCà‡;¿ûï¶ž€ 9@H±Á*`!ÇdÃí&`n¢òh. ”÷Û²§¸¥"0•çrÎ^¼Û¶à4¸•?==Å£‡ u)e.6€Ö¾€Áý5ÆYÀèÀWÞ–ç{Mä/ÏÒÔ­VÁ¾uv¯)º³¼L°XÂâ¹` Øš¨1€€ƒý} 1÷„,7ŒB?%Û>ù…JmI©@ÄÀˆeYÅVS)Ç˼ººÞ£²×93guEþDnjŠçcÐ:Ãáá!ƒKÞGÈÒ a;„à žï qù)´;/º¨¤¸&ç Ö:6›»ÎÝlëNOÁÂóþªûLyψ*âåˆãˆ¢Rˆ 6£¶2÷J=¥žÙ¸Xü`ŒA c4²LÃ[ã𜼯­¸R1o•þ‚tª÷[;÷5ˆa<žàäøZkH!Ë»¥’XYYA»ÝF¦3dYßó„8 Ê»ö‹× €R2—{  VaèEfŸ>_ñ¢ßºV°þ¢\ƒ GC 7I)r€!8®í]Ãõ›70 Ðï;ÛoŒÁÑá!,,;'ž:Õ8==EÍ g œq0Î\Ä(Œ±¸{ç2!3s÷p!À¹€ïû[Œ±•‹ˆ¿!ÄŠRÞjÁþ·o¿‹£ÃClnnâÒ¥K𤜛Ã9ù°da)aAËYÐG4Üê€$IÐ  µ†Œó¼10ÆËè“1BE899A†èõz¥3äy` ˆ€ :Œñ­§p®pÁw¥’ÍF³‰ÁpˆG Š"\™N±Þ]H’Ò†W¹t­€‰Ê®"`ǘN§î½œÏ¯(•ÃQÙoŒÁÉÉ)aˆÕ•UÌ¢Â0Ä_¿ñ×,®\¹Ê¥[ÞýW …Øô” ?@4DPJ!Mœžž Ž‹øIÐ_¶vÍÎ#rŠq6!Žcp¢³ænaµ Õ¾Q!I4 $I‚v§h6CB<Õž €òü›RJ+kkèml¢ÝjÁX‹££ãÜdÑÜ®®”-LÙYSWbÁ,š!Ë48wö}®@+÷.±ówXþúÉtŠ•Î ~ùßÀ“'ûHÒR=Ýœ€”Û\pXkÁ9G«ÙD’$˜L'‹é®Ü´³»§ùÿw1h­‘ÄqiÖ€yž¡J5O0'œ¡ŽSÁ…w‘f)„ð<õ¯ç©Ôç1c°¾¶£µ#>Ï÷ÍgÁP›w9»jNˆÀÄ++Œ iš"M¤E¬1GdIv™*œUãksEÉ 8‡çy»Oà\+ ¥ÚÐÆ ÙlÂ÷LgQ.“TY•g•>*&鈖Ra­ÛÅÎî.666ÐnuÛùÅR)4š-´ÚmøAç ¹3ÄÀÈsž7ÆÁ*æ‘sÆ„ ´PR‚1ß¶ÿôO~и€¥ðƒ?úãv··~™ˆ°¾¾ŽÉdct%ã{‹×!|ÏCocY–aÿ ¦“ 2­+#2'†1ÇQָ쯱s«[‚E–#w-OÔºðØëú¾·`rK9€q¾Êïaß&©SèÌE…DùˈJö.D£dw. ”‡­ŒÆ#|üÑGÇRHH)!¥‚œóûìÁ…–`©Ý ‚Zk(σ*’"¹81`®UØžæF*wy›Í†S`ÌåXåœ3‘+7V°7‘„ñhì||bNOTØ3NäZ!Ärð$¹0~ccãêE,å€^¯wY Éd‚À÷áûf³é†ƒ!,€V«étŠ8Š+îjMW359wFca÷òeì?ÙÇd2vYä*9ñ…‚’÷îÞÃÁÁ¾øÅ/ÂSZcΦú»Ï‚_pœ5×ö®m_»vüñÇK7KψÀ­[·x·ÛÝRž‡$‰±ÿä Ö{ëh·;PJUØÐùîThd"0F%k;™·8::ÄÁþ>ÂFˆV» å{ÎÆ&n—¨ßïãþG÷ñÎ?¾ƒ4Mðê«_ÅÚÚªÛùe.4žÛ}–G‰l~^4#·çÈÀ¥­-¬u×0‹\àrFs×c=k9Ex^;i8ŠDZ f’Fkxž‡—^zíN°.â«F‡K>P·,”+mÎŒÖØØØ\ÙÝÙÝpòLìíím5ÂÆ gF³Ù oþìgØÙÞ5¼°ug”j½U½@.[“¥#xJÁS Æ4Q$4 kcÙ*–§æmÖÆ®ŠD¡œ…ÐÆ B¹¾¾¾àŸž %å¥ $ÈÙòÝÝ]̦SœöûJ.˜ÃºíŸtv¥êŠ€‹èÒåŽV!½€ã ‹$„€N"—/ïî-#~)7oÞ¼!„çív»L8:tIÈZe¿s¸bá*-Þ{6à©9K9˜uE·4Læt‘TqCÍÍÍs-Á|ß¿Ìrj·Û ‡ùžÀ|Sd>áù䪹ZºúÕUª7õ/Õçë°à<ÝŒA 0 !¸Àx<ÃÕ+WÏõ–逛Fk”hµÛhµ]†e8b2ž ˲›oV®¬¬@ (ŠÇq¹RfÃê2òor)áKD¨:çaè, 899AÇ`ŒÐÛè=;anûAˆãã#¼õÖ[è®­agw½M~ŒÑh‹„'%Â0Äp4D·ÛÅæ¥K0Æ`6›!Š"¤i iÇ9E¸BE)2…Ô€™EÔ/úóNÆ8š­&=|ˆ»wî"Nblmmsíív»Ý‡g¢ÂÖÖÖšÿó‡?¼,•D·ÛE¿ßÇÝ»÷ðÁ૯¾Šõõu4Md:ƒÑÃÁï¾û1žŒÑëõpãÆ ìîî¢Ûí–ãkÝÆªÖ0Z/ìWSÚs1Ê娬ØKÌ­… Ö‚áö»ïâÃ?Äöö6®ß¸Žf³ ÆÖÖ»½­K—ºX/Ðl6WÖV׺RJüýÛo£×ëá¹çnâððÿ÷Í·ðü‹/@J‰,Mqrr‚ýý}¬¯÷°··‡ÑxŒ‡â§ÿç§xcöü ÀÚÚ*ºÝu¬®®¢Ùj!|H!Àƒ1®f Ë4’$Æd2ÍsÎÁ±ÖBJ!9k¡u–fNc¦)ŽñèÑc|áó_@ocwî|ˆƒƒ|ùË_Æjg¥Ùn··|v!ív{»Ùjvö÷÷ñ÷o¿(Žñ›ßú®^¹"†÷ß{Œ2¡ÑlâæÍ›ùæ°Þí¢·Þƒ6ãñ'ÇÇ8<<Äý{w10EH’Z¤I‚$I\5• M… Á…ËqÎ!¥€çye4)‡R "¯dŒÀ8‡ïyxùå—°Þëáôôï¿ÿ>~öÓŸ"IS¼þk¿†kׯ/ ‹¸¼»»Ûl61 pikD@o} `gg[Û[%ë1ÎÏ*+$X[]Åz·‹[·žwû‰Æ Ë‹¢\‘Fa2‘ÒqFžò*ŽZµÁâ»j‡‹)Ï: ö¹[Ïãê•«Pž‡¥¦p€—^zùÃîî..]º%²,CÅg§’iÅ^à5æÃåsYÏ{©$Ò"3Ð@²dšõB¬¼·U•;TàŒákÿákxíµ×ÀÇñѾòÊ+Ï=€V»EBp|öàNŽO@äòzE2’q›ã`ÜE]óG=EE”LÈs¬È8›Í…(·ÄqõEEÚœ1†Lk§†±'~E­‘Íë‹ÊªcËcœ¨Yk±ººŠço=õõ×_§ÿøÇö\&“ÉäñãÇøÑÿúÞx㯠µ†§¼<'Ë…bÑ•T¸{ÿîÞ¿OzàŒ•÷*år~óï RI¨<È9Ç»ïÝÆõk×pÚïÃhÕµ5|úÉ'xù¥—ñλïàÖçnáñÏÃ÷}ø¾àÕ¯¼šË¿Y(ºpëïxŠˆ& ŽŽ¡”¯ÿúoà—~éKq¯×óDçPØÉµµU¬t:HÓ žçåÄ䔌ë‚"Âl:à 3Æà)ÊSð”ÏË`<ÏåëópÙ0’n”GbïÊU€sŽF£Ÿ?y‚Ï¿üy<|ô/¿øž<ÙÇÎÎ6àøè­fœ‹y¥JÅu(8KÒQ£Ýnc{gÃáðôÎ;õ¢”EN£(Ò­V‹·[mDqßóá)éy‚ÏßÀSÚ-W&‡ç{ð<¾§ày>¼<›ä{žÆ÷ày‚ g Bo£‡?ùaØ@³Ñ„R ÍfÖZtVV`¬Áúú:†ƒš­Ö{ë¥7Z?˜ˆüÀ˜N§X뮡ÕlâôôtÿäääLRd€Á`0˜Mg³0›Œ³RcgZƒë º \Zkè<>°(öûM¥öO—2ªÑy`i€0aŒA§ÓAš¦¥.)ôJ’$Bb:›Á÷}:«ì+¸—ƒ8G´{‚¯¼¾ nšøÛ} óüD„ã““ƒû÷ïŸAn!¢Ùd<Ïl®Mµ6ŽÈ,C–é²é,s-MËü@¡„lQäh ´vDkm vMkdÚ Ó€ç{ÐÚäÙ‹F£Q戹Ê4¥$f³”rV)Ó®Ü^kwî¾[|jÞ¯¾`ðoÅø +È6Æ`4í/ãš8=íONN†J©žT¦àÆÀ²,×è˜ïÛåYÞ€jµ¨¯¶1Ú­~Ù4`,€$NXÄqŒ,ËÀózD¥w›³Jyˆã•÷[ƒ: d0oïâ?ýÁŸbkc­O_H# C¤i:;<<>$IrÆXú³9ʽçyö›ßü&}éK_âׯ_½^O6 ð}_*¥”t‡âœKιbŒI"’Œ1™Ÿ ÇED$ˆˆ{UE>/¢3ÖZ]|Zk³¼¥ÖÚÔ“YkcLªµNŒ1i–ei–eiš¦IEéx<ŽãÃÃäÑhdßþö·—î -ݳv¡Ê¯Ð8¶ÒP¹¶ì³ ˆà| ª4^é+>«ÏTDzµgÙ’g—QŸ_}ìòx–_ŽV#ÑE“ŸgyãùX€¬|çKÎëDÔßQŒo+奄O×Þ]´tIÿ™rÍ3Ä=ã§«¾¨ñsúë«WŒY%¾8ê`jçÅw]ù^=7µ~åÜû/ :ѺÜ.뫳dýþú˜õ£>©óD­NØÓ®Ÿ9žéÇÓç þÿÄñÏ-–Îø·IEND®B`‚gsmartcontrol-1.1.4/data/Makefile.am000066400000000000000000000031721417717230600173730ustar00rootroot00000000000000 SUBDIRS = 16 22 24 32 48 64 128 256 nsis dist_man_MANS = gsmartcontrol.1 install-extra-mans: cd $(DESTDIR)$(mandir)/man1 && $(LN_S) -f gsmartcontrol.1 gsmartcontrol-root.1 uninstall-extra-mans: cd $(DESTDIR)$(mandir)/man1 && rm -f gsmartcontrol-root.1 # This command is needed if installing with make install (the cache # file is ignored for rpms). gtk_update_icon_cache = gtk-update-icon-cache-3.0 -f -t $(datadir)/icons/hicolor install-data-hook: install-extra-mans update-icon-cache uninstall-hook: uninstall-extra-mans update-icon-cache update-icon-cache: @-if test -z "$(DESTDIR)"; then \ echo "Updating GTK icon cache."; \ $(gtk_update_icon_cache); \ else \ echo "*** Icon cache not updated. After (un)install, run this:"; \ echo "*** $(gtk_update_icon_cache)"; \ fi # Application data, installs to /usr/share/appname appdatadir = $(pkgdatadir) appdata_DATA = icon_cddvd.png icon_hdd.png # pkgdatadir = $(datadir)/gsmartcontrol # pkgdata_DATA = # Desktop files desktopdir = $(datadir)/applications desktop_DATA = gsmartcontrol.desktop # Appdata files metainfodir = $(datadir)/metainfo metainfo_DATA = gsmartcontrol.appdata.xml # PolKit files polkitdir = $(datadir)/polkit-1/actions polkit_DATA = org.gsmartcontrol.policy # Application pixmap (fallback icon for desktop files). # Same as 48/gsmartcontrol.png. # Also, xpm for debian menu, 32x32. pixmapsdir = $(datadir)/pixmaps pixmaps_DATA = 48/gsmartcontrol.png gsmartcontrol.xpm # Put everything inside the distribution EXTRA_DIST = $(appdata_DATA) $(polkit_DATA) gsmartcontrol.ico gsmartcontrol.xpm bin_SCRIPTS = gsmartcontrol-root CLEANFILES = $(bin_SCRIPTS) gsmartcontrol-1.1.4/data/gsmartcontrol-root.in000066400000000000000000000111661417717230600215500ustar00rootroot00000000000000#!/usr/bin/env bash ############################################################################ # Copyright: # (C) 2008 - 2014 Alexander Shaduri # License: See LICENSE_zlib.txt file ############################################################################ # Run gsmartcontrol with root, asking for root password first. # export GSMARTCONTROL_SU to override a su command (e.g. "kdesu -c"). EXEC_BIN="@prefix@/sbin/gsmartcontrol"; prog_name="gsmartcontrol" # Preserve quotes in arguments final_args_quoted=""; for i in "$@";do final_args_quoted="$final_args_quoted \"${i//\"/\\\"}\""; done; DESKTOP="auto"; # Compatibility with old syntax: # gsmartcontrol-root [ [program_options]] if [ "$1" == "auto" ] || [ "$1" == "kde" ] || [ "$1" == "gnome" ] || [ "$1" == "other" ]; then DESKTOP="$1"; shift; # remove $1 else # New syntax: # gsmartcontrol-root [--desktop=] [program_options] for arg in $*; do case $arg in --desktop=*) DESKTOP="${arg#*=}"; final_args_quoted="${final_args_quoted/\"$arg\"/}"; ;; *) # unknown option ;; esac done fi if [ "$DESKTOP" != "auto" ] && [ "$DESKTOP" != "kde" ] && \ [ "$DESKTOP" != "gnome" ] && [ "$DESKTOP" != "other" ]; then echo "Usage: $0 [--desktop=] [<${prog_name}_options>]"; exit 1; fi # Auto-detect current desktop if auto was specified. if [ "$DESKTOP" = "auto" ]; then # KDE_SESSION_UID is present on kde3 and kde4. # Note that it may be empty (but still set) if [ "${KDE_SESSION_UID+set}" = "set" ]; then DESKTOP="kde"; # same with gnome elif [ "${GNOME_DESKTOP_SESSION_ID+set}" = "set" ]; then DESKTOP="gnome"; else DESKTOP="other"; fi fi # echo $DESKTOP; # They're basically the same, only the order is different. # pkexec is for PolKit. # sux requires xterm to ask for the password. # xdg-su is basically like this script, except worse :) # su-to-root is a debian/ubuntu official method (although gksu is available). gnome_sus="pkexec su-to-root gnomesu gksu kdesu beesu xdg-su sux"; kde_sus="pkexec su-to-root kdesu gnomesu gksu beesu xdg-su sux"; other_sus="$gnome_sus"; candidates=""; found_su="" if [ "$DESKTOP" = "gnome" ]; then candidates="$gnome_sus"; elif [ "$DESKTOP" = "kde" ]; then candidates="$kde_sus"; elif [ "$DESKTOP" = "other" ]; then candidates="$other_sus"; fi if [ "$GSMARTCONTROL_SU" = "" ]; then for subin in $candidates; do which $subin &>/dev/null if [ $? -eq 0 ]; then found_su="$subin"; break; fi done if [ "$found_su" = "" ]; then xmessage "Error launching ${prog_name}: No suitable su mechanism found. Try installing PolKit, kdesu, gnomesu, gksu, beesu or sux first."; exit 1; fi fi # gnomesu and gksu (but not kdesu, not sure about others) fail to adopt # root's PATH. Since the user's PATH may not contain /usr/sbin (with smartctl) # on some distributions (e.g. mandriva), add it manually. We also add # /usr/local/sbin, since that's the default location of custom-compiled smartctl. # Add these directories _before_ existing PATH, so that the user is not # tricked into running it from some other path (we're running as root with # the user's env after all). # Add @prefix@/sbin as well (freebsd seems to require it). # Note that beesu won't show a GUI login box if /usr/sbin is before /usr/bin, # so add it first as well. EXTRA_PATHS="/usr/bin:/usr/sbin:/usr/local/sbin:@prefix@/sbin"; export PATH="$EXTRA_PATHS:$PATH" # echo $found_su; # Examples: # gnomesu -c 'gsmartcontrol --no-scan' # kdesu -c 'gsmartcontrol --no-scan' # beesu -P 'gsmartcontrol --no-scan' # su-to-root -X -c 'gsmartcontrol --no-scan' # xterm -e sux -c 'gsmartcontrol --no-scan' # sux asks for password in a terminal full_cmd=""; if [ "$GSMARTCONTROL_SU" != "" ]; then full_cmd="$GSMARTCONTROL_SU '$EXEC_BIN $final_args_quoted'"; elif [ "$found_su" = "pkexec" ]; then if [ "$GDK_SCALE" != "" ]; then final_args_quoted="$final_args_quoted --gdk-scale='$GDK_SCALE'" fi if [ "$GDK_DPI_SCALE" != "" ]; then final_args_quoted="$final_args_quoted --gdk-dpi-scale='$GDK_DPI_SCALE'" fi full_cmd="pkexec --disable-internal-agent $EXEC_BIN $final_args_quoted"; elif [ "$found_su" = "sux" ]; then full_cmd="xterm -e sux -c '$EXEC_BIN $final_args_quoted'"; elif [ "$found_su" = "gksu" ]; then full_cmd="$found_su '$EXEC_BIN $final_args_quoted'"; elif [ "$found_su" = "beesu" ]; then full_cmd="$found_su -P '$EXEC_BIN $final_args_quoted'"; elif [ "$found_su" = "su-to-root" ]; then full_cmd="$found_su -X -c '$EXEC_BIN $final_args_quoted'"; else # gnomesu, kdesu, xdg-su full_cmd="$found_su -c '$EXEC_BIN $final_args_quoted'"; fi # echo $full_cmd eval $full_cmd gsmartcontrol-1.1.4/data/gsmartcontrol.1000066400000000000000000000050271417717230600203200ustar00rootroot00000000000000.TH GSmartControl "1" "" "gsmartcontrol " "User Commands" .SH NAME GSmartControl \- Hard disk drive and SSD health inspection tool .SH SYNOPSIS \fBgsmartcontrol\fP [OPTIONS] \fBgsmartcontrol\-root\fP [--desktop=\fI\fP] [OPTIONS] .SH DESCRIPTION \fBGSmartControl\fP is a graphical user interface for smartctl (from smartmontools), which is a tool for querying and controlling SMART (Self-Monitoring, Analysis, and Reporting Technology) data on modern hard disk and solid-state drives. It allows you to inspect the drive's SMART data to determine its health, as well as run various tests on it. .PP This manual page documents briefly the \fBgsmartcontrol\fP and \fBgsmartcontrol\-root\fP commands. .PP \fBgsmartcontrol\-root\fP command launches \fBgsmartcontrol\fP with administrative privileges. The \fIdesktop\fP argument specifies which desktop is currently running, for automatic selection of native su mechanism. Valid values for \fIdesktop\fP are \fBauto\fP, \fBkde\fP, \fBgnome\fP, \fBother\fP. .SH OPTIONS .SS "Help Options:" .TP \fB\-?\fP, \fB\-\-help\fP Show help options .TP \fB\-\-help\-all\fP Show all help options .TP \fB\-\-help\-gtk\fP Show GTK+ options .TP \fB\-\-help\-debug\fR Show logging options .SS "Application Options:" .TP \fB\-l\fP, \fB\-\-no\-locale\fP Don't use system locale .TP \fB\-V\fP, \fB\-\-version\fP Display version information .TP \fB\-\-no\-scan\fP Don't scan devices on startup .TP \fB\-\-no\-hide\-tabs\fP Don't hide non\-identity tabs when SMART is disabled. Useful for debugging. .TP \fB\-\-add\-virtual\fP Load smartctl data from file, creating a virtual drive. You can specify this option multiple times. .TP \fB\-\-add\-device\fP Add this device to device list. The format of the device is \fI::::\fP, where type and extra_args are optional. This option is useful with \fB\-\-no\-scan\fP to list certain drives only. You can specify this option multiple times. Example:\fR .nf \-\-add\-device /dev/sda \-\-add\-device /dev/twa0::3ware,2 \-\-add\-device '/dev/sdb::::-T permissive' .fi .TP \fB\-\-display\fP=\fIDISPLAY\fP X display to use .TP \fB\-v\fP, \fB\-\-verbose\fP Enable verbose logging; same as \fB\-\-verbosity\-level\fP 5 .TP \fB\-q\fP, \fB\-\-quiet\fP Disable logging; same as \fB\-\-verbosity\-level\fP 0 .TP \fB\-b\fP, \fB\-\-verbosity\-level\fP Set verbosity level [0\-5] .PP .SH COPYRIGHT Copyright \(co 2008 \- 2012 Alexander Shaduri .PP .SH AUTHOR This manual page was originally written by Giuseppe Iuculano for the Debian project. gsmartcontrol-1.1.4/data/gsmartcontrol.appdata.xml.in000066400000000000000000000025341417717230600227760ustar00rootroot00000000000000 gsmartcontrol CC0-1.0 GPL-2.0 and GPL-3.0 GSmartControl Hard Disk and SSD Health Inspection

GSmartControl is a graphical user interface for smartctl, which is a tool for querying and controlling SMART (Self-Monitoring, Analysis, and Reporting Technology) data in hard disk and solid-state drives. It allows you to inspect the drive's SMART data to determine its health, as well as run various tests on it.

gsmartcontrol.desktop https://gsmartcontrol.shaduri.dev Main window https://gsmartcontrol.sourceforge.io/home/images/main_ok.png Drive identity information https://gsmartcontrol.sourceforge.io/home/images/info_identity.png Attribute list of a failing drive https://gsmartcontrol.sourceforge.io/home/images/info_failing.png
gsmartcontrol-1.1.4/data/gsmartcontrol.desktop.in000066400000000000000000000015621417717230600222360ustar00rootroot00000000000000[Desktop Entry] # desktop file version Version=1.0 Type=Application Categories=System;Monitor; Name=GSmartControl # short description, usually shown in parentheses after Name GenericName=Hard Disk and SSD Health Inspection GenericName[fr]=Inspecteur de santé de disque dur et de SSD # tooltip Comment=Monitor and control SMART data on hard disk and solid-state drives Comment[fr]=Surveille et contrôle les données SMART des disques durs et des SSD # If it's a name only, it looks for "name.[png|xpm]" file in # $XDG_DATA_DIRS/icons and /usr/share/pixmaps. # An absolute file path is also supported. Icon=gsmartcontrol # Run with root permissions. This should work with newer GNOME too. # It _should_ work with kde, but it hangs on mine, so use plain kdesu. #X-KDE-SubstituteUID=true #X-KDE-RootOnly=true # Run with root permissions. Exec="@prefix@/bin/gsmartcontrol-root" gsmartcontrol-1.1.4/data/gsmartcontrol.ico000066400000000000000000002765721417717230600207510ustar00rootroot00000000000000 h¦H ¨ V 00¨þ l¦4 h÷ ˆ zû  ¨00 ¨%ª@@ (BR;( @325A@C ! $#%87:.-/++-,+-BBEGFJ;BAutuMLO__b%$%" #;:<)**$#%$#$SRUcceUqyzzšš›’’“žž ŒŒ†…ˆ“’’Ÿž “’’‘‘”ŽŽžžŸ¦¥§¡ž ¢¡¢˜˜™ÛÜÜæçæããäÞÞàäááááàÚÚÛáâáììëëééäåæÜÜÞ´³´¨§©÷÷øÜÛÛÚÙÚááàââääàãäâãääååãääåäæççðïð¾¿À¾¿ÀñðòÏÏÏÏÎÐÌÍÌÔÔÖÕÕÔÌÌÌÈÇÉÖÕ××Ö×ÕÖÖíí쾿À¾¿ÀñññÄÃÅÈÇÈÊÉÈßãâÆÈÈÊÉÊ¿¾À¾¾ÀÎÏÏÎÎÏäæè¾¿À¾¿ÀÑÑÒ½¿ÁÂÂÂÂÄÃÓÔ×ÈÈÈÅÆÆÅÅÈÁÀÂÅÄÇÂÅÆÖ××ÒÒÒÒÒÒ»»¼ÄÅÅÀÀ¿ÀÀÁÂÀÂÆÅǽ»¾¶µµ¸¹º¸¸¹¾¼¿¾¿ÀÒÒÒÒÒÒº¹ºØÚ׿¿ÀÀÀÂÁÁÂÂÁÃÁÁ²²µ¨««»¹»ÁÃÁ¾¿ÀÒÒÒ··ºÍËÏÀÀÀÃÃÄÄÄÄÄÄÅÅÅÆÈÉɯ±±ª¨ª¼»¾¥¤¦ÒÒÒ©ª¬ÕÔÕÆÅÆÇÇÈÈÈÉÉÉÉÉÉÊÊÊËÐÓеµ·¨§©’’”··ºÙÚÚÕÔÔÒÒÒÓÒÓÔÓÔÕÕÖÔÔÕÖÖØÙÛÜÔÔÔ··º½½½¿ÀÁ½½¾¼¼½¼¼½½¼¾½½¾¼¼¾ÃÃÅÃÂÄÿÿÿÿÿÿÿÿÀÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿÀÿÿÀÿÿÀÿÿàÿÿ(0 ++-//2658!!#  !,*0//35061'/Œ‹LKMZY\edhA?B''(???LLK(()99;767544--.qsuqlp_wjJ…c„ƒ…‹ŠŒHGJ[Z]edg113225?>B*++-..#"##$#bbfeagb€n_žzuuwžžŸdceqqsyxy[[]===HJNZWZkhkrqr`_acbdeed^^_^^_~}€{y|xrv††ˆÎÎÏÑÏÑæäèæçæòõôøøøôõóðïðéäâÞßàÝßÞèçêçèêääãããäâããááâëëëÞßàÁÁÂÁÁÂÀ¿¿ààÞÞÝÜÀÁꏏ´¶¶ÉÇÈÑÑÐËÍÏÉÆÅÊÆÉÏÏÎØ×ÙÚÜÜßßàÝÞÝÓÒÒÁÂÁÁÁÂÃÃÄòòñëëîÞßáßÝÝææçêêêåääâãããâäååæéåéççèææçæçèåæèææéòðîèèëÁÁ¼¼¾ÝÝßÌÊÌÐÓÒÔÔÔÔÔÕÔÔÕÕÔÖÖÕÖ×רØ×ÚÜÛÜÜÝÜÚÚÛÚÚÚÜÚÛÜÜÝÝÝÝìéë¼¼¾¼¼¾ÜÝÝáâäÌÊÌÍÍÎÎÎÏÎÎÏÑÒÐÛÚÚÒÒÓÉÈȽ¼¾ÁÃÀØ×ÚÖ×ÕÖÕÖÖÖÖ×רéèë¼¼¾ÉÈÉìííÎÌÎÇÆÈÈÈÉÉÈÉÉÈÌòîîÐÐÑËËÌÌÍÎÐÌϱ¶²´´·ÕÔÔÏÏÐÏÏÐÑÐÒçéêÉÈÉÉÈÉÞÚÝÃÂÄÃÃÄÅÅÅÅÅÆÊÈÊèâ丹¼¿ÂÃÄÁÄÆÆÇÌÎʶ··ÊÌÎÉÈÉÉÈÊÍÍÍÛÜÞÉÈÉÉÈÉÌÌÍÆÆÉ¾¿¿ÁÁÂÂÂÃÂÂÃÎÏÏØÜÙÌËÌÆÆÇËÊÑÄÁÂÀÀÃÂÁÂÂÂÃÂÂÃÉÈÊÉÊËÉÈÉÜÚÛ¼¼¾ÈÉË¿¿ÀÀ¿ÀÀÀÁÁÀ¿ÀÁÅÄÅÆÆÈ¿¿À»»¼º¹»ºº»»º¼¼»½º¹»ÇÅÇ¿¾¿ÜÚÛÜÚÛ²²´¶µµØÙÙ¿¿¿ÀÀÁÁÁÁÁÁÂÁÁÂÂÁþ½¾³³´±±³´´µµ´µ³´µ»»½»º»ŸÜÚÛÜÚÛ¼¼¾ããäÌËÍ¿¿ÀÁÀÁÁÁÂÁÁÂÂÁÂÂÂÃÃÃÅÀÀ±±±ª©ª­¬­ÇÅÆ½½¾³³´§¦¨ÜÚÛ¼¼¾ÞÞÛ»½¿ÁÁÃÂÁÃÃÂÃÃÂÄÄÃÄÄÄÅÄÄÅÅÅÆÅÄų²³  ¢»½½ËÌÌÄÄÅœ›¼¼¾ââãÅÄÂÄÄÅÅÄÅÅÅÆÆÅÆÆÆÇÆÆÇÇÆÇÇÇÈÈÈÉËÊË··¸”’“–•—»¼¿¼¼¾¼¼¾æçéÉÈÉÇÇÈÈÈÉÉÉÉÉÉÊÊÉÊÊÊÊÊÊËËÊËËËÌËËÌÎÎо¾¿›Ÿ³²´¼¼¾¼¼¾ÛÛÜÏÐÑÑÑÒÎÍÎÎÍÎÎÍÎÎÎÏÏÏÐÑÑÑÏÏÐÏÏÐÏÏÐÐÏÐÓÔÓÐÐÑ­¬®¼¼¾¼¼¾ÓÒÔÏÏÐÕÕÖÔÔÔÓÓÓÔÓÔÔÔÕÔÔÕÕÕÖÕÕÖÕÕÕÔÔÕÔÔÕ×רÛÛÛÍÍͼ¼¾­­®¹¹¹¸¸¸ÄÄŶ¶·¹¹º¹¹º¹¸º¸¸¹¹¹º¹º»¹¹º¿¾ÀÇÇɶ¶¸¶¶¸ÿÿÿÿÿÿÿÿÿÿÿÿàÿÀÿÀÿÀÿÀÿÀÿÀÿÀÿÀÿÀÿÀÿÀÿÀÿÀÿÀÿàÿàÿàÿàÿàÿðÿ( @€ ('**),.-0//1,,.""$++-0/2*)+.-0--/--/)(+(').-03260/3/.1/-1'&*ŒŽFEHSRUZY]\[_POQ !/.1>=ACBE!!"--.--.0/1''(%$&DCFgfibaecbf_^_[[ZwuyGGIWVY_^abaePPR %%(436CBEGFI))*889656112*)+''(CBDkknffihbfHpJ­{jhhˆˆŠIHKZZ\bbdeehPOR $#&436CCEHGJ !*)*++,&&'$$$##$;;BBCCCDBBCA@A>>>IIJnmpiikihjidie`eWWYÂÁÂÎÎÏÓÓÔÕÕÖ××ØØØØÚÚÚÜÜÝßßßááâââãããääååèèéèèèçççæåæååæääåãâãßßàÞÞßÝÝÞÚÚÛÕÕ×ÂÁÂÂÁ¯¯°àßàÎÎÏÐÏÐßßàéééààáÓÓÔÆÆÇ½½½¶¶¶°°°¬¬¬¯¯°¸¸¸½½¾ÁÁÁÆÆÇÈÈÈÌÌÍÍÍÎÎÎÏçççÌËÌÂÁÂÂÁÂÁÁÂÑÑÒìììãããÐÏÐÂÂÂÁÁÂÍÍÍÚÙÙâââæåæååæããäáàáàààáááäääææææææçççæææçççÌËÌÌÌÍÂÁÂÂÁÂëëìóóóããäÝÝÝßßàåååïïïëêëååæááâßßààààààáââãäääääääääääåååååååååæìììóóóÛÛÛÂÁÂÂÁÂççèÖÖÖÔÔÕÖÖÖÖÖ×ØØØÖÖ×ÖÖØ××ØØØÙÙØÙÙÙÚÚÚÛÛÛÛÛÛÜÜÛÜÜÜÝÜÜÝÝÝÝÝÝÞÞÞßÞÞßááâççèÂÁÂÂÁÂÎÍÏÐÐÒÔÓÔÒÑÒÒÒÓÒÒÓÒÒÓÔÓÔÔÓÔÓÓÔÕÕÖÕÔÕÒÒÓÒÒÓ×רÚÚÚØØÙØØÙÙÙÚÚÙÚÚÚÛÛÚÛÜÜÝÝÜÝÂÁÂÂÁÂÍÍÎììíÏÏÑÌËÍÍÍÎÍÍÎÎÎÏÎÎÏÐÏÐÜÜÝÙÙÚÐÏÐÉÉÊÀÀÁ··¸ÁÁÁÕÕÖÔÔÕÕÕÕÕÕÖÖÖÖÖÖרØÙÚÚÜÂÁÂÂÁÂââãÛÛÜÆÆÇÉÈÉÉÉÊÊÊÊÊÉËÉÉÊáàáÞÞßÌÌÎÍÌÍÎÎÏÏÏÐÍÍγ³´ºº»ÑÑÒÐÐÑÐÐÑÐÐÒÑÐÒÔÔÕ×רÂÁÂÂÁÂààáÍÍÎÄÄÅÆÅÆÆÆÇÇÇÈÇÇÈÉÉÊãããÆÅÆÆÆÇÇÇÈÈÈÉÈÈÉÊÊÊÊÉʳ³³ÌÌÍËÊÌËËÌËËÌËËÌÒÒÒÔÓÕÂÁÂÂÁÂÕÕÖËËÌÁÁÂÃÃÄÃÃÄÄÄÅÅÄÆÅÅÆÜÜÜÎÍÎÀÀÁÁÁÂÂÂÃÃÃÄÅÅÆÃÂĽ½¾ÇÆÈÆÆÇÇÇÈÇÇÈÇÇÈÐÏÐÍÌÎÂÁÂÂÁÂÆÆÇÏÏп¿ÀÁÁÂÂÁÂÂÂÂÃÂÃÃÂÃÇÇÈ×רÔÓÔÌËÌÊÊÊÉÉÊÇÇÈÀÀÁÁÀÂÁÀÂÂÁÂÂÂÃÃÂÃÂÂÃÎÍÎÅÅÇÂÁÂÂÁºº»ËËÌÅÅÆ¿¾¿ÀÀÁÁÁÁÁÁÂÂÁÂÁÁÂÂÂÃÈÈÉÇÇÈÃÃÄÀ¿Á¼»½»º¼»»¼¼»½¼¼½½¼¾½½¾¼¼½ÎÍÏÕÕÖÂÁÂÂÁ¸¸¹±°²ÃÂÄÊÊË¿¿ÀÀÀÁÀÀÁÁÁÂÁÁÂÁÁÂÂÁÂÂÁü»½³´µµ´¶¶µ··¶···¸¸·¸¸·¹¸·¹¿¿À»º¼ÕÕÖÂÁÂÂÁ´´µ¸¸¸ÔÔÕÚÙÚ¾¿ÀÀÀÀÁÀÁÁÁÂÁÀÂÁÁÃÂÁÃÂÂÃÃÄľ¾¿±±²±°²²²³²²´³²³³³´¾¾¿¼¼½²±³ÕÕÖÂÁÂÕÕÖÇÇÈÝÝÞÞÝÞÃÃÃÀÀÁÁÁÂÁÁÂÁÁÂÂÁÂÂÂÃÂÂÃÃÂÃÃÃÄÅÅÅÀÀÀ°°±¬¬­®®¯°°±ÌÌͼ¼½²²³´³´ÕÕÖÕÕÖÕÕÖÕÕÖÜÜÝÇÇÈÀÀÀÂÁÃÂÁÃÂÂÃÃÂÃÃÃÃÃÃÄÄÃÄÄÄÄÄÄÅÄÄÅÆÆÇÃÃı±²§§¨©©«ÆÆÇÏÎÏÈÈÉÁÀÂÇÇÈÕÕÖÕÕÖÕÕÖÙÙÚÍÍÎÂÂÃÃÃÄÄÄÄÄÄÅÄÄÅÅÅÅÅÅÆÅÅÆÆÆÆÆÆÇÆÆÇÆÆÇÈÇÉÈÈɸ·¹¤¤¥££¤¥¥§²±³½½¿ÇÇÈÕÕÖÕÕÖÕÕÖÖÖ×ÓÓÔÄÄÅÆÆÇÆÆÇÇÆÇÇÆÇÇÇÇÇÇÈÇÇÈÈÈÉÈÈÉÉÈÉÉÉÉÉÉÊÉÊÊÌÌ;¾¿¢¡£ž ²²´´´¶ÄÃÄÕÕÖÕÕÖÕÕÖÒÒÓÚÙÚÈÈÉÈÈÈÉÉÊÉÉÊÊÉÊÊÊÊÊÊËÊÊËÊÊËËËÌËËÌÌËÌÌÌÌÌÌÍÌÌÍÏÎÐÂÂßž ´´µ®­¯ÄÃÄÕÕÖÕÕÖÕÕÖÉÉÊÒÒÓÐÐÑÐÐÑÌÌÍÍÍÍÍÍÍÍÍÍÍÍÎÍÍÎÎÎÎÏÏÏÎÎÏÎÎÏÏÏÏÏÏÏÏÏÐÏÏÐÑÑÒÌÌÍ¿¿À›šœÄÃÄÕÕÖÕÕÖÕÕÖÉÈÊÔÔÔÐÐÑÜÛÜ×רÔÔÔÕÕÖÕÕÖÖÕÖÖÖרØÙÛÚÛÚÙÚ×ר×ר××××רÖÖÖÙÙÚááâ××ׯ¯°ÄÃÄÕÕÖÕÕÖÕÕÖªª«ÌÌÌÄÄÅÁÁÂÑÑÒÇÇÈÈÇÈÈÈÉÉÈÊÉÉÊÇÆÈÆÆÇÈÈÉÉÉÊÉÉÊÉÉÊÈÈÉÎÍÏÐÐÑÆÅÆÎÎÏ··¹ÄÃÄÕÕÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÿÿÿÿ(0`€žžžžžž.-/!! #"!$""$""$##%  ! """$%$&'&)((**),++-+*-**,))+('*'&)&%($#&"!$ #!žžžžžžžžžžžžihk@@CIHLQPSUTXUTXUTWYY\##$ $$&/.0669@?CBBE769$#%113557''(99;(()--/213_]cZX]ZY]ZY^[Y^][`_]bXVZYX[žžžžžžžžž©©«uuwBADKKMSRUXW[XW[XW[^]`   ")(,214::=FEHGFI<;=../779##$>>?! !.-/++,gfja`ca`ca`caad`\`YVQc^c[[]žžžžžžžžž°¯±onpEDGOOQWVZ]\_]]`]]`bad  ""%,+/547>>@IIKJILFFG%%&546?>@$$$DCE001++,kjnedgedgedgjdh:zT*´{>€UXSV˜—˜žžžžžžºº¼jikIIKSSV\[^aadcbecbeffi &%(//298;BADMLONMPGGH$#$445@@A !BBD556))*porjiljiljhmmgjM¦v‰þßV¢qTQR‘’žžžžžž½½¾dceLKNWVY__bedgeegfehkjm  ('*214;:=EDGPORQQS@?A334::;BAC<<=&&'srummpmmpmmpnmppwpi…lmtnSRUˆˆ‰žžžžžž¶¶·HHI446?>@FEHKKMKKMKKMOOR  %$&--/779:9<$$% ! ! (')))*VUXSRTSRTSRUSRUTRVVRWQPT879gfhžžžžžžÒÒÒ‘‘’ˆˆ‰““”—–˜™˜š›š›žžŸŒŒŒˆˆ‰‹‹Œ”””———šš›žžž¡¡¢¥¤¥¦§§šš›¤£¤ž¡ ¡   œœœ  ¡™™šŸŸ ™™š©©ª¦¦¨¥¥¦££¥¢¢£¡ ¢ŸŸ žŸ‘‘“¢¢£žžžžžžìëíÙÙÚááâææçååæçççççèèèèççèêêëììííííííîííîîîïðïðððñññññññòòòõôöóóôóóôòòóòñòòòòñññòòòñðñòñòîîïîîîîíîíìîíìíëêëêêëââãááâññòžžžžžžÝÝÞ‘††‡ããäâââÔÔÕÓÒÓØØØååæææççççééêêêëççèÜÜÝÐÐÐÇÆÇ¿¿À¼¼½¹¹¹··¸¹¹ºÀÀÀÅÅÆÉÉÊÊÊËËËÌÍÍÎÏÏÐÑÑÑÓÓÔÕÕÖÖÖÖ×××ÛÛÜíííïîï©©ªvvwÝÝÝžžžžžžââ㨨ªœœÛÚÛÁÁÂÈÇÈËËÌÄÄÄÆÆÆÝÜÝßßàÈÈȲ²³¨§¨¥¥¥ªªª°°¯²±²´´´±±±­¬¬§§§¤¤¤¦¥¥«««­­­±±²···º¹º¹¹º¾¾¿ÂÂÂÀÀÁÀÀÁ»º»ÂÂÃëë쿾¿’’“èçéžžž¶¶¶ÖÖ×ÜÜÜÌÌÍ»»¼ÙÙÚÿÿÿõõöïïðÙØÚ¼¼½¬«¬»»»ÔÔÔëêêúúúýýýûûûúúûùùúùùúúúûûûûüüüýýýüüüûûüûûûûûûúúúúúúúúúúùúùùúúùúþþþÍÍι¹ºØØÙëëìòòó¶¶¶¶¶¶ÄÃÄÐÐÑãâãôôõýýýççèÜÜÜÛÛÜááâææçñññüüüóóôêêêãããààáßÞàÞÝßÞÞßÞÞßßßßßßàààááááááâááâââââââââãââããããããäããäääåíííýýýåååÃÂÄÑÐÑääå¶¶¶¶¶¶­­®ÛÚÜùùùìììàààØØØ××ØØØÙØØØÛÛÜááâÝÝÞÚÚÛÚÙÛÚÚÛÚÚÛÛÛÛÛÛÜÛÛÜÜÜÜÜÜÜÜÜÝÜÜÝÝÝÞÝÝÞÞÞÞÞÞßßÞßßßàßßààààààáààáááâááâççèöööîîïÕÕ×ÑÑÒ¶¶¶žžžÛÚÛãâãÚÙÚÓÓÓÔÔÕÕÕÖÖÕÖÖÖ×ÖÖ×ÖÖÖÖÖ××ÖØ×ÖØ×רØ×ØØ×ØØØÙÙØÙÙÙÚÙÙÚÚÚÛÚÚÛÛÚÛÛÛÛÛÛÜÜÛÜÜÜÜÜÜÝÝÜÝÝÝÝÝÝÞÝÝÞÞÝÞÞÞßÞÞßâââáàáÙØÚžžžžžž×××ÒÑÓÈÈÉÌÌÍÓÒÓÒÒÒÒÒÒÓÒÓÓÒÔÓÓÔÓÓÔÓÓÔÔÔÔÔÔÕÔÔÕÕÕÕÕÕÕÖÖÖ×××Ø×ØØØÙÙÙÚÙÙÙØØØØ×ÙØØÙÙØÙÙÙÚÙÙÚÚÚÚÚÚÛÚÚÛÛÛÛÛÛÜÛÛÜßßàÛÛÜ×רžžžžžžÕÔÕÃÃÄÆÆÇñññÕÕÖÎÎÏÏÏÐÏÏÐÏÏÐÐÏÐÐÐÑÐÐÑÑÑÑÑÑÒÐÐÑÒÒÓÖÖ×ÕÔÕÎÍÎÆÆÇÀ¿ÀÀÀÁÉÉÊÔÔÕØØÙÖÕÖÖÖ×ÖÖ××Ö×××××ר×רØ×ØØØÙØØÙÞÞß×רÙÙÛžžž¶¶¶ÍÍλ»½ïïðêéêÎÎÏËÊÌËËÍÌÌÌÌÌÍÌÌÍÍÍÎÍÍÎÎÍÎÎÍÎÖÖ×ãããÝÜÝÓÓÔÐÐÐÎÎÎÉÊË¿¿À°°±¦¦¦¿¾ÀÕÕÖÓÓÓÓÓÔÔÓÕÔÔÕÔÔÕÕÔÖÕÕÖÕÕÖÕÕÖÜÜÝÒÑÓÚÙÛ¶¶¶¶¶¶ÂÂÃÎÎÏõõõÑÐÒÇÇÈÉÉÉÉÉÊÉÉÊÊÊÊÊÊËÊÊËÊÊËÊÊËÖÖ×ððñÜÜÜÎÎÏÍÍÎÎÍÎÎÎÎÏÏÐÑÐÑÒÒÓÆÆÇ¡ ¡···ÓÒÓÐÐÑÐÐÑÑÑÑÑÑÒÑÑÒÒÑÓÒÑÓÒÒÓÚÚÛÍÌÎÓÒÓ¶¶¶¶¶¶µ´¶ÖÕ×èèéÉÈÉÆÅÆÇÇÇÇÇÈÈÇÈÈÈÈÈÈÉÈÈÉÈÇÈÉÈÉééêßß߯ÅÇÉÈÊÊÉÊÊÊÊÊÊËÊÊËËËËËËËÎÎÏÈÈÉ¡¡¢ÊÊÊÍÍÎÍÍÎÍÍÎÍÍÏÍÍÏÎÎÏÎÎÏÐÏÐÚÙÛËËÍÊÉ˶¶¶žžž×רÝÝÞÅÅÆÄÄÅÅÅÆÅÅÆÆÆÇÆÆÇÆÆÇÇÇÈÇÇÇÊÊÊèèèÍÍÎÄÄÅÆÆÇÆÆÇÇÆÇÇÇÈÇÇÈÈÇÉÈÈÉÉÉÉÍÌÍ®­®ÃÃÄËËÌÊÊËÊÊËËÊËËËËËËÌËËÌÍÍÎÙÙÚÉÈÊžžžžžžÓÓÔÛÛÜÃÃÄÂÂÃÂÂÄÃÃÄÄÃÄÄÄÅÄÄÆÅÄÆÅÅÆÇÇÈßßàÛÚÛ¿¾ÀÂÂÃÄÃÄÄÃÅÄÄÅÅÄÅÅÄÅÅÅÅÆÆÇÄÃĵ´µÆÆÈÆÆÈÆÆÈÇÇÈÇÇÈÈÇÉÈÈÉÈÈÉÌÌÍ×ÖØÅÄÆžžžžžžÇÇÇÝÝÞÄÄÅÀÀÁÁÁÂÁÁÂÂÂÃÂÂÃÂÂÃÃÃÄÃÃÄÄÄÅÊÊËßßàÙÙÙÄÄÅ¿¿À¾¾¿¿¿ÀÀ¿ÁÁÁÂÅÅÅÆÆÆ¾½¿ÁÀÃÃÃÅÃÃÄÃÃÄÄÄÅÄÄÅÄÄÅÅÅÆÅÄÅËËÌÒÒÓÁÀžžžžžž»º»ÒÑÓËÊÌ¿¿ÀÀÀÁÁÀÁÁÁÂÁÁÂÁÁÂÂÁÂÂÂÃÂÂÃÃÂÄÇÆÈÕÕÖÞÞÞÞÝÞÔÓÔÏÏÐÎÍÎÐÏÐÌËÌÃÂÄ¿¿Á¿¿ÁÀ¿ÀÀ¿ÁÀÀÁÁÀÁÁÁÂÁÁÃÂÁÃÂÁÃËËÌÌË̾½¿žžž¶¶¶»»¼ÃÃÃÏÏÐÂÁ¾¾¿ÀÀÁÀÀÁÀÀÁÁÁÂÁÁÂÁÁÂÂÁÂÂÂÃÂÂÃÂÂÃÆÆÇÉÉÊÊÊËÊÊËÇÇÈÁÁý¼¾»»¼»»¼¼»½¼¼½¼¼½½¼¾½½¾¾½¿¾¾¿¾¾¿¾¾ÀËËÍÈÇȹ¹»¶¶¶¶¶¶¹¹º¾¾¾ÄÄÅÎÍÏÃÃľ¾¿À¿ÀÀÀÁÀÀÁÀÀÁÁÀÂÁÁÂÂÁÂÂÁÃÂÁÃÂÁÃÂÁÿ¾À¶¶¸µµ¶·¶···¸¸·¹¸¸¹¹¸¹¹¹º¹¹ºº¹»º¹»»º»»º¼»»¼º¹»ÌÌÍËÊ̶¶·¶¶¶¶¶¶´´¶»»¼žžŸ¹¸ºÑÐÒÈÇÈ¿¿ÀÀ¿ÁÀÀÁÀÀÁÁÁÂÁÁÂÁÁÂÁÁÂÁÁÂÁÁÃÁÁÃÂÃÄÀÀÁ¶¶¸³³´´´¶µ´¶µµ¶µµ·¶¶·¶¶··¶···¸··¸··¸¸·¸ÂÂÿ¾À‘’¦¦§¶¶¶žžžËË̪ª«¾¾¿ååæÒÒÓ¿¾¿¿¿ÀÀÀÁÀÀÁÁÀÁÁÁÁÁÀÂÁÁÂÁÁÃÂÁÃÂÁÃÂÂÃÃÃ÷·¸°°±²±²²²³³²³³³´³³´´³´´³´³³´º¹»ÄÃĽ¼¾½¼¾››žžžžžž¾¾¿ÏÏÏíìíÜÜÝÄÄÅ¿¿ÀÀÀÁÁÀÁÁÀÁÁÀÁÁÁÂÁÁÂÁÁÂÁÁÂÂÂÃÂÂÃÂÂÃÂÂÃÃÃÄÃÃź¹»®®¯¯®°°°±°°±°°±±°²²²²½½¾ÁÀ´´µ°°±³³´¸¸¹žžžžžžÂÂÃëëëÖÖ×ÂÁÂÀ¿ÀÁÀÁÁÀÂÁÀÂÁÀÂÁÁÂÁÁÂÂÁÂÂÂÂÂÂÃÂÂÃÃÂÄÃÃÄÃÃÄÃÃÄÄÄÄÅÅÆ½½¾­­®««¬­­®­­®¬«­ÂÁÃÙÙÚ¶¶·±±²±±²¯®°²²³žžž¶¶¶ÄÃÄßßßÈÈÈÀ¿ÀÁÁÂÁÁÂÁÁÂÁÁÂÂÁÃÂÂÃÂÂÃÂÂÃÃÃÃÃÃÃÃÃÄÃÃÄÄÃÄÄÄÄÄÄÅÄÄÅÅÄÅÆÆÇÀÀÁ®®¯¨¨©ªª«ª©«µµ¶ßßàÜÜÝÊÊËÊÊË¿¿Áªª«¶¶¶¶¶¶ÂÂÃààáÌËÌÁÁÂÂÂÃÂÂÃÂÂÃÃÂÃÃÃÃÃÃÄÃÃÄÃÃÄÄÃÄÄÄÅÄÄÅÄÄÅÅÅÅÅÅÆÅÅÆÅÅÆÆÅÆÆÅÆÇÇÇÄÄű°²¥¥¦§§¨§§¨«ª¬³³´²²³¼»½ÇÇɦ¥§¶¶¶¶¶¶¿¾¿ââãÐÐÑÂÂÄÃÃÄÄÃÄÄÄÅÄÄÅÄÄÅÄÄÅÅÄÅÅÅÅÅÅÅÅÅÆÅÅÆÆÆÆÆÆÆÆÆÇÆÆÇÆÆÇÆÆÇÇÇÈÇÇÈÈÈÉÈÈɶ¶·¤£¤¤¤¥¥¤¦¥¤¥¤¤¥´´¶ÃÃÅ¡¡£¶¶¶žžžÞÞßÕÕÖÄÄÅÅÅÅÅÅÆÅÅÆÅÅÆÆÆÆÆÆÇÆÆÇÆÆÇÇÇÈÇÆÇÇÇÈÇÇÈÇÇÈÇÇÈÈÈÉÈÈÉÈÈÉÈÈÉÉÈÉÉÈÊÉÉÊËË̽½¾££¤  ¡¢¡¤¡¡£¹¹º½½¿žžžžžžØØÙÚÚÛÇÆÇÇÆÇÇÇÈÇÇÈÈÇÈÈÈÈÈÈÈÈÈÉÈÈÉÈÈÉÉÉÉÉÉÉÉÈÉÉÉÉÉÉÊÉÉÊÊÉÊÊÊËÊÊËÊÊËÊÊËËÊËËÊËÎÍÎÂÂ䤦œœžžž ¼»¼²²³žžžžžžÔÔÕßÞßÌÌÍÈÈÉÉÉÉÉÉÊÉÉÊÊÉÊÊÊÊÊÊËÊÊËÊÊËÊÊËËËÌËËÌËËÌËËÌÌÌÌÌÌÌÌÌÌÌÌÍÌÌÍÌÌÍÍÌÍÌÌÍÌÌÍÏÏÐÊÉ˪©«™™šºº»¨§©žžžžžžÐÐÑÒÒÓÒÒÓÑÑÒÍÍÎËËÌÌÌÍÌÌÍÌÌÍÌÍÍÌÌÍÍÌÍÍÍÍÍÍÎÍÍÎÍÍÎÍÍÎÎÍÎÎÍÎÎÎÏÎÎÏÎÎÏÎÎÏÎÏÏÎÎÏÏÏÏÏÎÏÑÐÑÏÏй¸º¸¸ºŸŸ žžž¶¶¶ÓÒÔËËÌÍÍÍÏÏÐØØÙÓÓÓÍÎÎÎÎÏÎÎÏÎÎÏÏÎÏÏÎÐÏÏÐÏÏÏÏÏÏÐÐÑÖÖÖÒÒÓÏÏÐÐÏÐÐÏÐÐÐÐÐÐÑÐÐÑÐÑÑÑÐÑÑÑÒÑÐÒ×רááâÁÀÁ™™›¶¶¶¶¶¶ÚÚÛ×××ÍÍÎÐÐÑÖÕÖßßàÕÔÕÓÓÓÓÓÔÓÓÔÔÔÕÔÔÕÔÔÕÔÔըרÚÚÛÚÙÚÜÛÝÚÙÛÖÖ××רÖÖ×ÖÖ×ÖÕÖÖÕÖÖÕÖÖÖ×ÝÝÞÞÞßÚÚÚÙÙÙÅÅŶ¶¶¶¶¶ÑÑÑØØØÏÏÏÏÎÏÎÎÏÓÓÔÒÒÒÒÒÓÓÓÓÓÓÔÔÔÕÔÔÕÔÔÕÕÕÖÔÓÔÑÑÑÐÏÐÑÑÒÔÔÕÕÕÖÕÕÖÕÕÖÕÔÕÔÔÕÔÔÕÔÔÕÔÔÕÖÖ×ÒÒÓÕÔÖØØÙ×ר¶¶¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿÿàÿÿàÿÿàÿÿàÿÿàÿÿàÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿøÿÿøÿÿøÿÿøÿÿøÿÿøÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‰PNG  IHDR\r¨fÂ3IDATxœì½¸Çu&XÝ÷Þ—ðð3A‚¤˜FŒ`³²?Ëšñ|³³+KV°¬,‡ùìY{>KÛ³þ<¶$K–mYÖˆÞY'Ù³+ÛJ˜˜Ä$‘3òKxïÝÞúOÕ©®®®îÛ7=PÎCáöí[]©ëÄ:u*ˆ¢Hœ…³p~:!<Ó 8 gá,œ98KÎÂYø)†³à,œ…Ÿb8KÎÂYø)†³à,œ…Ÿb8KÎÂYø)†³à,œ…Ÿbh ‚àŒ¦vÁÍo {ôÑiÛk¯a¬8µ¯Ò³ÐjÀ»¢÷ö£gŸ ù}"}é _hÉ{üqžÿg%€ øð/~0¼á†ëÃ'7o^Þòrøê«¯¯¿öÚYÄÿ1„SÇ‹];wû÷ï÷ïݾøü ¥Gz(œšš ?ù±­"?ŽÔë ø±ürêÞŸ}í/ZÕ/|ô—>œžšº½\.mé²e+×w®èêì³Õ*ÈoÓåg”pÙÄòƒPe”UFò?úhÀ‹2àÚZ8åüEÕYÁ£@ÐâJ½%)IC®ÆßåkÃoòÝUõûÓߣÀ)%³u¾d5³33âÄÉ“bûk¯íß·ÿKccc_ÿŸÿwÛêP ñ#uï«ñç…ž-}ö³Ÿ­«²ïü뿦îm~òɺʨ.2’¬c´pa°|é²àœs×uÈß7ÎLOxÕòåûž}î¹gÚ‰W]qñ*;½ó]ï*ôlÝ*€[QUp‚÷þ‡ÿðž¾þ¾÷^qÕ•â²s™èëë¥0TЬ<‘ä};:Ùßùºä|V9æ·’ü,•ägY¦’¾æOý;—i×Ï×n›J*vÛt9vY%§|¯”ø-°R˜ñé¤ ýë‘¡5Ónê³§MÉ<év—2ú’l·ûÝ;»-zìã”G;\–~}°Ûc®ÑßÐz/<¯c™,Ë´Ë{ø”åôööŠóÏ?_\sí†Þ…‹}CÎáÕíÅ’Æ¡n à»ßùNŠ<±ys{Z'áÒ‹.úK—.ÝxÁ…ŠÁB7€}vtwwâõ€ó¡QœÏõ"aÒàÂåA}2*T”°etÓjSb<œï‘•·¥íá2}ê~ËS“2Ún?á~Îo©º¬:=?ã÷ÐûÂ;M´=£ýxΞœ»³«Ký&Ÿ™™¥òÜùC÷dž‰ñqqôر=›Ÿzêáìh®¼òÊN•#´•\|ñçÖ¬]»jí¹çŠŽŽº‰$ÂJ*+–-[&:;;ÅÎ;ļùóÅÂ… é·“R'QHM~{²è—i@OzÁR¯C¹årÙp‘˜+(‰ ;•ås%Q.©T¡2ÊôY¦TQ¿ãºR±îg§ òÙI–ÑQ©Ðoú·Ýgßq‰D!°‘ËWÚ²Æ í"ŽËã§Ç"t8²+•1wÆÛœ83¤ ™—Þs{Nú;ê§OŸS2áž!?Ašhºc†ß¡>,^²„æáŽíÛÍ<³;uŠúhÓäÄ„8~ü¸8|ðàS=ñÄÝõ y=pÕWq³ÿÞñ®wz¶nðýï~/µ ñøæ'hv1¸ìâË>°lùŠ•+W­$„qaÖÉ1Ð;wîÛ^]>|Xœ8qBÌŸ?L`rrRMJOù„#QŒŠ€ª‰7!ŸÃ ¦ÉB}çP/8`³RÆÒŒ“Ò‹•±ºÆ)Tå'ïa²—bõÁ#bûÄqŒ&i¥R¦Vb‚Ú‚J`u9‹ç 4¨„¦KrÂÎŽNª§RQõ q²‘>FÈRŒä6†ü›îoÀ}O«v^õʨ/±:è1æñª!1–%<=ΤàqPÄÈ>_&Ì+̳íÛ¶%æÞó”œ/ lj&I)TJãbǶ'Ûüøßdhspõ•W¥æß;ÞÙ&ðÐG´Ó=Ø@³‹Á¥_òuç®K—-¥ÉaÞ ¸?àÅ_CƒƒbÁȈ˜šš¢3(¿c’ÓœãcýoffZô÷õ‰î®n)æÍpn•ÜUÿ¹àZùÃa‚R™5\ë“æ›¹Å¥EvVMrRg.*(d€„wDª!h<þCG‡Fz’ˆ*þ5èÀצ2ê¿Àô,¶Â«~[ò‰%¶Ç½Õ¿ÖÓ… &Ò(Ì ·§Gêé}t÷ ûC¢hÝŒæàÅ^óæÍ#B’˜grL:$çK—y|zzšˆÅÖW·î}æÙg¿YG«ë‚« 8P””ë­ žKJJAZSÄ\“/´W\pÁDq=JÜï¼õëղ̉“´Üc-øÈª0˜¦o«IbÒ—ÙÙª’/bô=¼ÔªüŒx)*hehM–BF*GêR›ð‰tzú´˜”7’ýóAª7 ñ$"„:X ^µqIÒžaA5fnu€O)wÔ9u+2YØ@’‹äþP ;d/^,FG‹SrA}R’0DÒ¶ïŒ#ãlQ4ÏŽ‹ç™ü~xt4A\q½gÏ^ñÀý÷ãúÆú:Y4ã T·À§>þ‰Ô½/õOn@¼ÿ½¿pSÿ½×^wYUƒRzÑ¢žÌ#z%·bIñBp}@rþíRW»@>kƒÛãX”W¢öé©Ó$îuH‘vff–&üôéiqZr â¢˜ÈøäçµÎÍŸv™ÍF\ lB•›1ûy×YÜÅ4Ÿœš$‡À‹T‚DåÉ-Cg‚a< KOËD¾sþ& ·#¢ehd5m)@ìÁe€8çÊRʤ7[„yÚ˜`,ú|óìÀšg_| eŸ•që+[Å믽&^à›ÿ÷_·m½ì“ûxêÞŸüéW =[7øô'>Ùpeõ‚$7 Þ³aÃIy/0÷Y¬f¤›–œþ¤äôàD Ê Î‡Hëë%…ÕO?kYXbC'] E< Oµ:+ÅÅ)úLr±(¡Kû l®E;móvAc¡û“‡ËÎíDm©[Æ„ÀŽ+J N´PþÆ¢¾ ˜ØS’8ž>=%”Â1¡eq$C‰0Æí´í‹‰þ¸*“ðÓ*[L­1ЗþÀõ¡V¢¯©Þ`Μ–ïö Rð4q‹ô»>-™lMÓr¼0ÏŽ;.Åþƒ´út¡” @T V¼ô‹$e6?þ87äæoüõ›¼Ó$ø˜ò—¾òåBÏ6dtá±'÷älÞ|Ùe”bÚÆµk׊þL.Äíîî"±“ †©µkÖ’/䥑!–Ÿ°úÈçÀÔ{¯J±†(>ŒHl4t g–" î·Nþ¯—ØíñB¿Ð—2V(dê„x/Ç êCggéȸfu>’„ªÖ¤$àz‘^‡±Ì[î5XXtj>äo, ò úBÌ@ŽËŒTí õa.OŒ«åäÄ2¡ _wO·" ˜g’p¬[·N,^´˜òƒ8<÷£IÉjÂtÒPóíÛO?ûÌ–Ö÷Jˆk®¾:e|Û;Þ^èÙº%€Ï|òS©{E©M½ðÁ÷½ÿwW­Zõ[W]sµÔ×–Ð=Ÿø.­*¥MMŸ–ˆ?A}gðqÞý\pÇÆ·éCv7N½.Huyz«ˆð"a „5ÿè‘#R˜åJÙØ~TõJÄXAb„ïÉÞ½{M½¶ÍäõmÛĨT-$|ñ¯¾ùÍ_):<õÀ§?é!ò“Aàú˯¼RôÈIZ ò¬¡¶Øo뢈ø ŽAG¡™[°àÇz¿gáÏ£›»×n»\–eH[ä3ú—ᤓyßS Ôþ.Ògµ«àú‡®°¶‹üwÚP}NÙÆ> Õ@DHø„ÕNC ¼’ÀLs+D¯H®Ñß›ìÙ³GìÛ·—›¾þoÜÜXgòÁ‡“_øÒ— =[7øÕO&]ÙŸ«¬^øÐ>m¸öZ2°¸©zÀué´'úO.ÆÚê„õrdÂrß^îóŽ•×zåÉ㊖ª¸-’*2‹ÙÜTÔø­XãVôwÇmCfÖÄc±4Àö"èó•Žúìîî!?ÑÑQZµ HÿÊË/›eâªÕ>& (‡¥j°mûv|Ý. ÀšüN4?±àÃø "oRVÛ=&¾—ÛA&5)4W¦å 8´°·þä5¬½³ÕY¥óE±ÑÈ—ã{ê7 t±¤&æÿ\Ä ²‘0Î’…ª©êjKy•ea\…ðô'K*òÝËRñíßâï±Z“xïLDØ \Ñ.Ö@h¨ ð)uÿøñcbû¶íf´ ŸŠH/rÿ´jyRŠ—_V¶¿üÆ_µ…Rÿʧ>º÷Ç_úb¡gß°@"ÿÍýý÷\qÅbݹç/À,µ0edøÂã…Âê­<À´Y ¸¨·cV–Õ½| öîÝ#àGäïêî -;¥nÿºLP°Š4;“ôŠdÕû°-XÂ/þù×ÿò›5£Nð1åÿþ…?.ôì™üîÊ•+ëÒ7ÿrè¸<³_?à]`IÎ ƒ!}Àû¿J4â“Cˆµ&,!B]’hžòìi—©(žÐ²dBÒvÃ~Ý»”'‹RÕ.©%Àá5d5¯¸r¢ÊÉõwíÞEÈßÙÙ%:º:Äü¡!±xéR180(yø±{Ï.ÑÓÝK‚pš"ž¤WT¯lÙ"N<…ËÏýÙ_~í³…ª¯|8ù‡üG…ž­Ûð‡wÞiÓÃ>RWEàÊ˯øì²åËW-‘ÝÕÕs(›SòóVÙ€DûŠÞ[]p%¨!Öý•[kd­û{!гÝ]wÎÀ£,ñÛ/OdÉžŠ²˜ó[:KOfOŠÛ.WWâ1KXnsQ»^ j` <[K¼¯SB`3–ïvïÜE¿„&¶A õýˆ ÂŒ8~‚vvtV”—$lC´ç@oEm †!áØæ§žüûb­)×^»Å0“ÞòÖ·z¶!àVöð#m W\þÅ‹­\²t ‰X4‰ÂÑz°n ·L2ÖX.œf‚ê*¹õN‹©ÓSdèãr ¸â¦Ã}}"°=ï\‘=5'3ü¼íHQO>‘œ´YH]‹ëÙ›™TU‘AðÔXêë„;u²°äüöuäÿݺ¸w ±Ÿ@¤‰kž:%¬ß„ý»Óèô;wíáÆhQÒdY4Ñ{K[\÷îÝä®°!MT”&IJmÛ6ZîãùäÆ$ä='‹-"†²{×®˜@êHÕ.ŽQÎDCOlÞüÍtÏ×]{mª¾Ûßò–BÏ6&8ÐpõUW}vùòåäž[Òn¿,|ž?_,Y¼˜Ä+cüƒãŽ| SàôSSäÐSÕz™|ÓÃǽSù²&–Yu‹PÙ¥¸×µÔ w›nø¶,»¿(D}æ9DµZ_b>=zTìØ±ƒæ n€T¨™ `>ØH…è@Ê.$ßFÆl7Åí.I>×êvƒ¸Ð6à“z¸µñ?þÑÞÜÓÛû>Xÿ}…Â,iÄfB DêêêãÊÂJ@1ß$ò³ ¯WE†1ÌþnCžgŸ•ÇE…(°ÒäáÜcNnDlëS8ùâfXb1çw“M@}\Ü%PލÍm÷õÕ©Ý<>1;9D2ƒƒ Ï)uÆól¢]úúÀþýb—ä䮄hB‹qè²rÙÌM„-ƒ¡b~ÛÉ­ºyŸ ¼XaOÀý«®¼ò>I¶gFýpýu×ÍàB« €¤sz{{"öåã%¢´h¤†ž||  Ä|ˆûUíŸó´‚Kytßlé"6§Ù!­¬ ~KuJ÷<É)ˆ>rDs”™ÕІdî"CK²†Àùäë䨉<`{$â:xH¿£$"‘9<:Ç:ìîì=½Ý´åRhX.Õ“Ÿ/%²Àž L+Áo~b{ »-®¿öº¸íúï¶·Ü^èÙº ÀÝ?¼+UY« ÀÕW^õ)^m\, ÀÀà AxæISä%‹“1ÁaÍÇË„õ.¼l©=·ëœ¥9D¤U“Ù&™K’…+K£EJ½pÊrO>Y°â,éIK8Y/¦–ÐåBh 1"Ûð§~O®f ¾böÀfž,ÚÊm2‡%Š¡ÀÑž{{{D_?¹C}€­ªª· ›e껟 EvH°©xkà ×]Ÿ’n»½]à®»R•=øðC 4;®¹êª[çÏŸ¿qñÒ%¢§»G˜)-Êcy¥³«§¹P”ˆýàø  $ ¤SUq½”4dOò˜ßúv0BjŽ#Øb»+žÆ.?2íñˆãf2fK/YZKºÕYP e\ˆ„±Øs…<ÂA\§¨„mN¤Ç;Ï0¤ÞVòW'⣈_°ûLÜöãÇŽ‰Ý»v«ˆÑËÒ¬?Þ'¢U 4¶Ó†!„'Ož¤ˆQ¿Åó ärå'¢T!Ú¼~æ±'žøAFg‚®O€[o¿­Ð³ €»Sú僵–H àwFެZ8²Ptuwä·¸¿€Œ0ÓØµG{ô¶]ÛUÀˆÿZŸõ uB4÷ˆäéå.—Md\'ÆHxgwq¿~h¬ÝJ)¦†Â¢®ƒô=Y>5(0ÿÛ€Èú5‘ä¿C’Næá3—$I?ƒ}]^ÑKØp6¢£Pïß¿ŸˆñÿHÙ— òœR¡È'{âñ;š[ ¸Ð6pÏÝw§%€‡¿æª«?°hñ¢U#‹R|6’Œø¯ò€òöô‹q©{ñRß,ëÿ´“ñÜ]0f(Œ­B°ú°0«É1·ÌG›br@ñ6e‹Öõ2,úW"â ª³NÏU»̉}{÷Rì¿´1[ˆÅyK ÓÀ„Û¹cõ‰Nލ² ŒúWàð ‚c=þXKOÓm†Ô|N o(•;Œ˜­ ªvR“ªétU…l½µ †øV¥Íö˜UO$œß˜Û'u3Yí Ø"|\^ KêF”Ènu8ù5Š?xø®Ûq;½>óøŒj¾À)?¢øAÛç?ä:y]µ%H´!bJn·/ä*\ÍiDƒ´‘žœCyäyD½¼Ò*ššCÜű±qÚgÒÙÝI±ˆH0â³Sšü×Ó×Ë^V´æÂîL Sh¥ðŸã7åÔ­þ<ç/Öç®±å_ÝNÑT‘TõMÄo«jãß,«¼¤c¿@Cä=KMy£øÓÊÓ*ñØ´©UEµªœ KÐö+:¹òGà,êW%q)þ-» øºÈÆO>xáÁ[oŠôý¤V–JŽtË*Š$Ðrã(‚É@"@À I ` ¬jޕܫHÉã •wÍÕWßñèc«Ý‘b°6§·ÞÖ&àÞ{îNéL<Ø:pÅåo¾¹££ã}­(J+s<ü¨_|ÅŠô9 A6€YK÷¬¸ýÍ€YSÙBµ}?OðTváñµÏ>§M6å© µÆ âa/U;‰AëÝ›$Žvÿ˜X¦Ã Q†Âïèü~ÂÇó€NÆ2 kV¢+O¿cÇ‹yŸœÌ­1¡I,`'Ðõ|[€íMuÒ]h¸çî{„;D­$®¹fmÿûp|­ "k É0!휘4œ?ñiƒe9µÐW§N{ºyìÆ&wiÌ7¹³îq[ìk£ó¦ëãï.BGV>» o¢ë¶?é-EQò­á~RêUc¿öYø3^Äð;  !eˆ„ÃvõæÏÀùn@>CÓçþí“l2ÛÅ6söbÙ8a‡à@?HuÑÊûUSÑ&,  Fzçé}’<“Wu=pãÆ)Éå–[o-ôlýàž»S$òZG®Ý°áûúún„Çê‹x²FͧLÖWþ Öãsä^vJ8Õ›0“Û‚È*+5é}ÞnöwSHL |“;StÍï BR ¹­‰{6’ç”å…—gáyÍñøü6ao>ô}>œ#-Öû%æð^i@ÃýõJôœ 8.õÿ}f)0JO(l?—÷ž}äÑG7µªû7Þ°Q¸+!7ßzK¡gë&›î¹7UÙý<Ð@³ý %€[‡†6BÀÆÇ.½RÙ}X¤ã©މŶþëOz.£ït97¯%ÂÖ)²ú¸;ß·Ý~óÜ}ÓMHÞÏEâš ô·¡i(PVSµÊÚäÇú~˜š©µÿ²È+чŸ\˜ÓÁÑÁ¬àï÷ªäŒÆÒW@Eà ±Ê{Û~ôÑo7ÓU@\h¸÷Þ5½ïûë*#®»fÃgæÍZ¿`ÁˆÔ­´±¨éÑ×ÛK'SÜz ¿^ŒfÙ ¢øþ±­õéãÀ6âØñÉÙ©LÙ5¥Å8¸=­ïö¤eQ›ßIÆ‚„§ž–Ù\1S•Éhià0OOø~R’ 2rëtég!bøÁýHlÖd›ô¹‚6ò—‰”ÄüùóH5ݵk·`¯URuÔ{@ÏLŒOŠS§H*9& ÀM5Ђ¹%Rp¡¥`õŸX¸pÑÊ# hŸ2$·ârK–,ÝR:Pú¿:±wVû¨h¾U%&¹p®O¯§À‡ÁéY\Δ_A J*PNºw^U&H~Ï.+H {‚°jQÎ=ˆßOxÕU£Òª@VJKqbâ†åPôK†„}hvŸa·“TÑ‚à2,¯‡~ô‘–m ¾ùÆ›Rí¿é–› =[7¸ïÞMi àþÖ€ßùíÿHdþ¢©:!æS(pcfàXgœÇŽåZÐ;ÿŒ$ä¯ ¯¨LPp¶Ç1ƒó ¿$g Ô:—WðV3¼Æš­±«¶ýßÍæíBà^Ö–W2j+¦)"Vq&¼÷½{öê[–0:!Ý'*Äœ²è#MI-J  ·¿Ot”+âÐáC¤–ª³ªÔ_eSP’VªŽŒèz葇?W÷ fÀMoLßk€ àB«€ìH Å÷ßG €ð mÀBssaôÜÕ«V‘vÒ!U}\3KBØ–ï< ¹I!4Õ$!Ç“’FÌ1"ÍÌÚ·E SaWnÀ±©gÙ"¬g¼ím@B±9xÖg¤«Î[úŒÇ¼vÅŒ ¼‰Æ4?aÓ­ŠÕXÀ äŸäcÎ-šß¿ pÛܧ•?€ò¬`cPE­ÖÝÕIû Ž;®ØÈ9¨†“íí‡ßÀ©¢¤¤ûmIö×Ù/Ü´ñ&áÎæö€M›„« Ýwÿ}u•‘W_uõ›Ë•ÊG þ Ååí•„Ì ¶ˆ8°ôNÒ.À*Åhª³FP~E}ÜŠLТ=(VV\"ýlöå<–r”‰üz‚ÜÙmª ¦:Öƒí¶q¦„ÝO!Ìi.º:{É‘f‚ ƒ­ïÓqnvYéTDH+ýé¤,úÊH§+ë ÷àà€“'N¨¥@¨¤ð”ÔK‡€Pûèòþþ¡‡Þ^hPkÀÍ7ݘjîM7·‰Ü' €[Ù¦ûZC®»öºó{z{Þ7Þ|1x؈þ¼Rê j»pá"úa||Béþr¨½ÕØ€žÍR4«03J¤>8ns:×KT!bwæê¡F5_eYýõZ³Ô’‚`;ÙåûHßœ{)_€˜Bd4Þi·Ó´éÈè(­ï+¤Ú¸›Û?“ŠÐè¢?^KRU-ÓRàéÓ3bßþ}Êv5[µ^ƒ*ó‚°M^î`SvëŠÃÍ7Ý”RYn”÷Š@CÀ…V€ë¯»î==ÝÝoš7Dª|Þ…YG†%6B=˜šœ¢TŽ?³ÆõwV«)QÙ7#ò˜lè糲؈à—²ÆŒö`ºáæ.îqQ¶Ó·ÕV·]ŠÆ« ©õ|·íö};¿Ã9mȲõÛmJ7Ví÷×ÕÎ>.&©××ÿ¬P[ƒCŠ<=oþ|REH'*¨ UÝŸ@‡gâuZŸ¯•À…¶€û%²»Ô¦Uà†ë¯›Düa¢›#¹„𠏯úÿüyóÄ$"û"ô×l5–´@dOHG§Ìzñ.Ò™‰|µ"ñ`þä­jf”ã7ÙõÕª3å<“ü1)uä”Rf"!2 Ž6v¼¾z¬úµ¾È³JÿOÆÄJˆœÖð;D|Þ€f<1uŸKòª«ÞŒmôwäV1¸EŠûs&øÀ½© À¯ö ¬’÷ôô˜€6µ‡`?逳³‘8=sš~Ÿ™¥€ªóL ês¸`ÓN2–4‘‡„Œ$ìê›Bf÷ž…d^qÚróMpiç^9:ðý^t\ì1µÛlõŸcããR „¾UÈœG8â´íhôQÖÑIâ?¾ãðPôàÈ‘QÚpx0Ý&Ø»2¦À±z¨%Û‚ç\p¡U`ãõ×jxxx9ö Æ¿ë˰§§W"Zq¬xn(°BÓ1kÂgq;›08¢iÂ`ªJ[ä›)K_$î׳PÐçw“)õ¸«ö8h7Žá=|¸¦È_hÏ‚pâ ÍÁ2éÄÑ>kBËfOï ì¢s)*âðÁCtîZ Íò!Gq†ýBÂbI>W`jÂ-7Ý,ÜÕ7¥—}Pw<€ú—Nê*û"XYyíŸÅ~ûåcýM˜ž!=Jéû ¯?=Q‹0x»+Y,r2&²ñÒ”¹•ï5PdF¾-ZRB+É/+·Ä@$ó¸eYcÏãÅá̹5Ô'XÑøˆøìíoÁ!Œ|j¯º¶ÓS^Õ3fD4J¡ŠC` >±å H <,aW`h5VÅ U]ÙoýÆoýÞü_Mo n†áÔ-+R„ǽJ¹C”;ÔN?BrˆŠ:aº¢ïWD£¬bÿõõ÷‰‰É qðÀ!R ª¤Zm°ƒâ½-÷ÝÿÀMo ¾õ–[R„ï†Óû|ÐàB+ÀÆ/èîé~býÍ›?LIãù‚—„}t¾Ÿä³ä %€ªr"ªKÏpØ* Îä(à4Vx8¸Å½mbÉ\ÉÇÅ™[D5–&R?æ`X¢€zúîãÙN#¹B$ÛŸõ¨ Ú°ŽUúqŠƒ§SkF&æöŠkëC=€ü@ü®NÒÛütê/™•­Xº}96ôÉß:x °$%ä­h‰Bå´ƒø[ÔÞ½û(8¨±Pâ¡ð`ÕKÏJ°©ðkÈ€[o¹5Å”oØØ&ðЃ¤¨ÍÝ÷ÜÓ@³“°ñ†?×ÝÝýVPÑÁÁ!£ûGš l½„ —þ@Ô*€Ú†E>ç\‘3'MÎo äN$O6ª<¨eÎã©5ùDÖ ñ¹¿y –霾K»âb„¡`®<Ö ï a³ÙŸ? ™“óÎóáäQ§GÃh§‚wt²c‰¹î»Ý=¢«³›BÌwtv(Î^VÜÞìùgk?8G§èï]=Ý¢«£‹N­F™­BtJ‚0«ÆnÍš•¸=8qâ-gÊëí÷?pÓÛ‚!¸Ð6ð gï+Ào|[ÿMX@EcüÓÜ"漡ù& {¾ÉíWЉ‘öü‹Wl/@—37\”ë›Ïà[ïÏ€Vå²éK~îcÐìeØK²A (ܸ>LŽ]©&ÙL>|”—ŸSƺ)ÎÃxÜÄ—È‹8]HR -¤ fp0[$?‘0ÁlFdÂá³pô™7oHôɹÙ'Ÿíéí!¯Ô®Neý±˜Ÿ¤Ý€'ŽŸ$U LnÂd$Vp‚Õ8-Šc÷Ýÿu³n»õ–!,JÞ0QåĹ ÔW±ë° ˶·¿—8ÅsŽe[sý„PÁÝY(Á.Ê®Ë6¦yžµ?¹?V‹ )?³í–þè¾Õ†4¾‡Ìê RnzÖÏN9x¯8(ƒCvaƃ ¶Ié݈ß’¨^‘ ‹“D\œèƒ£çl€AyFJ—3ð%™®ÊÏé8ÌÒŒ¼Ž”¡Œ†N¡Š´ûù Ò4EüŸ#;TV"s¤ÿÇÈ uCÃêâ½nn<² þeÀ&*Ë-7 ¨ª‘.oÁwù µcàE D+>Œƒ,xcˆÔãdRò–© rG1Jfò¯áûÚ‘h«s?Ðe¹egá„E˜×]›Gúlsî5—ëµ(¸„Ïuv|ŒT¨•,ïaóLѵû"`—ÃÎ;àÂðÍïbƒŸœ_«V¯6GÎ3Àßž†ÐÏI„Q’ÏŸ²ÂŠ%æ‘gNa‰Ì ÇГ}@û„Vq "¤¿¯nIÇ›€l¦lwÝ^/Ü´qã+ð¨êÔÒvëÅçúóÏ'} ‡ö\ë³Ø‰`X'Eîdô@ž|Ž AaŽéAóS'¯lWÔV”U@&ÏpK R³ˆŸåÍÇb·2úUˆãù1·Ö{®Ù@!ÌéÇœìP+q *žííé“ß{ÈnÕ «QðT% «K;•Í ¢êDrÛfçŽãÂqÿæ›núö¦ûîkj[ðí·Ý–‹ëo¸¡Ð³o@¾¬ 5yŠXTfd®j±/HyV©í•%!Ìz-Å„ÁùUôUï’ ò°¾‹$ÂÛHï#¹DÁ·™Ç±=ö/ÃRh¤S'çç¼íøvÜåØ5¢Œ>EdˆØà´Úž‚VJ—ÇîÓk÷`*ëÖ­KäÙ¼y³xîÙgöžœwÞyDœ¦a\†oI%÷•x Ò~*FZÕÏ ÞYglxRˆCM÷µ òþ†ðüüï|ö2ˆd¡=±´CB/WÇg#‡ * €õ*y=«íJBHÔ÷½sïÕÔq{€“y€üäÏŸµ‘…æœ•Åæ´ŒÄ@r øwþå;âÔ©âœuç’…zrJLMŸŽƒÊº’¤‡øEºm¼R¥š¡·õ-! ò3`bb‚™à¤ Mµ;ب_ÀA ŽÉñ®»ïjªR :_ŠPïïíí#ë«mcÊŠ¥–eË–‘X××ÛOþÖôrMe#õ2h'´EB‘tëŒ9Mà$2|ýS×ïK‹Ø›0½7Àe*)m ÈVQÏUú»c’ww¸RÛ>âs¸Ø‚z‹¨üèÄĸ8vüXl›H¨þäóæK'9ê–Z¤³üäœÂ!Ìlû7CY׬Q^¥ð)™ž™¦Óf/I¤N¢â”Ùÿ(6T›q ­¶„¬$ÔX åÝ»©¹Í4·ßv{j\¯ó⃺ ÀÃ=œÒ7~xW“àæ›ß-õ©·a™fq‚8P¤/K€¸bÚ-óR$ ¼öôfÂ0òñœ_B–”™|ÇúÊ5t9׬['ÜCL\;As´¸âNæDÄõûÈe¸YÒHa÷ÍÊäëžØt2òèÞáØØ)ÉiÇüãb—ãÒ…Ük•emÀÑœ; a |PêûSRY»v-I%“@þÓÓ$؈Oí¯ÎÆ„œÕoãÒcëþ6QŠ|Yp® @€;òz\ Þrûí)œ¼îúë =ÛHÞ,¸åæ[~¦§§ç8õéCi3‰vU>6z”<,Dùû Z‹å%{G—r» c }ȼH¹b‡ãæá€úz¸œÊe¯$s„ÖëÚ WæÔDå\""b–êܯŜ}­ž{¹Ì6‘j;Tá½À¢>iŸÇ—JA QÜ:;»¬Ð*KXL@‘?0Š#GÒÚþª•+ißBtcéב‡ÕVX(–(âïõ9%¹ýÂA"z[ðö{7ÝÛ À¶€G~(ÕÑf À­·ÜüŸ{{{×öõ÷ÒV_Bþ ©«ÁK1rLr u$õÉî®.R fÔ‰+q7‰”žKB_|"¿ýâ|¢ KµüS“pŠÒYÍmþž\¬s«²ÈŒÝð"NHbl-⡪*’+jèûøt!,„8Ö»ËjSè†ùvŸã{ê“Á>“®®n2 vËÏ):\fš;Z¨M¹í*Ø?´åø1D«ï¹÷ÞÏÕ5À( Yþµ×ýI·ÝrËêïï[„¨*pË4=Ú§?2›9@5[›†à_PËX9ÀR…dÒû· 1 ôn@¯0ð5Ò3a†WŠÍ©˜ÒÄV ÄO¦#æ÷ ¶ž‹$rÕB³lÒÀEçðäÀ?¯,w¶Û„Êw×’ãÃÕÕÞÉi#}3`#}¡üA`‰õÊB„ïê褃:ÀTúzûhþ ˆ‡oûo‘:|K‘µ*ï{óÿKwß{ïdÝ•kxë[Þ’jGÛÀ#§mwþð‡ 4;†[o¹åúûûÉÎ@®(g4hƒÄÊŠÁ¡AÚš‰ÁqÀºìŒ>&ÔjŸÌ"¬ò®_¡—h—á"ˆËr~¯¹ž^‡[qhéòšCdÌ}íÜ3>6f[Þx¦9xí”Û¦Œüîs˜ Pxÿ]´§ Ì sJ/Q¶«Mœ@tåH—ñI¶×úLð€ ×^[èÙº À£<ÒrpÛ­·þ.-ù0x £xwm¿”t£” ÌèuœÒ®š!ÃÀˆÍ‰#î'sg®¯•ˆ#ÒH“Δ$Ryù˜ˆØŸ6aÉ"2¶Äã•ò¤_[#u"ŒlzƒK[§Þ²|€÷cå`£À|#×\)&‚ `‚èS|nWØÒ˜˜¾þöÝ÷ܳ%ó¡àBÛ$š!ÿí÷~ï2Éõß/U¬É2ð¤‹O°µDYmð;tø0‰þý*8è̬èîé6ŽA%ÞŠÁu,?~Û±ÅEÓçeªJ]«½Èÿž“’j‚ȰC´B«¯TWo÷Ñùž ²,^ĹóéûE¡ºe‘W"Ø9AÒ#F‰"O÷‘TÀH›mW– ‰I;E½, À¦FËÛ[ßÚ°PÇ l µS3 ;TÒ»¦ˆû»ÈÅÖDö±F,¶Î.1zxTl}ù}Zðir3E™©µ¬örCµè¨TÌa¼b@¯$Ã[Ð÷âYI.;eXˆÉA5ð‘Üt­Ÿ5õ{ž¯—c»)¡ÛíÏ’r\tFïpÇî& d¥IÙ]»w‰ãR5ÀwŽ K—,£6©Î”Ó(nêìLí…†T~pç 7@Šÿïîêì|ŒxØïï.ÜG eÁíRáÂfÕA‹]Ý]´‡{LRqDd…$¢„µ]PsølÏê5\p˜¦Tàȸ ³X/:3ö}ç{1îPŒƒ RáÐþcB°+±þý ¨\O‰YRH$H„SYŠ!aÕŽrŠ–ÛÒøÄ¸dDJÍ”·»3 £‹¶ùzݼ ¨1Å ÕŠ•+i?A¹ÜAólšBœ&6;®Á4…'‰cwÝ}÷9…çÂ[ßòÖTÿ7\»¡Ð³g|/€DÖ•@X„hØK±àmç­?€°uôïþîïÅ}‡øüç?Oçb÷óÏ?/††æI®‹ò¼ºõ5±tÙRÚ»}êTUttNQ ã V˜ó18²v«¹(ès „QM<ËòIQ]˶×Xè<ÿâ[  Õ á—Eqi•ß¡A L+R}s]”íþ&²ERß#ŸùÚ´¯á+&a×ÎÔŠrNKƱÿÀ180(%È~ÊÞÙÕ!u-¢c¿NIu'2+É7¸æœ5b¥D~„5ûæÿøâWý×Åg>ýiñ¡~ØSO=-Ëí'U—žº»ºHË3{Ô„ŠrÆ·Ëò®T±ÕËÆPÀD[¼t1‰ñÏ>ó¬xþ…票Hœ»v­¸C.6ýÛÿïÉ©ã…_+–¯ _ÐIwîÚIz)­’ma’½¹¢¤¸M*Å,¿Ô,*Ÿ$ ¾\ì»ÃHf£oì*”¯2©eÊŒ<kKû3˜à{¼±Ç}7Î×,—Öô-ÿ`6¸¾ùýó`n‘¹XY®OUF®Ä7¨6†‡(òÑ€EèÄɉJè79O—/_.öîÝ+þêë_GFˆ«ß|¹ø—þgqß½÷Šën¸A¼ûgV<÷£çÅùRÿ j|Z]¨«m€ºU€Ç},%^}ÿ?h¸·ßvûotww/@d„k²"?6ž|òIÚn¹añpáBÚÂÙ××/)îZŠððÉsÎU;½*Ý$¥ „—‚ÓPoŸ "‚|þqÚà‘µÄ…•¤nùì´Þ+€^´ Ó9B¬¡~ÕùL¹ìbŠÔãÙÊë-»|úd—c¿1°¯«,-ÁxšÓÏÜe®:!Ó»ÞrZÙ&§,rá•*how/ù›PÿåÆ6v´þ[ÿø-k8!Âб£ÇÅ“Om;wí¿ò™ÏˆkÄìèêbІƒüØÃ0œgN[qb´q=ƒâꫯ¦„H…Á‚ÿžŸû9±zõj“ߢƒ…°„Ú} û°ækÇL=[ýoÀw¼!‚ãÖã|÷•„½/CüÏ"H0°ù'3YÐ*¤oÂSYu¶©Ùy<>9N"=8~W'V°bDïï/ˆ—·¾"‘þ)ê_¹R?ÿó?O¡»Íþ«nŠqõýÒFÛÒL_PKÝûÞ÷¿ßPåo{ë[ÿ79·b ñßåxBˆNØÇ½~ýz1oÞ¼”á \¤NHýʈÀQ@BÃì€,òŠ iðʺv‘È™³ kMš¯íSd­ÝB|gß ‚´!“SÖ}¡ì.YãÝ’u8ÛÔéß_t¼ÚQV½à{o BÌ©²v23Cô¡‹/¼P¼éM‰K/¹D¬•Œ ¤4²Æ”Õ Œ56¸IØÿƒ;ï¼£î†IxÇÛßžºw•dšEàŒ† ƒÒ*Úñž¦Ÿ»Ìe2Æ—y‹tOÀ¬¯êu¯ã§ŽÇ‡LÆ nŠcÚ¥ùÄâT…Y¿¹¥Dù÷½Òåòç3ÊtÙYm†K,»ôÖZ®$(ÈÀ Í™BYZSN+ËÂ8Aÿ‡lU=R¤‡‡ª¤‚ˆ7HñiU6€ùé±¾©XË}m˜C Å°\£ÄîÄö¤·'WÖÛ§h­Ý=zÑ—7Eâ‚Pê5[;v[VY±ëNÌá…¾6´ÉúÍ÷Éu›:SuÄ?âMN‘s/âþE ?E]˜£ŠdØ Ü¬¦õÖ3lì›å`ŠyPé OÈ"Vˆ[×ÎSœé×Θð+Ù=XŠ9.9z©Ç§@UGªŠ*­& ¦ >i#R1G…œ&7þ|Pþ øÓ/%uïŸúdC•é _|µ¯¯ïœáùÃb`Þ`¡gÜ®b‚ `(‡aq`Y=~ò-ùyË):fAI4ê+«Î‰•Ÿ³hÆKý*£¬üYú~\JzZ4­,«•mÊz7a)>E‡ŠÒ±b2óÒeËhÙúÈá#’(LÛ9Æ›ÓÀü0O_|áE.îæÏüê¯l*Ô |8ù±O|¼Ð³gÔ(KZHFºRÏÈþƒÞß×G4§ äWzl±²LŽ ÍÑíÇÒ’A`D;WÄ#¯ÉÓûT¦™#»§MI(à­˜‘ÓÎý+–BrŠ)\_*Ç04âè­)Tõ™eMùvŸâÌ 9¦0BŸÀù:^â6*¿Ä·°ŒªM‡¯ê6>ñø©{ßýÞwªüoûïc~×ààñÜçM+±(Íkût-?áˆAÈÏQWõ'íó>¥NsQ¢tèHÆùÍ|g ˜¤¶¯=ƒë¢Ï÷‚øÚ>/joûiæ\¼¼Ü X ™a}ß;k“]_ÒVÚzÏÀ¬²æÒ3Ð×·@o!B- è“€áw‚ÕǤ 1#ÒÏ—8ˆ­¸ÃÏE^¿ü½ïoSÍéÀ»ÞùÎÔX]yÕ•…ž­[›ÕW4|ù‹²š–%IDh&@\2¯YG‰{ª~µ‚|P=‰½ÝcbêtYI½$W ðXÜ ×.ò¹Ú•®¥"T9Ú‚¯ ž\‰k¶ØjZ8Ù]ýߣ²Ø}EYù)>¿ÓÁ,9,ßMÊmwN®7§Ú eË©0ZŸ·²Ê¢àÄà²8)Ç»LGˆW¡3Ä¥êü¥’iD1=Ø|õ-ê–6?‘–¾óÝú%€w¾ãí——+å_¨tTT8&M¹2—Äe+±è•È_¶¨žÁÓ0¸D××Ùd³Sðr—МBdr‹|.ž^žJæ·?kIîòR’S)BŽ4 ƒhyóÙÉü.ü{àE!®[¬í?ŽžœBÍÑÕà%jC‰ÖÿË”ÆtÐ@i&Ãq+ñ¼Úg1†Û“ßýÞ÷îÈn•üÀU…ž=ƒà?/%€Û±~Šýþ 6RÇVrelÁwŠÈÞƒ4¨Uҳܠ7úMS _œ½,¬²]Õݧ{›MFÖ̳U—&ÆÀɧ3%Ê0MµúÍcGÑ’8r2&©vÀò%ÁˆÄ„ŽDV@E;ïØÄÀL<á#jµS¼ý R„ÓjcÙ"tÌœÛØ•zòÔ)ú-²Ú¢¤_õ<Ž §ejù]€/eVž>pÅ•ÅT€º À“›7§*ƒ«nþ¨DþKàG­”öÀ'E^ÍÁupØ Â€£þFt¸–_8Ÿûñl`îí'éZãϬ®'qßI¾<Þrì¶{<‘ÏùΊ'%s&Ú‰3-¤/å ?ÇL ‰ËFt®ÏŽÎ«¹#C#Þ‘¦oÂ7­*ÇEx_=ÌùÉ£U]™‚Ôöˆ‰qÄÿ?©N²Öj÷Ófp–0$ Àçêmãϼëg<àŠbý«·²Â9%íl# ûç›qUæÃv^DYá(¿HÞ˜…üæ…ÕË)ÜÏ9â:>ð¥•@F¡cÉZŠ¢gü)Ó¬>A™6° <š‘VÒ¿»©l¥òà‚p |§Â×úý•@lt²‰IPgjÕXÖâà­çå¶™sVUƒÉ9ˆTÃR@ï ÐÏø˜‰=×¾ú•¯¬Î¬4ø„b;…3é´ ƒ‰ÄâsÕxáDÚS/0Ça-[¶TLNLÊïU(œã–8e6Ô™îA¶Í÷TN8/Σ¹š÷Ó¤ÿÔ'´9¿.'9CšX¡áÀ%m µ%[†Ø«Ï>[hâaÝp;eÝÓc†“.#;G$Jr¦Gðx£½Ô*ÔšÐ'ãÒ%»Z{Ç¡6W.Ƹkgj¥oE!ƒa¤§¯¶ËiÁÛÕ˜³-ïMŽPÕ‡ƒo㾬–i{ÑVž{î¹z²'à  †Ñip#:¹Ç:½Gí‰g„«ttJQvZYþ±Smr”dë™ޝí{q?œ™ÁÕ|êgìO'3]e ²¸W2šlH30€¸Y h;*&qiâÐe–Õ™xäq&ÅM6PÙ¢õ@_í•@ܺå+VˆÕkV‹ó/8ŸB¯{Þ¹bÝ9ëÄÚsÖŠ5kÖˆ5«×ˆeK—ˆáá´Û öì® õ¡› ƒÚSÖmPRˆ±1P¾å·Œ‘²„bãé =§l”Q[']q%@ú˜ÜU)¼Ú +?Zk¤¸ôü+Å›áVçµÚô…?N¥¢P7ööH½ÆM€œ˜=äDwªÆa{©«‘úKêòeËÅ¡C‡Œ!n|rÊÖȆ¡—íü)ðLÂ|$l¦,û_6.‡¿{h‹DltTb~@>ñ¨T2¨ N¡&%…ü´,ŠþA±rÕjqÑE‘Uøš Ä›.¾˜¢Óà¤el®B„% 6|* :b¼C±›¶Rw‰¾"ÀCó牑E ÅB™–¯\!-^L÷{{zÕ®Ë TN-hG ’"±ÊV Ýqu Ÿ?%År‘‘B“ÒÈ­RRå)H¤ZmfþéjÌjþÊþwvvˆXicI70RÚƒ³-t™«B¨¡nà±'oºÒ¿øêŸ¯Uó_Ja)a¤ãƒ?%ÉÑÎ9ç:É¥L¢ªØÚ;©£žØ—=-jÚÖ/ŽÛD8[b÷‰È¾2o;w’#E©’u.ã9&ô±d–ެ #ÐÊ­wdÁÚ¶qp^"Õà“ïÄÉã©Ú}ã§">•Nj›ry )LBca…[´§õ9{Ó*Kª QIÑuùl•ý84qÒÚrÌÑ>×VHM( "èOBH¬ÒÊ¥Šüí´Éê†+£¨U\ ºßð¶àF nð•?Mû× Ó33—‚â¡Ã%RT¸.‘ñ™w–\fë+[•#…ÌÇ›+U–ü5÷/s§²&—½Œ¨qcDf“C¨uzZõ§0<ˆÏ4‚80DöËø"‰íÏ£GFÅÑ#GM­>ÇD»a¿¨~ü—è> ³ä_)+‡.I¤;:šðxOXyÀæÉû)xˆÁHvµé(J8°´HwÚž­sŽ%H®WÊ2"H-ÆŸSc§ˆp'ö»qŸ mRÛ‚çÔøŒåX]Î"–Bl5bN®8Þºuëä$¥ Êê?M1üªZ]„½ëN$JîÀ c ×#DR‹"áý=qBqhagÂóξŽ9±ùÐÁ«„ýÑúÎb%K1ÇWD – ÐåW®Z™ÛÝ»v‰ýû÷© zbáZ5Ê ôá½MÄÆ& Ê8©NæÍDh퇧6«± å{³Jª¹‚$TµÇ$Ú€óT#ýÛ–snèó´Éþð|¤ ¯ €%I”+•Í%÷DB‘‹`EÅÖpY‘´ êÞ Øìº*àO¿ü•ÿ·§§çÝ-Y"0b 3Rú?ê˜74O\qÕ•âÅçžS+ò7p"hDÌ5p"‘õmÉ_EšøF e•\ÆÔºlÉÞ5¦ÝG5b!Öœø——·lÑuXf(µÿT°¸­tÏúW'Bm„´‘Þ‰U ë™j¤ˆ 8=BŠ—t¿b¢~g@ên£æ.âP Ž+©€ ˆ`›Á¼EàþÜ»wí_Á; µ´Ã:ÜÇöíTÚ‡?ò‘¦ (^Ÿ @Ö*0pÁ\_%ê¹sÎY+Ž=¦ñ*Q„_îT5â`žJÐJ¼›œ¡k%Ò -‚[&Ÿ÷ù7!’žÆ1„×уÀx?2À jÕêÕ&ê)©Ëoyå³£Ëå®Fä¶ÃãGîGœjBŠˆ<ùl)kVÄ&3ŠÂTSU©¾ÍDS辚¥ÛŒ±ªæ:® É—0ø4ÆU½®ÝÑÑI†ZjΟÀr©Þè³0)h­.ëë_ûÚMú¥_ÚÔ’ŽÔ€3DÂ%$ö襗ôÈ 2oxXl}›^ÃΈ߇A¯C‚ɢРMšÈ¿E&ÁkÏË‹­ÝÚkîÒ8u†áÙgŸ¥H3æœ? ‘³Î¹÷ö³@ß¹q@½u–¦KŒæŒƒ7W½3¾Nü  ³ebtê UehLËú©@KgÎ RzO)Øòþr=ÑúF”éÓ±áÏÇéPpÒ³ó0ªÍ_‘6=y˜ó—4×/c™ %;DÀ)N¾þúëé)Gµj¸½íÆÓH›|mtÁwâäü[d­n0Æ»¦ƒ5JäÉZIKŸ¥%»œfÛMÙZTV­¶VoÚ%-åÉ~÷tu‹©é)-Ei‡)gp±ª¢·n’iS‘æ7 gÄXŠõ]´¬¥½gõÄ­jN²rÕ*š(AYù£³žOâ6&;ŸèczÆ)ëÓ'ž›ïÖBµyQ—e-ã%˱U„Ô‡@»ÏjnÏÈo;­¿àƒü/½ô’xýµ×>1ò'û™×&“2 ¶{ŒHŒ¹2-X„™·z¯üÁ'„×FE,IŽ9¢ìªÚðëú)zÛžÕ½D RwÌ/¶ÿAv.óW³·!“9·%BóùQ´çߌ°H¬„²Myejh[p#0çÀ_|õÏ.¤€‘ˆ¹œLä¼"/‡ )‹G³–‡Ÿšc¡A¾(kB¸®»Qlœ·W¹lnØÏzïÅåé5‚”ñ:aæ•ë»"±ÁŒ-ûêˆ1åUIÎ2úÐ E‹Ï/½ø"m‰E~Ul5Ñ”Qˆ ¶hÝÝ'uK½‹ø8Í@Ìœ'Ž?&Æ¥xŒx‡‹-Œ_Hz%2¯Uµs‰.+94ÅQ°Ô]]tÌ9¹Ùó@]Ã(>16ŽË9[ ˜s0[­nP^qÏ–4ÎΛ?Ÿ¾NMNBTgOÇK}X!…>È30ÏÙ°&qÓ½. úž4,-i8íyÆ®µðä#¿{ 8-Fþ—$ò×D;\Íigû–»¼¹¼ÙÜ¥V^ÞUãܹ=-‘búô¤8tø091«°5™Zéï¡H玴4Ÿ©Æ–:ÕnÀhV-{Ϫ  1‹PcR*WØhÎ|æœÈ q)/wa¹^dXÎã—±dáBúœšš¤Áò‘åYIK€äDB+í£îÙ–}%ιñÿ\“`Vä$ÚÝH‚Kh<ùÈ­—¼ûTXù±|xõÕWõVQ%XGÖ²žU[;Qû[„ôŒ9 ePçØ{ öíÛ« ƒ'OÁƒoƒ«£µðÊÔˆÔ¤B¸)nn$U¡TÖJ©":;*B¬† P¾omgIQþØÝÕ­‹Hðiª-‡9'r¢ŸK\¯ç : “~D¯>;]9áTiЪÚ;P½»jÒ˜áÀ“XXbgÛiÇÜw°ÞA£$â0…7”ÞÜwE_ÿKzÙ'ÐV¥÷ s (\³f åÁTˆ*ËåÅe†sÀÁë/+«œø~d­$À?°DD`ÏÞ=toôð(ýH‡º™û-¤ Í«Jz–Le=Oa$WšïeºÃaì(dzÔPŽCƒa)põ‡~é—¶iU30çF@ø Ï¦<5zËëAeû˜ÕË%‘cìc &¿½„ŽêQâ{¬~‰ !„„"™‚ôuf_‚"›T’‰-èöÒï Ã½…‹™ò·½þ:‰±12ÊÝ™ÆåÖÞДL­*«ÄÈO/ƒROO7mB‚ŸÀLuF<|PäìÁIާÈH‰÷RìÝdŽA¡ Dª?ª{Î3psŸ!_I3µ@/mó3<×pÖ ¥”†VÕÑæá €d|f´ö}=zK 2’Ð8ïp¾Z\$èDáö[«±¬¨7ìé§ËÂXŒŒŒÐ5Öøã¨½ÅµÈÚ–\+_­Ä`Ì=`¿GU/kBê™Õq²búR!Ë~›ƒ”˜ßœ|ì©ÊAT²F¶d…º“iufå-„9'’Ûõ“»$ŽGŽ”5ƒ4¨%x¶q°‹Ô2œ& &ºŠ¾æïö}sì%=þäÈ:©zžId#½[–"LzY¬›ä—OåÚ„DNúþÞ^ÓÍ]»w§Æ®‚Íßl9 ƒÑwÃD9°ypÔ\Œå‘#G g¿ŸL(°D\¨¸åP힬2qsZÍeöÆs`uÓ .sJ>ö±•$•ƒ´©XYC“žl´‘%ŒsHÒÝ2½>oÖ›õ3.ÒÙ÷lÏ:˜–퉃uk”£“pމ…çSü³ˆ~>Ò„eŒh(àä‰q{òÊÉá„™0‡åˆÀbËsœÉh"–¾;þ"ôpç"©Ec9žç” 0fPrUP7A1¥šså'OxÓ…Ò‘¥xÉð #-(P"ᬤ¬ÿöôôîÈoëÌ©€9“ÔÕ³3s&ªÉqÜ ‘ø)î •cqDäí±$†Z¦0WR)2‘ÝgL²'{dÎÔÁÃt ÑÔøÚc!¤¡¼ÆôašEÛT¯¤Óìä•Ã6a•G6­0Ôç_(i±XNbVþ^Š{9wAæ”ÈÉp #u‰¹f•­úñT‡5”’£­–J ®É\¼ÈÌâ )}O´gBØå0àšt^«\—ð ä+3¯Mü¼•2!‡X%Þ›•²3ÕA@ƒX2o]Ku&–a6ê_Q(0EëKØ ¸<Ù/Ì÷N½÷Á !¼óC/¶æ””ÂÒ…¤ýDÂ8¹ð U«öòž:j‰mÔN2E2)Oı£¤€½î¨d͵­“}[ˆÀ+ÁVYœ!¾zM70Ï ë:ö8t[÷Õ­Ì}ÓÊ!DõÓŽ«—Kd¬¿¬…8¸ó—•«­Ù&4fäRM¼¿zûWè¯(Ñ.RŸUNè!À¿Ù™*-â{D±1—Cµ˜lÍC<ßß×/”… œg 9õˆ|. T‰L8ÓÞlWšÓ”EqÃx •U5¹ 8¥ÇÛ>¿5Á=>,Y†ÝÖÑÓõòp‰²¬%ÈH°#ì…CDÄ:B:À²Ì¤‘¿­=è²f®"™ò©•'™éäÉjO¤€Y5ÞEÊ)Ú®BE5]Ÿ"Úæ•ÓÿU½ò#( *ÇÀ ­÷Ï™PoV¿ýÍÿü.ýÿÇÿþLÑV5s«”Âeème„²w–Eqs8ˆ¤Ú$&ÃcÅ(²JiI7`¢bDï"eÕ.'¯,Õ?kCÝ–(‘€á‘aPICì=Wtýºˆà^+â­Î•ìƒ/‡3&µÊr‘›dŽ?¡Â"ñ©H95ÛU3G#™ò2òòfúéY}r$ô.Öø®ÎD,ǶKsE¨‡’ú-P@ÉZ‚Ó¢n„­®ûÍp ôi«ê!§¤Å­D„Ùz‘yn  –b.aË,øer2޹‡0ÜDø¸Ÿ¡Þ"\DÏuZ D­ˆ·í'UVò½©tôØQ³²œÝbœ‘’jPFJäK^7qØzׯ%Xͨ½ˆ£«uü“–x8L^¼+ô¦:p¬!˜S @@ú,ë—¡ˆx)/¦Œä9¥'³9eF#“ú(B‘="y ’-½ý7JÍ¡ÌÀ"î$Ê×ÙöRLD1²¶õâÑz°6ŽCPã3übBà÷DËè]aÂW›ƒ·ª×¹g÷îÝä!ÇÂ7VV¢:RSFJÔ™•­‘Õ„H ˜zßxÿ3Ú¾¢N{#iA@¯‰ÀHsx´ËòäRÚuŸ ø¹Ï}®¡Šþëç?ß»`xø¿`"wë3þÔÞþYÚ ÊÏžp ‚Ø/Î`8Äl;°¿¸<Wë·úʲ†\È#\I3B­\ ¸ækG\Ÿ'rŠQT§Pûùw[úhÒêMse•,"·§ÑÑQZÁ\À˜­?ï<-Êa«Úä¤ì ã5ýšeúümeïEþorbJbmåçÍ`î^ *Ðj þñŸþéŽFúV¯çLºÂ4V|´Ø ®„#r"0P)¦ì¡³ÂøTÛB,ÈJ…&rrŠžçs&JÐ1ù¢É¯ œ- Ì7àà“®Çc²EýkU9vY¥Œ±›˜;wíÒȯö},Z´Ht"¼yc^Ä1¨nG¢œ‰Ø¡É&U½{KÜdpâ+ÚìÌ öcE7Ç•²hÏñÿø<2z4ñ ŽøwK ‚ø †”¨æNæ"-š÷+0“ß×&«]<98¬K8‡q’8¹ghÞ<Á'Ζ¬¾3‡Éë_¡å®ãTo9\ùp”J©±—Èÿ /˜µŽô´fõê3ïËQ4eÔ—˜›zN otø‡ŽðLê‚ýŒu §(='Ûd.„Dæ Ñh€žÄݵڌà ñ8^œ:ùV#€>ý––MÕd½Ô p‘;;cm`¿@€Wäw'›&r¯^ÿFÉ=Î>€¥K–P¤$:L÷ßì ´ˆ¢o2f7¾öX5ZNy=m‚§ßóùq’PÄ*ÍRÛéY³¬& > Øž8# j×ãìàwû­ø‡¶†›3 @"îZ>f:1`è( #8$BÜþ}ñJ@ÄÖM,¬Ée£"&zƒO©†^ñ¼7!7I¸tä6àÙYVä5fêóâw”ŸPŒŒ,‡¡àƒ?Õuy$‘̉œÑ®f¤&nO^9ˆHÈ?=P}зsÖ®s ÅG, ™ŽªçƬ^ÒN¹Â[õê1íHÚ?Ú*Ì…z÷s?ûî߬tTæA¼écŸwMùɸ73Kû`X²t‰yXÝŸ5ÔµZå˜xzCPŒ}”ÌŸ6ÄØ/5Ñ }EåFjð} ÜŽì“¥;5‹Œé—š¤æY֣Ȕ11>FÒNŒ¥$!(ÝË£ÓúԟКüF-@"ƒ’µ<%2ÛBž0·J É÷VÄ:øÎ;ÅöíÛéØ2Òùg•Þ —\r‰² ¹ã”‡Ð Œy­ròr…©œž?&ĦN½‚#£ˆ~OMLç2ü•Â’ñ!ca©,Fæïû‡o}«ng 7œ°««k„ˆAMÜ’Ú©ïïÚ¹+ñÜàÐ &r9Tâ/Ξ#I@£Qà‹!²¬ðIúîÛh3„ìVòxº•+É\²!ÒG£cUDoŽ:°ÿ€8~â„Ò“#8‡~.X¸Pô (? ÛW‚¥$ifiÊ]¯®u’nmŸE`ÕñàAÍþ•\ÿGÏ='öïÝ«NsÒú>Vr€üo~ó›‰ð×'±³[$Ï&|u¯óg¤ÀNšLøÚŽ#ÛÕ©ÉžßÙ/@¨ñíÒÒ¯L«­†·æ‚DŸþä§:Ë’ü¡C8%Å„AÎ.å-@´==5)'þþDÝ=ŠJÊ|à€XNÁ¡¢àe}yfQäDj|ÒÔ[N‘²Ì i„P1fÕº¸¼>tð@„AÛHpp*–M»ººI¬,kÛ@¹ìS•YÃDrÂŽ:/…©¾ñäwû†ã®^Ú²E¼¼u«äzZäW§Œ,X(.¿ür½/¤¾1OB1bÜ2ʵ±$Á±¹Í8炜¼} q(ñqpB´u[ðœHCCC‹äDì$.Ew’b£9 §¬:ýúëÛÏ÷ öÑ@ñ1ÑáHTÑ“> 5ÔR “b_ލö! Uã1&4·ç#ÏQ@”œô;3=c  l=½=b @YŽÙ¾*¡2–ÆÁmCÆŸ|]ùåhn‰ÉK+££â…^/¾ô’8ßÙHÈ®¾òzíÚ5â’K/¦w9g¼e£ eV…& ZjÖ÷òs ¶—b; ýBUM˜”“XwwJh Ì ¸’¯Á±Ä~ýXóGäÔ2‰®¡Ôw$Übýr’Ó®@p9™úTXRA…ÔŒSN&Ôf"=Ù2rer£ú*Lz&ž 2Vµ³ÔøØ88ˆ¡Ø!Š °#¤kTÊ%‡û3AàT²ˆCIÛ œÄ+-¹ˆ¦ êÊñÇÅÎ;ÅóÏýˆôü1slY•|û«š˜Á¹éª«®kÖ¬±Æ (B׿àEcøÕïE™EØ­y SÄAl…ÞÞbÍHhtx(÷U½tk†¨úh~«¾®z¼€9!ý}½×ó‹‹Ý%ùZ°ž¨ä&ä©3ÚÑŸ‰¥ŠH,–õ„¥ D"%^’ÈHu¾è<¤of"»g$rœåÅDžÑzçŒ&Ì íÊ‹ {!‚кL¥ÑüLÕ(c)LÞÜŸTðÉHsž ºêyØSœ³(U] ZÑ¿V–ÓʲŠÅ_¤œVyêÓ–ÚhggE¡ù P^—Œ+Á¶è“rþ[¹±Äš†vÓ‹+˜³wu¤µ¢€úE!†š–Aâ5p‘ ÃÒ°Ô1ÉOZr?B~½¾LN&Uµ W{”Þx„¡Ñàž‘ÕY^ÓfC¡•+¾Š|$¼á–ÿ. •UPoaYíóTmÞ,ñx)?­ã” ÎrÜF[Åãò´S«on*ÒÒF 6jùàÀàÕñÐ|¸ŽjÝšãÿ)ѳRîú~_ÕöÈçŸ^<ûÌ3b׎âÈÑ#Š$jßù¨ªÂ*T½&W@k$‡'cLsí¥f7Î2x æÌ§‰DÖ&«l‘Ü_No¾´WaãeQÏÀý3kü"¶SÅañy°-JªŽCà>ýÅŸýùh¾¶=,¸wV°ØßÙÕ™›WIjÀ謸;- Hg²ßôä L—Ôi#íZiÇ0ebľ¥,ªPiyžiõV˜7õHµÊ²rÍY9€örpoÎÚ9|Yªqhpø¼D³: 6.Ç‘æ•3@¤-=eö›¿­X±üb¡VÌ#­€vI¼±MŽ%MíÃ8ì¨$‚c.# ‰ÎŽNÑ;ЯŽRjRLI‚À;嬅‡lHTæoS‘’…E†Ä „ð¦"n©éª=žµËÊîOA[gÚTì½ä"ÿmÌͲzD€6±•c?–\uèø@3̸̶¬´S ™F"p>;ÀÞèä½Ôð @T\¸vtuÐæˆ# D¿ü±BJ?¾¤Ër¡Æ¢9a¹Êþ¤,6SV¡rš,«ÕáØÞˆcn‚ÆÒÜSç?Té¬@¡<#«ú „ Ž h˜éˆ£ñQa?~Tî/¼÷½=ñûL ËŒ#?+uKñ¾[°å7P«sš Q2äû½û&VÉÕtª"P¥ª¯ÎrZYV¡rrʲÇ?;“…àsЦ–“0ˆ$B3‡zɯd–O…ñá°‰LE;Ïm:-¸­À¢E‹VÉN–Ñèìî@s'9JI‹ü ze>”¶ýªpaXú£PX¥²Ú9hyO½áƒH¨ë'ƒg½›72¯kœà”ÄóȧÍ/äÊNH¯Â×ùˆLGÅ,Ù®vЧ%ÐN§Ÿå²ÃÊ€;e nÜŠU°•8=_ŒÕ$üÖ!*é ¢¶ÿªXx&"Žh3R42i~ŒC½RSaBÛî÷ÒHYs8æFбž‡½JE@J†L7²€–ðTUÖêzñ¯´ƒ*508x5‡:Žû*×Ñ= vHeöäû,2•¬ æoAš’f52—œÖYNÓ¤¹•õ(«nq¼•"y«Êi¨¬ø—BÈìüeåòP·nSB¯VÊéíß–†ùM¡ÁÔßj«ñ-ƒvÓ…¡ÁË“ƒ¦3è *Ôàƒüû5EÄ(`§‚Fº/ÀûzŠRå/±eåýkQ}…9¸õ—•£/ˆgê½ä—U“v8Ïø¡æª„ÖïÙï ¤µ’ çx¥¤‚(•Vk¼`©À"ºÿòí¾$¯Å@[%€r¹¼˜'$–ðª–c Ý×ß/V®\‰ CÔYx÷ÍÌVÉHBç¨%ÂPÙ„ßÕŠLšÔóÍ”U œŒ”FÔŒ”È—žl®cK!d[ãØRt Z:žsЦV޹‰¨ªv©=*àŠ:ü3¤pvtD‡„‹´Û6%E<º-g )%Û§¦¶Újèïë[Æ×|´7ˆøvXÎ[¶TeÁ¦‡PŸ S.—L”Ÿ²ªÒ>é‡ú©{+ËòC!Q»X“ •Vh{ŽÇÀEÊBeeàj¡rúËi‡_UUMŒ¨ÏGÜ1qPÅ–Ô™B=[¥ÇxÍ@- ªÀ ª½¥réRÑBä¢}@ô®Ÿù™ŽÞ¾^2ýW:;„:ûW…Ššfu”ÛY¢ŒÄÁ§ÐßôS…ý.ë]it˜BYí`£]lvDÛNˆBeÙ-WtråH£µ>ed¹°¸cKcÐ@YÅÊqÊâ,‡ªH…í²{$¤ Û^E1+KÚ½]³å“®Â’¶èHL¼¥ÛÄYÐÐrg vIÑ‚ †*=<ñ#–ôHÔ]xÕZ¾ Aa»ü ÀŸaꚃZšã±2d¿VXYü2u†±JÂßKaì½Ørg¢íž‹U‚3i‘Ï*çL®\Øí2~.úù2ǵЧqK }EÏ#Ê_V+YØ@ÛØuJ#HÎ3]öP]82²N6žŽ9åÓNíh· Ñ<( ¶IB ½ëò¹ýÙ‰kœŒ£c„+Ùljr =aÌjƒ~îj„¡ðÖoyÈÒhw+Ç h9mAhÑ"dnåR¤çy~ïeÍÙñùšYEÏÝrG'yÅšU1êU¼ÝìäÔ©§AZMÌ|‘ÅY4Húü>JŸN€¾ó ξC(©^íÄ,0pe‹(TTd`ˆRMaM˜ëR©öÄ*:¹L#e„–DÂí1Ô÷µ´uˆEB×[1m ,D‹&r+ËÊœ ÎsP·HžU•²3kS‘²˜Ñ·ç«­˜Ë0ä!õôöŠ®Î.#Ñ¢7PŒgtVâüÚm·‚›ôgËð¶m`ÞÐÐ-æ4àØ›)> [ŽL¶&¤¨ÁXÀ‹= ‰su6^|]âql䶸Naw/·lt·R¬šè¨¼F¯Ó!¸˜ p›YBë³0¢þ†Vy¶Í™¨@9Eʲ%À²¥–5Cª`Ëù f†¹Œ9]²üä0k3¢­Jg(T-ýŸånʱB¨³‘ —Ö…¶­ ^`*±)·I¶œCÑ0¨}ƒƒD °dÑ©ÃU˜êjµ h„º[ÏØÔÝÞcÌàÐù,plâÔ¡ÛS±E@ëØ.Ã-rÔ„¦!‡Ã5ŒÌºúu³ÝjÂ`$Zë}23èТ~·d^@ür%y‰~vš|\€ôã2a7+ÔáéiužÏ?–Cxü~xçWÚ´š˜Mæ#Æ(‚p&&2.,2Ú í„z  @I‹ãÃΞ´E'„‹B$'Ê'%‘y B’ãL¾Á¡yjß‚lKYK&œøä"V˜c$¸…mÈä6xl­”²C»,äÙ™j·ÉíGÍr!þÛd_ϯrÄ[×ñN±‰M"~ìá¼ZOž8!FGGÅÁýđãâèÑcb|bœ~#=ž¼À-èáÓ´1ÄAQ£ õ@« •wÎ9ç„ÃKŒS Q~¦f±^üꫯ’«o½ I–X çHB‰ƒ1s¸(ªvi2ž(G™Ã6ÑÉqÚõK‚EmAÂá%=:à©’PXJéLJº%‹(¸+ R°×˜â–g:Ù©(?ëX³,µK•QÓÑâÝ!¾"–ö*¢«§× }FÎï#é÷ïÝG¡ì8(öïß/öîÙ-Èë#ÇŽê ¶Í;ò ,;’sŠ p€®ñ@jzkp«€MX£Ë.½lH™þ餡9l¨±„¨´2mß¶MÜùýï‹ýçwßùCñÔSO‰Ý»v‰ñññšBìêÕ¢¸2Êu(ä²A0'µ&°u”35œ¨½N_K2uvTÄÀÔŽÃ¥)’«žÜ´Dv"˜Cg§V@´êBÒBEK ìóPÒãÂ'cy¨ÌzCó Œ£ôVð£ðx—‘R"z:‹{j°qa qRÉgI¥Í)e©]V£å •Ì<¬q6"–C~úùr|&%Íþ^ä“Fn þ^‰ø»vﯾöºØ½{—=2*Æ&'ÅLuF©OÛÌÜ-*š¶z·oh P\ 'y¯ÐÍžT4¹+ê˜o íþ“ù&ä€ìݳ—âþ=pÿâ¾ûî/oyY?v,§õê°F"ˆÝ±-@9 r•-qÛ܈©ºæÆÝ’ëΛ¯¶0‡*äØž½{ŶmÛ)íÛ·_LLLd6‹#! IWo_l#0+³ÂÁ* &y@V´Í×ÖÑç4éK.(ïÊ@ýíð%'e¨¶‹k¦zaÎŒm)$·Ê)in–UŽ{š®›ˆë¼ÂzSÉNa^Š>‹0$ÎHt$(ûíhUŒü.àÐqäȱgÏñšD|D¶ž˜œ ývFéúeÝ.š%õîCM¸ˆÆ€’85 0ˆMš‚v¬Ì.>Gb(WʱHÆž~ôÙÅ"3ôè’:&öôé)qâøq±uë+âÁ>òˆ8$©(AUMÆüe‹¼²”â¥Â²_°uïØ@M ´i``Pêq}‚=yrL<ûì³âч¡€¤/oÙB/uçÎÝbÇŽâhê4Øm îÓÆ!î·mÐLµtPî(+b¡“26•Í ³Heí\BŸ¸_.G {,2¬l§rìÉV±®ÏJœ@ìíïìâmžñ%ËpŠç)ù «PY‰ ‘¹ä&>]Ú—¸¿Ä`J꜔Ù{è8¦rß·oŸxùå—¥¸¿OLjÄW‘€B:ضGª °#á$¥##bhÞîw¬*B¨ ¾Z¸[­«jÉj@«Ç$yENæ•vbÊå#“{»=¢»§ËL|:æK¯ÐaRŒzD^xAÌœžÓ§“võBK(Ç“›×çs&q}ÙŽA‰ ´O[–U•õîØ±Cl~üQñº¤àx‰ããcR¤Û+žxâqñÈÊWä Þ½k” vˆýˆñ©€åw[m,!ð$ì–„YõàþUTœÿ’¥þØH”› !aœøô¦ÐÎkÙ6*¶„U†ù-ñ9a~d¤”—J:eünˆ` ’18®'^]â¹jÉÓ5‚Ž:D+l[»¤* )6u€šŸCCCt^âÈÂ…bžDzÌ}0„k%‹‘ß6HV’ql|kÊÐ ’ÜÈb!”ˆ-ÑŒ˜äᇎwwuÃ1S8ø©Or`TxÓö €‚nݺU<úØ£bL"¢+‚£,ÍØ•¸bßÒ\9TŽˆ3ØÐã%%­=rT<+U‘d:*%‘ééÓd¬ÄËÄõŒlËqyË‹/ˆû6Ý#6o~\¼ºõU";wí–Äà ©4E ¢·BÃÒk¤„ž^#¥Mx¹“ˆ?)iŸ„<¤®•tyey}‰m-œ·ÃN¶ÃK œt»KVûË•˜x¹)t’M¸JM&µŒ—´§¸),ñ¾E€0ÎññuIK?ô}Hƒ[^yEŠþGÅäÔiRñ,ÆkxxX,_º”V’ðžYìÒj!s~æþg8^Ò?~ë[+›GU­6’82þüÕ¶ÓD`éP¤G…ji `²ábUßà9…å6 D¹¤:_™öí=ú¨Ô³&hÕt$TVy6Äõ÷õ‹Þþ~r6‚ÿÅ5ôrYGwW·ÌÓCV~´%/ ÈýÚk¯ŠgžyšyrjŠzí9çˆ%òâeÍV#1-¥“)™g§”žxìqñùñÈCိ`ÇÎR< ‰Å)ubqA€¸Ü¡ûÒk¬6ôéï4y´Ki§ý©Õ‰²þ,œdÿSÉRUºÜzôu7ñnÑ ƒ¬åÑÍÏrùˆø¤ÛΩG'¼Ë©—Þa:õé÷NŠùÑTÎIò÷y”æQ]ðEA[A¬mÇ6–ö ï¿ôÒ:¹ú´œGàú xgËW®ò¬^-ñq2ÄÒ¨a–‹³i; >àÎ`|¤ˆ;È7ÝÁ²—8˜3óš>yù`Â`©O& þ‚uÚ,¨2܈J=ëùçŸ#õÀ& lPÁ3Ä]å`óÒ!‰îžnB^f9|ð Ôñ$^}í5#º‚C\»è¢‹ÄIÁW®X!.¿â qá…ˆ…Ro£Îê7§gN‹)I víÜ!žyú)qç÷¿'î¹ûnñøã‹­¯¾L’Á6Iccã¢zì›–Ðî[‚P€HÈDK“Qº`sÈIÝ:¥î3²õ¨Ô«í„¸<ž@x^Žulz¬»t>\»*›l)Äýnß')/l‡ýº~8&¹þ1Éñ·lÙ"&'ÆImż€=i©d‹."O>"]´4L1-uØeØF~ߺ‹-Èwp±hÑJ@+G‘tÉÅ÷ÌŸ7o ¶øÆ>ð «³¥‹ã“¤{ò€»èɃɇ#Á¥dÀ”pï¾}Äy'¥¨g‘7Õº¾íô˜œ˜¯HÕâEù%ñ\ç^ò¦7‰ò%–õ OjH—\z©ØpíµâÜõëImÚn`&Ó² ” KðK/¼ î¹ënñOÿwâ¾{î%ûÁ‹/¾(¶Ë¶¿.Ó™çرã÷°Õ`¼Ôr‹Û©û,âÖô úé…±ScäÜóÊ«[ɳ»[¾Pp¤=ˆp7ì[z>³Q·\N¢³v´ºÆH"|Ê(iiŠ´êd Óõë×/ìîîêdïºå‚Kb{ ×y‘"ãÈ¢E¢Ä›!0yá:)?«%åÃN4À¡·¾ú*p`ãHSvºT­êHDU½áªJuOKuâàÁRTß&NÉ—G:¾¤Þ@â¥K–б×LÀ€ LÈØ_u‚`á%¯Y³FœïfÂSy(ôºŠ½ózóÐ7šZq…#üðˆ Ù @‚Qiµ,À=Ø455”‘yl™s׫´‚ÐS‹ìJŒçT@^d"Æ9æ1+ùÏžNUÿØá#óá–f»wïVÑ“oÈÏ€ì 6»©©…Z›[¨¢ª‚Û,4™†ï¸j­ì¬®ªQ_½¶¶*jg— êT¼/8"³Yé*ž nãN«Ê…Y17©ø<EˆÌe4561ÇÑé`+fª8‚SkO1 j”q3}±¬Rè46!×Dì¬e[õ@·“rÑf°»&qf41!(¢(¢yje¯¡LOÆÚ.ð/\jD¾Øƒø* &£{`Ì{çec áä„óÓAs@šËãô|¸`‡4‘«ú‰Ûy©snº¨{ yÅ )ß3?°èáˆbÐ ±B°‰Œ‚8 ƒYMm¸½X¤ŠÉ´ÜÛÝÝ¥DLÌ\ÝtóÍ´lñRZzøRZ¼pš}Zµê·´qÚ¿`¡¢É–­[éD£·…BáùO|ðM\xá*š$ùìg?;®>÷¹ÏÙI‚DÌÅgòsÅŸ>}úl\KŠŽ :¼VÕ-Y²„³Ì^Ã,d.hó?ÿ…/Ðy矯L€1ªÿìç?§Ë.½4ñÒ£‚Œ6 mùˆ¶º&œò«ÑS5Uá\?,üÐðH«×ÓeµæÎ.Ý4_P®,Â1M1Äo¨aÈ{Ìrdb}žÉÆfȈ˜E€eØÜ9shÑâÅtÌŠ£i1·4É (D¸¾{ûúâ šðY.-˜ÜŽ€žˆ;…|”®ô"Z† óñ,ƒæõ=±x‚ª¼¹8q)ˆËªHNÑD–h«˜uÚÇO¶Øv"ÑÊ7Áu›Íjom” Gƒ¶mäwCdKP[W¯l%Äì£,gÁâtúŒéÜžmÊ·D ‹hPØ57·¨Ø %¬RÄ6+ñyšôtªDô›ÆÆðfšÆeÍäo6cÚ4ÅiUêiìÈK•—œƒ¡útMGñà‡ïŒh&¿ßîÝ»”Òù#÷wtìŠcB:¥Õk×ÐI'ŸÏ pyP0£$î¼ó»ßÝDñž¢‰P.]Odt¨hmiñYny7H;*‰öó/¼ 0Ç#5”XP~Ý0O–sí£,+ó‹¿øÒK´‰YÜ·_rIÑô¡êlÆi¡Q­2 Š M˜ÐÁn¡£„‘;æÇµ¥a£6ù«²È—€–M/>öÔŒ9EudJÎÉØÓ7òñ¤“ÛfÈÑѸ CGÛÌfÙrÙáË8N¯ýëéøã§…šŽ>æ:š+Ž~p tî¹çÒßøzÃÂx2ç=‘YYùúñù=ð>¯?î8:qåJZyìJ:æØcéX.óŽ‹.TJÞE8r\ ‘ë1þ|çÍŸ§Àò0£y\wØlt¨8—Ám3‡x`Röˆ°Ìká(†?Íüž-‘ñO«"jD:¢·¨c£$ØCTs”E]èO"FÊÚ~ Îç˜ûmfàÉø!€BYüÐCцõø«¾ â}0q«÷…݉ô Ì8 ðà·á¶Ûn{”Bà @ÔçQÖQGÕ|ñ_ýÕõL”Êó¡Â³<üðÃÊ4£6Ð †:K—,UZSŒú#ê¥ÿƒn¹õVú.¿<ß&BÏ ¦Èõr6-ÏŒlÒÅ)¢e–Z¡MX#wb¾9Á¨“I!j‘%Çת6Ó]™gÆXÿ­eJ9FDDDñc·Sè€-°–䨣ã0ê@§çÎB|ˆÊño|#vê©tÚi§Ñé§ŸNgœx†Ò4#žÅñœsÎQÄ‹´‘r<ò¨£"B<Œ XÚ4! A[ð˜«/#jå[XîÓ’‹‰*¢•¦ñ¬‡P&Çfºáj,¢DÌ&}MŠZ1lrÅlE줶B×#zÈ­¾+×õÌâ?ûܳj`D*}&swŠøëë ìaðk7Þ¨Ö¡ÜzË-Eå ë%éÜGþxË-·ü†’»Š¦Ò8`TžÆ£ô æ”X[ ûx€¾~ÓMtïý÷Ñý?úÝó_÷0ÑßI7|ñ‹ôÎÿõתq~Äé@õ´‡ÈÈ›Ñ6Öö"”¸3èu÷Yã¼Â†l¼`("Ê¢Ö™zï7EA¯Ì³ψý¼2DÊdb߃†-~ÈzÆKŒMWêE¶øF¬°£<ÃŽ&Áè:TêÎ/çÒžö" Ô¨ß)c¿ùþ†wè$Ñj"7 ³„u¡¶óš±LÛôÙàŠ¦Ùt¥Qû ˜l¿¾ÉÛ¾ýmúöíÿN<ø ÝûÃÒ]wn¿ãúø'>AïÿÀTÿÿsÇ¥¸Oæ VŽÕåÊ S fYgs5)k%Ãt•†-ý§>õ)zó›ß¬ˆ lþºõëiíºujdÁ Àã=Æ,à±IÔs°ã‘Û,}ŒFÈDôSb¼²KX¾AäÜ¡7pq<¡ƒxs “3"²Wòµ&k”U2f³ÉxÆÂœL n&ÁÉ”ŠžáXÓQ?»É÷ÓÜ^`Y åâЏ Z‹yŸ¹ò22áÂ#œþùô™Ï|†N9åeE¸yËfE ñÏ;ÿÉkûy×&ˆ‡Â¡p($CDð^h!æUµ5ÕóW¬XñÔ /¼0ÊÁ²‰k²êWVVzPܬ\¹²}Ú´iµÊ¡¢/{ü%½¸ˆâ§\Yùjp±è‡â¡hGÓm¼­¸Åµ‘‘ÑK—/_ž9ï¼ó²,~Oˆ¨&c€úUUU™yóæÕr/Zt~kkëhJMWG‘ ) ¦[§¬ž×Mh^m_úæ”1uc;Ò¬4<ØD–Ú£MµÅ`µá´¢ÆŽp´áJ³#¬Î`a(Q[ ‰çž:íñ&áíÆ‘&žnêMÏ7:­Q{6R±©)4f2£¶Rk²Ó,K¶&mÌÒlþ†g±‚C„LK‹2.R†0ú¨"6GÑùõ½EÖrrŽúia›õŽÞïh¥«¨-%ÖE–IÏAê\[ÿ™é¦÷!Ó Qá@%òLd8W‘X]S[Ü/”s«“éձÙDIJîꪢtñ’”ˆ•U Ç* oO†(Ðtk½{ûú9ÜÅ`‘?âˆ# Ï<óLdx *æÎ[ÏDša`þ’%KVrE}©´L³È4]dä¢AÀôAgN·ˆÁŽ9 £@¡ÊØlÃj¬0ˆ+í`DÓt£Um•oB†î sƒð•û1œ‹Ù©òçWw4!tq{ÅÇ£×ÙÑÑaM×YòÌУ‘]&ÕŒ® >fL™G-ÓÊ2AUb©úIÛšÑW3¦µ´³@¡Ë+Qìsy.ú¬Kë¤N†g)˜–×Zí.¦èê}+à A¥ßUD,ýTú[e"½¢h𪒼pSöi,W‡ÏÉ­[·ìdñû~¦Ã~Ï‘U«VE ƒ”!ØæR<Ÿ ¸>—ÏwåóùQelêy±£ÃÔV±7“|hôðèÄ+N“àûÅi)uP¦K†“'eK~㨊6Ë÷’%ºÒþ2UXÁl{óÛ”<—µ'qŠê¿éçZ?¯ûþŠÜ.l}tNŽs?Ü×):Ç5_;$ÍVPŽåÃÎDàˆéj;ŽÌ,5V g.»ì²qÏLT ¨F …‚‹$~%½\.W€ãÐ/¼ø¢r–ˆeÀ˜ÄB¬ÞÃZrاƒñ ùÄ–Y¦m¾a/ÉFŽsD) Á\`¥k"–Æ¢7e/_²d2Y’ yÉE,fÙÑRTùðff¹F=í<¾ñrÔ¯(Ý6("*~^‰t™¦5ó!†Ò¶h5"+Í¼Æ F’í¯MçaaÅ+5õ§h‰²uM­’Ž–/‡«- Ú¿©•áµhyµuMùEÐ׈âç…×Hßß§ž^W˜Ú×dfb R¶~žä+Ý«ŠÔŽQB×÷9¥(Ç9ÖË`Ç ä ­ +˜ƒl9 ½Ëðò×Ñ6×Íd{™£¬fZÊ2W‘Ù±c‡šŽ×ôY–"pÒÓ€úƒsñ²ûöíëÍÁ¿:(|¤ƒà@®=jÄÊ‘"g¥ÜÈ(aÃ$8_#¢Ú—ÃÓ» %:¿§Öi«É,•â„CŠ]Ü=õéÕw"v˜ëÒCg”\/G}]ý7‰ä]åÕ\»1‚$Žzí}´¦Þ5(j¯¸Lw9Ñj=3®£8mÝßjY“[›rNh2ß>­ÈqßQÂ6Åq ŽcrðaÁįç¹ð<\ºœQ«êÕJAèbš•Xý,y¾·Åjî“,1g3Ë–-óyä‘qÕz"`¶¯‰ßCÀãÿ(Ð  Y n³5JŽ3´é:f(H”ZCmŽJzT…s!x’tǺý €` ñ4Šª…1š1=ÓHýØN7©<Æ–]ÑŠ@õÛ·î1¶©Bš±‚0~¦§€S•©ë“,3^”„Æ„®åÁŸþ´è}°¸+÷¤UžøýïˆÛáÍ\ À£'Ÿ|’:]y.¼0:߯múÇgŸ-ÊsìŠj5£„Ÿ<ð@Qì¢sÒ‰'–Ì…"=ì yGß*øc"dwJí» ç°{–^å =’RŒòûUjs`.wsÜ•3j÷á¾>‘Iöë4 Yx Ù@îS~¶³³³{p``òÑ7±¶>µÃ¬"Úl”VY9~ö+iä¢=%b…R\º‚Iü‘âÎPøùz$¹?Aì‘òNˆß 9%dü˜ýW­ïËú‘xàGÛkû €¡·äÎh»Bî ¼Ï¸ž¨ÍS8ø®‡«µRÜêZ¦U„ÏðÓ8€±¹„ºDw·¬®© ¿{ Í‘GQÖ§S#ñsQŠT"lF® TÅÆpª]ŸëeºËcÚ†A===Z]7€z |”a5 *ÃÇÿÌ+N ¯”HÊ7ØC¥ïÇ[2¶Û+CGÍj ÖvWøšè¡Y ¬¡Aµ?FÚ–ÓiÑ".­v±ÜïÅò}¨F€!iˆù±×ú€ˆKPç5êG†OŒ¢ ˆ®q§î o0™Õà!+[mG~ë„sÒíë Ê,º®6jW@Û×IžŠ¬z';`Ú*›‰9=W„ºÚ˜pÓ€äÄOÔú  íØ¹Ã™&»’g¿Ç‘R¹g«ªˆ]µÙ±¢"Ömi¥v&âæL·m±h+³Cè}¡ñRЋ—Ïq€( V€±ybaÒ"FT èÀ’`ï4¾¢2yZ™•©ÐÞS y7Ô’kùÙóµ­eiÍFg5÷àÍôÄ…–Þß]”g®è—AXª<# qºB<­ÖÛ3Ú]Œ|ä\ z"[±w0€ô¹bù##!]¶1÷í’ ì(,k¾¨}ds‹L˜Äêí™ÕúÇ;Vk{öŒW”%•äqÙRˆÜZ*êHz­»§ëì ~Òe›+d´˜q@¢ô!puØõ¨‚c%×­’ß¡Šcñ»RÇ DÍýe-g±ìÕ Lãj…pµÚD¶¼ z¸ÍF  ‘óÉ \ä ´T˜èb 1ÆràL]]s^5µŒn3gÌœÙ>kÖ±\áJ ÝÀà åsåò;7Œí¼ø|H™+óQ˜{bÿlÈÖbEGâòÄW¯Ž ÚŠ. ƒÒ­K¡gžT”—“Ö¬]£¬íÛ’GšPC`HŽøINAmm•x¿ØòÑ‚ׯ;á;Æ&Õõ 6ÖæƒÚú0‹((´D$äYçÓ­©Îalòû§ž*jžåË e˖ҮݻUë¸ò`Jꬳ΢ÝêIkÖ¬¡.í¶:ÊÃ"¼‹‘+Œ…ÔþŽà2»'ÛáüóÎSn±ñ.ðž‹ÍaípÂñÇSšÀ~ ‚ý‘s|±7V¥qÌy4‰‚yìTÛ(³àB8`*]0t'µÊÅ}C¸K/pƒ)=sÙ«z{{7sììêêêݹsçÐöíÛ#áåÒµ7^ÇÚÄTèk*¸‚K–,™>sæÌŽã?þ´“O:éý,·4Á:ömÃ5ØNwìØÁ ‹ºöt… ÏɪF'Ò£ºîÔšˆÔ³Η›vò2¢Ù!…Tp0Þ)%{·­ß¸¡(o<¢ê©>Cóo®QPœ„¶ä 7»0l´µ^FFyÍê›ûÚE B×=æ3µháû&Ÿ›‰Ý’{¡ÂQqa\^Cs#}ã›7½;ö¥»à¼óéOú“úíÊ?û_øÜèùçŸSïðÓŸýŒÖ®[›Ì3mš4^zñ%õÛ•áë_»Q™^—Êsã¿}ä5u¾zõZzðçÅŒÿöþ•Ö;îÝA™¤ %€À+­˜¥˜àAšÃ¹»{C†ó42£Ñ¡Ð%>\å³@¯vE‚hƒåz½µUµ3€Ë¸¡ãP }~÷îÝ/1ѯY»ví6{Ÿxâ QS]3£©©™zûzi˜ ]íØ¾“ºº:(î@oXAÚö?êÔ‘¡Óg£N›¹úÅ ‘Rá1ß)MúùC9@FS_4ü¾½6Êx1ˆe qDÌœ}8|ÅE„’jâ7Ö× íW‘©D_·M1šizbS u’éF 6J¬bѧ¡©¾qó7‹ÞýòË/g8žú}8ò»ò´ÏœI7üã?ÒþðŒ*÷?º{’£;vÏùÇ/ü#=«ä§?ÿ™òm†$/<æùÙO‹ò Üü›è¥—B Y½f5—õó¢<߸ñëôª’ý ‰F}|-ÇNÇÞØù¡¯“å9ç8qÜÇIý ûFi´Ÿ‰(§6ÆÁºlÕ†Y)ÌŠL›6]mZ‚öÁh3¨ º{z™vnصk×Ë[·n]Çm²íµ×^ëÛ´iÓ( 2 ±±0ƒ`^GGǼ3Ï<ó“,?v`7hk÷) GÀŽí;Ôî°Ø* {áå"0e[Ñ x‰}úB"€.@o&¶î)ÓO© PføÅ/*bEM% oMùE  G`¹é9ŠFæäµŒ éwÎhpóýŒ›°IƒÅ-ØàV@ ¡±‰nºåæ¢w×»ÞEç}=öècê·+vÃýâ 7Г?¡žá‰Ùí³Už§µ¡F÷õIâäzæ™gRó ÜöÿÝBÏéuȳÚ1ÒßróÍôÂsÏ»>çÔO‹§àÐõ˜è±7slÝ™nÚ×¶‹*§÷Ñè`ž*zçRGßéá‹Ììe¶¿{”A€ _Ní"Í)Z¨¨¬R;ZÍœ>“Z¦µÒ´¶iJy[ ,êéíÝÕ·oßW[¶lY÷â‹/îäã¾ 6f8€×¯[¼xñŒöööæž|âI×±ü²¬¥µ…FGr,³ì£^fÿ° V²lÙ¯@- 6ØzåLP[t% Š-߸V¢® ýò—)@mc@,Ä`ˆ‘ößO°ì‘¡/S|bMŽèQE >cöÀ‹Å‚ˆ[0Fú"NB8¤AíùjÉ7o½¥èݯ|ï•tÎÙgÓoóõÛ•çâ‹/¦Ë/{ýnU¸ÍØ–ë+_ü"=öØãê78ìaÉ—¾È@òÄ“ê7ö†´[È}‰Aâ)'ðµýWzíµÕ®ÏYv(ke)ºgÖ )DÑ¿h´1GOTÝOï{s]”9Ž“Nå¬U´ƒ~Gßézœ6üᯨiÛ4Ê÷(·› ‡¹€}Ã42˜cCÈú˜e‘:Úq #?Îûúö¾ÖÕÛ{ ÓÒËÌú¯[³fÍ.ýL ¬wà‘¾véÒ¥Óg̘qØÂE wìÊ÷¶´´‡­«@Ä顼aÔb`{»v)Î §„Š¢*–Ui6MHÈ“•ßzjJFSg]S€¡Ü€}Û6nÞœ,S^>"¬˜£Ö÷ %xCƒ#Y>´ä‹¦¥ ßV=fà%Ÿ-Ïs´§qM,4áºëæ[o-z÷~ðƒ ~ùó_¨ß.¸ê}WѹÌ%üúW¿TïäìÙ÷•/™Výæ·ê÷￟¶íܞȃ=üþ‰ó<òÛH~öÐÏ‹@@òwþ(­Y· $ ”ü‡O~жlÝ’öIÃo8É!,„û@…²ÿ`ù¹«¶ŒÒ |‹~ü¡,­¤«8ùÃ|aQtË}‰¾OŸ¡ùò":­é ÝÉÀ\@®§üHN© jaƒQß@3g…€EPø†³fÎR›Ãöô?Í\À,ÿƒØðüóÏwòà:À@`r彯$' Â,@€CÇ|Ç­\yƒÁ뱊 f¢½Jþß«|˜\+À˜’©+SÓž ´Y­!Â1ê:¾wÔÀñë_ÿšÖmØP|’[”%‰«4G :×}™Œˆñ¨Eb4÷“œCt­$Ç‘9‡bÉ×^{-{Î9ôàO~¢~»ò\}õÕ gÓÏ~úSUÖ7o)‰ ÐWÿùŸé¡_h qäá.ÃÄýáH¾‡îÅÉÕ J5G°óH®ùЇhó¦$hï—Y_³ýì|N¸l}uÅQ4“®câ¸Êºim¥kéŒ=OG¿ò6jj¥Ñ®Qf± ?È@0:U[ºúF€™í ššÕÌ8¾Þ>úeWw÷},¼àvãŸÝƒÀÈ[»páÂ6Fé¹üÁuÔQ—Ìno?§¦¦Ö‡‘.Œú èÚµ›úú!Œªýëeýº(­’ëê“ʾPé¥ ;(V=ò­^[,g†PCö"½@`x±qPb·FißÏD@ç3M‹eª/M”È&fJü7¢Å…ˆSɨno¹í¶¢wüèG?ª8€ûî½Wývå¹þúëUžñ¨Ž2] qÁЇ˜pK ÷ºúPS„·ç;Ê„$€uü=ð>.\ÃyÖ;@{ʃ” £u£tËn£OÞÉà™9œYÿ¿¥jú_|1öqP ÿ¤íôºò¹Wè¹ÿ;ƒ.š÷éQz@~$œ‡VƒÁ@LƒÃPìjÝ»·Þ…~°cçŽ_ìܹÀfn·ž®®.Dí@iÿëd& ‚åýZFèÖY³fÍá°ðu¯{ÝÛæÏ›wA% .ðäŠM†‡Ã™€ º˜ØÃ€Ð«~`ÿz¥I'*r†éLðeV f½M¢6ÍV&Ãî½üÊ+ôØ“O·Y¡=c*/ž†ó”¦ßPêùIÞ¼–ÑJ@Sé$ºìÒK£i@Wì â^½:þýŽ;Š@B skWONPVðõ,€: »éî?ý€Þú]Ÿ¾Ô\`¸€¥‚3Ž"° 9ZC½ôm¦ûéòßíü·Fºlù¥ŠøGöŽÐè ³ÿ¹‚úöµÚrû¬YÊóf°.£ªªR‰ÒUU·lݺõ ޝñ€º…E€î¡¡¡áƒŠ¨¬¬¬ž3gN³`³Á,_¾üÂ%‹_Â#ÀZKø0g.`íÚ¹“ötí¡ÎÎ=J,P¾ÐX ‰Û´H¼Î|sZÌ mäÓFu· [y#Ìì*n g"_†ž—dÙÉ‹Flsþ¾hjФœh3ñ"‹ àÈxchù#®Âõu](¼/«uÌÜúíbâàÞÿúúíÊÀݾUL¸’¹ííô /ªß®<(gNûlt\tÑEtö™gÒ+/¿¬~»8‰%K–ÐE^¨öÏÛïA”€€€\'Ýóä½Tõ¢/ÿ?Dgpr ÍàËsTæ&ý´›îc&ýË75=ÙH—ývÝ›£‘>ˆ9,ðQßwê< *¶sÿõÊ€§€Å߯0á¿°mÛ¶5,myõÕW{3™Ìð‹/¾˜Óµ; ³ÀÐÌ@;8Fë7qøáïa¶Hsœýýûh7Ëþ]]´k×Nêeaö‡9¥ÈÓF*¦òÏ#kÜe«Y^rŒô“Uõ¬ß°ž~ëð±&3æús$'ϳXöb£Oä}t^{:m`Ú¤‚ïi¿ÚÚÏ2"_›M3ðÕÛíß.zÇ믻ŽÎ:ó,úÑïW¿]y>ÌrûÙœç‡÷Þ§ž$³çÒË/‡ò}Ix1$B ‰óÜ|[1 œ¾îž¢ô) ^|T›j"œE€lŽþýáÛ)_S 9ßl¤‹ªἓã÷Ýóx þÝ ¶òTZܲ‰˜Fús,ÿç”.Ú~µO8€fèÑ0ÂPhïÞ¾AþxÿÂÄÿ"ƒÀÚM›6m{ê©§` 8B¡¹‘öÅz`f” 8€™3g61bÍdQ€`ñ‰+9æÊB¡Ðùìÿ(WÎA;:Yؽss{i„Ù¸Ðåû–‡_ÑPL0ø" Pf’²äª'‹¾n‹hTv]öØ–~6‘‹> hÎ^ƒGĮۢEÂ((æ$¢çùá: åkÔKy&—žãû|ëŽÛ‹Þ¬4àÁBÙý[·ç¹öšküäÇaž4 é˜=‡^}-´às€ä°ö9ôò«¯¤æìãþ"! Hz»ŠÍŒÇÆã\V@4ƾþYzâÙ'Õt ½¿‚æžQOm•#,ûû´s°’v=ÀàtKžVq4·`%yCD#z 0NbåÄ2hþÛgµ«ÍW|%ª5Ð`ÿ íìß•Ëå¾Áðlø¸ó¹çžÛËy(2>©ªªªaV¥±­­múbÌ ,<ñ„>ÆÙ $ñÃî6Ìp-Õɺ€nˆ€\..ߜ㗎nM šnÀM ¹ ‡”(åe5ÕÁPZÚ~¹N™X·Ql=˜ÔÞ‡3¢5YýŒ“¦ÁEÖZ|H1&ò=ÓÆ€ ¡‘¾ý;ŠÞíª÷½Î:ãÌP{ï¹àƒï?ü†7Ъ‡V\Š ®þÐÕtØÜ¹´fu¨àsåtÌí Õ¯¾–š@2°¯?ú$ý{ûR¿”Lêà‹îIχz£ôØóOÐË[È€U˜Y@0.UDÿüÙ¯Ð`×mÞ°™òý£ªÿ çyti? ,:@߯t-Lé‡GFÖ ‹åÿ8®g`×úõëû P@M%¨YÅ0T3ñ72ÌÀ$øä“Oþ0¿Ô,L3¡3ŽäG•l†}jMŸ «õ½Gu| •êbè£;/B4×nÀ„ƒf9ë×­£G}´ä­YYÉÕÉeh¹€qŸ©Dô¼ÈSp´?€9S`ݶrÌaÍ6(«I?ÖC3èX.xÏ»ßMg2<¬ ¾íà®|ï{iþ¼ùªÐj.N@2ÿ°yJ{O)y$°wß¶ekjp$#ƒ±Ò/ H†‡_iÿ„„òYs9¥µ[ÖS÷@ ±ä?0:HµÕ5tÂQ'Ðÿ¾öÓôðª_ÑÓO=­f¾FGÕrúœr žWåaìg ü›Å"Ü©¡ðææ&¦—=LØÞŸúûïb‚l|íµ×vmß¾½Ÿ9ˆ:HP5mÚ4p3,X°Àq+W¾ _Ò¤0ß—U°k7q¥eQC hC™ È{I°uå€3[Fo ” Ö­§G+ ÊÃQ¤˜³Fyߣ"ƒ ‡æ%?É!ø~lTdXäX|dÏï›ö±ÍA±MØML»Ù኿þk:óÌ3iÕï~§~ß~G1HüÍ»þ†.\@˜SBGø¶#€dáΣ§ì\y$A>væÊ Á†3ž^¹á€K.¹„ZšR¾Òԇȷ¡Õ8f¶F9VTU*;þ,ç / ƒ`ó?: éMrà;£µµED8Á·ª¯«Wâ3Äo»{{~IJ?tÌlìd èg@8`"@´ €Ù–i0ž;wîÂcŽ^ñÎ{dSc“ò;ŒO#1lÛ±=Z÷aâ9È4N Rº¦5P˜[0M(xŰacy ¡¦¢ª¤¾¾=Z'µÿ±TËébX$¤•ãENFí)ÆäìClRŒgî¸ó΢wyÇe—±p=þD8 ê‰w^~9-Y¼˜6kkÉ4 ÁÞÆM›RóüÍ»Þ;Ž.øÚ¿}žýÓ3Ño—Hò…ÏžA»Ø‹‹Ž<òÈ‹ëkëN©ª¬ôjêjCY+™ººiwçn€J!844¨4 `‰sþÁ›vÒùÉÍ&ià±üWÿfÂå6Ö5Äí‘-ídK  ;£=&Ùöÿ‘í€J(á8áOÀ“‰^1 xP6ÑwîúnQÝ/zó›éÌSO§gŸ]M»@ââ·¼…–¾\‰siy®¿ö:åîk›Îã¬áÿÓÿývåùû‚vïÜývé->õÉOÒŽmÛ‹Ò§4]e2}N‰£(`5u5Êú¶}µD ´óì¹so\¿~ýsfý·®^½º‡¹–-H”€ À¨Uͬd}CCCÛ’%KæÍž={áGñ6~‰SX¶Ë`O{8›ƒeÁ»´°{ )•h(8šòK® $sÄ´8ƒ‰†Rò®»¿?ár%TG:d€`²ï¾1êG >m$k\~lÂ-É04Š~æŠAáB`§qç÷î*ªóyçœKgœ~:½ôB8ï‰ /¸€Žd€× Üðù/(AâíÇ•çË_ü½¬×¤åùè‡?¢–#Àæ‡?ùqQžë¯½VÙ–L&Œkp2×Þà°1h!\ƒ‘`ÙÖ6Ú¦µÑŒ3 ®õàâ3·óƒÂM bXã>0 wݺuÐ|Fë¤ü²Þa*€c%³ûõÌ´2ÖÞÞ¾èð¥ËΚ9sæEŒr3áüÚÛÛ£Ü9éÚÓÍœÁ`ù‚!çSÌâ+]@Lð$òq´&ß–çN4”ïÝs÷„Ëu…µß|Á¾» Ù\5(³¾5²{‘ÑTlúdЏ€Ä2aãwC}=}÷ûß+ªçYLü§v:­~õUT.8笳é¨#TfÝ®<ŸþÔÿK×oŒ~»ò|î3Ÿ¡µÆš‹ï|·8Ïu×\÷úõhÕ£ÅÓ³ŸÿÌgåØa*§ÆvÀF+9£ÑŠXLÖ)O@3ÂeÀm­jŠZáJ¯§§{wK[Û¿ó;>ËbÀj–ûw`@gg§ö5Î p€ÙÿŒ3š:˜ð-Y¼ø”¹sæ\:48X§#ÌîÀÿVbÇà]»v©QöÜЂF+í¥À¢ LKSS\“û¸¦ÜæQ²œ©€ø9¤œeb÷˜z-ŠfüxQk6À5Í™¬¿ýÛ9‘&æ\pÊßH§rmÜjï]\Âé§žJ+ŽzRì¦åùÄG?FÛ·Å›¬ºò|êïÿže÷0Ïö];è—¿úeQžs9;·‡ž€¡œ]åPÎþ󗾬¶¤?Pa²\åô ¶…¾LÄßÒÒªlh CÛÛ·—¹åÁu5õõwñÈÿúàv0ÐÇ è¢òËz‡©Ðò­e ¥££cάööK-:~á‚Wìíí­‡k08…90¬±6ò€(QÑî:Ï $Ò gÎOQæR¹R||ÿž{ÆÑ:e÷yzõ•׊Ò÷WðÖ§åØŒäFB#¹< À} šêëj™ýŸN­ÓZ`}Ä šL!ÏV×TßË"Àó×±°‹¹€¾Ý»w‹ðAdAPmSSSË,Ó§OŸßqØaó—-^|}ß¾¶z”ãCæ0÷©,¹Sôõ)Ë&5%î‰î6‹oŒúqšh¿UÍônjŒõ^)éwÿàòâÑó¿õ¹j }ŽBòÚG߸GÀŒŒ<8ùÍÚ©J•ÞšbˆKhË"¿‘ƒ’xÎ?±-xÂ!xžùÞÝÅ\ÎqÇK§žrjäÿ®ïƒÄ•ï~b[áí)-Ïû¯|ŸZ.Á•çŠËß) ™ _ùâ—è½÷ݺ”<Ÿý‡OÓZ‡§àý<ÇÔqÉü`À¦òÿ(l`A^ Àþ«Ý”2Y5…@Ow/l ãß÷³ð³þP0¶îé3Ö ZïÀ---ð 4¿½½}Þò×_9Øßxuu WˆÙÿþ}jÞÆýýÑ"¡|!§g= §ú3ze`<Ý' 3Q&f D?P\Ùòß+%ý'ÊÙ…¼¬§1%téE‚gŽ/€$`Ý+GýTÏU.s ^7h‚ææå3œ‚ÚôCëF`,‚ëЩ`šIöoõbQÉà´ÐéÖ®_¯t4f8bé2å’ +7žs°ÖgŸ~ººß1-ÏygŸÍÀ”ËÁÊ[.¼¶h[xZc­ó/ÊÃå¬qÌ÷_tÁ´uKio@SlÀsY“¥„BÀÄ?šWJ>åçŸÂY€Zåþ»Rmì—zu5uÊM8tg —,¾£»»ç€6³еqãF°N`ÿˆ_Ôªšššz¬hnnžÃŒÀ¼¹sçž0súŒK¸ƒÔa4ƒ¬VÖXH¼8 cN^);€”4GeËz§RêÇW­J¹>³—ÂM+(ÊPDðNðˆªèEÇ$7ãëÿÒÅåHIb–Š_Øq¦ZíkèE;ÍÊ;(NÂw0MÛÔÐH¦sU6oÉšû:l#¥§WðPVA9®¨Š7í y›@Í ÀlÜ`´µ·. f°=Ý¡\KsKKäñIú;¾“ÀÞ0aŽÜÂQÈ}uwu+s¿›à'«`ö_1ŒY2³ƒñÚëZìóÌ%ÔÖÕ¿ÖÜÚ|?ÓЫLø¯0áo…+p¢<à€«¸£ñ÷mljmmmç8_bîôiÓVNom=—_°ì>¦†øƒ…«1"nÁŒŽ²Â1kvp‘ýãQ,¼FJòq¾‰~¿Bñ…ø/ú£¯¤¦ïÓÜL>„¥{g6mùh¶g×$:82R|›Ýç{šxšä(vµnß Óñ¬§°ÍW4›·Xø¸ÀÕ˜µ²mðbå0/¸¿WÁºÏ¨·+{Jz!åò·àJïLóŒQÏ¢keÖ"/¸4Lçµþ #]u­ZÜØÔ  `Ð:­mÓ¬öö‡z{{×öôô¬eXÇ\äx’U€ ¢™ø`Ù¥ Kf1‚uðH4;›ÍvLŸ6ý„l&3ßÞ½Y–=8âÑ¡a[1Ø×ˆ• +Bø“ œ„gœKãGç¾Ê ¾A„&Dà¥\"5 ^¥ó}BøÑQÒøX0Ê ä¾è:IŠ Ù¬gâyfºU¯°˜ïâ[Sñ片wߨ1Ũƒ§Vž9î·‰¿M±ókTyLâ6#ƒ€m7Ó‚8Aˆ80 Ç|¸\ØÓìé¼>žkÜ'טeÀ’È"5Ê ä=m01Ë2Ÿ¡‰<Ñ>HÐer§A ¶ÏÕq„‰=Ç"óÇAýûùØÇƒh/·o7ÓN7sÐÝ {wïÞÝ/À?bÌb8¾e©0€J¦âýQáÉ ¶¢¢¢Ÿóz>ÂGJ5P¼«Z•¾_öPÅsb‘@©ôìyL ’'šA°®E#7AMÊœC‚ÔižI zMŒ?§ £ Ϥˆ` À2i]ù£˜cˆòHç·ÞÝÏxOÏÈ+ÏõL"6ëc}ÏÀNã{«`‚‚9Ê:®V?"4 ±yB" ä]dÔW7S>T +@2Œc‚@S2 Ìkª~–YïÀ›Tä÷ÚõòQN>¯4°9þãtÀ0·Ã(÷-Á÷ °ÿý|ÜÇyzqdìéééÇ[þ‰#…ƒ*ÈÑœ”=RAÜuú¼ÆHÐÕ"]‚¦A¦®x´÷4Ñ(âÊd”E…œ«ßrMˆXîË„û¸ÛÄéebÇž.„裯”èç(ÂÇ>ìžpF{Òe{º.r- p‘g©d)Ã.Ï(ÏhvOêµ·h  ‘ûg4ëCâÝå¼.M-ºGSSV˜„&ׄÀ$sD%FY®‘Ù¼×xf šµçù¼<&§­Iõo³^ꇊ@Û Ê¨jùP+/Ï+è{ȸ.åü<<;É'¿,ä¨óò ¾ŸOó¼P@q€óùs#|äo1À²ÿs#ÌŒ Œä”Á„âoB)wq ØêÙ žu.ò X(* ¯@iyKXŽôéc/©íU„ÕÉîŒ]Œ~{øÅ; ÷ òËãØY[[»×8⼓e¦= {X^BìdÖIãȱ G–§»X¶ênnnî⸇ϻZ[[»9ʱimmmÝÓ¦MëA:ΧOŸÞcFNïåc/çÙ;cÆŒ}|ìã¸Óöñï>>F¿qŽ<ÆQÅ™3gösàsqŸ:×Gó¼ŸËÂù Ç~+_¿ŽFD¹RîÑӆtƒÆ¹Ê'¿q¯DNKü–(eàœë¥Îõq@®Éuëw¿qÕK—;€wÔéx_uÔ÷¨ßÒ&ȇ¶ÓçûÌ6Õmo²—óàØ+ß ×ðÝðGþ¶8öðµ^ŽsõQ_ïFàØƒhô9W¿¥ßàÈ}«}M÷3Õçð[÷E¹Ïv¢¯¢Ÿr¿îFŸæ¾ÝËÜr{¸ÿ÷0ìe€ ŒŒŒ€#àÃÈ0—™ã{ yƒöŠæ Ê¥ë©Ìà;¢°÷¢à³}QE„ýÕçê™üÒjÄcÂ÷¸‘<ì—Î æ1JBã¨FHŒªÉ^šÍzú^_ÒQ.îGÄs8MI¸®jäÖ£¹p¾”¯ƒ#ñÌkf:Åz ÒåfÌty†ü6ƒNôµÞ!d‰|“¡ðšBiGS´ ´lìK%"€i”P¬–øöžÝåÅü½ñM10Z×ȸ&ŠÒ@FweåÜâdDt¹"§ e7FôÀ(GÎ2"K¹…0`TޏÝŠ?£££ ô 2²cTÊŹ\“sØýãœGñ•kó¸†ã¼yóp-¯Ó ³gÏε··çù˜ÇqÖ¬YD¤ñs̈ô€G uΣDžë–Ç‘Óq,˜‘G}ðH *òÈP`4ÇQ¥¿£t>"ªM"°¤k÷ù<àc ÓqôpDØ×‡;Ëx:¿gþXê<žœKãZ`œG  ¿r ¿]ùŒ²߬ézD¿õóp®ÞA¿K «÷Dþ]Ð÷£žˆ:­€¨Û®À÷«öÄo´-®á(ß‘Û?o$ßG;ùÆDþîê7úÎÑ/ù˜C"úúô\ãßè_ª¿â7÷©Q¾o”ÓF‘gΜ9(c„Ÿ9ÊõF™\Ö×o”ßQõ{ðPNañâÅ þ\…sÏ=—Ž>úhz´„ÃÚrézª -R›@ 1gFF74JžGí7D5ô²eËᢑ–,Y’_°`A¿¹Õ9òá7º"p76ˆY:@cꨎ}TiBÀºszwVÕÁt ,b ¤ÃêÎur!8³Ã3ÑxLŠƒÁ9GŸ?²¯Ï=¸„—cÄ ®#]_—è3@úàŒt„g&¤+Âo_Òp]ç•ü¾]i¥bQùŽëž‘î:å‘÷wfüGf»èw ôï@§’m‹sµÜšH·uA˜ðà„XÐ €ÐäqðÐßéyãšôƒ<ú‹œç‘†~„ߺßäuÿ ¨ÁýçÈ«€Qà><Š‘@páÁ.¿téRU_pxï~÷»ƒcŽ9&xþùçlšêŠû·[a¬s Ìó¦ØŽ?þøuâ‰' îãŽ;.`‚Ð üÃ?œ˜àñ±#,,¦ÕòXÀÂ÷z T'|«aåFöŒ3A¨z”õ  4Ñúƈgv>ß<7‰Îè´QG‘ƒ£o¤ù: ¢ŒÏ1ƒ#‹ fÌ@4Ñçžþó¬•GÒ}]–”'âPÆ*WÊóŒ2ì43}¬è÷·QOߨ§·‘"œ£^žqÍŽ"ª£´/Ç@=ã\ù[à,ôw pÔß”4‘&<"îE@TKáh¸¯y Žà<„ƒ€‹åþ«ú;À\'~t '(.rùòåènÔAäi±Üp 8„4¡D@ ’ÕþéŸþ)xýë_œzê© éV¬X,\¸°pä‘G³BöбcôFcàњ͋Xfa5ûˆ˜¬¬Œ¸øØzù­:¢£èŽh¢ äºîdäè|Ò1݉‹~ËŒ…t`#¦¾AòY1rõmDÏ>‚è" ãk€oœKz¤rHI7¯Ûå™×ì²ЦHÇ"» sæÅ8÷Œ4ea¶›¥­s±£™i‘èo)é8ôoI÷ 2Î ºÈ¹Ê£9“¼îoŠÛà£Ý5w¡€DFpèÓèÇèß7Á…ò@`Ðc`0i'}޶Ì0UJÀ) (ûâ‹/¦ûî»ÏTlÙG[y(Ó®42Îý”rÈ8O+Ç·ò™P¾QYe¤•ç:šå™Š¹„=Äç … un*[ÍûÌ`··+HEÊ?+ØšiSùXùÇõ %Ýžúrå·ÓMNÓ,+ïÈO”œ[·9U²î R®™¿ Ž{ cDû^»-Ìã„ÃäÊ /‡»¿º¬sß‘>a¸Ê¡yÊI+õi÷§½¯]—ÀqÏXÏ”Pq¹d¥¹žX×ìŽWª#¦uV×=.·ÓÓîwåI{~©g”sžöœRD:Ö=®ëiaÊFá?+ÀÎ.ïxÇ;äg)µ¯Ù£(9ÎÓ@Á<'G~áÊ}.À!ǹë=Òê–V¢ârLP *™]÷šÁî°iyÒÎKuZ¹†2mnÀá\u±¯Œº¥¤Í™¤ÞX„Y*¯+¿×|×´÷´ë>Ñ~Ò@ðg#\zé¥tOÒÿž«ãºØk›Í–|öÈï;òØeÙ1“R¦\#G.¶<­<û^Wº–Jå‚Q|Òò¸F~óšM”f~3Ø#÷¬{„Ev°Í›åº®¥‰æ5;Í%˜uuÝg‹AJZZ½sVÙ¶`?s,Nh\áÏ0êßá+ŽŠ;j)™Ú5êºò’u_¹2»]®·bM#zWýÒên¶‹Í½˜y6B˜>ÅÅnSóyvZ) JvÊ4pyIc¯] `ƒy>žhÖÅEˆcÉÞ6¸êX°òÛe•znˆ¥·9ÎDzcgÙ¿áï|'ÝuW±«h*Ý!í4³£J/Õå>ŸâN—1ÎÓFÙ4âv‰ ®¼®t2ÒÓtc¥Ù@B)idÛù\é®`€ìv¶ó•ÃZ»Øâr@êàâ>ÌûÓ”ˆ6q›n—1Ö½.NÀ;Û™‡ ˜¬û-TàòË/§ï}¯ØM´ø]ùÒžéºî"|W=Ⱥ^ιëwZšiã×o”ºîÊc‚D)p1ÏËùízöX÷•:O+¿`¸Ð~¿ …ƒ[·nUÖ|c=Î:7G+lL©Q}<ÄHä¾,bï½å<7­¼±Î]¿ÓÒJ¥—JuÒñü.Ò®—:ŽEœ®)<{TžÈóÌrJ†yLã&Eü®ºê*úêW¿ªìîËydÊï4"p½¼¤oü.E„®4›ûp¿]bF9#½ë{ôyJÛùÍëi×ì`?w,"¶ÃD9…‰€œ›ùQïRú;- \÷ŽU–—¸"õ’ò=+Ý ®6üïÇ` 4b”ûÈ”ß6Kê;~§‚ëž±Xi3m,‚+÷úxˆ8í½\ïn^·ó˜e— ¥ÂX¢€]3˜ö®ßvZ¹œE¹À‘Fäi€1Þóñ¤•(i¿' lÚ´ «øÆ{[©»®¹ˆ;-¯=Ê»ò™Då[ùÆR×ÒÀ˾Ïõ¼44×ÇuÁXÁ5Ùe–Û‘ÒÊñ¬ëi `©™æG×uWyž#Ÿ=z»ê˜6JÛ X1»¸€±îTøïQÊLK»^”óŒ4öÚ&ò±Øo3o!MDNü‡Måv²©#Ìô4¢,uïDÓ\Ïkô.Un©r&º!ÐBÚ–OÎÓd×rØWWQ–K\‚hÓÞµu¼ã}æd€f¼ßi<ùËe±'ÒWÊ.WÛØºˆ) ÿ@B¹l‘ݨS%K§#Oõèº?Gëÿ.aª‰bª¸”rBZŸÜo„žZ‘ñŠ‡Â¡p(üÏ Q ‡Â¡ð?$€CáPø ‡àP8þ‚Ã!8…¿àp…Cá/8€CáPø ÿ?ãkûUg\HIEND®B`‚(  @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ%ÿÿÿ&ÿÿÿ&ÿÿÿ!ÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿ€€€?¹¹¹UÇÇÇnÒÑцÕÕÕ™ØØØ¨ÛÛÜ®ÛÛÛ«ØØØ¢ÐÐÐ’ÈÈÈ|½¼¼bªªª!ÿÿÿÿÿÿHHIÙ325ÿA@Cÿ !ÿ ÿ$#%ÿ87:ÿ.-/ÿ++-ÿ,+-ÿBBEÿGFJÿ;BAÿPPQêÿÿÿÿÿÿutuðMLOÿ__bÿ%$%ÿÿ" #ÿ;:<ÿ)**ÿ$#%ÿ$#$ÿSRUÿcceÿUqÿyzzûÿÿÿÿÿÿšš›ø’’“ÿžž ÿŒŒÿ†…ˆÿ“’’ÿŸž ÿ“’’ÿ‘‘”ÿŽŽÿžžŸÿ¦¥§ÿ¡ž ÿ¢¡¢ýÿÿÿÿÿÿ˜˜™îÛÜÜÿæçæÿããäÿÞÞàÿäááÿááàÿÚÚÛÿáâáÿììëÿëééÿäåæÿÜÜÞÿ´³´ùÿÿÿÿÿÿ€‚Ù÷÷øÿÜÛÛÿÚÙÚÿááàÿââäÿäàãÿäâãÿääåÿåãäÿäåäÿæççÿðïðÿ–•—ðÿÿÿÿÿÿ[[]¶ñðòÿÏÏÏÿÏÎÐÿÌÍÌÿÔÔÖÿÕÕÔÿÌÌÌÿÈÇÉÿÖÕ×ÿ×Ö×ÿÕÖÖÿííìÿ„‚…çÿÿÿÿÿÿ~|ÊñññÿÄÃÅÿÈÇÈÿÊÉÈÿßãâÿÆÈÈÿÊÉÊÿ¿¾Àÿ¾¾ÀÿÎÏÏÿÎÎÏÿäæèÿdbeÿÿÿÿÿÿÿ–”–•ÑÑÒÿ½¿ÁÿÂÂÂÿÂÄÃÿÓÔ×ÿÈÈÈÿÅÆÆÿÅÅÈÿÁÀÂÿÅÄÇÿÂÅÆÿÖ××ÿÅÿÿÿÿÿÿ‘‘”6»»¼ÿÄÅÅÿÀÀ¿ÿÀÀÁÿÂÀÂÿÆÅÇÿ½»¾ÿ¶µµÿ¸¹ºÿ¸¸¹ÿ¾¼¿ÿ¾¿Àÿ“”Sÿÿÿÿÿÿ‚„ º¹ºþØÚ×ÿ¿¿ÀÿÀÀÂÿÁÁÂÿÂÁÃÿÁÁÂÿ²²µÿ¨««ÿ»¹»ÿÁÃÁÿ§¦¨ÿƒƒ†%ÿÿÿÿÿÿÿÿÿ··º÷ÍËÏÿÀÀÀÿÃÃÄÿÄÄÄÿÄÄÅÿÅÅÆÿÈÉÉÿ¯±±ÿª¨ªÿ¼»¾ÿ¥¤¦ûÿÿÿÿÿÿÿÿÿÿÿÿ©ª¬ìÕÔÕÿÆÅÆÿÇÇÈÿÈÈÉÿÉÉÉÿÉÉÊÿÊÊËÿÐÓÐÿµµ·ÿ¨§©ÿ’’”òÿÿÿÿÿÿÿÿÿÿÿÿ™™›ÖÙÚÚÿÕÔÔÿÒÒÒÿÓÒÓÿÔÓÔÿÕÕÖÿÔÔÕÿÖÖØÿÙÛÜÿÔÔÔÿ‡†ˆáÿÿÿÿÿÿÿÿÿÿÿÿ}|~d½½½¼¿ÀÁ´½½¾´¼¼½´¼¼½´½¼¾´½½¾µ¼¼¾´ÃÃŵÃÂļ~‚mÿÿÿÿÿÿðÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿÀÿÿÀÿÿÀÿÿÀÿÿ(0 ` ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#ÿÿÿ5ÿÿÿEÿÿÿTÿÿÿ_ÿÿÿgÿÿÿmÿÿÿmÿÿÿhÿÿÿbÿÿÿWÿÿÿHÿÿÿ8ÿÿÿ&ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ,555Uzzzb‘‘‘p¡¡¡€°²±Œ¹¸¸™¾¾½¦Á°ÄÅĶÉÉÉ»ÆÉÇ»ÅÆÇ¸ÃÅñ¼¼¾ª³²² ª«ª’   ƒs}}~?ÿÿÿÿÿÿÿÿÿ(PPQ÷++-ÿ//2ÿ658ÿ!!#ÿÿ  ÿÿÿ!ÿÿÿÿÿÿ,*0ÿ//3ÿ506ÿ1'/ÿPOQýEÿÿÿÿÿÿ%%%KŒ‹ÿLKMÿZY\ÿedhÿA?Bÿÿÿ''(ÿ???ÿLLKÿ(()ÿ99;ÿ767ÿ544ÿ--.ÿqsuÿqlpÿ_wjÿJ…cÿ„ƒ…ÿFFFwÿÿÿÿÿÿ555b‹ŠŒÿHGJÿ[Z]ÿedgÿ113ÿÿÿÿ225ÿ?>Bÿÿ*++ÿ-..ÿ#"#ÿ#$#ÿbbfÿeagÿb€nÿ_žzÿuuwÿ^]^”ÿÿÿÿÿÿHHHwžžŸÿdceÿqqsÿyxyÿ[[]ÿ===ÿHJNÿZWZÿkhkÿrqrÿ`_aÿcbdÿeedÿ^^_ÿ^^_ÿ~}ÿ€ÿ{y|ÿxrvÿ††ˆÿ{{{­ÿÿÿÿÿÿ888jÎÎÏÿÑÏÑÿæäèÿæçæÿòõôÿøøøÿôõóÿðïðÿéäâÿÞßàÿÝßÞÿèçêÿçèêÿääãÿããäÿâããÿááâÿëëëÿÞßàÿÕÕÖÿ[[[–ÿÿÿÿÿÿ AÁÁÂÿÀ¿¿ÿààÞÿÞÝÜÿÀÁÅÿº¸¸ÿ´¶¶ÿÉÇÈÿÑÑÐÿËÍÏÿÉÆÅÿÊÆÉÿÏÏÎÿØ×ÙÿÚÜÜÿßßàÿÝÞÝÿÓÒÒÿÁÂÁÿÙÕ×ÿ888jÿÿÿÿÿÿÃÃÄýòòñÿëëîÿÞßáÿßÝÝÿææçÿêêêÿåääÿâããÿãâäÿååæÿéåéÿççèÿææçÿæçèÿåæèÿææéÿòðîÿèèëÿÛÛÜÿ Eÿÿÿÿÿÿ°¯²÷ÝÝßÿÌÊÌÿÐÓÒÿÔÔÔÿÔÔÕÿÔÔÕÿÕÔÖÿÖÕÖÿ×רÿØ×ÚÿÜÛÜÿÜÝÜÿÚÚÛÿÚÚÚÿÜÚÛÿÜÜÝÿÝÝÝÿìéëÿÇÇÉýÿÿÿÿÿÿÿÿÿ““•ìÜÝÝÿáâäÿÌÊÌÿÍÍÎÿÎÎÏÿÎÎÏÿÑÒÐÿÛÚÚÿÒÒÓÿÉÈÈÿ½¼¾ÿÁÃÀÿØ×ÚÿÖ×ÕÿÖÕÖÿÖÖÖÿ×רÿéèëÿ¢¢¤ôÿÿÿÿÿÿÿÿÿÿÿÿvwvËìííÿÎÌÎÿÇÆÈÿÈÈÉÿÉÈÉÿÉÈÌÿòîîÿÐÐÑÿËËÌÿÌÍÎÿÐÌÏÿ±¶²ÿ´´·ÿÕÔÔÿÏÏÐÿÏÏÐÿÑÐÒÿçéêÿ‹ŠŒäÿÿÿÿÿÿÿÿÿÿÿÿ””•ªÞÚÝÿÃÂÄÿÃÃÄÿÅÅÅÿÅÅÆÿÊÈÊÿèâäÿ¸¹¼ÿ¿ÂÃÿÄÁÄÿÆÆÇÿÌÎÊÿ¶··ÿÊÌÎÿÉÈÉÿÉÈÊÿÍÍÍÿÛÜÞÿ‹Ëÿÿÿÿÿÿÿÿÿÿÿÿœž„ÌÌÍÿÆÆÉÿ¾¿¿ÿÁÁÂÿÂÂÃÿÂÂÃÿÎÏÏÿØÜÙÿÌËÌÿÆÆÇÿËÊÑÿÄÁÂÿÀÀÃÿÂÁÂÿÂÂÃÿÂÂÃÿÉÈÊÿÉÊËÿœž«ÿÿÿÿÿÿÿÿÿÿÿÿ–•˜a¼¼¾ÿÈÉËÿ¿¿ÀÿÀ¿ÀÿÀÀÁÿÁÀÂÿ¿ÀÁÿÅÄÅÿÆÆÈÿ¿¿Àÿ»»¼ÿº¹»ÿºº»ÿ»º¼ÿ¼»½ÿº¹»ÿÇÅÇÿ¿¾¿ÿ––˜†ÿÿÿÿÿÿÿÿÿÿÿÿ‹Š>²²´ÿ¶µµÿØÙÙÿ¿¿¿ÿÀÀÁÿÁÁÁÿÁÁÂÿÁÁÂÿÂÁÃÿ¾½¾ÿ³³´ÿ±±³ÿ´´µÿµ´µÿ³´µÿ»»½ÿ»º»ÿŸÿŒŒŽWÿÿÿÿÿÿÿÿÿÿÿÿ€‚¯¯±üããäÿÌËÍÿ¿¿ÀÿÁÀÁÿÁÁÂÿÁÁÂÿÂÁÂÿÂÂÃÿÃÃÅÿÀÀÂÿ±±±ÿª©ªÿ­¬­ÿÇÅÆÿ½½¾ÿ³³´ÿ§¦¨ýƒ#ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©©ªòÞÞÛÿ»½¿ÿÁÁÃÿÂÁÃÿÃÂÃÿÃÂÄÿÄÃÄÿÄÄÅÿÄÄÅÿÅÅÆÿÅÄÅÿ³²³ÿ  ¢ÿ»½½ÿËÌÌÿÄÄÅÿœ›øxwzÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿž›ŸåââãÿÅÄÂÿÄÄÅÿÅÄÅÿÅÅÆÿÆÅÆÿÆÆÇÿÆÆÇÿÇÆÇÿÇÇÈÿÈÈÉÿËÊËÿ··¸ÿ”’“ÿ–•—ÿ»¼¿ÿ’îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ’ÏæçéÿÉÈÉÿÇÇÈÿÈÈÉÿÉÉÉÿÉÉÊÿÊÉÊÿÊÊÊÿÊÊËÿËÊËÿËËÌÿËËÌÿÎÎÐÿ¾¾¿ÿ›Ÿÿ³²´ÿ€€‚Ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ„„†²ÛÛÜÿÏÐÑÿÑÑÒÿÎÍÎÿÎÍÎÿÎÍÎÿÎÎÏÿÏÏÐÿÑÑÑÿÏÏÐÿÏÏÐÿÏÏÐÿÐÏÐÿÓÔÓÿÐÐÑÿ­¬®ÿnnpÅÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿrruŠÓÒÔÿÏÏÐÿÕÕÖÿÔÔÔÿÓÓÓÿÔÓÔÿÔÔÕÿÔÔÕÿÕÕÖÿÕÕÖÿÕÕÕÿÔÔÕÿÔÔÕÿ×רÿÛÛÛÿÍÍÍÿrqu’ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿgfj*­­®™¹¹¹œ¸¸¸œÄÄÅœ¶¶·›¹¹º›¹¹º›¹¸º›¸¸¹›¹¹º›¹º»›¹¹º›¿¾À›ÇÇÉœ¶¶¸œ¶¶¸›edi*ÿÿÿÿÿÿÿÿÿðÿøÿÀÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿÀÿÀÿÀÿÀÿÀÿÀÿÀÿàÿàÿàÿàÿàÿàÿ( @ €ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#ÿÿÿ&ÿÿÿ*ÿÿÿ0ÿÿÿ1ÿÿÿ3ÿÿÿ3ÿÿÿ3ÿÿÿ1ÿÿÿ.ÿÿÿ,ÿÿÿ(ÿÿÿ#ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ1ÿÿÿCÿÿÿUÿÿÿdÿÿÿqÿÿÿÿÿÿˆÿÿÿÿÿÿ“ÿÿÿ”ÿÿÿ“ÿÿÿÿÿÿˆÿÿÿÿÿÿsÿÿÿdÿÿÿTÿÿÿAÿÿÿ3ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿE hEEEpcccysssƒ‚‚‚Œ“’““œœœ›£££¢©©©ª­­­±°°°¸²²²»µµµ¾µµµ¾µµµ¾³³³»°°°µ¬¬¬²¤¤¤®œœœ§•••ŒŒŒ”‚‚‚‹ppp[[[\PPPÿÿÿÿÿÿÿÿÿÿÿÿ! !hJJLü('*ÿ*),ÿ.-0ÿ//1ÿ,,.ÿÿÿÿÿ""$ÿ++-ÿ0/2ÿ*)+ÿ.-0ÿ--/ÿ--/ÿ)(+ÿ(')ÿ.-0ÿ326ÿ0/3ÿ/.1ÿ/-1ÿ'&*ÿ@@BþDDE™ÿÿÿÿÿÿÿÿÿÿÿÿNMN£ŒŽÿFEHÿSRUÿZY]ÿ\[_ÿPOQÿÿÿ ÿ!ÿ/.1ÿ>=AÿCBEÿ!!"ÿ--.ÿ--.ÿ0/1ÿ''(ÿ%$&ÿDCFÿgfiÿbaeÿcbfÿ_^_ÿ[[ZÿwuyÿzzzÓÿÿÿÿÿÿÿÿÿÿÿÿ```´ÿGGIÿWVYÿ_^aÿbaeÿPPRÿÿ ÿÿ%%(ÿ436ÿCBEÿGFIÿ))*ÿ889ÿ656ÿ112ÿ*)+ÿ''(ÿCBDÿkknÿffiÿhbfÿHpÿJ­{ÿjhhÿŽãÿÿÿÿÿÿÿÿÿÿÿÿvvvȈˆŠÿIHKÿZZ\ÿbbdÿeehÿPORÿÿ ÿÿ$#&ÿ436ÿCCEÿHGJÿ !ÿ*)*ÿ++,ÿ&&'ÿ$$$ÿ##$ÿ;;<ÿporÿjilÿjgkÿl‘zÿk’{ÿ`_`ÿœ›œñÿÿÿÿÿÿÿÿÿŽÙ€ÿHHIÿXWZÿ_^`ÿbbdÿTSUÿÿ&&(ÿ102ÿ<<=ÿHGIÿSSUÿYYZÿ==>ÿBBCÿCCDÿBBCÿA@Aÿ>>>ÿIIJÿnmpÿiikÿihjÿidiÿe`eÿWWYÿ¥¥¥ù...ÿÿÿÿÿÿÿÿÿ””•ßÜÛÜÿÎÎÏÿÓÓÔÿÕÕÖÿ×רÿØØØÿÚÚÚÿÜÜÝÿßßßÿááâÿââãÿããäÿäååÿèèéÿèèèÿçççÿæåæÿååæÿääåÿãâãÿßßàÿÞÞßÿÝÝÞÿÚÚÛÿÕÕ×ÿØØÙÿÃÂÃøÿÿÿÿÿÿÿÿÿuuvǽ½¾ÿ¯¯°ÿàßàÿÎÎÏÿÐÏÐÿßßàÿéééÿààáÿÓÓÔÿÆÆÇÿ½½½ÿ¶¶¶ÿ°°°ÿ¬¬¬ÿ¯¯°ÿ¸¸¸ÿ½½¾ÿÁÁÁÿÆÆÇÿÈÈÈÿÌÌÍÿÍÍÎÿÎÎÏÿçççÿÌËÌÿ¬«¬ÿ§§§ëÿÿÿÿÿÿÿÿÿÿÿÿWWX¡ÚÚÛÿÁÁÂÿÑÑÒÿìììÿãããÿÐÏÐÿÂÂÂÿÁÁÂÿÍÍÍÿÚÙÙÿâââÿæåæÿååæÿããäÿáàáÿàààÿáááÿäääÿæææÿæææÿçççÿæææÿçççÿÌËÌÿÌÌÍÿââãÿ‰‰‰Ôÿÿÿÿÿÿÿÿÿÿÿÿ==>vÚÚÛÿëëìÿóóóÿããäÿÝÝÝÿßßàÿåååÿïïïÿëêëÿååæÿááâÿßßàÿàààÿààáÿââãÿäääÿäääÿäääÿääåÿåååÿåååÿååæÿìììÿóóóÿÛÛÛÿååæÿfff±ÿÿÿÿÿÿÿÿÿÿÿÿ*)*JÒÒÓÿççèÿÖÖÖÿÔÔÕÿÖÖÖÿÖÖ×ÿØØØÿÖÖ×ÿÖÖØÿ×רÿØØÙÿÙØÙÿÙÙÚÿÚÚÛÿÛÛÛÿÛÛÜÿÜÛÜÿÜÜÝÿÜÜÝÿÝÝÝÿÝÝÞÿÞÞßÿÞÞßÿááâÿççèÿààáÿGGGˆÿÿÿÿÿÿÿÿÿÿÿÿ%ÅÄÅþÎÍÏÿÐÐÒÿÔÓÔÿÒÑÒÿÒÒÓÿÒÒÓÿÒÒÓÿÔÓÔÿÔÓÔÿÓÓÔÿÕÕÖÿÕÔÕÿÒÒÓÿÒÒÓÿ×רÿÚÚÚÿØØÙÿØØÙÿÙÙÚÿÚÙÚÿÚÚÛÿÛÚÛÿÜÜÝÿÝÜÝÿ×Ö×ÿ000aÿÿÿÿÿÿÿÿÿÿÿÿ °¯±ùÍÍÎÿììíÿÏÏÑÿÌËÍÿÍÍÎÿÍÍÎÿÎÎÏÿÎÎÏÿÐÏÐÿÜÜÝÿÙÙÚÿÐÏÐÿÉÉÊÿÀÀÁÿ··¸ÿÁÁÁÿÕÕÖÿÔÔÕÿÕÕÕÿÕÕÖÿÖÖÖÿÖÖ×ÿØØÙÿÚÚÜÿÇÆÈÿ7ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ””•íââãÿÛÛÜÿÆÆÇÿÉÈÉÿÉÉÊÿÊÊÊÿÊÉËÿÉÉÊÿáàáÿÞÞßÿÌÌÎÿÍÌÍÿÎÎÏÿÏÏÐÿÍÍÎÿ³³´ÿºº»ÿÑÑÒÿÐÐÑÿÐÐÑÿÐÐÒÿÑÐÒÿÔÔÕÿ×רÿµ´µüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ’’“ÚààáÿÍÍÎÿÄÄÅÿÆÅÆÿÆÆÇÿÇÇÈÿÇÇÈÿÉÉÊÿãããÿÆÅÆÿÆÆÇÿÇÇÈÿÈÈÉÿÈÈÉÿÊÊÊÿÊÉÊÿ³³³ÿÌÌÍÿËÊÌÿËËÌÿËËÌÿËËÌÿÒÒÒÿÔÓÕÿ¦¦§óÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸŸ¡¿ÕÕÖÿËËÌÿÁÁÂÿÃÃÄÿÃÃÄÿÄÄÅÿÅÄÆÿÅÅÆÿÜÜÜÿÎÍÎÿÀÀÁÿÁÁÂÿÂÂÃÿÃÃÄÿÅÅÆÿÃÂÄÿ½½¾ÿÇÆÈÿÆÆÇÿÇÇÈÿÇÇÈÿÇÇÈÿÐÏÐÿÍÌÎÿ¡¡¢åÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ›››ÆÆÇÿÏÏÐÿ¿¿ÀÿÁÁÂÿÂÁÂÿÂÂÂÿÃÂÃÿÃÂÃÿÇÇÈÿ×רÿÔÓÔÿÌËÌÿÊÊÊÿÉÉÊÿÇÇÈÿÀÀÁÿÁÀÂÿÁÀÂÿÂÁÂÿÂÂÃÿÃÂÃÿÂÂÃÿÎÍÎÿÅÅÇÿ£¢¤Êÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ—–˜pºº»ÿËËÌÿÅÅÆÿ¿¾¿ÿÀÀÁÿÁÁÁÿÁÁÂÿÂÁÂÿÁÁÂÿÂÂÃÿÈÈÉÿÇÇÈÿÃÃÄÿÀ¿Áÿ¼»½ÿ»º¼ÿ»»¼ÿ¼»½ÿ¼¼½ÿ½¼¾ÿ½½¾ÿ¼¼½ÿÎÍÏÿÀÀÁÿ𙛍ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽF¸¸¹ÿ±°²ÿÃÂÄÿÊÊËÿ¿¿ÀÿÀÀÁÿÀÀÁÿÁÁÂÿÁÁÂÿÁÁÂÿÂÁÂÿÂÁÃÿ¼»½ÿ³´µÿµ´¶ÿ¶µ·ÿ·¶·ÿ··¸ÿ¸·¸ÿ¸·¹ÿ¸·¹ÿ¿¿Àÿ»º¼ÿ¥¥§ÿ’‘”}ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ……‡!´´µþ¸¸¸ÿÔÔÕÿÚÙÚÿ¾¿ÀÿÀÀÀÿÁÀÁÿÁÁÂÿÁÀÂÿÁÁÃÿÂÁÃÿÂÂÃÿÃÄÄÿ¾¾¿ÿ±±²ÿ±°²ÿ²²³ÿ²²´ÿ³²³ÿ³³´ÿ¾¾¿ÿ¼¼½ÿ²±³ÿ  ¡ÿ‰ˆ‹Rÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ~§¦¨ùÝÝÞÿÞÝÞÿÃÃÃÿÀÀÁÿÁÁÂÿÁÁÂÿÁÁÂÿÂÁÂÿÂÂÃÿÂÂÃÿÃÂÃÿÃÃÄÿÅÅÅÿÀÀÀÿ°°±ÿ¬¬­ÿ®®¯ÿ°°±ÿÌÌÍÿ¼¼½ÿ²²³ÿ´³´ÿ§§¨þ€€‚.ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸŸ îÜÜÝÿÇÇÈÿÀÀÀÿÂÁÃÿÂÁÃÿÂÂÃÿÃÂÃÿÃÃÃÿÃÃÄÿÄÃÄÿÄÄÄÿÄÄÅÿÄÄÅÿÆÆÇÿÃÃÄÿ±±²ÿ§§¨ÿ©©«ÿÆÆÇÿÏÎÏÿÈÈÉÿÁÀÂÿ—–˜úzy|ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ••–ÜÙÙÚÿÍÍÎÿÂÂÃÿÃÃÄÿÄÄÄÿÄÄÅÿÄÄÅÿÅÅÅÿÅÅÆÿÅÅÆÿÆÆÆÿÆÆÇÿÆÆÇÿÆÆÇÿÈÇÉÿÈÈÉÿ¸·¹ÿ¤¤¥ÿ££¤ÿ¥¥§ÿ²±³ÿ½½¿ÿ‹Šðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‹‹ÀÖÖ×ÿÓÓÔÿÄÄÅÿÆÆÇÿÆÆÇÿÇÆÇÿÇÆÇÿÇÇÇÿÇÇÈÿÇÇÈÿÈÈÉÿÈÈÉÿÉÈÉÿÉÉÉÿÉÉÊÿÉÊÊÿÌÌÍÿ¾¾¿ÿ¢¡£ÿž ÿ²²´ÿ´´¶ÿ€‚Þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ~€¢ÒÒÓÿÚÙÚÿÈÈÉÿÈÈÈÿÉÉÊÿÉÉÊÿÊÉÊÿÊÊÊÿÊÊËÿÊÊËÿÊÊËÿËËÌÿËËÌÿÌËÌÿÌÌÌÿÌÌÍÿÌÌÍÿÏÎÐÿÂÂÃÿŸž ÿ´´µÿ®­¯ÿvuxÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿrrtyÉÉÊÿÒÒÓÿÐÐÑÿÐÐÑÿÌÌÍÿÍÍÍÿÍÍÍÿÍÍÍÿÍÍÎÿÍÍÎÿÎÎÎÿÏÏÏÿÎÎÏÿÎÎÏÿÏÏÏÿÏÏÏÿÏÏÐÿÏÏÐÿÑÑÒÿÌÌÍÿ¿¿Àÿ›šœÿkknžÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿdcgPÉÈÊÿÔÔÔÿÐÐÑÿÜÛÜÿ×רÿÔÔÔÿÕÕÖÿÕÕÖÿÖÕÖÿÖÖ×ÿØØÙÿÛÚÛÿÚÙÚÿ×רÿ×רÿ×××ÿ×רÿÖÖÖÿÙÙÚÿááâÿ×××ÿ¯¯°ÿfehtÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ\[_ªª«åÌÌÌïÄÄÅïÁÁÂïÑÑÒïÇÇÈïÈÇÈïÈÈÉïÉÈÊïÉÉÊïÇÆÈïÆÆÇïÈÈÉïÉÉÊïÉÉÊïÉÉÊïÈÈÉïÎÍÏïÐÐÑïÆÅÆïÎÎÏï··¹ë``d0ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`_cdµµµv²²²v¦¦§vÎÎÏv²²³v±±²v²²³v²²³v²²´v²±³v²²³v²²³v³³´v³³´v´³µv²±²vÂÂÄvÈÈÉv«ª¬vºº»v™™œjVV[ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðø€€€€€€€€€€€€€ÀÀÀÀÀÀÀÀàààààààà(0` €%ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ!ÿÿÿ#ÿÿÿ$ÿÿÿ%ÿÿÿ&ÿÿÿ'ÿÿÿ'ÿÿÿ'ÿÿÿ&ÿÿÿ%ÿÿÿ$ÿÿÿ"ÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿ"ÿÿÿ(ÿÿÿ.ÿÿÿ4ÿÿÿ:ÿÿÿ?ÿÿÿDÿÿÿIÿÿÿMÿÿÿQÿÿÿSÿÿÿVÿÿÿVÿÿÿWÿÿÿVÿÿÿUÿÿÿRÿÿÿPÿÿÿLÿÿÿGÿÿÿBÿÿÿ=ÿÿÿ8ÿÿÿ1ÿÿÿ,ÿÿÿ%ÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÌÌÞÞÞ èèèîîîóóó÷÷÷'úúú.üüü6ýýý=þþþEÿÿÿKÿÿÿRÿÿÿXÿÿÿ]ÿÿÿbÿÿÿeÿÿÿgÿÿÿhÿÿÿhÿÿÿhÿÿÿfÿÿÿcÿÿÿ_ÿÿÿZÿÿÿTÿÿÿNÿÿÿGþþþ@ýýý9ûûû1øøø*õõõ"ñññééé××× ÍÍÍÕÕÕÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ8HMP...T===WJJJZVVV^bbbannnawwwd}}}h„„„l‹‹‹or“““u———wšš™z›››|}}žžž}}œœœ|šššz———x”””vt‰‰‰tƒƒƒr}}}nvvvjnnnffffb\\\^QQQ[>>>U***D*** ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ667.-/àí!ð! #ð"!$ð""$ð""$ð##%ð ðîíî î!î "í""$í%$&í'&)î((*î*),î++-î+*-î**,î))+î('*í'&)í&%(í$#&ï"!$ð #ð!ððððððïæ\\]±...%ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ!!!-‹‹Œçihkÿ@@CÿIHLÿQPSÿUTXÿUTXÿUTWÿYY\ÿ##$ÿÿÿÿ ÿÿ$$&ÿ/.0ÿ669ÿ@?CÿBBEÿÿ769ÿ$#%ÿ113ÿ557ÿ''(ÿ99;ÿ(()ÿ--/ÿ213ÿ_]cÿZX]ÿZY]ÿZY^ÿ[Y^ÿ][`ÿ_]bÿXVZÿYX[ÿ‘‘“ÿZYZTÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ444<©©«òuuwÿBADÿKKMÿSRUÿXW[ÿXW[ÿXW[ÿ^]`ÿ ÿÿÿ ÿÿ "ÿ)(,ÿ214ÿ::=ÿFEHÿGFIÿÿ<;=ÿÿ../ÿ779ÿ##$ÿ>>?ÿ! !ÿ.-/ÿ++,ÿgfjÿa`cÿa`cÿa`cÿaadÿ`\`ÿYVQÿc^cÿ[[]ÿœžÿpppiÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>>>J°¯±üonpÿEDGÿOOQÿWVZÿ]\_ÿ]]`ÿ]]`ÿbadÿ ÿÿ ÿÿÿ""%ÿ,+/ÿ547ÿ>>@ÿIIKÿJILÿÿFFGÿ%%&ÿ546ÿ?>@ÿ$$$ÿDCEÿÿ001ÿ++,ÿkjnÿedgÿedgÿedgÿjdhÿ:zTÿ*´{ÿ>€UÿXSVÿ˜—˜ÿ††‡}ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQQQ]ºº¼ÿjikÿIIKÿSSVÿ\[^ÿaadÿcbeÿcbeÿffiÿÿÿ ÿÿÿ&%(ÿ//2ÿ98;ÿBADÿMLOÿNMPÿÿGGHÿ$#$ÿ445ÿ@@Aÿ !ÿBBDÿÿ556ÿ))*ÿporÿjilÿjilÿjhmÿmgjÿM¦vÿ‰þßÿV¢qÿTQRÿ‘’ÿ˜˜™™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿedeo½½¾ÿdceÿLKNÿWVYÿ__bÿedgÿeegÿfehÿkjmÿÿÿ ÿÿ ÿ('*ÿ214ÿ;:=ÿEDGÿPORÿQQSÿÿ@?Aÿÿ334ÿ::;ÿÿBACÿÿ<<=ÿ&&'ÿsruÿmmpÿmmpÿmmpÿnmpÿpwpÿi…lÿmtnÿSRUÿˆˆ‰ÿ©©ª·###ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|||~¶¶·ÿHHIÿ446ÿ?>@ÿFEHÿKKMÿKKMÿKKMÿOORÿ ÿÿÿÿ ÿÿÿ%$&ÿ--/ÿ779ÿ:9<ÿÿ$$%ÿ ÿÿ! !ÿ ÿ(')ÿÿ))*ÿÿVUXÿSRTÿSRTÿSRUÿSRUÿTRVÿVRWÿQPTÿ879ÿgfhÿ°°±Î>>> ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‡‡‡…ÒÒÒÿ‘‘’ÿˆˆ‰ÿÿ““”ÿ—–˜ÿ™˜šÿ›š›ÿžžŸÿŒŒŒÿˆˆ‰ÿ‹‹Œÿÿ”””ÿ———ÿšš›ÿžžžÿ¡¡¢ÿ¥¤¥ÿ¦§§ÿšš›ÿ¤£¤ÿžÿ¡ ¡ÿ   ÿœœœÿ  ¡ÿ™™šÿŸŸ ÿ™™šÿ©©ªÿ¦¦¨ÿ¥¥¦ÿ££¥ÿ¢¢£ÿ¡ ¢ÿŸŸ ÿžŸÿ‘‘“ÿ¢¢£ÿÈÇÉØMMM ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿyxyìëíÿÙÙÚÿááâÿææçÿååæÿçççÿççèÿèèèÿççèÿêêëÿììíÿíííÿííîÿííîÿîîïÿðïðÿððñÿñññÿñññÿòòòÿõôöÿóóôÿóóôÿòòóÿòñòÿòòòÿñññÿòòòÿñðñÿòñòÿîîïÿîîîÿîíîÿíìîÿíìíÿëêëÿêêëÿââãÿááâÿññòÿ¸¸¹ÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿTTTfÝÝÞÿ‘ÿ††‡ÿããäÿâââÿÔÔÕÿÓÒÓÿØØØÿååæÿææçÿçççÿééêÿêêëÿççèÿÜÜÝÿÐÐÐÿÇÆÇÿ¿¿Àÿ¼¼½ÿ¹¹¹ÿ··¸ÿ¹¹ºÿÀÀÀÿÅÅÆÿÉÉÊÿÊÊËÿËËÌÿÍÍÎÿÏÏÐÿÑÑÑÿÓÓÔÿÕÕÖÿÖÖÖÿ×××ÿÛÛÜÿíííÿïîïÿ©©ªÿvvwÿÝÝÝÿ¡¡¢ªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@@AOââãÿ¨¨ªÿœœÿÛÚÛÿÁÁÂÿÈÇÈÿËËÌÿÄÄÄÿÆÆÆÿÝÜÝÿßßàÿÈÈÈÿ²²³ÿ¨§¨ÿ¥¥¥ÿªªªÿ°°¯ÿ²±²ÿ´´´ÿ±±±ÿ­¬¬ÿ§§§ÿ¤¤¤ÿ¦¥¥ÿ«««ÿ­­­ÿ±±²ÿ···ÿº¹ºÿ¹¹ºÿ¾¾¿ÿÂÂÂÿÀÀÁÿÀÀÁÿ»º»ÿÂÂÃÿëëìÿ¿¾¿ÿ’’“ÿèçéÿxxy~ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ---3ÖÖ×ýÜÜÜÿÌÌÍÿ»»¼ÿÙÙÚÿÿÿÿÿõõöÿïïðÿÙØÚÿ¼¼½ÿ¬«¬ÿ»»»ÿÔÔÔÿëêêÿúúúÿýýýÿûûûÿúúûÿùùúÿùùúÿúúûÿûûûÿüüüÿýýýÿüüüÿûûüÿûûûÿûûûÿúúúÿúúúÿúúúÿúùúÿùùúÿúùúÿþþþÿÍÍÎÿ¹¹ºÿØØÙÿëëìÿòòóÿSSSRÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄÃÄêÐÐÑÿãâãÿôôõÿýýýÿççèÿÜÜÜÿÛÛÜÿááâÿææçÿñññÿüüüÿóóôÿêêêÿãããÿààáÿßÞàÿÞÝßÿÞÞßÿÞÞßÿßßßÿßßàÿààáÿáááÿááâÿááâÿâââÿâââÿââãÿââãÿãããÿããäÿããäÿääåÿíííÿýýýÿåååÿÃÂÄÿÑÐÑÿääåÿ../7ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ­­®ÊÛÚÜÿùùùÿìììÿàààÿØØØÿ×רÿØØÙÿØØØÿÛÛÜÿááâÿÝÝÞÿÚÚÛÿÚÙÛÿÚÚÛÿÚÚÛÿÛÛÛÿÛÛÜÿÛÛÜÿÜÜÜÿÜÜÜÿÜÜÝÿÜÜÝÿÝÝÞÿÝÝÞÿÞÞÞÿÞÞßÿßÞßÿßßàÿßßàÿàààÿààáÿààáÿááâÿááâÿççèÿöööÿîîïÿÕÕ×ÿÑÑÒù !'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿœž¯ÛÚÛÿãâãÿÚÙÚÿÓÓÓÿÔÔÕÿÕÕÖÿÖÕÖÿÖÖ×ÿÖÖ×ÿÖÖÖÿÖÖ×ÿ×ÖØÿ×ÖØÿ×רÿØ×ØÿØ×ØÿØØÙÿÙØÙÿÙÙÚÿÙÙÚÿÚÚÛÿÚÚÛÿÛÚÛÿÛÛÛÿÛÛÜÿÜÛÜÿÜÜÜÿÜÜÝÿÝÜÝÿÝÝÝÿÝÝÞÿÝÝÞÿÞÝÞÿÞÞßÿÞÞßÿâââÿáàáÿÙØÚÿÄÃÅîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‚‚ƒš×××ÿÒÑÓÿÈÈÉÿÌÌÍÿÓÒÓÿÒÒÒÿÒÒÒÿÓÒÓÿÓÒÔÿÓÓÔÿÓÓÔÿÓÓÔÿÔÔÔÿÔÔÕÿÔÔÕÿÕÕÕÿÕÕÕÿÖÖÖÿ×××ÿØ×ØÿØØÙÿÙÙÚÿÙÙÙÿØØØÿØ×ÙÿØØÙÿÙØÙÿÙÙÚÿÙÙÚÿÚÚÚÿÚÚÛÿÚÚÛÿÛÛÛÿÛÛÜÿÛÛÜÿßßàÿÛÛÜÿ×רÿ´´µÐ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpppƒÕÔÕÿÃÃÄÿÆÆÇÿñññÿÕÕÖÿÎÎÏÿÏÏÐÿÏÏÐÿÏÏÐÿÐÏÐÿÐÐÑÿÐÐÑÿÑÑÑÿÑÑÒÿÐÐÑÿÒÒÓÿÖÖ×ÿÕÔÕÿÎÍÎÿÆÆÇÿÀ¿ÀÿÀÀÁÿÉÉÊÿÔÔÕÿØØÙÿÖÕÖÿÖÖ×ÿÖÖ×ÿ×Ö×ÿ×××ÿ×רÿ×רÿØ×ØÿØØÙÿØØÙÿÞÞßÿ×רÿÙÙÛÿ•”–ªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ_^`_ÍÍÎÿ»»½ÿïïðÿêéêÿÎÎÏÿËÊÌÿËËÍÿÌÌÌÿÌÌÍÿÌÌÍÿÍÍÎÿÍÍÎÿÎÍÎÿÎÍÎÿÖÖ×ÿãããÿÝÜÝÿÓÓÔÿÐÐÐÿÎÎÎÿÉÊËÿ¿¿Àÿ°°±ÿ¦¦¦ÿ¿¾ÀÿÕÕÖÿÓÓÓÿÓÓÔÿÔÓÕÿÔÔÕÿÔÔÕÿÕÔÖÿÕÕÖÿÕÕÖÿÕÕÖÿÜÜÝÿÒÑÓÿÚÙÛÿllmÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿKJK6ÂÂÃþÎÎÏÿõõõÿÑÐÒÿÇÇÈÿÉÉÉÿÉÉÊÿÉÉÊÿÊÊÊÿÊÊËÿÊÊËÿÊÊËÿÊÊËÿÖÖ×ÿððñÿÜÜÜÿÎÎÏÿÍÍÎÿÎÍÎÿÎÎÎÿÏÏÐÿÑÐÑÿÒÒÓÿÆÆÇÿ¡ ¡ÿ···ÿÓÒÓÿÐÐÑÿÐÐÑÿÑÑÑÿÑÑÒÿÑÑÒÿÒÑÓÿÒÑÓÿÒÒÓÿÚÚÛÿÍÌÎÿÓÒÓÿhhhiÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ/./µ´¶ïÖÕ×ÿèèéÿÉÈÉÿÆÅÆÿÇÇÇÿÇÇÈÿÈÇÈÿÈÈÈÿÈÈÉÿÈÈÉÿÈÇÈÿÉÈÉÿééêÿßßßÿÆÅÇÿÉÈÊÿÊÉÊÿÊÊÊÿÊÊËÿÊÊËÿËËËÿËËËÿÎÎÏÿÈÈÉÿ¡¡¢ÿÊÊÊÿÍÍÎÿÍÍÎÿÍÍÎÿÍÍÏÿÍÍÏÿÎÎÏÿÎÎÏÿÐÏÐÿÚÙÛÿËËÍÿÊÉËÿTSTTÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿwvw ¨¨©ß×רÿÝÝÞÿÅÅÆÿÄÄÅÿÅÅÆÿÅÅÆÿÆÆÇÿÆÆÇÿÆÆÇÿÇÇÈÿÇÇÇÿÊÊÊÿèèèÿÍÍÎÿÄÄÅÿÆÆÇÿÆÆÇÿÇÆÇÿÇÇÈÿÇÇÈÿÈÇÉÿÈÈÉÿÉÉÉÿÍÌÍÿ®­®ÿÃÃÄÿËËÌÿÊÊËÿÊÊËÿËÊËÿËËËÿËËÌÿËËÌÿÍÍÎÿÙÙÚÿÉÈÊÿÁÁÂÿbbc;ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ••—£¢¤ÎÓÓÔÿÛÛÜÿÃÃÄÿÂÂÃÿÂÂÄÿÃÃÄÿÄÃÄÿÄÄÅÿÄÄÆÿÅÄÆÿÅÅÆÿÇÇÈÿßßàÿÛÚÛÿ¿¾ÀÿÂÂÃÿÄÃÄÿÄÃÅÿÄÄÅÿÅÄÅÿÅÄÅÿÅÅÅÿÆÆÇÿÄÃÄÿµ´µÿÆÆÈÿÆÆÈÿÆÆÈÿÇÇÈÿÇÇÈÿÈÇÉÿÈÈÉÿÈÈÉÿÌÌÍÿ×ÖØÿÅÄÆÿµ´¶òppqÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  ¢ºÇÇÇÿÝÝÞÿÄÄÅÿÀÀÁÿÁÁÂÿÁÁÂÿÂÂÃÿÂÂÃÿÂÂÃÿÃÃÄÿÃÃÄÿÄÄÅÿÊÊËÿßßàÿÙÙÙÿÄÄÅÿ¿¿Àÿ¾¾¿ÿ¿¿ÀÿÀ¿ÁÿÁÁÂÿÅÅÅÿÆÆÆÿ¾½¿ÿÁÀÃÿÃÃÅÿÃÃÄÿÃÃÄÿÄÄÅÿÄÄÅÿÄÄÅÿÅÅÆÿÅÄÅÿËËÌÿÒÒÓÿÁÀÂÿ°°±Ò††ˆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿž ‘»º»ÿÒÑÓÿËÊÌÿ¿¿ÀÿÀÀÁÿÁÀÁÿÁÁÂÿÁÁÂÿÁÁÂÿÂÁÂÿÂÂÃÿÂÂÃÿÃÂÄÿÇÆÈÿÕÕÖÿÞÞÞÿÞÝÞÿÔÓÔÿÏÏÐÿÎÍÎÿÐÏÐÿÌËÌÿÃÂÄÿ¿¿Áÿ¿¿ÁÿÀ¿ÀÿÀ¿ÁÿÀÀÁÿÁÀÁÿÁÁÂÿÁÁÃÿÂÁÃÿÂÁÃÿËËÌÿÌËÌÿ¾½¿ÿ©¨©´˜—šÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿš™›h»»¼ÿÃÃÃÿÏÏÐÿÂÁÂÿ¾¾¿ÿÀÀÁÿÀÀÁÿÀÀÁÿÁÁÂÿÁÁÂÿÁÁÂÿÂÁÂÿÂÂÃÿÂÂÃÿÂÂÃÿÆÆÇÿÉÉÊÿÊÊËÿÊÊËÿÇÇÈÿÁÁÃÿ½¼¾ÿ»»¼ÿ»»¼ÿ¼»½ÿ¼¼½ÿ¼¼½ÿ½¼¾ÿ½½¾ÿ¾½¿ÿ¾¾¿ÿ¾¾¿ÿ¾¾ÀÿËËÍÿÈÇÈÿ¹¹»ÿŸ ’’”ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”’•A¹¹ºÿ¾¾¾ÿÄÄÅÿÎÍÏÿÃÃÄÿ¾¾¿ÿÀ¿ÀÿÀÀÁÿÀÀÁÿÀÀÁÿÁÀÂÿÁÁÂÿÂÁÂÿÂÁÃÿÂÁÃÿÂÁÃÿÂÁÃÿ¿¾Àÿ¶¶¸ÿµµ¶ÿ·¶·ÿ··¸ÿ¸·¹ÿ¸¸¹ÿ¹¸¹ÿ¹¹ºÿ¹¹ºÿº¹»ÿº¹»ÿ»º»ÿ»º¼ÿ»»¼ÿº¹»ÿÌÌÍÿËÊÌÿ¶¶·ÿ”“•‹Ž‘ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒ‹0´´¶þ»»¼ÿžžŸÿ¹¸ºÿÑÐÒÿÈÇÈÿ¿¿ÀÿÀ¿ÁÿÀÀÁÿÀÀÁÿÁÁÂÿÁÁÂÿÁÁÂÿÁÁÂÿÁÁÂÿÁÁÃÿÁÁÃÿÂÃÄÿÀÀÁÿ¶¶¸ÿ³³´ÿ´´¶ÿµ´¶ÿµµ¶ÿµµ·ÿ¶¶·ÿ¶¶·ÿ·¶·ÿ··¸ÿ··¸ÿ··¸ÿ¸·¸ÿÂÂÃÿ¿¾Àÿ‘’ÿ¦¦§ÿ’hŠŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ††ˆ!­­®öËËÌÿªª«ÿ¾¾¿ÿååæÿÒÒÓÿ¿¾¿ÿ¿¿ÀÿÀÀÁÿÀÀÁÿÁÀÁÿÁÁÁÿÁÀÂÿÁÁÂÿÁÁÃÿÂÁÃÿÂÁÃÿÂÂÃÿÃÃÃÿÂÂÂÿ··¸ÿ°°±ÿ²±²ÿ²²³ÿ³²³ÿ³³´ÿ³³´ÿ´³´ÿ´³´ÿ³³´ÿº¹»ÿÄÃÄÿ½¼¾ÿ½¼¾ÿ››ÿ¦¦¨ÿ‰ˆ‹?‡‡‰ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒ¥¥¦é¾¾¿ÿÏÏÏÿíìíÿÜÜÝÿÄÄÅÿ¿¿ÀÿÀÀÁÿÁÀÁÿÁÀÁÿÁÀÁÿÁÁÂÿÁÁÂÿÁÁÂÿÁÁÂÿÂÂÃÿÂÂÃÿÂÂÃÿÂÂÃÿÃÃÄÿÃÃÅÿº¹»ÿ®®¯ÿ¯®°ÿ°°±ÿ°°±ÿ°°±ÿ±°²ÿ²²²ÿ½½¾ÿÁÀÂÿ´´µÿ°°±ÿ³³´ÿ¸¸¹ÿ§§¨ô‚‚„„„†ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ}|œ›ÉÂÂÃÿëëëÿÖÖ×ÿÂÁÂÿÀ¿ÀÿÁÀÁÿÁÀÂÿÁÀÂÿÁÀÂÿÁÁÂÿÁÁÂÿÂÁÂÿÂÂÂÿÂÂÃÿÂÂÃÿÃÂÄÿÃÃÄÿÃÃÄÿÃÃÄÿÄÄÄÿÅÅÆÿ½½¾ÿ­­®ÿ««¬ÿ­­®ÿ­­®ÿ¬«­ÿÂÁÃÿÙÙÚÿ¶¶·ÿ±±²ÿ±±²ÿ¯®°ÿ²²³ÿ››á~} €‚ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ•”–¥ÄÃÄÿßßßÿÈÈÈÿÀ¿ÀÿÁÁÂÿÁÁÂÿÁÁÂÿÁÁÂÿÂÁÃÿÂÂÃÿÂÂÃÿÂÂÃÿÃÃÃÿÃÃÃÿÃÃÄÿÃÃÄÿÄÃÄÿÄÄÄÿÄÄÅÿÄÄÅÿÅÄÅÿÆÆÇÿÀÀÁÿ®®¯ÿ¨¨©ÿªª«ÿª©«ÿµµ¶ÿßßàÿÜÜÝÿÊÊËÿÊÊËÿ¿¿Áÿªª«ÿ‘Ñzz|}}ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒŒ~ÂÂÃÿààáÿÌËÌÿÁÁÂÿÂÂÃÿÂÂÃÿÂÂÃÿÃÂÃÿÃÃÃÿÃÃÄÿÃÃÄÿÃÃÄÿÄÃÄÿÄÄÅÿÄÄÅÿÄÄÅÿÅÅÅÿÅÅÆÿÅÅÆÿÅÅÆÿÆÅÆÿÆÅÆÿÇÇÇÿÄÄÅÿ±°²ÿ¥¥¦ÿ§§¨ÿ§§¨ÿ«ª¬ÿ³³´ÿ²²³ÿ¼»½ÿÇÇÉÿ¦¥§ÿ„ƒ†¿wvyyx{ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ†…‡h¿¾¿ÿââãÿÐÐÑÿÂÂÄÿÃÃÄÿÄÃÄÿÄÄÅÿÄÄÅÿÄÄÅÿÄÄÅÿÅÄÅÿÅÅÅÿÅÅÅÿÅÅÆÿÅÅÆÿÆÆÆÿÆÆÆÿÆÆÇÿÆÆÇÿÆÆÇÿÆÆÇÿÇÇÈÿÇÇÈÿÈÈÉÿÈÈÉÿ¶¶·ÿ¤£¤ÿ¤¤¥ÿ¥¤¦ÿ¥¤¥ÿ¤¤¥ÿ´´¶ÿÃÃÅÿ¡¡£ÿ}}˜sruutwÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|{~U¹¹ºÿÞÞßÿÕÕÖÿÄÄÅÿÅÅÅÿÅÅÆÿÅÅÆÿÅÅÆÿÆÆÆÿÆÆÇÿÆÆÇÿÆÆÇÿÇÇÈÿÇÆÇÿÇÇÈÿÇÇÈÿÇÇÈÿÇÇÈÿÈÈÉÿÈÈÉÿÈÈÉÿÈÈÉÿÉÈÉÿÉÈÊÿÉÉÊÿËËÌÿ½½¾ÿ££¤ÿ  ¡ÿ¢¡¤ÿ¡¡£ÿ¹¹ºÿ½½¿ÿœ›ÿwvyjporqpsÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿqps?³³´ÿØØÙÿÚÚÛÿÇÆÇÿÇÆÇÿÇÇÈÿÇÇÈÿÈÇÈÿÈÈÈÿÈÈÈÿÈÈÉÿÈÈÉÿÈÈÉÿÉÉÉÿÉÉÉÿÉÈÉÿÉÉÉÿÉÉÊÿÉÉÊÿÊÉÊÿÊÊËÿÊÊËÿÊÊËÿÊÊËÿËÊËÿËÊËÿÎÍÎÿÂÂÃÿ¤¤¦ÿœœžÿžž ÿ¼»¼ÿ²²³ÿ––—ÿporDmlonmpÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿgfi¬¬­õÔÔÕÿßÞßÿÌÌÍÿÈÈÉÿÉÉÉÿÉÉÊÿÉÉÊÿÊÉÊÿÊÊÊÿÊÊËÿÊÊËÿÊÊËÿÊÊËÿËËÌÿËËÌÿËËÌÿËËÌÿÌÌÌÿÌÌÌÿÌÌÌÿÌÌÍÿÌÌÍÿÌÌÍÿÍÌÍÿÌÌÍÿÌÌÍÿÏÏÐÿÊÉËÿª©«ÿ™™šÿºº»ÿ¨§©ÿþhhk0jiljilÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿa`c¥¥§ÚÐÐÑÿÒÒÓÿÒÒÓÿÑÑÒÿÍÍÎÿËËÌÿÌÌÍÿÌÌÍÿÌÌÍÿÌÍÍÿÌÌÍÿÍÌÍÿÍÍÍÿÍÍÎÿÍÍÎÿÍÍÎÿÍÍÎÿÎÍÎÿÎÍÎÿÎÎÏÿÎÎÏÿÎÎÏÿÎÎÏÿÎÏÏÿÎÎÏÿÏÏÏÿÏÎÏÿÑÐÑÿÏÏÐÿ¹¸ºÿ¸¸ºÿŸŸ ÿ‚‚„õcbf fehfehÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸ¼ÓÒÔÿËËÌÿÍÍÍÿÏÏÐÿØØÙÿÓÓÓÿÍÎÎÿÎÎÏÿÎÎÏÿÎÎÏÿÏÎÏÿÏÎÐÿÏÏÐÿÏÏÏÿÏÏÏÿÐÐÑÿÖÖÖÿÒÒÓÿÏÏÐÿÐÏÐÿÐÏÐÿÐÐÐÿÐÐÑÿÐÐÑÿÐÑÑÿÑÐÑÿÑÑÒÿÑÐÒÿ×רÿááâÿÁÀÁÿ™™›ÿxwzç__bbbebbeÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ’ªÚÚÛÿ×××ÿÍÍÎÿÐÐÑÿÖÕÖÿßßàÿÕÔÕÿÓÓÓÿÓÓÔÿÓÓÔÿÔÔÕÿÔÔÕÿÔÔÕÿÔÔÕÿØ×ØÿÚÚÛÿÚÙÚÿÜÛÝÿÚÙÛÿÖÖ×ÿ×רÿÖÖ×ÿÖÖ×ÿÖÕÖÿÖÕÖÿÖÕÖÿÖÖ×ÿÝÝÞÿÞÞßÿÚÚÚÿÙÙÙÿÅÅÅÿ}}È\[_a`ca`cÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿyx|„ÑÑÑÿØØØÿÏÏÏÿÏÎÏÿÎÎÏÿÓÓÔÿÒÒÒÿÒÒÓÿÓÓÓÿÓÓÔÿÔÔÕÿÔÔÕÿÔÔÕÿÕÕÖÿÔÓÔÿÑÑÑÿÐÏÐÿÑÑÒÿÔÔÕÿÕÕÖÿÕÕÖÿÕÕÖÿÕÔÕÿÔÔÕÿÔÔÕÿÔÔÕÿÔÔÕÿÖÖ×ÿÒÒÓÿÕÔÖÿØØÙÿ×רÿŒ‹Ž\\`bbfbbfÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿcbfšš›3®®®5¬¬¬5¬¬­5£¢¤5ªª¬6ÙÙÙ6©©ª5««¬5¬¬­5¬¬­5¬¬­5¬¬­5¬¬®5­¬®5®­®5®®¯5®®¯5­­®5­­®5­­®5­­¯5®­¯5®­¯5®­¯5­¬¯5ÙÙÚ6³³µ6¤¤¦5±°²5²²´5  ¢4[Z_ VV[WW\WW\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿüÿÿüÿÿþÿÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿÿàÿÿàÿÿàÿÿàÿÿàÿÿàÿÿàÿÿàÿÿðÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿø?ÿÿøÿÿøÿÿøÿÿøÿÿøÿÿøÿÿüÿÿüÿÿÿüÿÿÿüÿÿÿÿÿÿÿÿÿÿÿ(@€ Bÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿ!ÿÿÿ#ÿÿÿ%ÿÿÿ&ÿÿÿ&ÿÿÿ(ÿÿÿ(ÿÿÿ)ÿÿÿ)ÿÿÿ)ÿÿÿ)ÿÿÿ(ÿÿÿ(ÿÿÿ'ÿÿÿ&ÿÿÿ$ÿÿÿ#ÿÿÿ"ÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿ$ÿÿÿ(ÿÿÿ-ÿÿÿ0ÿÿÿ4ÿÿÿ7ÿÿÿ:ÿÿÿ>ÿÿÿAÿÿÿDÿÿÿFÿÿÿHÿÿÿIÿÿÿKÿÿÿKÿÿÿLÿÿÿLÿÿÿKÿÿÿKÿÿÿIÿÿÿHÿÿÿFÿÿÿCÿÿÿAÿÿÿ>ÿÿÿ:ÿÿÿ7ÿÿÿ4ÿÿÿ0ÿÿÿ,ÿÿÿ(ÿÿÿ$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿ!ÿÿÿ'ÿÿÿ,ÿÿÿ2ÿÿÿ7ÿÿÿ=ÿÿÿBÿÿÿHÿÿÿMÿÿÿRÿÿÿVÿÿÿZÿÿÿ^ÿÿÿbÿÿÿdÿÿÿfÿÿÿhÿÿÿiÿÿÿiÿÿÿiÿÿÿiÿÿÿhÿÿÿfÿÿÿcÿÿÿ`ÿÿÿ]ÿÿÿYÿÿÿUÿÿÿQÿÿÿLÿÿÿGÿÿÿBÿÿÿ<ÿÿÿ7ÿÿÿ1ÿÿÿ+ÿÿÿ&ÿÿÿ!ÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿTTT„„„¦¦¦ »»»ÆÆÆÏÏÏ×××ÝÝÝ"âââ'ççç-êêê2ííí8ïïï=ðððBòòòGôôôLõõõP÷÷÷U÷÷÷Yøøø]ùùù_úúúbúúúeúúúfúúúgúúúgúúúhúúúgúúúfúúúdùùùaùùù_øøø\÷÷÷XöööTõõõOôôôJòòòFñññ@ïïï;ììì5êêê0æææ+âââ&ÝÝÝ ÕÕÕÉÉÉ´´´ ŸŸŸªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(>IOQTW)))X333[==<]FFF`NNNbWWWa```agggcnnmfssshyyyk~~~nƒƒ‚p‡‡‡rŠŠŠtuw’’’y””“z•••{–––{——–{——–{––•{•••z“”“y’’‘wvŒŒŒtŠŠ‰s……„s~~~tyyyrtttpooonjjikccbh\\\eUUUbNNM`EFE^;;;[+++UG/ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ D&&&¡ÍÚßààààààááß Ý  Ý! "Ý""#Þ$#$Þ%%&Þ%$&Þ%$&Þ%%'Þ&%'Þ'&(ß(')ß((*ß--/ß+*,ß--/ß++-ß**,ß,+-ß'')ß+*,Þ%%&Þ(()Þ"!#Þ$#%à !áâââááààà à àÜ ÒBBB¯QQQT ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ XXYšqqsÿHGIÿ:9<ÿ??BÿDCGÿIHLÿLKOÿLKOÿLKNÿKKNÿNNQÿ==@ÿÿÿÿÿÿÿÿ!ÿ$$&ÿ++-ÿ214ÿ669ÿ<;>ÿ>>Aÿ++-ÿCBFÿ224ÿ?>Aÿ;:=ÿ447ÿAADÿ/.1ÿDCGÿ--.ÿFFIÿ002ÿ>>AÿJJOÿHHLÿHGLÿHGLÿIHMÿIHMÿIIMÿKIMÿKINÿLJOÿFEIÿ=ÿÿÿÿ ÿÿÿ"ÿ&%(ÿ-,/ÿ326ÿ:9<ÿAADÿIHKÿKJMÿÿRQSÿÿ334ÿ334ÿÿMLOÿÿVVXÿÿOOQÿÿ001ÿgfjÿ`_cÿ`_cÿ`_cÿ``cÿ``cÿ`acÿdbfÿ_VZÿjaeÿ`]bÿRQTÿ‰ˆŠÿ¢¢¤é[[[:ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ)))-žžŸÜ¤¤¥ÿ\\^ÿEDGÿLKNÿQPSÿWVYÿ\\^ÿ\\_ÿ\[_ÿ\\_ÿbbdÿ>=?ÿÿÿ ÿ ÿÿÿ! #ÿ('+ÿ//2ÿ658ÿ=ÿÿWWXÿÿ[[\ÿÿJJLÿ ÿ$$$ÿonqÿggiÿggjÿggjÿggjÿgfjÿmilÿ,ŽKÿoÿâÿvûÚÿ0ƒHÿVPTÿwvxÿ®®¯ÿ^ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿBBBD¼»½ó¡ ¢ÿWWYÿJJLÿRQTÿXWZÿ^^aÿbbeÿccfÿdcfÿdcfÿjilÿ99:ÿÿÿ ÿÿÿ ÿ%%(ÿ--/ÿ447ÿ;:=ÿBADÿIIKÿQQTÿTSVÿÿ\[]ÿÿ656ÿ>=?ÿÿYXZÿÿZY[ÿ ÿEDFÿ(()ÿÿsruÿkjmÿkjmÿkjmÿkjnÿkinÿnloÿ`jÿqÖžÿ}Ô¡ÿ^ƒfÿSQSÿpoqÿ°°±ÿŒŒŒuÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQQQSÅÅÆü——˜ÿUTWÿMMOÿUTWÿ[Z^ÿbacÿeehÿfehÿffhÿffhÿmloÿ;;=ÿÿÿ ÿÿÿ "ÿ(')ÿ//1ÿ669ÿ=<@ÿDDFÿKKMÿTTVÿWVYÿÿRRSÿÿ'&'ÿCBDÿ ÿWVXÿÿWVXÿÿ@@Aÿ113ÿÿrrtÿnnpÿnnpÿnnpÿnnpÿnnpÿnnpÿrtrÿm|jÿm|kÿlmmÿSRUÿgghÿ¯¯°ÿžžŸ‹"""ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿgggdÍÍÎÿŠŠ‹ÿGGIÿCBDÿJIKÿOOQÿTTVÿXX[ÿYX[ÿXX[ÿYX[ÿ_^bÿ../ÿÿÿÿ  ÿÿÿÿ$#&ÿ+*-ÿ214ÿ88:ÿ?>AÿGFIÿJILÿÿ=<>ÿÿÿ778ÿÿKJLÿÿGFIÿÿ,,-ÿ112ÿ ÿ^]`ÿb`cÿa_bÿa_bÿa`bÿa`cÿbadÿbadÿc`dÿcaeÿ]\_ÿGFHÿUTVÿ£££ÿ««¬¢444ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿuuuoÍÍÎÿyxyÿ889ÿ556ÿ<;=ÿA@BÿFEGÿJILÿKJLÿKJMÿKKMÿQPSÿ--.ÿ ÿÿÿÿÿ!!#ÿ'&(ÿ,+-ÿ102ÿ768ÿ<<=ÿAACÿGFHÿJIKÿÿ??@ÿ///ÿ,,-ÿ>>>ÿÿJIKÿÿCBDÿ))*ÿ112ÿ99:ÿÿRQSÿVUWÿTTVÿTTUÿTTUÿSSUÿSSUÿTSTÿSSTÿTTUÿNNPÿ;;<ÿAABÿ‰ˆ‰ÿ¹¹º³ZZZÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€€€zççèÿÊÊÊÿ¸·¸ÿ¸¸¹ÿ¼¼½ÿ¿¿ÀÿÂÂÃÿÄÅÅÿÆÆÈÿÉÈÉÿÊËËÿÍÍÎÿÎÎÏÿÏÏÐÿÒÒÓÿÔÔÕÿÖÖ×ÿØØÙÿÚÚÛÿÛÛÜÿÜÝÝÿÝÝÞÿÞÞßÿàààÿáàáÿááâÿãããÿçççÿæææÿæççÿåååÿããäÿääåÿááâÿããäÿàààÿáááÿßßàÿÝÞÞÿßÞßÿÛÛÛÿÙÙÚÿ×רÿÖÖ×ÿÔÔÕÿÒÒÓÿÑÑÒÿÐÏÑÿÍÍÏÿÌÌÍÿÊÊËÿÄÄÆÿÃÃÅÿÔÔÖÿÆÅǺEEEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿsrsyäãäÿããäÿßßàÿåäæÿáàâÿßßàÿßßàÿàßàÿàßàÿàßàÿàßàÿßßàÿßàáÿààáÿààáÿààáÿààáÿáàáÿáàáÿáááÿáááÿááâÿâáãÿââãÿãããÿããäÿäãäÿäãåÿäãäÿããäÿãããÿãâãÿââãÿââãÿââãÿââãÿââãÿãâãÿãâäÿãâäÿãâãÿãããÿãããÿãâäÿãâäÿãâäÿãâãÿââãÿâáãÿäãåÿäãåÿææçÿéèéÿååæÿžžŸ›ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGHRââãÿÇÇÈÿ–•–ÿ––—ÿÜÜÝÿæççÿääåÿãããÿããäÿããäÿæææÿææçÿææçÿçççÿççèÿçèèÿèèéÿééêÿëëìÿíìíÿìììÿêêêÿååæÿàààÿÝÝÝÿÚÚÚÿÚÙÚÿÙÙÚÿÜÜÝÿááâÿååçÿèèéÿëëìÿëëìÿëëìÿëëëÿëëëÿëëìÿëëìÿëëìÿìììÿììíÿììíÿììíÿììíÿììíÿîîïÿïïðÿññòÿâáâÿ¬¬­ÿŽÿÌËÌÿùùùÿkkkvÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ+++6ÖÖ×ÿÎÎÎÿ||~ÿuuvÿÈÈÈÿææçÿÊÉÊÿµµ¶ÿ·¶¸ÿ³³´ÿ¿¿¿ÿÛÛÜÿääåÿääåÿååæÿçæçÿäääÿØØØÿÅÅÆÿ°°±ÿžžŸÿ‘‘’ÿ‰ˆˆÿ‚‚‚ÿ€ÿzyzÿtstÿonnÿmmnÿqqqÿwwxÿ}}~ÿ‡†‡ÿŠŠŠÿŒ‹ŒÿŽÿ““”ÿšš›ÿœœÿœÿ  ¡ÿ¦¦§ÿªª«ÿª©ªÿ««¬ÿ®®®ÿµµ¶ÿÚÚÛÿïïïÿÞÞÞÿžžŸÿggiÿ­­­ÿòòóÿUUUTÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊÉËðåååÿ®®¯ÿÃÃÄÿÝÝÞÿÈÈÉÿ·¶¸ÿàààÿåååÿààáÿÎÎÏÿ··¸ÿÌËÌÿÞÞßÿÒÒÒÿ¶µ¶ÿžžÿ•”•ÿžÿ¯¯¯ÿÃÂÂÿÔÓÓÿáàßÿééèÿíííÿïïïÿîííÿìììÿéèèÿâââÿØ××ÿÕÕÕÿ×ÖÖÿÙØØÿØ××ÿÜÜÜÿàààÿâáâÿâáâÿÞÞßÿàààÿáââÿâââÿàààÿÛÛÛÿÜÜÝÿÉÉÊÿ¯®¯ÿÙØÚÿïîïÿÈÈÉÿ²²³ÿääåÿæåçÿ3348ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ¸¸¹ÝàààÿÚÚÛÿÊÊËÿ¹¸¹ÿµµ¶ÿëëëÿÿÿÿÿø÷øÿôôôÿòñòÿãâäÿ¾¾¿ÿ©¨©ÿÿ²²²ÿÕÕÕÿñññÿÿÿÿÿÿÿÿÿÿÿÿÿþþýÿùùùÿöö÷ÿóóõÿóóôÿóóôÿôôõÿõõöÿ÷÷øÿúúûÿûûüÿûûûÿúúûÿûûûÿúúûÿúúúÿùùúÿúúúÿûûûÿûúûÿúúúÿúúûÿúúûÿûûûÿþþþÿÿÿÿÿÒÒÓÿ««¬ÿÅÅÆÿààáÿììíÿîîïÿÖÖÖó%%&ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ ¢ÀÛÚÛÿÇÇÈÿËËËÿÝÝÞÿùùùÿÿÿþÿììíÿßßàÿÝÝÝÿÝÜÜÿâáâÿìëìÿÞÞÞÿñññÿÿÿÿÿýýýÿôôôÿìììÿçççÿäãäÿâáâÿáàâÿàßáÿàßàÿàßàÿàààÿáàáÿáàáÿááâÿââãÿãããÿãããÿããäÿããäÿãããÿäããÿäääÿäääÿääåÿääåÿääåÿåäåÿåäæÿååæÿèçèÿôôôÿÿÿÿÿããäÿ¾½¿ÿ··¸ÿÐÐÑÿëêëÿÃÂÄÞ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ——˜¦ÑÑÓÿÙÙÚÿÿÿÿÿÿÿÿÿöö÷ÿèééÿÝÝÞÿÙÙÚÿÙÙÚÿÚÚÛÿÙÙÚÿÛÛÛÿëëëÿïïðÿççèÿààáÿÝÝÞÿÜÜÝÿÜÜÜÿÜÛÝÿÜÜÝÿÜÜÝÿÜÜÝÿÜÜÝÿÝÝÝÿÝÝÝÿÝÝÞÿÝÝÞÿÞÞßÿÞÞßÿÞÞßÿßßßÿßßàÿßßàÿàààÿààáÿààáÿáááÿáááÿáááÿááâÿááâÿâââÿâââÿââãÿååæÿïïðÿýýýÿýýþÿÞÞßÿÊÊËÿççèÿ¨¨©Äÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿwwx‚ÔÔÕÿÙØÙÿññòÿãããÿÜÛÜÿØØÙÿÖÖ×ÿÖÖ×ÿ×רÿ×רÿØ×ØÿØØØÿØØÙÿÙÙÚÿÙÙÙÿÙØÙÿÙÙÚÿÙÙÚÿÙÙÛÿÙÙÛÿÚÚÛÿÛÚÛÿÛÚÛÿÛÚÛÿÛÛÜÿÛÛÜÿÜÛÜÿÜÛÜÿÜÜÜÿÜÜÝÿÜÜÝÿÝÜÝÿÝÝÝÿÝÝÝÿÝÝÞÿÝÝÞÿÞÞÞÿÞÞßÿÞÞßÿÞÞßÿßßßÿßßàÿßßáÿààáÿààáÿààáÿâââÿææçÿððñÿèçéÿÑÑÒÿæåæÿ“’”¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿoopeÒÒÓÿÕÕÖÿããäÿÜÜÝÿÔÔÕÿÔÓÔÿÕÔÕÿÕÕÖÿÕÕÖÿÖÕÖÿÖÕÖÿÖÖÖÿÖÖÖÿÖÖÖÿ×Ö×ÿ×Ö×ÿ×ÖØÿ×ÖØÿ×רÿ×רÿ×רÿØ×ØÿØ×ÙÿØØÙÿØØÙÿÙØÚÿÙØÚÿÙÙÚÿÚÚÛÿÚÚÛÿÚÚÛÿÛÚÛÿÛÛÜÿÛÛÜÿÛÛÜÿÜÛÜÿÜÜÜÿÜÜÝÿÜÜÝÿÜÜÝÿÝÜÝÿÝÝÞÿÝÝÞÿÝÝÞÿÞÝÞÿÞÝßÿÞÝßÿßßßÿääåÿÝÝÞÿÑÑÒÿääåÿttu†ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿKKLBÎÎÏÿÒÒÒÿÒÒÓÿÖÖ×ÿÌÌÎÿÍÌÍÿÒÒÒÿÒÒÓÿÒÒÓÿÓÓÓÿÔÔÔÿÔÔÔÿÔÔÕÿÔÔÕÿÔÔÕÿÔÔÕÿÕÔÕÿÕÕÕÿÕÕÕÿÕÕÖÿÕÕÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖ×ÿÖÖ×ÿ×רÿ×רÿ×רÿ×רÿØØØÿØØØÿØØØÿØØÚÿØÙÚÿÙÙÚÿÚÚÚÿÚÚÛÿÚÚÛÿÚÚÛÿÛÛÛÿÛÛÛÿÛÛÜÿÛÛÜÿÜÜÜÿÜÜÜÿÜÜÜÿÝÝÝÿãâãÿØ×ÙÿÐÑÒÿààáÿ]]^dÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿFFF*ÅÄÆúÒÑÒÿÏÎÏÿºº»ÿÂÂÃÿÝÜÞÿÔÓÔÿÐÐÑÿÐÐÑÿÑÐÑÿÑÐÑÿÑÐÒÿÑÐÒÿÑÑÒÿÑÑÒÿÑÑÓÿÒÑÓÿÓÒÓÿÓÒÔÿÓÒÔÿÓÓÔÿÓÓÔÿÔÓÔÿÕÕÖÿÕÕÖÿÕÕÖÿÔÓÔÿÓÓÔÿÔÔÕÿ×רÿÙÙÙÿØ×ÙÿÖÖ×ÿÖÖ×ÿ×Ö×ÿ×Ö×ÿ×××ÿ×רÿ××ÙÿØ×ÙÿÙØÙÿÙØÚÿÙØÚÿÙÙÚÿÚÙÚÿÚÙÛÿÚÚÛÿÜÛÜÿâáâÿÔÓÕÿÒÑÒÿ×ÕØÿA@ADÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ&&&ºº»êÏÏÏÿÃÃÄÿ··¸ÿøøøÿðððÿÕÕÕÿÎÎÎÿÎÎÏÿÎÎÏÿÎÎÏÿÎÎÏÿÏÏÏÿÏÏÏÿÏÏÐÿÏÏÐÿÐÐÐÿÐÐÐÿÐÐÐÿÐÐÑÿÐÐÑÿÔÔÕÿØØØÿÖÖÖÿÏÏÏÿÈÇÈÿÁÀÁÿ¹¹¹ÿ³³³ÿ°°±ÿ··¸ÿÈÈÉÿ×××ÿ×Ö×ÿÕÔÕÿÕÕÖÿÕÕÖÿÖÕÖÿÖÕÖÿÖÖÖÿÖÖÖÿÖÖ×ÿÖÖ×ÿ×Ö×ÿ×רÿ×רÿ×רÿÚÚÚÿààáÿÐÏÑÿÑÑÓÿÍÌÎú-,-)ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ&&&°¯±ÑÌÌÌÿ´´¶ÿßßàÿûûûÿÞÝÞÿÎÍÏÿËËÌÿËËÌÿËËÌÿËËÌÿÌÌÌÿÌÌÌÿÌÌÍÿÌÌÍÿÍÍÎÿÍÍÎÿÍÍÎÿÎÎÏÿÐÐÑÿÜÜÝÿæææÿÞÞÞÿÖÖÖÿÒÒÓÿÒÑÒÿÑÐÑÿÏÏÐÿËÊÌÿÂÁÂÿ°°±ÿžžÿ¥¥¦ÿÊÊËÿÕÕÖÿÒÒÓÿÓÒÔÿÓÓÔÿÓÓÔÿÔÔÕÿÔÔÕÿÔÔÕÿÔÔÕÿÕÕÖÿÕÕÖÿÕÕÖÿÕÕÖÿ××ÙÿÞÞàÿÌÌÎÿÔÔÕÿ¶¶¸êÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿœž¸ÅÅÆÿ»º¼ÿ÷÷÷ÿçæçÿÏÏÐÿÈÇÉÿÉÉÊÿÉÉÊÿÊÉÊÿÊÉËÿÊÊËÿÊÊËÿËËËÿËÊËÿËÊËÿËËËÿËËÌÿÏÎÏÿåäåÿóóóÿÜÛÜÿÐÐÑÿÏÎÐÿÏÎÏÿÏÎÏÿÏÎÏÿÏÏÐÿÐÏÑÿÒÑÒÿÓÒÔÿÌÌÌÿ«ª«ÿ•”•ÿÄÃÄÿÒÓÔÿÐÐÑÿÑÑÑÿÑÐÒÿÑÑÒÿÒÑÒÿÒÑÓÿÒÒÓÿÓÒÓÿÓÒÔÿÓÒÔÿÓÒÔÿ×Ö×ÿÝÜÞÿÈÇÉÿÒÑÓÿ°¯±Ñ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽŽ—½½¿ÿÉÉÊÿ÷÷øÿÙÙÚÿÈÈÉÿÇÇÇÿÇÇÈÿÇÈÈÿÈÈÉÿÈÈÉÿÈÈÉÿÉÉÉÿÉÉÊÿÉÉÊÿÉÉÊÿÉÉÉÿÊÊËÿÛÛÜÿùùùÿÖÖ×ÿÉÉÊÿÊÊËÿÊÊËÿËËËÿËËËÿËËËÿËËÌÿËÌÌÿÌÌÍÿÌÌÍÿÏÏÏÿÒÒÓÿ°°±ÿ™™šÿÍÍÍÿÏÏÐÿÎÎÏÿÏÏÐÿÏÏÐÿÏÏÐÿÏÏÐÿÏÏÑÿÏÏÑÿÏÐÑÿÐÐÑÿÐÐÑÿÖÖ×ÿÚÚÛÿÅÅÆÿÓÓÔÿ·ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿyyzz¹¹ºÿÔÔÕÿïïïÿÒÑÒÿÆÅÆÿÆÆÇÿÆÆÇÿÆÆÇÿÇÆÇÿÇÆÇÿÇÆÈÿÇÇÈÿÇÇÈÿÈÇÉÿÈÇÉÿÇÇÈÿËÊÌÿëëìÿããäÿÅÄÆÿÈÇÉÿÈÈÉÿÉÈÉÿÉÈÊÿÉÉÊÿÉÉÊÿÊÉËÿÊÊËÿÊÊËÿÊÊËÿÊÊËÿÌËÌÿÎÍÏÿžžÿ»»¼ÿÎÍÏÿÌËÍÿÌÌÍÿÌÌÍÿÌÌÍÿÌÌÎÿÌÌÎÿÌÌÎÿÍÍÎÿÍÍÎÿÎÍÎÿ×Ö×ÿØØÙÿÄÃÅÿÐÏÐÿˆ‡‰•ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽV¯°±ÿ×רÿèèéÿÌÌÌÿÄÄÅÿÄÄÅÿÅÅÅÿÅÅÅÿÅÅÆÿÅÅÆÿÆÆÇÿÆÆÇÿÆÆÇÿÇÇÇÿÇÇÇÿÇÇÈÿËËÌÿëììÿØ×ØÿÂÂÃÿÅÅÆÿÆÅÆÿÆÆÇÿÆÆÇÿÇÆÇÿÇÇÇÿÇÇÇÿÇÇÈÿÇÇÉÿÈÈÉÿÉÉÉÿÉÈÉÿÍÍÎÿªªªÿ¹¹ºÿÌÌÌÿÊÊËÿÊÊËÿÊÊËÿÊÊËÿÊÊËÿËËËÿËËËÿËËËÿËËÌÿÌÌÍÿ×××ÿÓÓÔÿÂÂÃÿËËÌÿrqsvÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ<­¬®ÿÐÐÑÿååæÿÊÊËÿÂÂÃÿÂÂÄÿÂÂÄÿÃÃÄÿÄÃÅÿÄÄÅÿÄÄÅÿÄÄÅÿÅÅÆÿÅÅÆÿÅÅÇÿÅÅÇÿÊÊËÿßßàÿããäÿÀÀÁÿÃÃÄÿÄÃÄÿÄÄÅÿÄÄÅÿÄÄÅÿÅÅÅÿÅÅÆÿÅÅÆÿÅÅÆÿÅÅÆÿÅÅÆÿÇÇÈÿÈÇÈÿ¬«¬ÿÃÂÄÿÈÇÉÿÇÇÉÿÇÇÉÿÇÇÉÿÈÈÉÿÈÈÉÿÈÈÉÿÈÈÊÿÉÉÊÿÊÉÊÿËÊËÿÖÖ×ÿÍÍÎÿÁÁÂÿÀ¿Áÿ‘Tÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ—–˜«ª¬õÆÆÆÿåäåÿÌËÍÿÂÁÃÿÂÂÂÿÂÂÃÿÂÂÃÿÂÂÃÿÃÂÃÿÃÃÄÿÃÃÄÿÃÃÄÿÄÃÄÿÄÄÅÿÄÄÅÿÇÆÇÿÎÍÎÿéééÿØØØÿ¿¾Àÿ¿¿ÀÿÁÀÂÿÂÁÃÿÂÂÃÿÃÂÃÿÃÂÃÿÃÂÄÿÃÃÄÿÃÃÄÿÅÅÅÿÇÆÈÿ¹¸ºÿºº¼ÿÆÅÇÿÅÄÅÿÅÅÅÿÅÅÅÿÅÅÆÿÆÅÆÿÆÅÆÿÆÆÇÿÆÆÇÿÆÆÇÿÇÆÇÿÉÈÊÿ×רÿÉÇÉÿÁÀÂÿº¹»ÿhhj8ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”“–¨¨©æ¼¼½ÿÜÜÝÿÑÑÒÿÂÂÃÿÀÀÁÿÁÁÁÿÁÁÂÿÁÁÃÿÂÁÃÿÂÂÃÿÂÂÃÿÂÂÃÿÃÃÃÿÃÃÄÿÃÃÄÿÃÃÄÿÇÇÈÿÎÎÐÿåäåÿáàáÿËËÌÿÀÀÂÿ½½¾ÿ½½¾ÿ½½¾ÿ¾¾¿ÿÀ¿ÁÿÂÂÃÿÅÅÆÿÇÇÈÿÁÁÂÿ¾¾ÀÿÂÂÅÿÁÁÃÿÂÂÃÿÂÂÃÿÂÂÄÿÃÃÄÿÄÃÄÿÄÃÅÿÄÄÅÿÄÄÅÿÄÄÅÿÅÄÅÿÈÈÈÿØØØÿÄÃÅÿÁÀÂÿ¯¯±óŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¤¢¤É»»¼ÿÈÈÉÿÙØÙÿÅÄÅÿÀ¿ÀÿÀÀÀÿÀÀÁÿÁÀÁÿÁÀÁÿÁÀÂÿÁÁÂÿÁÁÂÿÂÁÂÿÂÁÂÿÂÂÃÿÂÂÃÿÃÂÄÿÅÄÆÿÉÈÉÿ×××ÿãâãÿààáÿÚÙÚÿÔÓÔÿÑÑÒÿÏÎÐÿÏÏÏÿÐÏÐÿÌÌÍÿÅÅÆÿÁÀÂÿ¿¿Áÿ¿¾Àÿ¿¾ÀÿÀ¾ÀÿÀ¿ÁÿÀÀÁÿÁÀÁÿÁÀÂÿÁÀÂÿÁÁÂÿÂÁÃÿÃÁÃÿÃÁÃÿÇÇÈÿÖÖØÿ¾¾ÀÿÀ¿Áÿ¨§©à–•— ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžŸ³½½¿ÿ¼¼½ÿÐÐÑÿÍÍÏÿ¾¾¿ÿ¿¿ÀÿÀ¿ÁÿÀÀÁÿÀÀÁÿÁÀÁÿÁÁÁÿÁÁÁÿÁÁÂÿÁÁÂÿÁÁÂÿÂÁÂÿÂÂÃÿÂÂÃÿÃÃÄÿÄÄÅÿÇÆÈÿÊÊËÿËÌÌÿÐÐÑÿÒÑÒÿÎÎÏÿÉÉÊÿÃÃÅÿ¾¾Àÿ¼¼¾ÿ¼¼½ÿ¼»½ÿ¼¼½ÿ½½¾ÿ½½¾ÿ½½¾ÿ½½¾ÿ¾½¾ÿ¾¾¿ÿ¾¾Àÿ¿¾Àÿ¿¾Àÿ¿¾Àÿ¿¿ÁÿÈÇÉÿÖÕ×ÿ¹¹ºÿ½½¿ÿŸŸ¡Æÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜—𻼽ÿ¾¾¿ÿÀÀÀÿÎÍÏÿÉÈÊÿ¾¾¿ÿ¿¾Àÿ¿¿ÀÿÀ¿Àÿ¿¿ÁÿÀÀÁÿÀÀÁÿÁÁÁÿÁÁÂÿÁÁÂÿÁÁÂÿÂÁÂÿÂÂÃÿÂÂÃÿÂÂÃÿÂÂÃÿÄÃÄÿÁÀÁÿº¹»ÿ¸·¹ÿ¸¸ºÿ¸¸ºÿ¸¸¹ÿ¹¸ºÿ¹¸ºÿ¹¹ºÿ¹¹ºÿºº»ÿºº»ÿºº»ÿºº¼ÿº»¼ÿ¼»¼ÿ¼»½ÿ¼»½ÿ¼¼½ÿ¼¼½ÿ½¼¾ÿ½½¾ÿÉÈÉÿÔÓÕÿ··¸ÿºº»ÿ˜˜šªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ“’”s¸¸ºÿ¿¿Àÿ½½½ÿÂÁÂÿÏÎÐÿÎÍÎÿÀ¿Àÿ¿¾¿ÿÀ¿ÀÿÀ¿ÀÿÀ¿ÀÿÀÀÀÿÀÀÀÿÀÀÁÿÁÀÂÿÁÀÁÿÁÁÂÿÁÁÃÿÁÁÃÿÂÁÃÿÂÁÃÿÂÁÃÿÃÁÄÿÁÀÃÿ¸¸ºÿµ´¶ÿ¶µ·ÿ·¶·ÿ··¸ÿ··¸ÿ¸·¸ÿ¸·¸ÿ¸·¸ÿ¸¸¹ÿ¸¸¹ÿ¸¸¹ÿ¹¸¹ÿ¹¸ºÿ¹¹ºÿ¹¹ºÿº¹»ÿº¹»ÿº¹ºÿ¹¹ºÿÉÈÊÿÓÓÕÿ¼»½ÿ¶¶·ÿ’‘“ˆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‹‹ŽO³³µÿÀ¿Àÿ««­ÿ™™šÿ¸¸¹ÿËËÌÿÓÓÔÿÂÂÃÿ¿¿ÀÿÀ¿ÁÿÀÀÁÿÀÀÁÿÀÀÁÿÀÀÁÿÁÁÂÿÁÁÂÿÁÁÂÿÁÁÂÿÁÁÂÿÁÁÂÿÁÁÂÿÁÁÃÿÁÁÃÿÂÂÃÿÂÂÃÿ¹¹»ÿ²²´ÿ³³µÿ´´¶ÿ´´¶ÿ´´¶ÿ´´¶ÿ¶µ·ÿ¶¶·ÿ¶¶·ÿ¶¶·ÿ·¶¸ÿ··¸ÿ··¸ÿ··¸ÿ··¹ÿ¶¶·ÿ¸¸¹ÿÃÃÅÿÂÂÃÿ——˜ÿŽŽÿ°°±ÿ‹‹Žhÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‡‡‰8­­®ÿÏÏÏÿµµµÿ˜˜šÿ²²³ÿÑÑÑÿåäåÿÊÉÊÿ¿¿Àÿ¿¿ÀÿÀ¿ÁÿÀ¿ÁÿÀ¿ÁÿÀÀÁÿÁÁÂÿÁÁÂÿÂÁÂÿÂÀÂÿÁÁÂÿÁÁÂÿÂÁÂÿÂÂÂÿÂÂÂÿÂÂÂÿÃÂÃÿÃÃÄÿ¼¼¼ÿ²²²ÿ²±²ÿ²²³ÿ²²³ÿ³²³ÿ³²´ÿ³³´ÿ³³´ÿ³³µÿ´´µÿµ´µÿµ´¶ÿ´´µÿ´³´ÿ¾½¿ÿÇÆÈÿ¾¾¿ÿÈÈÉÿ‰ˆŠÿˆˆŠÿ§¦¨ÿ‡‡‰Fÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ„ƒ†¨§©óÃÃÄÿ¼¼½ÿºº»ÿÛÛÜÿðððÿÞÞßÿÅÅÇÿ¿¿ÀÿÀÀÀÿÀÀÁÿÀÀÁÿÀÀÁÿÀÁÁÿÁÁÁÿÁÀÁÿÁÀÂÿÁÁÂÿÁÁÂÿÁÁÂÿÁÁÃÿÂÁÂÿÂÁÃÿÂÂÃÿÂÂÄÿÃÂÄÿÃÃÅÿ¾¾¿ÿ²±²ÿ¯¯°ÿ±°±ÿ±±²ÿ±±²ÿ±±²ÿ²±²ÿ²±²ÿ²²³ÿ²²³ÿ±±²ÿ¶¶·ÿÅÅÆÿÄÄÅÿ¶¶·ÿ²±³ÿ¸¸¹ÿ±±³ÿ¶¶·ÿ  ¢üƒ‚„-ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€‚ ¡¡£â¾½¾ÿ½½¾ÿçæçÿðððÿÙÙÙÿÆÆÇÿÀÀÀÿÀÀÁÿÀÀÁÿÁÀÁÿÁÀÁÿÁÀÁÿÁÀÂÿÁÀÂÿÁÁÂÿÁÁÂÿÁÁÂÿÁÁÂÿÂÁÂÿÂÂÂÿÂÂÂÿÂÂÃÿÂÂÃÿÂÂÃÿÂÂÃÿÃÃÄÿÄÄÅÿÁÀÂÿ²²³ÿ¬¬­ÿ®®°ÿ®®°ÿ¯®°ÿ¯¯°ÿ°¯±ÿ°°±ÿ±°±ÿ¹¹ºÿÈÈÉÿ»»¼ÿ¯¯°ÿ°°±ÿ±±²ÿ±±²ÿ³²³ÿ¶µ¶ÿ——™í€€‚ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ™˜šÇº¹»ÿÚÚÛÿïïðÿÔÔÔÿÄÃÄÿ¿¿ÀÿÀÀÁÿÁÀÁÿÁÀÁÿÁÀÁÿÁÀÂÿÁÀÂÿÁÁÃÿÁÁÃÿÁÁÂÿÂÁÂÿÂÁÂÿÂÂÂÿÂÂÃÿÂÂÃÿÃÂÃÿÃÂÃÿÃÃÃÿÃÃÄÿÃÃÄÿÃÃÄÿÃÃÄÿÄÅÅÿÃÃÄÿµ´µÿ«ª«ÿ¬¬­ÿ¬¬­ÿ­¬®ÿ­¬®ÿ¬¬­ÿ²²³ÿßßßÿÌËÍÿ²²³ÿ°¯°ÿ¯¯°ÿ¯¯°ÿ®®¯ÿ¯®°ÿ²²³ÿ‘Ô~~€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘’®ºº»ÿÔÔÕÿââãÿÈÈÈÿÀÀÀÿÁÁÂÿÁÁÂÿÁÁÂÿÁÁÂÿÁÁÂÿÁÁÂÿÂÂÂÿÂÂÂÿÂÂÃÿÂÂÂÿÂÂÃÿÂÂÃÿÃÂÃÿÂÂÃÿÂÂÃÿÃÃÃÿÃÃÄÿÃÃÄÿÃÃÄÿÃÃÄÿÄÄÅÿÄÄÅÿÄÄÅÿÅÅÆÿÅÅÇÿ¹¹¹ÿªª«ÿª©«ÿ««¬ÿ««¬ÿ«ª«ÿ®®¯ÿÏÎÐÿòòòÿããäÿÓÓÔÿÌÌÍÿÊÊËÿÁÁÃÿ©©ªÿ¬¬­ÿ‡‡‰ºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿˆ‡ŠŒºººÿÏÎÏÿããäÿÉÈÉÿÁÀÂÿÁÁÂÿÁÁÂÿÁÁÃÿÂÁÃÿÂÁÃÿÂÁÃÿÂÂÃÿÂÂÃÿÃÂÃÿÃÂÃÿÃÂÄÿÃÃÄÿÃÃÄÿÃÃÄÿÄÃÄÿÄÃÄÿÄÃÄÿÄÄÄÿÄÄÅÿÄÄÅÿÅÄÅÿÅÄÅÿÅÄÅÿÅÅÆÿÅÅÆÿÇÆÇÿ½¼½ÿª©«ÿ§¦¨ÿ©¨ªÿ©¨ªÿ©¨ªÿ®­¯ÿ¾½¿ÿÌËÍÿÈÈÉÿÄÄÅÿÌËÍÿÖÖ×ÿª©«ÿ¦¥¦ÿ€šÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‚p¸¸ºÿÊÊËÿæåæÿÌÌÍÿÂÂÃÿÂÂÃÿÂÂÃÿÂÂÃÿÂÂÃÿÃÂÃÿÃÃÃÿÃÃÄÿÃÃÄÿÃÃÄÿÃÃÄÿÄÃÄÿÄÄÄÿÄÄÅÿÄÄÅÿÄÄÅÿÄÄÅÿÅÄÆÿÅÅÆÿÅÅÆÿÅÅÆÿÅÅÆÿÅÅÆÿÆÆÇÿÆÆÆÿÆÆÇÿÆÆÇÿÈÈÉÿÂÂÃÿ¬¬­ÿ¤¤¥ÿ¦¦¨ÿ§§¨ÿ§¦¨ÿ§¦¨ÿ¨¨©ÿ¨¨©ÿ¨¨©ÿ··¸ÿÍÌÎÿ¦¦¨ÿŸŸ¢ÿyy|yÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ{z|Oµ´µÿÈÇÈÿææçÿÏÏÐÿÃÃÄÿÃÃÄÿÄÃÄÿÄÃÄÿÄÃÄÿÄÄÅÿÄÄÅÿÄÄÅÿÄÄÅÿÅÄÅÿÅÄÅÿÅÅÅÿÅÅÅÿÅÅÅÿÅÅÆÿÅÅÆÿÆÆÆÿÅÅÆÿÆÆÆÿÆÆÇÿÆÆÇÿÆÆÇÿÆÆÇÿÆÆÇÿÇÇÈÿÇÇÈÿÇÇÈÿÇÇÈÿÉÉÊÿÆÆÇÿ±±²ÿ¢¢£ÿ¥¤¥ÿ¥¥¦ÿ¥¥¦ÿ¥¥¦ÿ¥¥¦ÿ¦¥¦ÿ¹¸»ÿÇÆÉÿ¢¢¥ÿ™™›ÿtsvWÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿutx6¯¯°ÿÆÆÇÿåååÿÓÓÔÿÅÅÆÿÄÄÅÿÄÄÅÿÅÅÅÿÅÅÆÿÅÅÆÿÅÅÆÿÅÅÆÿÅÅÆÿÆÅÆÿÆÅÇÿÆÆÇÿÆÆÇÿÆÆÇÿÆÆÇÿÇÆÇÿÇÇÇÿÇÇÈÿÇÇÈÿÇÇÈÿÇÇÈÿÇÇÈÿÈÇÈÿÈÇÈÿÈÇÉÿÈÈÈÿÈÈÉÿÈÈÉÿÈÈÉÿÊÉÊÿÊÊËÿ¶¶·ÿ¡¡£ÿ¢¡£ÿ£¢¤ÿ£¢¥ÿ£¢¥ÿ¤£¦ÿ½½¿ÿÀ¿Âÿ Ÿ¡ÿ’‘”ÿpos:ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿnnp¨§©óÆÆÇÿââãÿØØØÿÇÇÈÿÆÆÆÿÆÆÇÿÆÆÇÿÆÆÇÿÆÆÇÿÆÆÇÿÇÇÇÿÇÇÇÿÇÇÇÿÇÇÈÿÇÇÈÿÇÇÈÿÇÇÈÿÇÇÈÿÇÇÈÿÈÈÉÿÈÈÉÿÈÈÉÿÈÈÉÿÈÈÉÿÉÉÉÿÉÉÉÿÉÉÊÿÉÉÊÿÉÉÊÿÉÉÊÿÉÉÊÿÊÊÊÿÊÊÊÿÊÊËÿÍÍÍÿ½½¾ÿ¢¡£ÿžž ÿ  ¢ÿ  ¡ÿ££¤ÿÂÂÃÿ¸·¹ÿžžŸÿ‰‰Šõlln ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿjil Ÿž âÇÆÇÿßÞßÿÜÜÝÿÊÉÊÿÇÇÈÿÇÇÈÿÈÇÈÿÈÇÉÿÈÈÉÿÈÈÉÿÈÈÉÿÈÈÉÿÉÈÉÿÉÈÉÿÉÉÉÿÉÉÊÿÉÉÉÿÉÉÊÿÉÉÊÿÉÉÊÿÉÉÊÿÊÉÊÿÊÊÊÿÊÊÊÿÊÊÊÿÊÊËÿÊÊËÿÊÊËÿËÊËÿËÊËÿËËËÿËËÌÿËËÌÿËËÌÿËËÌÿÎÎÏÿÄÃÅÿ¥¥§ÿ››ÿžŸÿ£¢¤ÿÅÅÆÿ¯¯±ÿœžÿ‚ãjil ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ’“”ÉÈÉÉÿÙØÙÿãâãÿÏÎÏÿÈÈÉÿÉÉÉÿÉÉÉÿÉÉÊÿÉÉÊÿÉÉÊÿÉÉÊÿÊÊÊÿÊÊËÿÊÊËÿÊÊËÿÊÊËÿÊÊËÿËÊËÿËËËÿËËÌÿËËÌÿËËÌÿËËÌÿËËÌÿÌÌÌÿÌÌÌÿÌÌÌÿÌÌÌÿÌÌÍÿÌÌÍÿÌÌÍÿÌÌÍÿÌÌÍÿÌÌÍÿÌÌÎÿÌÌÎÿÏÏÐÿÊÊËÿ«ª¬ÿ˜—™ÿ¢¢£ÿÇÇÈÿ¦¥§ÿ›šœÿwwyÈÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‡‡Š°ÌÌÌÿÌÌÍÿ×רÿÔÔÕÿÒÑÒÿÎÎÎÿÊÊËÿËËËÿËËÌÿËËÌÿËËÌÿÌÌÌÿÌÌÌÿÌÌÌÿÌÌÌÿÌÌÌÿÌÌÍÿÌÌÍÿÌÌÍÿÌÌÍÿÌÌÍÿÍÍÍÿÍÍÎÿÍÍÎÿÍÍÎÿÍÍÎÿÍÍÎÿÎÎÎÿÎÎÏÿÎÎÏÿÎÎÏÿÎÎÏÿÎÎÏÿÎÎÏÿÎÎÏÿÎÎÏÿÎÏÏÿÐÐÐÿÏÏÐÿ²²³ÿ¥¥¦ÿÈÇÉÿžžŸÿ——˜ÿonq­ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ{z}ŽÌËÌÿÌÌÍÿÌÌÍÿÍÍÍÿÏÎÏÿÓÓÔÿÔÔÕÿÎÎÏÿÍÍÍÿÍÍÍÿÍÍÎÿÍÍÎÿÍÍÎÿÍÍÎÿÎÍÎÿÎÍÏÿÎÎÏÿÎÎÏÿÎÎÏÿÏÎÏÿÏÎÏÿÎÎÏÿÎÎÎÿÎÎÎÿÏÏÏÿÏÏÏÿÏÏÐÿÏÏÐÿÏÏÐÿÏÏÐÿÏÏÐÿÏÏÐÿÏÏÑÿÏÏÐÿÐÏÐÿÐÐÑÿÐÐÑÿÐÏÑÿÑÑÑÿØØØÿÑÑÒÿ®­¯ÿ–”—ÿ”“•ÿhgjŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿoorrÉÉÊÿÏÏÐÿÌËÌÿÍÍÎÿÎÎÏÿÎÍÎÿÙØÙÿÜÜÝÿÐÐÑÿÏÏÏÿÏÏÏÿÏÏÏÿÏÏÐÿÏÏÐÿÏÏÐÿÏÏÐÿÏÏÐÿÐÐÐÿÐÐÐÿÐÏÐÿÐÏÐÿÓÓÔÿÚÚÛÿØØÙÿÑÑÑÿÐÐÐÿÐÐÑÿÐÐÑÿÑÑÑÿÑÑÑÿÑÑÑÿÑÑÑÿÑÑÒÿÑÑÒÿÑÑÒÿÒÒÒÿÒÒÒÿÒÒÓÿÚÚÛÿààáÿÚÙÚÿÃÃÃÿŸž ÿ‰‰ŠÿaadlÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿdcfPÁÁÂÿÖÖÖÿÕÕÖÿÇÆÇÿÎÎÏÿÐÐÐÿÒÒÒÿàßàÿÛÚÛÿÒÒÒÿÒÒÒÿÒÒÓÿÒÒÓÿÓÒÓÿÓÓÓÿÓÓÔÿÓÓÔÿÓÓÔÿÓÓÔÿÕÕÖÿÚÙÛÿÚÚÛÿ×Ö×ÿÛÛÜÿÞÝÞÿØØÙÿÖÖ×ÿÖÖ×ÿÖÖ×ÿÕÕÖÿÕÔÖÿÕÕÕÿÕÕÕÿÕÕÕÿÕÕÕÿÕÕÕÿ×××ÿßßàÿÞÞßÿÖÕÖÿÙÙÚÿÏÏÏÿÊÊÊÿ™˜šÿ\[^JÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿYX]6»º»ÿàßàÿáááÿÔÔÕÿÕÕÖÿÕÕÖÿÔÔÔÿÚÚÛÿÞÞÞÿÚÚÛÿÛÚÛÿÛÛÜÿÜÜÜÿÝÜÝÿÝÝÞÿÝÝÞÿÞÞßÿÞÞßÿßßàÿÞÝÞÿÚÙÚÿÖÖ×ÿÖÖ×ÿÖÖ×ÿÙÙÚÿÞÞÞÿÞÞßÿÞÞßÿÞÞßÿÞÞÞÿÞÝÞÿÝÝÞÿÝÝÝÿÜÜÝÿÝÝÝÿÜÜÜÿààáÿááâÿÙÙÚÿרØÿåååÿ×רÿÞÞßÿÄÄÅþfeh/ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ[[`…„ˆ•¿¿ÀÃÀÀ¿½¿¿À½¿¿¿½¿¿¿½¾¾¿½´´µ¾ÌË̾ÇÇȽ½½¾½¿¿¿½¿¿¿½¿¿À½¿¿À½¿¿À½¿¿À½¿¿À½¿¿Á½À¿À½À¿À½ÀÀÁ½ÀÀÁ½ÀÀÁ½ÀÀÁ½ÀÀÁ½ÀÀÁ½ÀÀÁ½ÀÀÁ½ÁÀ½ÁÀ½ÁÀ½ÁÀ½ÁÁ½ÀÀÁ½ÉÉʽÎÎϾºº»¾¾¾¿½ÂÂĽÂÁýÄÄÆ½ÂÂÃÆ†‰ˆVV[ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿkjn§§¨)««ª'ªªª'ªªª'ªªª'©©ª'‰‰‹(ÜÜÝ)ÍÍÍ'¦¦§'ªª«'ªª«'««¬'««¬'««¬'««¬'««¬'««­'¬«­'¬«­'¬«­'¬¬­'¬¬­'¬¬­'¬¬­'¬¬­'¬¬­'¬¬­'­¬®'­¬®'­¬®'­¬®'­¬®'©¨«'ÎÍÏ'ßßß)œœž)¡ ¢'°¯±'­­°'¯¯±'©©ª)dchÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀÿ€ÿÿ€ø?àààààààààààààààààðð?ð?ð?ð?ð?ð?ð?ø?øøøøøøüüÿüÿüÿüÿüÿüÿüÿþÿþÿþÿþÿþÿþÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿgsmartcontrol-1.1.4/data/gsmartcontrol.xpm000066400000000000000000000174551417717230600207740ustar00rootroot00000000000000/* XPM */ static char * gsmartcontrol_xpm[] = { "32 32 359 2", " c None", ". c #ABAAAA", "+ c #CCCCCC", "@ c #C5C4C4", "# c #C2C1C1", "$ c #D2D1D1", "% c #C8C7C7", "& c #C8C7C8", "* c #C9C8C8", "= c #CAC8C9", "- c #CAC9C9", "; c #C8C6C7", "> c #C7C6C6", ", c #CFCDCE", "' c #D1D0D0", ") c #C6C5C6", "! c #CFCECE", "~ c #B9B7B7", "{ c #D4D4D4", "] c #DCDBDC", "^ c #D8D7D7", "/ c #D6D5D5", "( c #D6D5D6", "_ c #D7D6D6", ": c #D9D8D8", "< c #DBDADB", "[ c #DAD9DA", "} c #D7D7D7", "| c #D6D6D6", "1 c #DAD9D9", "2 c #E2E1E1", "3 c #B0AFAF", "4 c #D3D2D2", "5 c #CDCCCC", "6 c #CDCDCD", "7 c #CECDCD", "8 c #CECECE", "9 c #CFCFCF", "0 c #D0CFCF", "a c #C0BFBF", "b c #9C9A9B", "c c #6E6B6B", "d c #807E7F", "e c #C8C8C8", "f c #CAC9CA", "g c #CACACA", "h c #CBCACA", "i c #CCCBCB", "j c #CCCBCC", "k c #D0CECF", "l c #C3C2C2", "m c #A09E9F", "n c #B5B4B4", "o c #AFADAE", "p c #787576", "q c #8D8B8B", "r c #D4D3D3", "s c #C7C6C7", "t c #C7C7C7", "u c #C9C8C9", "v c #C9C9C9", "w c #CACAC9", "x c #BFBEBE", "y c #A3A1A2", "z c #A09D9E", "A c #B4B2B2", "B c #B6B4B4", "C c #827F80", "D c #969595", "E c #C4C3C3", "F c #C4C4C4", "G c #C5C5C5", "H c #C6C5C5", "I c #C6C6C6", "J c #C9C7C8", "K c #B9B7B8", "L c #A5A4A4", "M c #A4A3A3", "N c #A7A5A5", "O c #B3B1B2", "P c #BFBDBD", "Q c #8D8A8B", "R c #A09F9F", "S c #DDDCDC", "T c #C0C0C0", "U c #C3C1C2", "V c #C3C2C3", "W c #C3C3C3", "X c #C4C3C4", "Y c #B2B1B1", "Z c #A8A7A7", "` c #ABA9A9", " . c #CFCECF", ".. c #C2C0C1", "+. c #989697", "@. c #A8A6A7", "#. c #DEDDDD", "$. c #DEDDDE", "%. c #C1C0C0", "&. c #C2C1C2", "*. c #B1B0B0", "=. c #ADACAC", "-. c #AFAEAE", ";. c #BDBCBC", ">. c #B3B2B2", ",. c #B4B3B4", "'. c #B8B8B8", "). c #D5D4D4", "!. c #C0BFBE", "~. c #C1C0C1", "{. c #C3C1C1", "]. c #C4C4C3", "^. c #B2B0B1", "/. c #B3B2B3", "(. c #B4B3B3", "_. c #A1A0A0", ":. c #B9B8B8", "<. c #C4C2C3", "[. c #BDBBBC", "}. c #B5B4B3", "|. c #B6B4B5", "1. c #B7B5B6", "2. c #B7B6B7", "3. c #B8B7B7", "4. c #B8B7B8", "5. c #BCBABB", "6. c #BBBABA", "7. c #BFBEBF", "8. c #C1C1C1", "9. c #C1BFC0", "0. c #BCBBBB", "a. c #BEBCBD", "b. c #BEBDBD", "c. c #9B999A", "d. c #9D9B9B", "e. c #C2C2C2", "f. c #D4D3D4", "g. c #CECDCE", "h. c #C7C5C5", "i. c #A4A2A3", "j. c #A19F9F", "k. c #C6C4C5", "l. c #DCDCDC", "m. c #D0CFD0", "n. c #CECCCD", "o. c #A2A1A1", "p. c #939292", "q. c #E1E0E0", "r. c #E3E3E3", "s. c #B3B3B3", "t. c #CCCACB", "u. c #D2D2D2", "v. c #D5D3D4", "w. c #A7A6A6", "x. c #959494", "y. c #E3E2E2", "z. c #DCDBDB", "A. c #CBC9CA", "B. c #E1E0E1", "C. c #DFDEDE", "D. c #CECCCC", "E. c #CDCCCD", "F. c #D2D0D0", "G. c #D2D0D1", "H. c #B5B4B5", "I. c #B1AFB0", "J. c #EDECEC", "K. c #D1CFCF", "L. c #CDCBCC", "M. c #D5D5D5", "N. c #DCDADA", "O. c #C5C4C5", "P. c #D2D1D2", "Q. c #D5D4D5", "R. c #DADADA", "S. c #DBDADA", "T. c #DDDCDD", "U. c #D7D6D7", "V. c #E8E7E7", "W. c #D8D8D8", "X. c #D8D6D6", "Y. c #D9D8D9", "Z. c #DBDBDB", "`. c #DDDDDD", " + c #474747", ".+ c #ECEBEB", "++ c #F3F3F3", "@+ c #E4E3E3", "#+ c #E0DFDF", "$+ c #E5E5E5", "%+ c #EFEFEF", "&+ c #EBEAEB", "*+ c #E6E5E5", "=+ c #E0E0E0", "-+ c #E4E4E4", ";+ c #E5E4E4", ">+ c #ECECEC", ",+ c #666666", "'+ c #585757", ")+ c #D9D9DA", "!+ c #E2E2E2", "~+ c #E6E5E6", "{+ c #E1E1E1", "]+ c #E6E6E6", "^+ c #E7E7E7", "/+ c #898989", "(+ c #767575", "_+ c #E0DFE0", ":+ c #E9E9E9", "<+ c #BDBDBD", "[+ c #B6B6B6", "}+ c #B0B0B0", "|+ c #ACACAC", "1+ c #ACABAC", "2+ c #A7A7A7", "3+ c #DFDFDF", "4+ c #E5E5E4", "5+ c #E9E8E8", "6+ c #E8E8E8", "7+ c #E3E2E3", "8+ c #D7D5D5", "9+ c #8E8D8D", "0+ c #807F7F", "a+ c #494848", "b+ c #5A5758", "c+ c #605E5F", "d+ c #646262", "e+ c #555354", "f+ c #1F1F1F", "g+ c #282626", "h+ c #323031", "i+ c #3D3C3C", "j+ c #494748", "k+ c #555353", "l+ c #5A5959", "m+ c #3E3D3D", "n+ c #434242", "o+ c #444343", "p+ c #414041", "q+ c #3E3E3E", "r+ c #4A4949", "s+ c #706D6E", "t+ c #6B6969", "u+ c #6A6869", "v+ c #696469", "w+ c #656065", "x+ c #595757", "y+ c #A5A5A5", "z+ c #767676", "A+ c #8A8888", "B+ c #4B4849", "C+ c #5C5A5A", "D+ c #686565", "E+ c #524F50", "F+ c #000000", "G+ c #090607", "H+ c #171415", "I+ c #262324", "J+ c #363334", "K+ c #454343", "L+ c #4A4748", "M+ c #212020", "N+ c #2A292A", "O+ c #2C2B2B", "P+ c #272626", "Q+ c #242424", "R+ c #242323", "S+ c #3C3B3B", "T+ c #726F70", "U+ c #6C696A", "V+ c #6B676A", "W+ c #7A916C", "X+ c #7B926B", "Y+ c #605F60", "Z+ c #9C9B9C", "`+ c #606060", " @ c #8F8D8D", ".@ c #494747", "+@ c #595657", "@@ c #615E5F", "#@ c #656162", "$@ c #525050", "%@ c #020101", "&@ c #0C0A0A", "*@ c #1A1818", "=@ c #282525", "-@ c #454243", ";@ c #494647", ">@ c #2A2929", ",@ c #393838", "'@ c #363536", ")@ c #323131", "!@ c #2B292A", "~@ c #282727", "{@ c #444243", "]@ c #696666", "^@ c #666268", "/@ c #709D48", "(@ c #7BAD4A", "_@ c #68686A", ":@ c #8F8E8F", "<@ c #4E4D4E", "[@ c #8E8C8D", "}@ c #484546", "|@ c #555253", "1@ c #5D595A", "2@ c #5F5B5C", "3@ c #514F50", "4@ c #0F0D0D", "5@ c #211E1F", "6@ c #312E2F", "7@ c #413D3E", "8@ c #222121", "9@ c #2E2D2D", "0@ c #312F30", "a@ c #262425", "b@ c #464344", "c@ c #696667", "d@ c #666263", "e@ c #5F5E5F", "f@ c #5A5B5B", "g@ c #797577", "h@ c #7A7A7A", "i@ c #4C4A4A", "j@ c #2A2728", "k@ c #2C292A", "l@ c #302D2E", "m@ c #312F2F", "n@ c #2E2C2C", "o@ c #100F0F", "p@ c #110F10", "q@ c #141212", "r@ c #1B1919", "s@ c #242222", "t@ c #2D2B2B", "u@ c #322F30", "v@ c #2F2D2D", "w@ c #2B2829", "x@ c #292728", "y@ c #363233", "z@ c #332F30", "A@ c #312D2F", "B@ c #2A2627", "C@ c #424040", "D@ c #454444", "E@ c #737373", "F@ c #828282", "G@ c #939293", "H@ c #9C9C9C", "I@ c #A3A3A3", "J@ c #A9A9A9", "K@ c #ADADAD", "L@ c #B2B2B2", "M@ c #B5B5B5", "N@ c #A4A4A4", "O@ c #959595", "P@ c #8C8C8C", "Q@ c #707070", "R@ c #FFFFFF", " ", " . + @ # $ % & * = - ; > * - - - * , ' ) ! ~ ", " = { ' ] ^ { / / ( _ : < [ ^ ^ } ^ | 1 2 } 3 ", " - 4 ' ' 5 6 6 6 7 7 8 9 ! ! 9 9 0 0 $ 5 a b c ", " d 4 [ * e - - f g h h h i i j + 5 5 k l m n o p ", " q _ r @ > > s s t % % * * u v - w 5 x y z A B C ", " D 1 7 l E F @ @ G H H I > > > J * K L M N O P Q ", " R S % T U U l V W E X F @ @ > E Y Z ` > .* ..+. ", " @.#.$.W %.# # # &.l l V E G T *.=.-.*.5 ;.>.,.Z ", " n '.).[ !.T ~.# ..{.U l ].x Y ^.>.A /.(.x ;.O _. ", " :.^.<.h a %.%.# # # &.U [.}.|.1.2.3.4.K K a 5.N ", " 6.i H 7.%.8.# &.# l * % E 9.[.5.0.[.;.a.b.;., %.c. ", " d.> 0 a # &.e.V V % ^ f.j g - % %.....&.l V l g.h.i. ", " j./ i # E E @ k.H l.g.%.# l E H <.b.; > % % % m.n.o. ", " p.q.7 @ ) > % % - r.) > % * * g f s.5 t.i i i u.v.w. ", " x.y.z.> u - g A.- B.C.D.E.! 0 7 (.6.$ ' ' F.G.).^ H. ", " I.7 J.K.L.7 7 ! ! m.S 1 m.- %.3.8./ ).M./ | _ : N.; ", " O., F.f.P.4 4 4 f.f.r / Q.4 4 ^ R.: : 1 [ S.< S T.U. ", " 4 V.| ).| _ W._ X.^ : Y.1 S.Z.z.] S S `.#.C.C.2 V.q. + ", " S..+++@+`.#+$+%+&+*+2 #+=+q.y.-+-+-+;+$+$+*+>+++Z.*+,+ ", " '+S.# $ >+r.m.e.# 6 )+!+~+*+@+B.=+{+-+]+]+^+]+^+j 5 y./+ ", " (+b.3 _+! m.#+:+q.r > <+[+}+|+3 '.b.8.> e 5 7 ! ^+j 1+2+ ", " x.] ! r / ^ W.R.S 3+2 y.@+4+5+6+^+~+*+;+7+#+C.#.S.8+: V ", " 9+0+a+b+c+d+e+f+g+h+i+j+k+l+m+n+o+n+p+q+r+s+t+u+v+w+x+y+ ", " z+A+B+C+d+D+E+F+G+H+I+J+K+L+M+N+O+P+Q+R+S+T+U+V+W+X+Y+Z+ ", " `+ @.@+@@@#@$@%@&@*@=@J+-@;@>@,@'@)@!@~@{@c ]@^@/@(@_@:@ ", " <@[@}@|@1@2@3@F+%@4@5@6@7@-@8@9@9@0@~@a@b@c@#@d@e@f@g@h@ ", " i@j@k@l@m@n@o@p@q@r@s@t@u@!@l@v@v@w@x@l@y@z@6@A@B@C@D@ ", " E@F@G@H@I@J@K@}+L@M@M@M@s.}+|+N@H@O@P@F@Q@ ", " R@R@R@R@R@R@R@R@R@ ", " ", " "}; gsmartcontrol-1.1.4/data/icon_cddvd.png000066400000000000000000000120241417717230600201350ustar00rootroot00000000000000‰PNG  IHDR@@ªiqÞsRGB®ÎébKGDÿÿÿ ½§“ pHYs7]7]€F]tIMEÙ3Û9iZ”IDATxÚí[ ˜TÕ•¾÷½Ú«««»éFš¦4«¶¨€@B“ˆ:Ž“ù2ãŒ~FMbbpA5QÐ1n3F3Îçd‚¸EIÒ²Š¨ KXT‘nèê®õ-wþó꯺´»éf3óùŠŸóÞ}ÛýÏ9÷œsoU³/¶/¶ÏÇæp8ÎÊ{ù™|Ù57ÜRÆúýYÃóss‹<^o–ª:º¦G#ÑHGKk¨.‘Ð<¶ì¾¸ÜØœy_ckWWþm*à{×-RZZúÝ‘¥¥—Ùçdg³Ü`óû¼ŒãÃ.86IUåB˜,‰°¶ö6’¡ææ5»÷~ôʃ÷Üñrš2>ÿ ¸ãgKo¬=æ‡ÅEEå%Ń™a‚3œÆ98S.¥ÒíXUI**s9Â4M^]{„ÕÕׯܲ}û²¥?_²}Ù£O°Å?ºå󧀟?øäÏ*F•ß3²t„"°q–Ú3IJyÂí¤ ¯×-::;ùÞïÙøî;‹î»sñƹîzöüsÏž=|ã²ï²?½ú;vë]üãä “ž]Vî1M©ä઴ôI“Wä¾bí;ááp˜oÙ¾cãòW\õ¿ÿýlÓYQ@YÅ—™?Ìú‡«ÿµrÆùf¸\ؼŠè°ú) o+€Ðý:Û#>9\ÍW¯ë–…×]óäì9sÙúµkΜ¾·ð®é3fÌÚX6¬Luñ8wI‹«è¤C…Nž¤Ý–©ÙFCC0!øŸÖ®]wÙ7æÏÛ¼s9mâ¸Ó¯€%÷/¹fÊ”¯>ïö ÝT¸äý.Á\Ù¹”N‚¼´>¤"3c_CØôÞæú_<¸tÜÜ9sZoûá‰H¥¿äùð=?™?gÖóƒóÝÂåˆÃåM¦ ΦÊKuŽpRä%”ôv‰îϦÆÞ…Ó§ þÅ}÷~û÷?ñëgOÜÿð‹æ^4óI·Û‹w"o37ëJxYÂp¡C*ó¹ó:mË“g(Ê@È“US ȼ>ãZ›[wì ÿôÎ%Ãf^ø•–{ï¾óÔ)à§w/ºô[_ŸÿºßëÖhWTŠÌnv,%˜.WXÀÍ0’JÀ!†Hô“<ö¥ûŸyòÓ´‹,á6½Û8û¢¯œ‡vóÞ)öY€O›> +2|ÞÌé¯ü>÷pÅ*f:b åi,ÀUxö„ÂâR=•s øô›<öûM>% #ÆgNŸ>ø¥×Þx×]Ò?µ¯ jk›Ø}÷,ÜS:ì¼€i ÿéÎ;TpÄ÷%÷wØÑ[UúG^z@äÀ3ÈÛ’ ë:+>løyeå«þøêæ“RÀÓKïÿ·©&Ï×E^o¦Æ´.Ýœ$¥AºP™8U´ÜV@_ä“%oJ)è:QTP8¿¦±ñ?YYõuµý‹CaSÆM~Ûµ7ÊÍó1áI0îÕ1À˜Rtsânuñû\¸XDó².ÍÒ* ºÉRµ‘ì|zj8yÀD;¤ËåïnÙöÎÜÙ]Ôo8ÖuŒ-þÁÿ0jä¨á&òœb‚¬AwÕ I° pÇD˜Ú4J‹.gÚPP{')¡½}‘·®£IÏ4è¼ìAïT}r°ª½½ýÄê·Ûͯ¼ìª‰å壿$“›n°87¯"èCC'ÞÁ…fx¹Ãå{¿lhÉŸáæzÆðÀA ²££óœh4ru4-ˆÇã,77Ï&߃ËÓUð¡!äñzpÁŽ‹°®®Œ˜°”6¸°ˆýóõ ¯xá7O-Ç=foA0« °x²A&‡& P%$Ò>•Àäâ$U5yÎ 8=Œkp÷.¤À¼Òv¿;ÎÇ +|nT鹯ƒ¼ ò=¤co0˜ÝR\\ü:¹EÓtÖÜ¢Ó6y3¼‘ždmÀÏ>G¶°ºº£¬£­“%@Þ×*0èùã'~›îíÍ”WÿÓ%EEƒA0ÝÔ@J€ @z_H‹§t5àqÓ;<8/.©xÊëËmÊ ®¦4‚¥tN!½Cº¡„WëêêbäTxÃJ²5îMi<ŠN¯çaíÎ0‹ÆbÌ4ˆ,®ÉÌ ðˆ‚yIIÉÅ}Í”²Ò‘ÓY>‹Çí|R€"5 ÆÇ!3™,ŽpÜÕ…N\<0¤ð/^_^cš·i¡PèK¹x¶/S $ wÃC4ž ý®!C†¼\]}¸Ô0Âù.·‡S¹ívºHïVÿºà溡'ã‚Cf©IÞ€d¦Î‚ÙÙÅô^ ¥Ç0tèyã Ü ò¼[„8ÇØliAX`R :^ªÁÕª/œ[2j#‘‘å©^[[{É9çœS¶#iî/£µµu2ØÇÙÙÙ-²Ý‹@¸Ca!Umv…£”>åÄÇLËÿ†$œIÞN™9 ûÚ¥ &U¾ñÊ:ò¾ÏŠ<',Eø§fc€¢¦¤šŒ*`Å ŠI(ŠA¹”û‹Jˆ¼ ç˜ Ó@þc:|ôÑÇ#JËZ=n_ü¹óÝ5(//¯žPŽ æHÕAƒòj[›X$gš¦1Cµ]¼WòØO¶y ƒ¹9#éý=Ÿ×WÙ×ìMJÅ.V4Y Ò"°Œêd9…E¥^‹ÅüØL:^¼øöÈC-{Zº¢¾fme`Òù•wíÜóÍñÆlll°]ÞëÁ»@`6eÊí’Ô‰’·%V0¨pˆ4¼ñY z<ÉÛkuÒ3,0éšÀ±ÛMmÍLn#àÚ5{?Ú—òO¡i/p¨È+6,}`)LÅæ‚ÔÓJóz"@Ö7HÞ4¤¸=9é d¤"|SçT¶†gO‘9)Ä錥½Hp\@ÇUUÕDºK+˜@½½iÓ.Hz¿jWlÔ)5šóÆÀÉ AŽ©zzw‡»ô“<ÏœÖ:ÓI¤ö³9ôΨpòÄ9Ƭe©ƒfzB†óüüüýMMMC³ƒÎ5ëÿ|W{c¸0½#Û·m¿aê´/›{ñ¬æ4ïÔ£¡ÈH3Ï4U"lÁy"&-IH)ç2È›rŠÜt´±…xö4D{G[r1:¢÷ƒ¼­(Ü‹ú=—áû;À7 ©m—| }h;wÌØQ ooóÑšÃuÕ9á`Nö¨Â¢‚(‘E8ÐFyÊÙ¨[3;GrÓË8]AÅŽbÖ„LÎŒ¤u != Óò²:ŒDã¬%ªíU5µµ‡œÇh¶OòØh¢Riz|‚V¤½(Ž2“E†,‚‡šõÍÍÍ#ÑáBjƒ4á);á9qô!E^47WHÄ:ÏWVY1fÄ ´ÒlÏ ŠVukDôQL(#EZFò<@7´¶µŠPcý¡Þ oX¿úÝ£-Í,/'˜*r.ÈÊ MãÛi=ówzùg ( sñGîÎBíSAAÁSR ŒHB)Õi®È3b’ÙÞÚ:¤®æÈ5‚«"Ûç¡ð‡ÞFñÔŠ­ŠÀ8€T¤å™½ÆÉ3L@®!¢f¡5õu»dÊ=e£5Ôtàð‘êJ§’ ”>Ÿ‡ååàÂ~rsŒï\øXÄézié~·¿ªJ·pWë¯WUD}ê3‚_$¹/t¹Ð‘¼ÞH(Ì€ Í#9ý¹®p±óƒµ·ßqãtt¥0z[J|¼ïÃM¯­ª< Ë Î©8ùÔ>¥TXžåÊgYþ,îpº,ki 1"á$"*‚ªÛƒÙZ0×Zá1ezp¿ rnP´W…©V¦/lºwƒg„ÑŸ8¥‰d<€äØÞÚ´~¥ô8£¯eq(ša¯Xpù :ä|@ä¥DGˆˆH¶‘O»\.S/¬ìäÇgµ9Õ^ú–CHצ€Ç䘷ñBø˜\£!Ž={a„#WV×ԈǻwNÖg*@éAá—W,aóÖ­Šž§ˆ<¤LMª7@7traHŽ“nÍÔ,b©"‡ qËÍ5ËœŒJ·÷Ñ&ÃÊŠ¡F™ËiðµV-Ç© q¢_Yzniït_õwWÌ8Uäå ®ï)-I#½ˆ‘«?&¤54ˆxêûݾG¤Úˆr†8t¤J[ºìÁ›äÔ[;ÑŸÈ sÕk/=ÿêoÔb%Eœ!ò²¶·ï5¤d€Õn¬ï°â‚Bp;eàÐIªùò•/>‰3Õ@¼¿¿Š oøþ’õu\qFÈ_ó—Ï–ûòyÉgtò)%¸äp`¹•IÄ[›ÞÝ¿nõÛÏ£«í€9ßè±h´­áèÑà• LJvZ-Ÿ¾|¢r"úÏñ×ùdmâË¥²¬ ©x‡kkÌ[pÏuèê‡@x ?0ć{vôçäM¹pÆ´Ág„†ªòWO<ôÛ ë*A??ºvª@›)5ÚþÚ+/½¯züã¦M™RÄ9Üît· "Y /“ûŒ&k§hj ñ{¸ëñÛÞ{ºäm D Ö­yϾý¾©Ó¦ÍËÉ Mã§œ|úy9ñ,ùŽc‡âd[vlk[|×Íw6Ö×R­H’§û×â †ùüþ9ÿûS·~ç›ß,¿ääèè©%ŸÊ ròã@yjná¿[ñÛÕoüq:ÈY^ôLþÁ¼@P>c欫ßvûUS'_àÓ5M$¤"Nž¼-iަÔkׯÙ÷Ì3ÿFOÄ7É*¯ ÐÎÖŸÌ8¤7”åΞ{ùõ×^ÿÕ &OÎÏøY4OþÊ4ûG^Ðyë;Jë÷Du lã¦u{–/aå±ö6"^„¤Õͳý7Cp9@!04¿xðÔ—]9ç’Ù—T”/ÍÎËÍ!ÃZG4= éÒ:Àˆ0M»!ÁXPÁ‰ÅXS(Äöîûàðºu«·¿³q]%ž»¨•ÅMÐ?_5f+"+MÅLQGL¾`ÊÄq&>txQQaQb‡Ïíö8løæØG"Ѷ֖pCC}ëþûêöïýpõ¡ƒ;å¾h:¤ÅõÏûß r@•Êð~©”€„py­)Çp è’8&eX¶k€ù·ú—£ŠTˆ# jÚlTHrz ÙüÿÞ8ûb;{Ûÿpc,­lJÀIEND®B`‚gsmartcontrol-1.1.4/data/icon_hdd.png000066400000000000000000000063651417717230600176230ustar00rootroot00000000000000‰PNG  IHDR@@ªiqÞsBIT|dˆ pHYsììu85tEXtSoftwarewww.inkscape.org›î< rIDATxÚå›IoK…£Òå2ƒcã7óؤ^ Ñ oÓHlÙôÿ@ü™^±{HH½@H,B,ZšyžÍ< .÷ýRuø*3#«Ü,ïJ¡ÌÊ!òžwŠÈ¬ÆÌÌLø#K3$¤aò{8cÒ Þ8xðàÊE‹ý9˲ÐÓÓÓ°šÍf@ü>Âom%:߉LOO‡X¾}û¦­Îë·ß§Í´Ûíðþýû«ã< )<øÌÀ¯?tèЖ-[Ö¼~ýzøøñcضm[0BÒßß01Í}lÅ}`—m|^¢Ët~jê«îÕ¶l_[ÿL‡K—.…… †M›6…/^ÀÖÖcǎݲëÚP×дeà¶ÙÍOŸ>…'N„]»v…/_¾HNÆüùóQÀZ;ß¶ÛÚÿßV )ÛNL< Ïž=Wß•×û} º={6ìØ±#¬X±"˜Õ6Á`§pÚZm2kóZ­ÖºyóæåŒ9r$ oÞ¼ ˜ŠöööDìsüóç/fçÍ zr¿~ý†2v~Úö1S~÷„V«7'±ËÕŸ]ó•þJI /tḇÇçÏ~ûöm~ `¡[kí$2k}¦Ü ”B¡W¯^qŽ†Ù³…m¶RJûê'Ú‹úCQõûèÑ£\ù5kÖp¬|ýÚµkaåÊ•¸ã,^¿~­ýœP0€ÅÚG°áu] wÉ’%£t)‹_Ù(¦s) x5~ÓDˆŽÇ¿gù´¹£D<Éß»w/lÙ²…ß…¾¯¶xñbÈ u œu ÏZºté(XrL’šô,_¾|\Š«;w.8p@ÄMDYBéo\àÇ(ËÈ[0œܬàŠxŸçùø<×Å€= £££ã`醀¦™×°ÌvëÖ­üÁ}}}eàµM‚—Éarr2¼{÷.w³ÁÁÁ°`ÁŸËýpPFdƒ¥+ 0b09|.>†tC}ŒË”Q÷"}±Ÿ"€ ß{âÔHÕºµ€»y‘À¢Áphhˆ}O€]µRJS–úžå£õêÕ<ð תˆŒ´°Ž{+C§4ä L¢Ñ†qF¬[¨ñI¬(ïëþýûAŠæQ©K€*BöuÜ[" ÕÊÙöíÛ¬ãÌA”OàÁã¯XŽ*H‚ÝÄÄOàóX IUêS#í- n`ËÅ‹³N, 3_ƒÙn @(¢¬ŒVPË£ö“'OòíÓ§OsÖ®]ëç eä-ÿÇR°@ÝÔ%€8ŒbX˜B$ÜCE600Â_§~xùò%Js^Ä$ žúFY€7ý¸ ˜’4LD€A#܃eD „ó#cÜ#¥1u";e-$pŽ ‡âu ´;N[@¡0 `ªSд`·Ì[P€ €a®´ÅhÔˆêŒ4 Fšà]¸ò:²úÕ=¾¬Ž ˜:r»i”›cE¶nÝ ùïU«V©.0“$ö9ŒEM؄ϧ­A°'‡­R"X¼ $-ÀFwÜ?‘'¨ÑéãÇíx6X„RÔ,Eºî­tYç<ñý`éØ¬ó¡‚ 's¼¶7”é”Yó;:y­®#Ø:=Ê\Ce•`æ5ÀÍÏŸ?'Ož 7nÜðþƨC9“÷@ [B:¹Oóé£ûÑÑãÄ«¤ ø*Ð"v??~<\¾|9_ Ú¸q£DcãK×ÔhÏÉü1b Y è!"Ž=J­ÁàhæÚ_V 6\ŽÍˆ-vÃú3gÎüÛа@H€c^-¶Imy:«câs‰©cü¦¸Â5ÑG)’ÀŒK¢3³Ì™½{÷î4oáµvM»Ê²={ö ð†jmÕî vL[GFF|Þ-«ÆÒ&ž¾¶òYG'2Ò1ñA:ƒLEx³"ÿ_·nݨ<$JywïÞe&ǹ2Å~T (ý–ïܹ“ëè+G°€Iq z,¨ ÌÉ)g <´X‰NÍŸ4‰RŒŒˆÅ¥h¥&ïAùTGaFŸ¬-@†Ï\`*" +*ƒ-oŽDKR4̉Î1}Ï~]ó'U*c0ÂOi,•¬7µÝÀïSm¢c¼"„€L"©´ó–Ââ‡õUèÔ}ÿp,}±jë¬ r!„þEüWu†¤¬Èñý0Õ&.è8Ðg¹ø*Дs$À0é©3úŒ€¶*‹IUÌ ¤”oR7`¹N÷«¯ZV€+0Xž,0)&ƒà÷‹½@@±´Û’6‰ÒUÙ´:üàÁƒª>Ëô`¤y–·RRâXm °NßéMKðJEoÞ¼ ú]Ù¸—¸rå ûe}–A€¥nññ×[­,`#=àD)-B¦R“ßR;P©ÑGÙ! –þ%¨ßúš_çâ,H¥ÁFD@Á:|‘ÿ¦^^j­QКAá—óø°Ö8ïAK<ðX¸×ß&°ùrØ[@fi¨e‘¸Ï &ˆ vzÒPø6™€TE6 1‹ÔŠ×Ðp5LØtàYE#_E)"c‚ä}`3ò³ªíÞ½›Øp X@!}“–Š*vl,…°¬ÕÑØçç˜É±_ÕWá³i€gË$ɘÀÆ*ÈìåĨ«”c‰®XlÖ.Nb%Pމ ÍçöHü'÷õâÐ Kãœ_ÛéÓ§3—Ðcì© öËËËŒ&vJBjvèéº$"½ÐQ%¶D+Ú`ó°éË` ¸@‘yÓ•Õ–"íÿ‹¸3ðâ¥4úúWñìƒÍ¯{ àuØ2.vàeFZ‡£iòR/+¤ ðûÚ–¦¿8Èúç#*¬4S.0^5-Õ»"7n¡OY™¡@’߈ ²:›|ß>Õ‚­Èü+ñ±’hGtåuÕùEצ¬ ’Þ7­1úÌ( €N¼ ïXŠ€¦u¸´ÆÂ%ØÀ*‡ ÇI7ÀºÈ—™ócÖÌ3(³u¾j=Xšt•Á50¯µù®EX•a4ê»A}óÇíx™J1…òfY+¿uÞ+–ÃM¿l~½°x4 PÚ®^½ÅX0ɉyøð!„¤Ý mþÌ X†£Ó'p€'þÄ–‘$l~u8¶€Ì ˆ>3ëV]ðÚÇô/\¸€+è+U#$:i T)˜ÒÜh€ÇÏõ"k’lýWgòõ*–-0Y‘ d¶öï—Ââýä`}Ç4r4„ú÷`«5@åoYEÙE‰X«<(Ь-ÇSµ×ñÔ©S7‹\ 3EGTܾ}›y|®ü† üŠ­h_±A™ÀƒéÒØ`á+´˜ Jnú á^è] ˆÿ‘ï›!ýׯ_ŸM0r)]xzlä €èÎ÷€(Ã(1e_ ëN€´€‹@h<½Õê²¾¡ùB'm÷ŸÅîk†yþüù ô¸yóæüã@Øl˜Dߌ¨ 4&̧lPhµfnHTK(&øè_ºïM)"Açýû÷ço±÷íÛôQ%‘Y`,·cÜÌ×ø%&¨‡yÊ, ’N@¼x"hú¢@Lv‚h#X‹\ 3Ÿù«jkDÙ×ö*u]ÝJ°jß“’¼N ƲÙbÄ$Ÿ›³HQ$0Jl  t!ŒJ%š¶þ a‚;é 4ôõ%Ÿ°yQ`±·Æ²ŠŽ…4Fÿ%¢ ~”ðl0–•˜ ©J&ãEÇ $]®úö©ôÝÔ»0¸`ÐFB¬°ô &-ÌœàWE‘BçG9{.òÛëßÂõ¿]¿þë×ðËŠ_tX™ú¤UFßíOž5µ"ÑWc|~¤„²W³A,G2ל‹ì\¼3ü}a3Lmô}1‡È10ƒ‹Ø /(9ñuÀz!wSrN;“g!{ôv?‹Ê[®'¸q>èw.²¦×åÊ_ÂÓé§aº==Kw°™¼+˜ãY 6?hŠn±üÿSdÜÕßL~™1L_ Óc›´ýÓȸbÇ^áï¹§ßÚŸ¬­³F¤àøÏ ˜Â$™ÐÚmkðxÃ>-;o¿`VØó¢|ù»¹7I€5#VŽÅ_‰iiBZ4•‹? 4¬`ŠFH˜1‰ ˆIˆÚO%3jˆþáÿ>ÿ_~Š˜D'˜#IEND®B`‚gsmartcontrol-1.1.4/data/nsis/000077500000000000000000000000001417717230600163105ustar00rootroot00000000000000gsmartcontrol-1.1.4/data/nsis/Makefile.am000066400000000000000000000001311417717230600203370ustar00rootroot00000000000000 EXTRA_DIST = distribution.txt.in gsmartcontrol.nsi.in nsi_install.ico nsi_uninstall.ico gsmartcontrol-1.1.4/data/nsis/distribution.txt.in000066400000000000000000000015551417717230600222030ustar00rootroot00000000000000 GSmartControl - Hard disk drive and SSD health inspection tool Version @VERSION@ The latest version of this distribution, as well as the source code of GSmartControl, can be found at https://gsmartcontrol.shaduri.dev and https://sourceforge.net/projects/gsmartcontrol/files/ This distribution includes: * GSmartControl binary (gsmartcontrol.exe) and associated resources. The binary was compiled against Gtkmm (http://www.gtkmm.org/), GTK+ (http://gtk.org/) and related libraries, and PCRE (http://www.pcre.org/). * Smartctl binaries (from smartmontools 6.6, http://smartmontools.sf.net/) - smartctl-nc.exe (compiled without console output), smartctl.exe (compiled with console output), update-smart-drivedb.exe (drive database updater). The combined distribution is multi-licensed under GNU GPL versions 2 and 3. See LICENSE_gpl2.txt and LICENSE_gpl3.txt for details. gsmartcontrol-1.1.4/data/nsis/gsmartcontrol.nsi.in000066400000000000000000000200331417717230600223240ustar00rootroot00000000000000 ; NSIS2 Script ; by Alexander Shaduri . ; Compatible with NSIS Unicode 2.45. ; Public Domain Unicode True !define PRODUCT_VERSION "@VERSION@" !define PRODUCT_NAME "GSmartControl" !define PRODUCT_NAME_SMALL "gsmartcontrol" !define PRODUCT_PUBLISHER "Alexander Shaduri" !define PRODUCT_WEB_SITE "https://gsmartcontrol.shaduri.dev" ;!define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\AppMainExe.exe" !define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" !define PRODUCT_UNINST_ROOT_KEY "HKLM" !include "FileFunc.nsh" ; GetOptions ; --------------- General Settings ; This is needed for proper start menu item manipulation (for all users) in vista RequestExecutionLevel admin ; This compressor gives us the best results SetCompressor /SOLID lzma ; Do a CRC check before installing CRCCheck On ; This is used in titles Name "${PRODUCT_NAME}" ; ${PRODUCT_VERSION} ; Output File Name OutFile "${PRODUCT_NAME_SMALL}-${PRODUCT_VERSION}-@WINDOWS_SUFFIX@.exe" ; The Default Installation Directory: ; Try to install to the same directory as runtime. InstallDir "$PROGRAMFILES64\${PRODUCT_NAME}" ; If already installed, try here InstallDirRegKey HKLM "SOFTWARE\${PRODUCT_NAME}" "InstallationDirectory" ShowInstDetails show ShowUnInstDetails show ; --------------------- MUI INTERFACE ; MUI 2.0 compatible install !include "MUI2.nsh" !include "InstallOptions.nsh" ; section description macros ; Backgound Colors. uncomment to enable fullscreen. ; BGGradient 0000FF 000000 FFFFFF ; MUI Settings !define MUI_ABORTWARNING !define MUI_ICON "nsi_install.ico" !define MUI_UNICON "nsi_uninstall.ico" ; Things that need to be extracted on first (keep these lines before any File command!). ; Only useful for BZIP2 compression. ;!insertmacro MUI_RESERVEFILE_LANGDLL ;!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS ; Language Selection Dialog Settings ;!define MUI_LANGDLL_REGISTRY_ROOT "${PRODUCT_UNINST_ROOT_KEY}" ;!define MUI_LANGDLL_REGISTRY_KEY "${PRODUCT_UNINST_KEY}" ;!define MUI_LANGDLL_REGISTRY_VALUENAME "NSIS:Language" !define LICENSE_FILE "doc\distribution.txt" ; Pages to show during installation !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_LICENSE "${LICENSE_FILE}" !insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_INSTFILES ;!define MUI_FINISHPAGE_RUN "$INSTDIR\gsmartcontrol.exe" ;!define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\Example.file" ;!define MUI_FINISHPAGE_RUN_NOTCHECKED !define MUI_FINISHPAGE_NOAUTOCLOSE !define MUI_FINISHPAGE_NOREBOOTSUPPORT !insertmacro MUI_PAGE_FINISH ; Uninstaller page !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES ; Language files !insertmacro MUI_LANGUAGE "English" ; --------------- END MUI LangString TEXT_IO_TITLE ${LANG_ENGLISH} "GSmartControl" var install_option_removeold ; uninstall the old version first (if present): yes (default), no. ; ----------------- INSTALLATION TYPES ; InstType "Recommended" ; InstType "Full" ; InstType "Minimal" Section "!GSmartControl" SecMain SectionIn 1 RO SetShellVarContext all ; use all user variables as opposed to current user SetOutPath "$INSTDIR" SetOverwrite Off ; don't overwrite the config file ; File "gsmartcontrol.conf" SetOverwrite On File *.exe File *.dll File gsmartcontrol.ico File icon_*.png File drivedb.h ; Include the "doc" directory completely. File /r doc ; GTK and stuff File /r etc File /r share File /r lib ; Add Shortcuts (this inherits the exe's run permissions) CreateShortCut "$SMPROGRAMS\GSmartControl.lnk" "$INSTDIR\gsmartcontrol.exe" "" \ "$INSTDIR\gsmartcontrol.ico" "" SW_SHOWNORMAL "" "GSmartControl - Hard disk drive and SSD health inspection tool" SectionEnd ; Executed on installation start Function .onInit SetShellVarContext all ; use all user variables as opposed to current user ${GetOptions} "$CMDLINE" "/removeold=" $install_option_removeold Call PreventMultipleInstances Call DetectPrevInstallation FunctionEnd ; ------------------ POST INSTALL Section -post SetShellVarContext all ; use all user variables as opposed to current user WriteRegStr HKLM "SOFTWARE\${PRODUCT_NAME}" "InstallationDirectory" "$INSTDIR" WriteRegStr HKLM "SOFTWARE\${PRODUCT_NAME}" "Vendor" "${PRODUCT_PUBLISHER}" WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "DisplayName" "${PRODUCT_NAME}" WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\gsmartcontrol_uninst.exe" WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "InstallLocation" "$INSTDIR" WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}" WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\gsmartcontrol.ico" WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}" WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}" WriteRegDWORD HKLM "${PRODUCT_UNINST_KEY}" "NoModify" 1 WriteRegDWORD HKLM "${PRODUCT_UNINST_KEY}" "NoRepair" 1 ; We don't need this, MUI takes care for us ; WriteRegStr HKCU "Software\${PRODUCT_NAME}" "Installer Language" $sUAGE ; write out uninstaller WriteUninstaller "$INSTDIR\gsmartcontrol_uninst.exe" ; uninstall shortcut ;CreateDirectory "$SMPROGRAMS\GSmartControl" ;CreateShortCut "$SMPROGRAMS\GSmartControl\Uninstall GSmartControl.lnk" "$INSTDIR\gsmartcontrol_uninst.exe" "" "" SectionEnd ; post ; ---------------- UNINSTALL Function un.onUninstSuccess HideWindow MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) was successfully removed from your computer." /SD IDOK FunctionEnd Section Uninstall SetShellVarContext all ; use all user variables as opposed to current user SetAutoClose false ; add delete commands to delete whatever files/registry keys/etc you installed here. Delete "$INSTDIR\gsmartcontrol_uninst.exe" DeleteRegKey HKLM "SOFTWARE\${PRODUCT_NAME}" DeleteRegKey HKLM "${PRODUCT_UNINST_KEY}" Delete "$INSTDIR\*.exe" Delete "$INSTDIR\*.dll" Delete "$INSTDIR\gsmartcontrol.ico" Delete "$INSTDIR\icon_*.png" ; update-smart-drivedb may leave these Delete "$INSTDIR\drivedb.h" Delete "$INSTDIR\drivedb.h.*" RMDir /r "$INSTDIR\doc" ; GTK and stuff RMDir /r "$INSTDIR\etc" RMDir /r "$INSTDIR\share" RMDir /r "$INSTDIR\lib" ; clean up generated stuff Delete "$INSTDIR\*stdout.txt" Delete "$INSTDIR\*stderr.txt" RMDir "$INSTDIR" ; only if empty Delete "$SMPROGRAMS\GSmartControl.lnk" ;Delete "$SMPROGRAMS\GSmartControl\Uninstall GSmartControl.lnk" SectionEnd ; end of uninstall section ; --------------- Helpers ; Detect previous installation Function DetectPrevInstallation ; if /removeold=no option is given, don't check anything. StrCmp $install_option_removeold "no" old_detect_done SetShellVarContext all ; use all user variables as opposed to current user push $R0 ; detect previous installation ReadRegStr $R0 HKLM "${PRODUCT_UNINST_KEY}" "UninstallString" StrCmp $R0 "" old_detect_done MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \ "${PRODUCT_NAME} is already installed. $\n$\nClick `OK` to remove the \ previous version or `Cancel` to continue anyway." \ /SD IDOK IDOK old_uninst ; Abort goto old_detect_done ; Run the old uninstaller old_uninst: ClearErrors IfSilent old_silent_uninst old_nosilent_uninst old_nosilent_uninst: ExecWait '$R0' goto old_uninst_continue old_silent_uninst: ExecWait '$R0 /S _?=$INSTDIR' old_uninst_continue: IfErrors old_no_remove_uninstaller ; You can either use Delete /REBOOTOK in the uninstaller or add some code ; here to remove to remove the uninstaller. Use a registry key to check ; whether the user has chosen to uninstall. If you are using an uninstaller ; components page, make sure all sections are uninstalled. old_no_remove_uninstaller: old_detect_done: ; old installation not found, all ok pop $R0 FunctionEnd ; Prevent running multiple instances of the installer Function PreventMultipleInstances Push $R0 System::Call 'kernel32::CreateMutexA(i 0, i 0, t ${PRODUCT_NAME}) ?e' Pop $R0 StrCmp $R0 0 +3 MessageBox MB_OK|MB_ICONEXCLAMATION "The installer is already running." /SD IDOK Abort Pop $R0 FunctionEnd ; eof gsmartcontrol-1.1.4/data/nsis/nsi_install.ico000066400000000000000000000013761417717230600213320ustar00rootroot00000000000000 è( @€€€€€€€€€€€€€ÀÀÀÿÿÿÿÿÿÿÿÿÿÿÿwxÝxøðˆÝ™€p‡øýÙ™ˆxøð‡xˆÙ˜ˆ€xw‡‡øÙˆ‹°xøð‡xxxp ‹»°€xw‡‡𻸀xøð‡xˆ»ðxxp€xx»‰°‡‡€ÿð‡{¸›½ÿxxpÿðxw‰»½ø÷‡ÿð‡‡›»Ýxˆˆÿðˆˆ{½Ýˆøðˆpÿðˆˆ}݈€wwˆpÿðˆˆ÷wwÿÿÿÿpwwpˆˆwwwwwwwxpwwpˆˆ€ÿÿ÷ˆˆˆˆˆˆˆˆ‡wwwwpˆÿÿÿÿÿÿÿ‡wwò"""""/‡wwpò¸ƒ3:ª/‡ÿÿòûˆ33ª/‡wwpò¿¸ƒ3:/‡òûûˆ33/‡ò¿¿¸ƒ3/‡ò‹ûûˆ3/‡òˆ¿¿¸ƒ/‡òˆ‹ûûˆ/‡ò"""""/‡ÿÿÿÿÿÿÿ‡wwwwwwwwÿþÿÀøÿÀpÿÀ ÀÀ?À?À?À?À?À?ÀÀÀÀÀÀÀ€øüþÿÿøÿøÿøÿøÿøÿøÿøÿügsmartcontrol-1.1.4/data/nsis/nsi_uninstall.ico000066400000000000000000000013761417717230600216750ustar00rootroot00000000000000 è( @€€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿxwxˆwwxˆˆwwwxˆˆwwwwxˆˆwwwwwxˆˆwwwwwwxˆˆ€wwwwwwwxˆˆ€wwwwwwwxˆˆ€wwwwwwwxˆˆ€wwwwwwwxˆˆ€‡wwwwwwwxˆˆˆ‡wwwwwwwxˆˆˆ‡wwwwwwwxˆˆˆ‡wwwwwwwxˆˆˆ‡wwwwwwwxˆˆˆwwwwwwwÿˆˆˆ€wwwwwwøˆÿˆˆ€wwwwwø‡wˆÿˆ€wwwwøˆwwwˆÿ€wwwøˆ‡wwwwˆÿ‡wwøˆˆ‡wwwww‡wø€ˆˆwwwwwð‡øˆ€ˆˆwwwwðøˆˆ€ˆ‡wwwðøˆˆˆ€ˆ‡wwðÿˆˆˆ€ˆwwðÿˆˆ€ˆwðÿˆ€‡ðÿ‡ðÿÿãÿÿÿ€ÿÿþ?ÿøÿàÿ€þøøøøøðððððàààààÀÀÀÀ€€ÿÀÿðÿüÿÿÿÿgsmartcontrol-1.1.4/data/org.gsmartcontrol.policy000066400000000000000000000013271417717230600222440ustar00rootroot00000000000000 Authentication is required to run GSmartControl gsmartcontrol auth_admin auth_admin auth_admin /usr/sbin/gsmartcontrol true gsmartcontrol-1.1.4/debian.dist/000077500000000000000000000000001417717230600166075ustar00rootroot00000000000000gsmartcontrol-1.1.4/debian.dist/Makefile.am000066400000000000000000000003131417717230600206400ustar00rootroot00000000000000 noinst_DATA = changelog.in compat control copyright menu postinst postrm rules # include the generated file too EXTRA_DIST=changelog.in changelog compat control copyright menu postinst \ postrm rules gsmartcontrol-1.1.4/debian.dist/changelog.in000066400000000000000000000002561417717230600210710ustar00rootroot00000000000000gsmartcontrol (@VERSION@+nmu1) unstable; urgency=low * Permanently Initial Release. -- Alexander Shaduri Sat, 15 Nov 2008 00:12:04 +0400 gsmartcontrol-1.1.4/debian.dist/compat000066400000000000000000000000021417717230600200050ustar00rootroot000000000000009 gsmartcontrol-1.1.4/debian.dist/control000066400000000000000000000014351417717230600202150ustar00rootroot00000000000000Source: gsmartcontrol Section: utils Priority: extra Homepage: https://gsmartcontrol.shaduri.dev Maintainer: Alexander Shaduri Build-Depends: debhelper (>= 5), autotools-dev, libpcre3-dev, libgtkmm-3.0-dev (>= 3.4.0) Standards-Version: 3.7.3 Package: gsmartcontrol Architecture: any Depends: ${shlibs:Depends}, smartmontools (>= 5.43), xterm, menu Description: Hard disk drive and SSD health inspection tool GSmartControl is a graphical user interface for smartctl (from smartmontools package), which is a tool for querying and controlling SMART (Self-Monitoring, Analysis, and Reporting Technology) data on modern hard disk and solid-state drives. It allows you to inspect the drive's SMART data to determine its health, as well as run various tests on it. gsmartcontrol-1.1.4/debian.dist/copyright000066400000000000000000000023441417717230600205450ustar00rootroot00000000000000This package was debianized by Alexander Shaduri on Sat, 15 Nov 2008 00:12:04 +0400. It was downloaded from Copyright: Copyright (C) 2008 - 2022 Alexander Shaduri License: This product is multi-licensed under GNU GPL versions 2 and 3. You are free to choose which one you use. This program is free software: you can redistribute it and/or modify it under the terms of either version 2 or (at your option) version 3 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licenses for more details. You should have received copies of these licenses along with this program. If not, see . On Debian systems, the complete text of the GNU General Public Licenses can be found in `/usr/share/common-licenses/GPL-2' and `/usr/share/common-licenses/GPL-3'. The Debian packaging is (C) 2008 - 2022 Alexander Shaduri and is licensed under the GPLv2 and GPLv3, see above. gsmartcontrol-1.1.4/debian.dist/menu000066400000000000000000000003541417717230600175000ustar00rootroot00000000000000?package(gsmartcontrol):needs="X11" section="Applications/System/Hardware"\ title="GSmartControl" command="/usr/bin/gsmartcontrol-root"\ longtitle="Hard Disk and SSD Health Inspection"\ icon="/usr/share/pixmaps/gsmartcontrol.xpm" gsmartcontrol-1.1.4/debian.dist/postinst000066400000000000000000000003131417717230600204120ustar00rootroot00000000000000#!/bin/sh set -e if test -x /usr/bin/update-menus; then update-menus; fi # dh_installdeb will replace this with shell code automatically # generated by other debhelper scripts. #DEBHELPER# exit 0 gsmartcontrol-1.1.4/debian.dist/postrm000066400000000000000000000003131417717230600200530ustar00rootroot00000000000000#!/bin/sh set -e if test -x /usr/bin/update-menus; then update-menus; fi # dh_installdeb will replace this with shell code automatically # generated by other debhelper scripts. #DEBHELPER# exit 0 gsmartcontrol-1.1.4/debian.dist/rules000077500000000000000000000047771417717230600177060ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 # These are used for cross-compiling and for saving the configure script # from having to guess our platform (since we know it already) DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) config.status: configure dh_testdir # Add here commands to configure the package. ifneq "$(wildcard /usr/share/misc/config.sub)" "" cp -f /usr/share/misc/config.sub config.sub endif ifneq "$(wildcard /usr/share/misc/config.guess)" "" cp -f /usr/share/misc/config.guess config.guess endif ./configure --host=$(DEB_HOST_GNU_TYPE) \ --build=$(DEB_BUILD_GNU_TYPE) --prefix=/usr --mandir=\$${prefix}/share/man \ --infodir=\$${prefix}/share/info CFLAGS="$(CFLAGS)" LDFLAGS="-Wl,-z,defs" # Add --enable-optimize-options=auto to configure line # for optimized build (may not work on older gcc versions). build: build-stamp build-stamp: config.status dh_testdir # Add here commands to compile the package. $(MAKE) #docbook-to-man debian/gsmartcontrol.sgml > gsmartcontrol.1 touch $@ clean: dh_testdir dh_testroot rm -f build-stamp # Add here commands to clean up after the build process. -$(MAKE) clean dh_clean install: build dh_testdir dh_testroot dh_clean -k dh_installdirs # Add here commands to install the package into debian/gsmartcontrol. $(MAKE) DESTDIR=$(CURDIR)/debian/gsmartcontrol install-strip # Build architecture-independent files here. binary-indep: build install # We have nothing to do by default. # Build architecture-dependent files here. binary-arch: build install dh_testdir dh_testroot dh_installchangelogs ChangeLog dh_installdocs # dh_installexamples # dh_install dh_installmenu # dh_installdebconf # dh_installlogrotate # dh_installemacsen # dh_installpam # dh_installmime # dh_python # dh_installinit # dh_installcron # dh_installinfo dh_installman dh_link dh_strip dh_compress dh_fixperms # dh_perl # dh_makeshlibs dh_installdeb dh_shlibdeps dh_gencontrol dh_md5sums dh_builddeb binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install gsmartcontrol-1.1.4/doxy_main_page.h000066400000000000000000000021611417717230600175570ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ // This file is for generating documentation only. Do NOT include it. /// \file /// \author Alexander Shaduri /** \mainpage GSmartControl Index \section intro_section Introduction This is GSmartControl internal documentation. */ // Group definitions and titles. /** \addtogroup applib Application-Specific Components \addtogroup applib_tests Tests of Application-Specific Components \addtogroup hz HZ Common System Components \addtogroup hz_tests Tests for HZ Common System Components \addtogroup libdebug LibDebug Flexible Debug Logging \addtogroup libdebug_tests Tests for LibDebug Flexible Debug Logging \addtogroup rmn Resource Manager \addtogroup rmn_tests Tests for Resource Manager \addtogroup rconfig Resource Manager-Based Configuration \addtogroup rconfig_tests Tests for Resource Manager-Based Configuration \addtogroup gsc GSmartControl Application */ gsmartcontrol-1.1.4/file2csource.sh000077500000000000000000000023371417717230600173540ustar00rootroot00000000000000#!/bin/bash ############################################################################ # Copyright: # (C) 2008 - 2012 Alexander Shaduri # License: See LICENSE_zlib.txt file ############################################################################ # We use #!/bin/bash of #!/bin/sh because freebsd (tested with 6.3) in its # infinite wisdom has csh-like /bin/sh (even though the SUS says it # must be bourne-compatible). if [ "$1" == "" ] | [ "$2" == "" ] | [ "$3" == "" ]; then echo "Usage: $0 "; exit 1; fi if [ ! -e $1 ]; then echo "Cannot open input file \"$1\"."; exit 2; fi in_file="$1"; out_file="$2"; sym_name="$3"; size=""; if [[ $OSTYPE == 'darwin'* ]]; then size=`stat "-f%z" "$in_file"` else size=`stat -c "%s" "$in_file"` fi # header. extern means "global" in this context (needed because const # variables are static by default). echo " extern const unsigned char ${sym_name}[] = { " > "$out_file"; # file binary. "xxd(1) -i" can also do the trick hexdump -v -e '1/1 "0x%X,\n"' "$in_file" >> "$out_file"; # footer. add 0x0 byte for easy stringifying echo " 0x0 }; extern const unsigned int ${sym_name}_size = ${size}; " >> "$out_file"; gsmartcontrol-1.1.4/gsmartcontrol.kdev4000066400000000000000000000000731417717230600202600ustar00rootroot00000000000000[Project] Manager=KDevCustomBuildSystem Name=gsmartcontrol gsmartcontrol-1.1.4/gsmartcontrol.kdevelop000066400000000000000000000353741417717230600210700ustar00rootroot00000000000000 Alexander Shaduri ashaduri 'at' gmail.com unspecified KDevAutoProject C++ C++ Code GTK gsmartcontrol . false src/gsmartcontrol open64 /10/devel/projects/gsmartcontrol/current/gsmartcontrol/debuggy /10/devel/projects/gsmartcontrol/current/gsmartcontrol/debuggy false false false false false /10/devel/projects/gsmartcontrol/current/data/sda_smartctl-a /10/devel/projects/gsmartcontrol/current/gsmartcontrol/debuggy/./src /10/devel/projects/gsmartcontrol/current/gsmartcontrol/debuggy/./src /10/devel/projects/gsmartcontrol/current/gsmartcontrol/debuggy/./src /10/devel/projects/gsmartcontrol/current/gsmartcontrol/debuggy/./src /10/devel/projects/gsmartcontrol/current/gsmartcontrol/debuggy/./src/applib optimized kdevgccoptions kdevgppoptions kdevg77options --disable-user-flags --enable-optimize-options --enable-tests --enable-debug-options --enable-tests debuggy kdevgccoptions kdevgppoptions kdevg77options gcc-4.4 g++-4.4 --disable-user-flags --enable-tests icc kdevgccoptions kdevgppoptions kdevg77options icc64 icpc64 --with-windows-gtk-name=gtk2-runtime-2.16.6-2010-02-24-ash --disable-user-flags --enable-optimize-options --enable-tests --build=i686-linux --host=i386-pc-mingw32 PKG_CONFIG_PATH=/cross/w32/target/lib/pkgconfig PKG_CONFIG_LIBDIR="" PATH="/cross/w32/gcc-bin:/cross/w32/target/bin:$PATH" win32 kdevgccoptions kdevgppoptions kdevg77options i386-pc-mingw32-gcc i386-pc-mingw32-g++ --enable-tests PKG_CONFIG_PATH="/opt/sun/target64/lib64/pkgconfig:/usr/lib64/pkgconfig" PKG_CONFIG_LIBDIR="" suncc -R/opt/sun/target64/lib64 -L/opt/sun/target64/lib64 kdevgccoptions kdevgppoptions kdevg77options suncc sunCC -errwarn=%all -I/opt/sun/target64/include -I/opt/sun/target64/include --enable-tests portland -s kdevgccoptions kdevgppoptions kdevg77options pgcc pgCC -Minform=warn -Bstatic_pgi -Minform=warn -Bstatic_pgi -noswitcherror -define_macro=HZ_NO_COMPILER_AUTOINCLUDE --disable-user-flags --enable-debug-options --enable-tests --build=i686-linux --host=i386-pc-mingw32 PKG_CONFIG_PATH=/cross/w32/target/lib/pkgconfig PKG_CONFIG_LIBDIR="" PATH="/cross/w32/gcc-bin:/cross/w32/target/bin:$PATH" win32-debug kdevgccoptions kdevgppoptions kdevg77options i386-pc-mingw32-gcc i386-pc-mingw32-g++ --disable-user-flags --enable-optimize-options --enable-tests --build=i686-linux --host=x86_64-pc-mingw32 PKG_CONFIG_PATH=/cross/w64/target/lib/pkgconfig PKG_CONFIG_LIBDIR="" PATH="/cross/w64/gcc-bin:/cross/w64/target/bin:$PATH" win64-debug kdevgccoptions kdevgppoptions kdevg77options x86_64-pc-mingw32-gcc x86_64-pc-mingw32-g++ kdevgccoptions kdevgppoptions kdevg77options --enable-tests open64 kdevgccoptions kdevgppoptions kdevg77options /opt/open64/bin/opencc /opt/open64/bin/openCC -Wall true true 3 false 0 false false *.o,*.lo,CVS false ada ada_bugs_gcc bash bash_bugs clanlib w3c-dom-level2-html fortran_bugs_gcc gnustep haskell haskell_bugs_ghc java_bugs_gcc java_bugs_sun kde2book libstdc++ opengl pascal_bugs_fp php php_bugs perl perl_bugs python python_bugs qt-kdev3 ruby ruby_bugs sdl w3c-svg sw w3c-uaag10 wxwidgets_bugs Guide to the Qt Translation Tools Qt Assistant Manual Qt Designer Manual Qt Reference Documentation qmake User Guide KDE Libraries (Doxygen) true false false false true true 10 Doxygen Documentation Collection gsmartcontrol.tag gsmartcontrol gsmartcontrol Gsmartcontrol GSMARTCONTROL Alexander Shaduri ashaduri 'at' gmail.com GPL COPYING 0.0.1 2008 /10/devel/projects/gsmartcontrol/current/gsmartcontrol false 3 3 /usr/lib/qt3 EmbeddedKDevDesigner /usr/lib/qt3/bin/qmake /usr/lib/qt3/bin/designer true true true 250 400 250 false 0 true true true std=_GLIBCXX_STD;__gnu_cxx=std true true true false false true true false .;src;/usr/include;/usr/include/c++/4.2.1;/usr/include/glib-2.0 get_ set_ m_,_ the_value true true false true Vertical .h .cpp /10/devel/projects/gsmartcontrol/current/gsmartcontrol/tags gsmartcontrol-1.1.4/gsmartcontrol.spec.in000066400000000000000000000052671417717230600206140ustar00rootroot00000000000000 # This spec file is for openSUSE Build Service. # Supported distributions: openSUSE, Fedora, CentOS, RHEL. Name: gsmartcontrol Version: @VERSION@ Release: 0 License: GPL-2.0 AND GPL-3.0 Url: https://gsmartcontrol.shaduri.dev Vendor: Alexander Shaduri # Packager: Alexander Shaduri Source: http://sourceforge.net/projects/gsmartcontrol/files/%{version}/%{name}-%{version}.tar.bz2 BuildRoot: %{_tmppath}/%{name}-%{version}-build Summary: GSmartControl - Hard Disk Drive and SSD Health Inspection Tool Group: Hardware/Other # Empty debug packages cause errors in new RPM. Disable them. %global debug_package %{nil} # For non-specified distributions we don't specify any dependencies to avoid errors. # SUSE / OpenSUSE. SLES also defines the correct suse_version. %if 0%{?suse_version} Requires: smartmontools >= 5.43, polkit, bash, xterm BuildRequires: gcc-c++, libstdc++-devel, pcre-devel, gtkmm3-devel >= 3.4.0 BuildRequires: update-desktop-files BuildRequires: fdupes %endif # Fedora, CentOS, RHEL %if 0%{?fedora_version} || 0%{?rhel_version} || 0%{?centos_version} Requires: smartmontools >= 5.43, polkit, bash, xterm BuildRequires: gcc-c++, pcre-devel, gtkmm30-devel >= 3.4.0 %endif %description GSmartControl is a graphical user interface for smartctl, which is a tool for querying and controlling SMART (Self-Monitoring, Analysis, and Reporting Technology) data in hard disk and solid-state drives. It allows you to inspect the drive's SMART data to determine its health, as well as run various tests on it. %prep %setup -q %configure %build make %{?_smp_mflags} %install # %%makeinstall make DESTDIR=%buildroot install-strip # Remove the icon cache file "make install" generates, to avoid package conflicts. rm -f $RPM_BUILD_ROOT%{_datadir}/icons/hicolor/icon-theme.cache %if 0%{?suse_version} %suse_update_desktop_file -n %{name} # There are some png file duplicates, hardlink them. %fdupes # We install icons, so this is needed. %if 0%{?suse_version} >= 1140 %post %icon_theme_cache_post %postun %icon_theme_cache_postun %endif # endif suse %endif %clean rm -rf %buildroot %files %defattr(-,root,root) %attr(0755,root,root) %{_bindir}/gsmartcontrol-root %attr(0755,root,root) %{_sbindir}/gsmartcontrol # %%attr(0644,root,root) %%config(noreplace) %%{_sysconfdir}/* %doc %{_datadir}/doc/gsmartcontrol %doc %{_mandir}/man1/* %{_datadir}/gsmartcontrol # %%{_datadir}/gsmartcontrol/* %{_datadir}/applications/*.desktop %{_datadir}/metainfo %{_datadir}/metainfo/gsmartcontrol.appdata.xml %{_datadir}/polkit-1 %{_datadir}/polkit-1/actions %{_datadir}/polkit-1/actions/org.gsmartcontrol.policy %{_datadir}/icons/* %{_datadir}/pixmaps/* %changelog gsmartcontrol-1.1.4/src/000077500000000000000000000000001417717230600152125ustar00rootroot00000000000000gsmartcontrol-1.1.4/src/Makefile.am000066400000000000000000000024001417717230600172420ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) METASOURCES = AUTO SUBDIRS = libdebug hz rmn rconfig applib res if TARGET_OS_KERNEL_FAMILY_WINDOWS WINRES_OBJ = gsc_winres_win32.o endif # use different name for object file to avoid make auto-rules gsc_winres_win32.o: $(top_srcdir)/data/gsmartcontrol.ico gsc_winres.rc cp -f $(top_srcdir)/data/gsmartcontrol.ico . $(WINDRES) gsc_winres.rc gsc_winres_win32.o mostlyclean-local: rm -f gsmartcontrol.ico noinst_DATA = gsc_winres.rc.in gsmartcontrol.exe.manifest.in # if TARGET_OS_KERNEL_FAMILY_WINDOWS # WIN_MAIN_SRC = win_main.cpp # endif sbin_PROGRAMS = gsmartcontrol gsmartcontrol_LDADD = $(top_builddir)/src/applib/libapplib.a \ $(top_builddir)/src/libdebug/libdebug.a \ $(top_builddir)/src/res/libres.a $(WINRES_OBJ) noinst_HEADERS = global_macros.h gsc_about_dialog.h gsc_add_device_window.h gsc_executor_error_dialog.h \ gsc_executor_log_window.h gsc_info_window.h gsc_init.h gsc_main_window.h \ gsc_main_window_iconview.h gsc_preferences_window.h gsc_settings.h gsc_text_window.h gsmartcontrol_SOURCES = $(WIN_MAIN_SRC) gsc_about_dialog.cpp gsc_add_device_window.cpp \ gsc_executor_error_dialog.cpp gsc_executor_log_window.cpp gsc_info_window.cpp \ gsc_init.cpp gsc_main.cpp gsc_main_window.cpp gsc_preferences_window.cpp gsmartcontrol-1.1.4/src/applib/000077500000000000000000000000001417717230600164615ustar00rootroot00000000000000gsmartcontrol-1.1.4/src/applib/Makefile.am000066400000000000000000000040151417717230600205150ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) METASOURCES = AUTO noinst_LIBRARIES = libapplib.a libapplib_a_SOURCES = app_gtkmm_utils.cpp app_pango_utils.cpp cmdex.cpp \ cmdex_sync.cpp cmdex_sync_gui.cpp executor_factory.cpp gui_utils.cpp selftest.cpp smartctl_executor.cpp \ smartctl_parser.cpp storage_detector.cpp storage_detector_linux.cpp storage_detector_other.cpp \ storage_detector_win32.cpp storage_device.cpp storage_property.cpp \ storage_property_descr.cpp noinst_HEADERS = app_gtkmm_features.h app_gtkmm_utils.h app_pango_utils.h \ app_pcrecpp.h app_ui_res_utils.h cli_executors.h cmdex.h cmdex_sync.h cmdex_sync_gui.h executor_factory.h gui_utils.h \ selftest.h smartctl_executor.h smartctl_executor_gui.h smartctl_parser.h \ storage_detector.h storage_detector_helpers.h storage_detector_linux.h storage_detector_other.h \ storage_detector_win32.h storage_device.h storage_property.h storage_property_colors.h \ storage_property_descr.h storage_settings.h # don't use absolute path for the current dir's .a, because the make # dependency resolver won't get it (needed for parallel builds) app_ui_res_utils_test_SOURCES = app_ui_res_utils_test.cpp app_ui_res_utils_test_data.cpp app_ui_res_utils_test_LDADD = libapplib.a \ $(top_builddir)/src/libdebug/libdebug.a smartctl_executor_test_SOURCES = smartctl_executor_test.cpp smartctl_executor_test_LDADD = libapplib.a \ $(top_builddir)/src/libdebug/libdebug.a smartctl_parser_test_SOURCES = smartctl_parser_test.cpp smartctl_parser_test_LDADD = libapplib.a \ $(top_builddir)/src/libdebug/libdebug.a storage_detector_test_SOURCES = storage_detector_test.cpp storage_detector_test_LDADD = libapplib.a \ $(top_builddir)/src/libdebug/libdebug.a spawn_test_SOURCES = spawn_test.cpp # Build them first SUBDIRS=../libdebug # we don't list them in a separate variable because otherwise kdevelop won't see them. noinst_PROGRAMS = if ENABLE_TESTS noinst_PROGRAMS += app_ui_res_utils_test smartctl_executor_test smartctl_parser_test \ storage_detector_test spawn_test endif tests: $(noinst_PROGRAMS) gsmartcontrol-1.1.4/src/applib/app_gtkmm_features.h000066400000000000000000000041171417717230600225120ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef APP_GTKMM_FEATURES_H #define APP_GTKMM_FEATURES_H #include /// \def APP_GTKMM_CHECK_VERSION(major, minor, micro) /// Similar to GTK_CHECK_VERSION, but for gtkmm, which lacks this for some reason. #ifndef APP_GTKMM_CHECK_VERSION #define APP_GTKMM_CHECK_VERSION(major, minor, micro) \ (GTKMM_MAJOR_VERSION > (major) \ || (GTKMM_MAJOR_VERSION == (major) && (GTKMM_MINOR_VERSION > (minor) \ || (GTKMM_MINOR_VERSION == (minor) && GTKMM_MICRO_VERSION >= (micro)) \ ) \ ) \ ) #endif // If default virtual on_* members are disabled in gtkmm, use this in constructors // to connect on_signal_name methods. // Note: Some signals may be missing from earlier gtkmm versions. E.g. gtkmm 2.6 // doesn't have on_delete_event virtual function to override. In such cases it's better // to just connect a non-virtual signal. /* #ifdef GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED // nothing #define APP_GTKMM_CONNECT_VIRTUAL(signal_name) #else #define APP_GTKMM_CONNECT_VIRTUAL(signal_name) \ this->signal_ ## signal_name ().connect(sigc::mem_fun(*this, &self_type::on_ ## signal_name)) #endif */ /// Connect to a signal _before_ the default handler. That is, if you want /// to have, say, on_delete_event() in your window-inherited class, define /// on_delete_event_before() instead and return true (handled) from it /// if it's X event handler. /// This method will work regardless of presence of default virtual handlers /// in parent class. It will also avoid calling the handler twice (one from /// signal, another from default virtual handler). #define APP_GTKMM_CONNECT_VIRTUAL(signal_name) \ this->signal_ ## signal_name ().connect(sigc::mem_fun(*this, &self_type::on_ ## signal_name ## _before), false) #endif /// @} gsmartcontrol-1.1.4/src/applib/app_gtkmm_utils.cpp000066400000000000000000000063651417717230600223760ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #include #include #include #include // std::strlen #include #include "app_pango_utils.h" // app_pango_strip_markup() #include "app_gtkmm_utils.h" // Note: This works only if the column has custom widget set. Gtk::Widget* app_gtkmm_get_column_header(Gtk::TreeViewColumn& column) { Gtk::Widget* w = column.get_widget(); Gtk::Widget* p1 = 0; Gtk::Widget* p2 = 0; Gtk::Widget* p3 = 0; // move up to GtkAlignment, then GtkHBox, then GtkButton. if (w && (p1 = w->get_parent()) && (p2 = p1->get_parent()) && (p3 = p2->get_parent())) return p3; return NULL; } // Read column header text and create a label with that text, set it as column's custom widget. Gtk::Widget* app_gtkmm_labelize_column(Gtk::TreeViewColumn& column) { Gtk::Label* label = Gtk::manage(new Gtk::Label(column.get_title())); label->show(); column.set_widget(*label); return label; } // A wrapper around set_tooltip_*() for portability across different gtkmm versions. void app_gtkmm_set_widget_tooltip(Gtk::Widget& widget, const Glib::ustring& tooltip_text, bool use_markup) { if (use_markup) { widget.set_tooltip_markup(tooltip_text); } else { widget.set_tooltip_text(tooltip_text); } } namespace { /// This has been copied from _g_utf8_make_valid() (glib-2.20.4). /// _g_utf8_make_valid() is GLib's private function for auto-correcting /// the potentially invalid utf-8 data. inline gchar* gsc_g_utf8_make_valid (const gchar* name) { GString* str; const gchar* remainder, *invalid; gint remaining_bytes, valid_bytes; g_return_val_if_fail (name != NULL, NULL); str = NULL; remainder = name; remaining_bytes = gint(std::strlen(name)); while (remaining_bytes != 0) { if (g_utf8_validate (remainder, remaining_bytes, &invalid)) break; valid_bytes = gint(invalid - remainder); if (str == NULL) str = g_string_sized_new (remaining_bytes); g_string_append_len (str, remainder, valid_bytes); /* append U+FFFD REPLACEMENT CHARACTER */ g_string_append (str, "\357\277\275"); remaining_bytes -= valid_bytes + 1; remainder = invalid + 1; } if (str == NULL) return g_strdup (name); g_string_append (str, remainder); g_assert (g_utf8_validate (str->str, -1, NULL)); return g_string_free (str, FALSE); } } Glib::ustring app_ustring_from_gchar(gchar* str) { if (!str) { return Glib::ustring(); } Glib::ustring ustr(str); g_free(str); return ustr; } Glib::ustring app_utf8_make_valid(const Glib::ustring& str) { char* s = gsc_g_utf8_make_valid(str.c_str()); if (!s) { return Glib::ustring(); } Glib::ustring res(s); g_free(s); return res; } Glib::ustring app_output_make_valid(const Glib::ustring& str) { #ifdef _WIN32 try { return app_utf8_make_valid(Glib::locale_to_utf8(str)); } catch (Glib::ConvertError& e) { // nothing, try to fix as it is } #endif return app_utf8_make_valid(str); } /// @} gsmartcontrol-1.1.4/src/applib/app_gtkmm_utils.h000066400000000000000000000056471417717230600220450ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef APP_GTKMM_UTILS_H #define APP_GTKMM_UTILS_H #include #include #include "hz/down_cast.h" /// Get column header widget of a tree view column. /// Note: This works only if the column has custom widget set. Gtk::Widget* app_gtkmm_get_column_header(Gtk::TreeViewColumn& column); /// Read column header text and create a label with that text. Set the label as /// column's custom widget and return it. Gtk::Widget* app_gtkmm_labelize_column(Gtk::TreeViewColumn& column); /// A wrapper around set_tooltip_*() for portability across different gtkmm versions. void app_gtkmm_set_widget_tooltip(Gtk::Widget& widget, const Glib::ustring& tooltip_text, bool use_markup = false); /// Convenience function for creating a TreeViewColumn . template int app_gtkmm_create_tree_view_column(Gtk::TreeModelColumn& mcol, Gtk::TreeView& treeview, const Glib::ustring& title, const Glib::ustring& tooltip_text, bool sortable = false, bool cell_markup = false) { int num_tree_cols = treeview.append_column(title, mcol); Gtk::TreeViewColumn* tcol = treeview.get_column(num_tree_cols - 1); if (tcol) { if (sortable) tcol->set_sort_column(mcol); app_gtkmm_labelize_column(*tcol); tcol->set_reorderable(true); tcol->set_resizable(true); } Gtk::Widget* header = app_gtkmm_get_column_header(*tcol); if (header) app_gtkmm_set_widget_tooltip(*header, tooltip_text); if (cell_markup) { Gtk::CellRendererText* cr_type = hz::down_cast(treeview.get_column_cell_renderer(num_tree_cols - 1)); if (cr_type) { // may not be true if it's not Text (unless static_cast is used, in which case we're screwed) treeview.get_column(num_tree_cols - 1)->clear_attributes(*cr_type); // clear "text" attribute. "markup" won't work without this. treeview.get_column(num_tree_cols - 1)->add_attribute(cr_type->property_markup(), mcol); // render col_type as markup. } } return num_tree_cols; } /// Get Glib::ustring from gchar*, freeing gchar*. Glib::ustring app_ustring_from_gchar(gchar* str); /// Convert a possibly invalid utf-8 string to valid utf-8. /// \param str string to test and fix. Glib::ustring app_utf8_make_valid(const Glib::ustring& str); /// Make command output a valid utf-8 string. Essentially, this calls app_utf8_make_valid(), /// supplying true for \c in_locale under Win32, and false under other systems. /// The reason for this is that in Win32 we can't execute commands under C locale, /// but we do execute them under C in other systems. Glib::ustring app_output_make_valid(const Glib::ustring& str); #endif /// @} gsmartcontrol-1.1.4/src/applib/app_pango_utils.cpp000066400000000000000000000023701417717230600223530ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #include // pango_parse_markup() #include "app_pango_utils.h" // Returns true if markup was successfully stripped bool app_pango_strip_markup(const Glib::ustring& str, Glib::ustring& stripped) { gchar* gstripped = 0; bool ok = false; if (pango_parse_markup(str.c_str(), -1, 0, NULL, &gstripped, NULL, NULL) && gstripped) { stripped = gstripped; ok = true; } g_free(gstripped); // alternative method, check if it works (AttrList has bool() cast since gtkmm 2.10): // char dummy = 0; // if (Pango::AttrList(str, 0, stripped, dummy).gobj()) { // return true; // } return ok; } bool app_pango_strip_markup(const std::string& str, std::string& stripped) { gchar* gstripped = 0; bool ok = false; if (pango_parse_markup(str.c_str(), -1, 0, NULL, &gstripped, NULL, NULL) && gstripped) { stripped = gstripped; ok = true; } g_free(gstripped); return ok; } /// @} gsmartcontrol-1.1.4/src/applib/app_pango_utils.h000066400000000000000000000014521417717230600220200ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef APP_PANGO_UTILS_H #define APP_PANGO_UTILS_H #include #include // ustring /// Strip a string of all markup tags. /// \return false if there was some error. bool app_pango_strip_markup(const Glib::ustring& str, Glib::ustring& stripped); /// Strip a string of all markup tags. /// \return false if there was some error. bool app_pango_strip_markup(const std::string& str, std::string& stripped); #endif /// @} gsmartcontrol-1.1.4/src/applib/app_pcrecpp.h000066400000000000000000000211701417717230600211270ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef APP_PCRECPP_H #define APP_PCRECPP_H // A wrapper header for pcrecpp #include #include #include "hz/debug.h" /// Take a string of characters where each character represents a modifier /// and return the appropriate pcre options. /// - i - case insensitive match. /// - m - multiline, read past the first line. /// - s - dot matches newlines. /// - E - $ matches only the end of the string (D in php, not available in perl). /// - X - strict escape parsing (not available in perl). /// - x - ignore whitespaces. /// - 8 - handles UTF8 characters in pattern (u in php, not available in perl). /// - U - ungreedy, reverses * and *? (not available in perl). /// - N - disables matching parentheses (not available in perl or php). inline pcrecpp::RE_Options app_pcre_get_options(const char* modifiers) { // ANYCRLF means any of crlf, cr, lf. Used in ^ and $. // This overrides the build-time newline setting of pcre. #ifdef PCRE_NEWLINE_ANYCRLF pcrecpp::RE_Options options(PCRE_NEWLINE_ANYCRLF); #else pcrecpp::RE_Options options; #endif if (modifiers) { char c = '\0'; while ((c = *modifiers++) != '\0') { switch (c) { // Note: Most of these are from pcretest man page. // Perl lacks some of them. case 'i': options.set_caseless(true); break; // case insensitive match. case 'm': options.set_multiline(true); break; // read past the first line too. case 's': options.set_dotall(true); break; // dot matches newlines. case 'E': options.set_dollar_endonly(true); break; // not in perl. php has D. $ matches only at end. case 'X': options.set_extra(true); break; // not in perl. strict escape parsing case 'x': options.set_extended(true); break; // ignore whitespaces case '8': options.set_utf8(true); break; // not in perl. php has u. handles UTF8 chars in pattern. case 'U': options.set_ungreedy(true); break; // not in perl. reverses * and *? case 'N': options.set_no_auto_capture(true); break; // not in perl or php. disables matching parentheses. default: debug_out_error("app", DBG_FUNC_MSG << "Unknown modifier \'" << c << "\'\n"); break; } } } return options; } /// Accept pattern in form of "/pattern/modifiers". /// Note: Slashes should be escaped within the pattern. /// If the string doesn't start with a slash, it is treated as an ordinary pattern /// without modifiers. /// This function will make a RE object with ANYCRLF option set for portability /// across various pcre builds. inline pcrecpp::RE app_pcre_re(const std::string& perl_pattern) { if (perl_pattern.size() >= 2 && perl_pattern[0] == '/') { // find the separator std::string::size_type endpos = perl_pattern.rfind('/'); DBG_ASSERT(endpos != std::string::npos); // shouldn't happen // no need to unescape slashes in pattern - pcre seems to not mind. return pcrecpp::RE(perl_pattern.substr(1, endpos - 1), app_pcre_get_options(perl_pattern.substr(endpos + 1).c_str())); } return pcrecpp::RE(perl_pattern, app_pcre_get_options(0)); } /// Match a string against a pattern in "/pattern/modifiers" format. inline bool app_pcre_match(const std::string& perl_pattern, const std::string& str, const pcrecpp::Arg& ptr1 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr2 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr3 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr4 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr5 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr6 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr7 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr8 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr9 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr10 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr11 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr12 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr13 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr14 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr15 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr16 = pcrecpp::RE::no_arg) { return app_pcre_re(perl_pattern).PartialMatch(str, ptr1, ptr2, ptr3, ptr4, ptr5, ptr6, ptr7, ptr8, ptr9, ptr10, ptr11, ptr12, ptr13, ptr14, ptr15, ptr16); } /// Match a string against a pattern in "/pattern/modifiers" format. /// This overload is needed to avoid confusion with RE. inline bool app_pcre_match(const char* perl_pattern, const std::string& str, const pcrecpp::Arg& ptr1 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr2 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr3 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr4 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr5 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr6 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr7 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr8 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr9 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr10 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr11 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr12 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr13 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr14 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr15 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr16 = pcrecpp::RE::no_arg) { return app_pcre_match(std::string(perl_pattern), str, ptr1, ptr2, ptr3, ptr4, ptr5, ptr6, ptr7, ptr8, ptr9, ptr10, ptr11, ptr12, ptr13, ptr14, ptr15, ptr16); } /// Match a string against a pattern in "/pattern/modifiers" format. inline bool app_pcre_match(const pcrecpp::RE& re, const std::string& str, const pcrecpp::Arg& ptr1 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr2 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr3 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr4 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr5 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr6 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr7 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr8 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr9 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr10 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr11 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr12 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr13 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr14 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr15 = pcrecpp::RE::no_arg, const pcrecpp::Arg& ptr16 = pcrecpp::RE::no_arg) { return re.PartialMatch(str, ptr1, ptr2, ptr3, ptr4, ptr5, ptr6, ptr7, ptr8, ptr9, ptr10, ptr11, ptr12, ptr13, ptr14, ptr15, ptr16); } /// Replace every occurence of pattern with replacement string in \c subject. /// The pattern is in "/pattern/modifiers" format. inline int app_pcre_replace(const std::string& perl_pattern, const std::string& replacement, std::string& subject) { return app_pcre_re(perl_pattern).GlobalReplace(replacement, &subject); } /// Replace every occurence of pattern with replacement string in \c subject. /// The pattern is in "/pattern/modifiers" format. inline int app_pcre_replace(const char* perl_pattern, const std::string& replacement, std::string& subject) { return app_pcre_replace(std::string(perl_pattern), replacement, subject); } /// Replace every occurence of pattern with replacement string in \c subject. inline int app_pcre_replace(const pcrecpp::RE& re, const std::string& replacement, std::string& subject) { return re.GlobalReplace(replacement, &subject); } /// Replace the first occurence of pattern with replacement string in \c subject. /// The pattern is in "/pattern/modifiers" format. inline bool app_pcre_replace_once(const std::string& perl_pattern, const std::string& replacement, std::string& subject) { return app_pcre_re(perl_pattern).Replace(replacement, &subject); } /// Replace the first occurence of pattern with replacement string in \c subject. /// The pattern is in "/pattern/modifiers" format. inline bool app_pcre_replace_once(const char* perl_pattern, const std::string& replacement, std::string& subject) { return app_pcre_replace_once(std::string(perl_pattern), replacement, subject); } /// Replace first occurence of pattern with replacement string in \c subject. inline bool app_pcre_replace_once(const pcrecpp::RE& re, const std::string& replacement, std::string& subject) { return re.Replace(replacement, &subject); } /// Escape a string to be used inside a regular expression. The result /// won't contain any special expression characters. inline std::string app_pcre_escape(const std::string& str) { return pcrecpp::RE::QuoteMeta(str); } #endif /// @} gsmartcontrol-1.1.4/src/applib/app_ui_res_utils.h000066400000000000000000000170471417717230600222110ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef APP_UI_RES_UTILS_H #define APP_UI_RES_UTILS_H #include #include #include "hz/hz_config.h" // feature test macros (ENABLE_*) // Old glibmm versions had exceptions but didn't define this at all. // New ones define it to 0 if there are no glibmm exceptions. #if !defined GLIBMM_EXCEPTIONS_ENABLED || GLIBMM_EXCEPTIONS_ENABLED #include // std::auto_ptr #endif #include "hz/debug.h" #include "hz/instance_manager.h" #include "hz/down_cast.h" #include "hz/res_data.h" #include "gui_utils.h" // gui_show_error_dialog /// \def APP_UI_RES_DATA_INIT(res_name) /// Use these in window class definitions to declare ui resources. /// E.g. APP_UI_RES_DATA_INIT(main_window) will search for /// main_window.ui in data file search paths. /// Or, if you're using compiled-in buffers, it will make them available. #define APP_UI_RES_DATA_INIT(res_name) \ HZ_RES_DATA_INIT_NAMED(res_name##_ui, #res_name ".ui", UIResDataBase); \ struct UIResData : public UIResDataBase { \ UIResData() \ { \ UIResDataBase::root_name = #res_name; /* we need original name here, not with _ui */ \ } \ } /// \typedef app_ui_res_ref_t /// A reference-counting pointer to application UI resource typedef Glib::RefPtr app_ui_res_ref_t; /// \fn bool app_ui_res_create_from(app_ui_res_ref_t& ref, const unsigned char* buf, unsigned int buf_size, std::string& error_msg) /// Create application UI resource from a static buffer. inline bool app_ui_res_create_from(app_ui_res_ref_t& ref, const unsigned char* buf, unsigned int buf_size, std::string& error_msg) { if (!buf_size || !buf || !buf[0]) { error_msg = "Cannot load data buffers."; return false; } #if !defined GLIBMM_EXCEPTIONS_ENABLED || GLIBMM_EXCEPTIONS_ENABLED try { // ref->add_from_file("main_window.ui"); ref->add_from_string(reinterpret_cast(buf), static_cast(buf_size)); } catch (Gtk::BuilderError& ex) { // the docs say Glib::MarkupError, but examples say otherwise. error_msg = ex.what(); return false; } #else std::auto_ptr error; ref->add_from_string(reinterpret_cast(buf), static_cast(buf_size),"", "", error); if (error.get()) { error_msg = error->what(); return false; } #endif return true; } // These allow easy attaching of gtkbuilder widget signals to member functions /// Connect member function (callback) to signal \c signal_name on widget /// \c ui_element, where \c ui_element is the widget's gtkbuilder name. #define APP_UI_RES_CONNECT(ui_element, signal_name, callback) \ if (true) { \ if (!ui_element) \ this->lookup_object(#ui_element, ui_element); \ if (ui_element) { \ ui_element->signal_ ## signal_name ().connect(sigc::mem_fun(*this, &self_type::callback)); \ } \ } else (void)0 /// Connect member function (callback) with a name of \c on__ /// to signal \c signal_name on widget \c ui_element, where \c ui_element is the /// widget's gtkbuilder name. #define APP_UI_RES_AUTO_CONNECT(ui_element, signal_name) \ APP_UI_RES_CONNECT(ui_element, signal_name, on_ ## ui_element ## _ ## signal_name) /// Inherit this when using GtkBuilder-enabled windows (or any other GtkBuilder-enabled objects). /// \c Child is the child class that inherits all the functionality of having instance lifetime /// management and other benefits. /// If \c MultiInstance is false, create() will return the same instance each time. template class AppUIResWidget : public WidgetType, public hz::InstanceManager { public: /// Instance class type, which is also the parent class. typedef hz::InstanceManager instance_class; friend class Gtk::Builder; // allow construction through gtkbuilder friend class hz::InstanceManager; // allow construction through instance class /// Override parent hz::InstanceManager's function because of non-trivial constructor static Child* create() { if (hz::InstanceManager::has_single_instance()) // for single-instance objects return hz::InstanceManager::get_single_instance(); std::string error; app_ui_res_ref_t ui = Gtk::Builder::create(); const typename Child::UIResData data; // this holds the GtkBuilder data // this does the actual object construction bool success = app_ui_res_create_from(ui, data.buf, data.size, error); if (!success) { std::string msg = "Fatal error: Cannot create UI-resource widgets: " + error; debug_out_fatal("app", msg << "\n"); gui_show_error_dialog(msg); return 0; } Child* o = 0; ui->get_widget_derived(data.root_name, o); if (!o) { std::string msg = "Fatal error: Cannot get root widget from UI-resource-created hierarchy."; debug_out_fatal("app", msg << "\n"); gui_show_error_dialog(msg); return 0; } o->obj_create(); hz::InstanceManager::set_single_instance(o); // for single-instance objects return o; } /// Get UI resource app_ui_res_ref_t get_ui() { return ref_ui_; } /// Find a widget in UI and return it. Gtk::Widget* lookup_widget(const Glib::ustring& name) { return lookup_widget(name); } /// Find a widget in UI and return it. template WidgetPtr lookup_widget(const Glib::ustring& name) { WidgetPtr w = 0; return lookup_widget(name, w); } /// Find a widget in UI and return it. template WidgetPtr lookup_widget(const Glib::ustring& name, WidgetPtr& w) { ref_ui_->get_widget(name, w); return w; } /// Find an object in UI and return it. Glib::Object* lookup_object(const Glib::ustring& name) { return ref_ui_->get_object(name).operator->(); // silly RefPtr doesn't have get(). } /// Find an object in UI and return it. template ObjectPtr lookup_object(const Glib::ustring& name) { ObjectPtr obj = 0; return lookup_object(name, obj); } /// Find an object in UI and return it. template ObjectPtr lookup_object(const Glib::ustring& name, ObjectPtr& obj) { return (obj = hz::down_cast(lookup_object(name))); // up, then down } protected: typedef Child self_type; ///< This is needed by APP_UI_ macros typedef WidgetType widget_type; ///< This is needed by APP_UI_ macros // protected constructor / destructor, use create() / destroy() instead of new / delete. /// GtkBuilder needs this constructor in a child. /// BaseObjectType is a C type, defined in specific Gtk:: widget class. AppUIResWidget(typename WidgetType::BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui) : WidgetType(gtkcobj), ref_ui_(ref_ui) { // manually connecting signals: // this->signal_delete_event().connect(sigc::mem_fun(*this, &MainWindow::on_main_window_delete)); // signals of GtkBuilder-created objects: // Gtk::ToolButton* rescan_devices_toolbutton = 0; // APP_UI_RES_AUTO_CONNECT(rescan_devices_toolbutton, clicked); // show(); } /// Virtual destructor virtual ~AppUIResWidget() { } private: app_ui_res_ref_t ref_ui_; ///< UI resource }; #endif /// @} gsmartcontrol-1.1.4/src/applib/app_ui_res_utils_test.cpp000066400000000000000000000045401417717230600235750ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_unlicense.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib_tests /// \weakgroup applib_tests /// @{ #include #include #include "app_ui_res_utils.h" // #include "hz/instance_manager.h" class AppUiResTestWindow; /// Window for the resource utilities test class AppUiResTestWindow : public AppUIResWidget { public: // name of ui file without a .ui extension and quotes APP_UI_RES_DATA_INIT(app_ui_res_test_window); /// Action types enum action_t { action_quit ///< Quit action }; /// Constructor AppUiResTestWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui) : AppUIResWidget(gtkcobj, ref_ui) { Gtk::Box* vbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL, 5)); add(*vbox); Gtk::Button* button = Gtk::manage(new Gtk::Button("Clicky")); vbox->pack_start(*button, Gtk::PACK_SHRINK); // button->signal_clicked().connect(sigc::mem_fun(*this, &AppUiResTestWindow::on_button_clicked)); button->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &AppUiResTestWindow::on_button_clicked2), action_quit)); Gtk::Label* label = Gtk::manage(new Gtk::Label("test")); vbox->pack_start(*label, Gtk::PACK_SHRINK); this->signal_delete_event().connect(sigc::mem_fun(*this, &AppUiResTestWindow::on_delete_event)); show_all(); } /// Virtual destructor virtual ~AppUiResTestWindow() { } private: /// Action callback void on_button_clicked() { std::cerr << "AppUiResTestWindow::on_button_clicked()\n"; } /// Action callback void on_button_clicked2(action_t action_type) { std::cerr << "AppUiResTestWindow::on_button_clicked2()\n"; } /// Action callback bool on_delete_event(GdkEventAny* e) { Gtk::Main::quit(); return true; // action handled } }; /// Main function of the test int main(int argc, char *argv[]) { Gtk::Main m(&argc, &argv); // AppUiResTestWindow* app = new AppUiResTestWindow(); AppUiResTestWindow::create(); m.run(); // delete app; AppUiResTestWindow::destroy(); return(0); } /// @} gsmartcontrol-1.1.4/src/applib/app_ui_res_utils_test_data.cpp000066400000000000000000000033631417717230600245700ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_unlicense.txt ***************************************************************************/ // A simple empty window definition extern const unsigned char app_ui_res_test_window_ui[] = { 0x3C, 0x3F, 0x78, 0x6D, 0x6C, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3D, 0x22, 0x31, 0x2E, 0x30, 0x22, 0x3F, 0x3E, 0xA, 0x3C, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x3E, 0xA, 0x20, 0x20, 0x3C, 0x6F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x20, 0x63, 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, 0x47, 0x74, 0x6B, 0x57, 0x69, 0x6E, 0x64, 0x6F, 0x77, 0x22, 0x20, 0x69, 0x64, 0x3D, 0x22, 0x61, 0x70, 0x70, 0x5F, 0x75, 0x69, 0x5F, 0x72, 0x65, 0x73, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x5F, 0x77, 0x69, 0x6E, 0x64, 0x6F, 0x77, 0x22, 0x3E, 0xA, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x70, 0x72, 0x6F, 0x70, 0x65, 0x72, 0x74, 0x79, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x74, 0x69, 0x74, 0x6C, 0x65, 0x22, 0x20, 0x74, 0x72, 0x61, 0x6E, 0x73, 0x6C, 0x61, 0x74, 0x61, 0x62, 0x6C, 0x65, 0x3D, 0x22, 0x79, 0x65, 0x73, 0x22, 0x3E, 0x54, 0x65, 0x73, 0x74, 0x3C, 0x2F, 0x70, 0x72, 0x6F, 0x70, 0x65, 0x72, 0x74, 0x79, 0x3E, 0xA, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x63, 0x68, 0x69, 0x6C, 0x64, 0x3E, 0xA, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x70, 0x6C, 0x61, 0x63, 0x65, 0x68, 0x6F, 0x6C, 0x64, 0x65, 0x72, 0x2F, 0x3E, 0xA, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x63, 0x68, 0x69, 0x6C, 0x64, 0x3E, 0xA, 0x20, 0x20, 0x3C, 0x2F, 0x6F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x3E, 0xA, 0x3C, 0x2F, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x3E, 0xA, 0x0 }; extern const unsigned int app_ui_res_test_window_ui_size = 224; gsmartcontrol-1.1.4/src/applib/cli_executors.h000066400000000000000000000133421417717230600215050ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2011 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef CLI_EXECUTORS_H #define CLI_EXECUTORS_H #include "cmdex.h" #include "cmdex_sync.h" /// Executor for tw_cli (3ware utility) template class TwCliExecutorGeneric : public ExecutorSync { public: /// Constructor TwCliExecutorGeneric(const std::string& cmd, const std::string& cmdargs) : ExecutorSync(cmd, cmdargs) { this->construct(); } /// Constructor TwCliExecutorGeneric() { this->construct(); } /// Virtual destructor virtual ~TwCliExecutorGeneric() { } protected: /// Called from constructors void construct() { ExecutorSync::get_command_executor().set_exit_status_translator(&TwCliExecutorGeneric::translate_exit_status, NULL); this->set_error_header("An error occurred while executing tw_cli:\n\n"); } /// Exit status translate handler static std::string translate_exit_status(int status, void* user_data) { return std::string(); } /// Import the last error from command executor and clear all errors there virtual void import_error() { Cmdex& cmdex = this->get_command_executor(); cmdex.errors_lock(); Cmdex::error_list_t errors = cmdex.get_errors(false); // these are not clones hz::ErrorBase* e = 0; // find the last relevant error. // note: const_reverse_iterator doesn't work on gcc 3, so don't do it. for (Cmdex::error_list_t::reverse_iterator iter = errors.rbegin(); iter != errors.rend(); ++iter) { // ignore iochannel errors, they may mask the real errors if ((*iter)->get_type() != "giochannel" && (*iter)->get_type() != "custom") { e = (*iter)->clone(); break; } } cmdex.clear_errors(false); // and clear them cmdex.errors_unlock(); if (e) { // if error is present, alert the user on_error_warn(e); } } /// This is called when an error occurs in command executor. /// Note: The warnings are already printed via debug_* in cmdex. virtual void on_error_warn(hz::ErrorBase* e) { if (!e) return; // import the error only if it's relevant. std::string error_type = e->get_type(); // ignore giochannel errors - higher level errors will be triggered, and they more user-friendly. if (error_type == "giochannel" || error_type == "custom") { return; } this->set_error_msg(e->get_message()); } }; /// tw_cli executor without GUI support typedef TwCliExecutorGeneric TwCliExecutor; /// A reference-counting pointer to TwCliExecutor typedef hz::intrusive_ptr TwCliExecutorRefPtr; /// tw_cli executor with GUI support typedef TwCliExecutorGeneric TwCliExecutorGui; /// A reference-counting pointer to TwCliExecutorGui typedef hz::intrusive_ptr TwCliExecutorGuiRefPtr; /// Executor for cli (Areca utility) template class ArecaCliExecutorGeneric : public ExecutorSync { public: /// Constructor ArecaCliExecutorGeneric(const std::string& cmd, const std::string& cmdargs) : ExecutorSync(cmd, cmdargs) { this->construct(); } /// Constructor ArecaCliExecutorGeneric() { this->construct(); } /// Virtual destructor virtual ~ArecaCliExecutorGeneric() { } protected: /// Called from constructors void construct() { ExecutorSync::get_command_executor().set_exit_status_translator(&ArecaCliExecutorGeneric::translate_exit_status, NULL); this->set_error_header("An error occurred while executing Areca cli:\n\n"); } /// Exit status translate handler static std::string translate_exit_status(int status, void* user_data) { return std::string(); } /// Import the last error from command executor and clear all errors there virtual void import_error() { Cmdex& cmdex = this->get_command_executor(); cmdex.errors_lock(); Cmdex::error_list_t errors = cmdex.get_errors(false); // these are not clones hz::ErrorBase* e = 0; // find the last relevant error. // note: const_reverse_iterator doesn't work on gcc 3, so don't do it. for (Cmdex::error_list_t::reverse_iterator iter = errors.rbegin(); iter != errors.rend(); ++iter) { // ignore iochannel errors, they may mask the real errors if ((*iter)->get_type() != "giochannel" && (*iter)->get_type() != "custom") { e = (*iter)->clone(); break; } } cmdex.clear_errors(false); // and clear them cmdex.errors_unlock(); if (e) { // if error is present, alert the user on_error_warn(e); } } /// This is called when an error occurs in command executor. /// Note: The warnings are already printed via debug_* in cmdex. virtual void on_error_warn(hz::ErrorBase* e) { if (!e) return; // import the error only if it's relevant. std::string error_type = e->get_type(); // ignore giochannel errors - higher level errors will be triggered, and they more user-friendly. if (error_type == "giochannel" || error_type == "custom") { return; } this->set_error_msg(e->get_message()); } }; /// tw_cli executor without GUI support typedef ArecaCliExecutorGeneric ArecaCliExecutor; /// A reference-counting pointer to ArecaCliExecutor typedef hz::intrusive_ptr ArecaCliExecutorRefPtr; /// tw_cli executor with GUI support typedef ArecaCliExecutorGeneric ArecaCliExecutorGui; /// A reference-counting pointer to ArecaCliExecutorGui typedef hz::intrusive_ptr ArecaCliExecutorGuiRefPtr; #endif /// @} gsmartcontrol-1.1.4/src/applib/cmdex.cpp000066400000000000000000000346631417717230600203010ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #include #include // errno (not std::errno, it may be a macro) #ifdef _WIN32 // #include // close() #else #include // waitpid()'s W* macros // #include // close() #endif #include "hz/process_signal.h" // hz::process_signal_send, win32's W* #include "hz/tls.h" #include "hz/tls_policy_glib.h" #include "hz/debug.h" #include "hz/string_num.h" // hz::number_to_string() #include "hz/env_tools.h" // hz::ScopedEnv #include "hz/scoped_ptr.h" #include "cmdex.h" using hz::Error; using hz::ErrorLevel; // this is needed because these callbacks are called by glib. extern "C" { // callbacks /// Child process watcher callback inline void cmdex_child_watch_handler(GPid arg_pid, int waitpid_status, gpointer data) { Cmdex::on_child_watch_handler(arg_pid, waitpid_status, data); } /// Child process stdout handler callback inline gboolean cmdex_on_channel_io_stdout(GIOChannel* source, GIOCondition cond, gpointer data) { return Cmdex::on_channel_io(source, cond, static_cast(data), Cmdex::channel_type_stdout); } /// Child process stderr handler callback inline gboolean cmdex_on_channel_io_stderr(GIOChannel* source, GIOCondition cond, gpointer data) { return Cmdex::on_channel_io(source, cond, static_cast(data), Cmdex::channel_type_stderr); } /// Child process termination timeout handler inline gboolean cmdex_on_term_timeout(gpointer data) { DBG_FUNCTION_ENTER_MSG; Cmdex* self = static_cast(data); self->try_stop(hz::SIGNAL_SIGTERM); return false; // one-time call } /// Child process kill timeout handler inline gboolean cmdex_on_kill_timeout(gpointer data) { DBG_FUNCTION_ENTER_MSG; Cmdex* self = static_cast(data); self->try_stop(hz::SIGNAL_SIGKILL); return false; // one-time call } } // extern "C" bool Cmdex::execute() { DBG_FUNCTION_ENTER_MSG; if (this->running_ || this->stopped_cleanup_needed()) { return false; } cleanup_members(); clear_errors(); str_stdout_.clear(); str_stderr_.clear(); std::string cmd = command_exec_ + " " + command_args_; // Make command vector hz::scoped_ptr argvp(0, g_strfreev); // args vector { int argcp = 0; // number of args hz::scoped_ptr shell_error(0, g_error_free); if (!g_shell_parse_argv(cmd.c_str(), &argcp, &argvp.get_ref(), &shell_error.get_ref())) { push_error(Error("gshell", ErrorLevel::error, shell_error->message), false); return false; } } // Set the locale for a child to Classic - otherwise it may mangle the output. // TODO: make this controllable. bool change_lang = true; #ifdef _WIN32 // LANG is posix-only, so it has no effect on win32. // Unfortunately, I was unable to find a way to execute a child with a different // locale in win32. Locale seems to be non-inheritable, so setting it here won't help. change_lang = false; #endif hz::ScopedEnv lang_env("LANG", "C", change_lang); hz::ScopedEnv lc_numeric_env("LC_NUMERIC", "C", change_lang); debug_out_info("app", DBG_FUNC_MSG << "Executing \"" << cmd << "\".\n"); /* if (argvp) { debug_out_dump("app", DBG_FUNC_MSG << "Dumping argvp:\n"); gchar** elem = argvp.get(); while (*elem) { debug_out_dump("app", *elem << "\n"); ++elem; } } */ // Execute the command hz::scoped_ptr curr_dir(g_get_current_dir(), g_free); hz::scoped_ptr spawn_error(0, g_error_free); /* #if defined APP_CMDEX_USE_SYNC && APP_CMDEX_USE_SYNC hz::scoped_ptr stdout_str(0, g_free); hz::scoped_ptr stderr_str(0, g_free); gint exit_status = 0; g_timer_start(timer_); // start the timer if (!g_spawn_sync(curr_dir.get(), argvp.get(), NULL, GSpawnFlags(G_SPAWN_SEARCH_PATH), NULL, NULL, // child setup function &stdout_str.get_ref(), &stderr_str.get_ref(), &exit_status, &spawn_error.get_ref())) { // no data is returned to &-parameters on error. push_error(Error("gspawn", ErrorLevel::error, spawn_error->message), false); return false; } #else // async way: */ if (!g_spawn_async_with_pipes(curr_dir.get(), argvp.get(), NULL, GSpawnFlags(G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD), NULL, NULL, // child setup function &this->pid_, 0, &fd_stdout_, &fd_stderr_, &spawn_error.get_ref())) { // no data is returned to &-parameters on error. push_error(Error("gspawn", ErrorLevel::error, spawn_error->message), false); return false; } g_timer_start(timer_); // start the timer // #endif #ifdef _WIN32 channel_stdout_ = g_io_channel_win32_new_fd(fd_stdout_); channel_stderr_ = g_io_channel_win32_new_fd(fd_stderr_); #else channel_stdout_ = g_io_channel_unix_new(fd_stdout_); channel_stderr_ = g_io_channel_unix_new(fd_stderr_); #endif // The internal encoding is always UTF8. To read command output correctly, use // "" for binary data, or set io encoding to current locale. // If using locales, call g_locale_to_utf8() or g_convert() afterwards. // blocking writes if the pipe is full helps for small-pipe systems (see man 7 pipe). int channel_flags = ~G_IO_FLAG_NONBLOCK; // Note about GError's here: // What do we do? The command is already running, so let's ignore these // errors - it's better to get a slightly mangled buffer than to abort the // command in the mid-run. if (channel_stdout_) { // Since we invoke shutdown() manually before unref(), this would cause // a double-shutdown. // g_io_channel_set_close_on_unref(channel_stdout_, true); // close() on fd g_io_channel_set_encoding(channel_stdout_, NULL, 0); // binary IO g_io_channel_set_flags(channel_stdout_, GIOFlags(g_io_channel_get_flags(channel_stdout_) & channel_flags), 0); g_io_channel_set_buffer_size(channel_stdout_, channel_stdout_buffer_size_); } if (channel_stderr_) { // g_io_channel_set_close_on_unref(channel_stderr_, true); // close() on fd g_io_channel_set_encoding(channel_stderr_, NULL, 0); // binary IO g_io_channel_set_flags(channel_stderr_, GIOFlags(g_io_channel_get_flags(channel_stderr_) & channel_flags), 0); g_io_channel_set_buffer_size(channel_stderr_, channel_stderr_buffer_size_); } GIOCondition cond = GIOCondition(G_IO_IN | G_IO_PRI | G_IO_HUP | G_IO_ERR | G_IO_NVAL); // Channel reader callback must be called before other stuff so that the loss is minimal. gint io_priority = G_PRIORITY_HIGH; this->event_source_id_stdout_ = g_io_add_watch_full(channel_stdout_, io_priority, cond, &cmdex_on_channel_io_stdout, this, NULL); // g_io_channel_unref(channel_stdout_); // g_io_add_watch_full() holds its own reference this->event_source_id_stderr_ = g_io_add_watch_full(channel_stderr_, io_priority, cond, &cmdex_on_channel_io_stderr, this, NULL); // g_io_channel_unref(channel_stderr_); // g_io_add_watch_full() holds its own reference // If using SPAWN_DO_NOT_REAP_CHILD, this is needed to avoid zombies. // Note: Do NOT use glibmm slot, it doesn't work here. // (the child stops being a zombie as soon as wait*() exits and this handler is called). g_child_watch_add(this->pid_, &cmdex_child_watch_handler, this); this->running_ = true; // the process is running now. DBG_FUNCTION_EXIT_MSG; return true; } // send SIGTERM(15) (terminate) bool Cmdex::try_stop(hz::signal_t sig) { DBG_FUNCTION_ENTER_MSG; #ifdef _WIN32 if (!this->running_ || this->pid_ == 0) return false; #else if (!this->running_ || this->pid_ <= 0) return false; #endif // other variants: SIGHUP(1) (terminal closed), SIGINT(2) (Ctrl-C), // SIGKILL(9) (kill). // Note that SIGKILL cannot be trapped by any process. if (process_signal_send(this->pid_, sig) == 0) { // success this->kill_signal_sent_ = static_cast(sig); // just the number to compare later. return true; // the rest is done by a handler } // Possible: EPERM (no permissions), ESRCH (no such process, or zombie) push_error(Error("errno", ErrorLevel::error, errno), false); DBG_FUNCTION_EXIT_MSG; return false; } bool Cmdex::try_kill() { DBG_TRACE_POINT_AUTO; return try_stop(hz::SIGNAL_SIGKILL); } void Cmdex::set_stop_timeouts(int term_timeout_msec, int kill_timeout_msec) { DBG_FUNCTION_ENTER_MSG; DBG_ASSERT(term_timeout_msec == 0 || kill_timeout_msec == 0 || kill_timeout_msec > term_timeout_msec); if (!this->running_) // process not running return; unset_stop_timeouts(); if (term_timeout_msec != 0) event_source_id_term = g_timeout_add(term_timeout_msec, &cmdex_on_term_timeout, this); if (kill_timeout_msec != 0) event_source_id_kill = g_timeout_add(kill_timeout_msec, &cmdex_on_kill_timeout, this); DBG_FUNCTION_EXIT_MSG; } void Cmdex::unset_stop_timeouts() { DBG_FUNCTION_ENTER_MSG; if (event_source_id_term) { GSource* source_term = g_main_context_find_source_by_id(NULL, event_source_id_term); if (source_term) g_source_destroy(source_term); event_source_id_term = 0; } if (event_source_id_kill) { GSource* source_kill = g_main_context_find_source_by_id(NULL, event_source_id_kill); if (source_kill) g_source_destroy(source_kill); event_source_id_kill = 0; } DBG_FUNCTION_EXIT_MSG; } // executed in main thread, manually by the caller. void Cmdex::stopped_cleanup() { DBG_FUNCTION_ENTER_MSG; if (this->running_ || !this->stopped_cleanup_needed()) // huh? return; // remove stop timeout callbacks unset_stop_timeouts(); // various statuses (see waitpid (2)): if (WIFEXITED(waitpid_status_)) { // exited normally int exit_status = WEXITSTATUS(waitpid_status_); if (exit_status != 0) { // translate the exit_code into a message std::string msg = (translator_func_ ? translator_func_(exit_status, translator_func_data_) : "[no translator function, exit code: " + hz::number_to_string(exit_status)); push_error(Error("exit", ErrorLevel::warn, exit_status, msg), false); } } else { if (WIFSIGNALED(waitpid_status_)) { // exited by signal int sig_num = WTERMSIG(waitpid_status_); // If it's not our signal, treat as error. // Note: they will never match under win32 if (sig_num != this->kill_signal_sent_) { push_error(Error("signal", ErrorLevel::error, sig_num), false); } else { // it's our signal, treat as warning push_error(Error("signal", ErrorLevel::warn, sig_num), false); } } } g_spawn_close_pid(this->pid_); // needed to avoid zombies cleanup_members(); this->running_ = false; DBG_FUNCTION_EXIT_MSG; } // Called when child exits void Cmdex::on_child_watch_handler(GPid arg_pid, int waitpid_status, gpointer data) { // DBG_FUNCTION_ENTER_MSG; Cmdex* self = static_cast(data); g_timer_stop(self->timer_); // stop the timer self->waitpid_status_ = waitpid_status; self->child_watch_handler_called_ = true; self->running_ = false; // process is not running anymore // These are needed because Windows doesn't read the remaining data otherwise. g_io_channel_flush(self->channel_stdout_, NULL); on_channel_io(self->channel_stdout_, GIOCondition(0), self, channel_type_stdout); g_io_channel_flush(self->channel_stderr_, NULL); on_channel_io(self->channel_stderr_, GIOCondition(0), self, channel_type_stderr); if (self->channel_stdout_) { g_io_channel_shutdown(self->channel_stdout_, false, NULL); g_io_channel_unref(self->channel_stdout_); self->channel_stdout_ = 0; } if (self->channel_stderr_) { g_io_channel_shutdown(self->channel_stderr_, false, NULL); g_io_channel_unref(self->channel_stderr_); self->channel_stderr_ = 0; } // Remove fd IO callbacks. They may actually be removed already (note sure about this). // This will force calling the iochannel callback (they may not be called // otherwise at all if there was no output). if (self->event_source_id_stdout_) { GSource* source_stdout = g_main_context_find_source_by_id(NULL, self->event_source_id_stdout_); if (source_stdout) g_source_destroy(source_stdout); } if (self->event_source_id_stderr_) { GSource* source_stderr = g_main_context_find_source_by_id(NULL, self->event_source_id_stderr_); if (source_stderr) g_source_destroy(source_stderr); } // Close std pipes. // The channel closes them now. // close(self->fd_stdout_); // close(self->fd_stderr_); if (self->exited_callback_) self->exited_callback_(self->exited_callback_data_); // DBG_FUNCTION_EXIT_MSG; } gboolean Cmdex::on_channel_io(GIOChannel* channel, GIOCondition cond, Cmdex* self, channel_t type) { // DBG_FUNCTION_ENTER_MSG; // debug_out_dump("app", "Cmdex::on_channel_io(" // << (type == channel_type_stdout ? "STDOUT" : "STDERR") << ") " << int(cond) << "\n"); bool continue_events = true; if ((cond & G_IO_ERR) || (cond & G_IO_HUP) || (cond & G_IO_NVAL)) { continue_events = false; // there'll be no more data } DBG_ASSERT(type == channel_type_stdout || type == channel_type_stderr); // const gsize count = 4 * 1024; // read the bytes one by one. without this, a buffered iochannel hangs while waiting for data. // we don't use unbuffered iochannels - they may lose data on program exit. const gsize count = 1; gchar buf[count] = {0}; std::string* output_str = 0; if (type == channel_type_stdout) { output_str = &self->str_stdout_; } else if (type == channel_type_stderr) { output_str = &self->str_stderr_; } // while there's anything to read, read it do { hz::scoped_ptr channel_error(0, g_error_free); gsize bytes_read = 0; GIOStatus status = g_io_channel_read_chars(channel, buf, count, &bytes_read, &channel_error.get_ref()); if (bytes_read) output_str->append(buf, bytes_read); if (channel_error) { self->push_error(Error("giochannel", ErrorLevel::error, channel_error->message), false); break; // stop on next invocation (is this correct?) } // IO_STATUS_NORMAL and IO_STATUS_AGAIN (resource unavailable) are continuable. if (status == G_IO_STATUS_ERROR || status == G_IO_STATUS_EOF) { continue_events = false; break; } } while (g_io_channel_get_buffer_condition(channel) & G_IO_IN); // DBG_FUNCTION_EXIT_MSG; // false if the source should be removed, true otherwise. return continue_events; } void Cmdex::cleanup_members() { kill_signal_sent_ = 0; child_watch_handler_called_ = false; pid_ = 0; waitpid_status_ = 0; event_source_id_stdout_ = 0; event_source_id_stderr_ = 0; fd_stdout_ = 0; fd_stderr_ = 0; } /// @} gsmartcontrol-1.1.4/src/applib/cmdex.h000066400000000000000000000233231417717230600177350ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ #ifndef APP_CMDEX_H #define APP_CMDEX_H #include #include #include "hz/process_signal.h" // hz::SIGNAL_* #include "hz/error_holder.h" #include "hz/sync.h" // hz::SyncPolicyNone /// Command executor. /// There are two ways to detect when the command exits: /// 1. Add a callback to signal_exited. /// 2. Manually poll stopped_cleanup_needed() (from the same thread). /// In both cases, stopped_cleanup() must be called afterwards /// (from the main thread). class Cmdex : public hz::ErrorHolder { public: using hz::ErrorHolder::ptr_error_list_t; ///< Auto-deleting pointer container using hz::ErrorHolder::error_list_t; ///< Error list type /// A function that translates the exit error code into a readable string typedef std::string (*exit_status_translator_func_t)(int, void*); /// A function that is called whenever a process exits. typedef void (*exited_callback_func_t)(void*); /// Constructor Cmdex(exited_callback_func_t exited_cb = NULL, void* exited_cb_data = NULL) : running_(false), kill_signal_sent_(0), child_watch_handler_called_(false), pid_(0), waitpid_status_(0), timer_(g_timer_new()), event_source_id_term(0), event_source_id_kill(0), fd_stdout_(0), fd_stderr_(0), channel_stdout_(0), channel_stderr_(0), channel_stdout_buffer_size_(100 * 1024), channel_stderr_buffer_size_(10 * 1024), // 100K and 10K event_source_id_stdout_(0), event_source_id_stderr_(0), translator_func_(0), translator_func_data_(0), exited_callback_(exited_cb), exited_callback_data_(exited_cb_data) { } /// Destructor. Don't destroy this object unless the child has exited. It will leak stuff /// and possibly crash, etc... . ~Cmdex() { // This will help if object is destroyed after the command has exited, but before // stopped_cleanup() has been called. stopped_cleanup(); g_timer_destroy(timer_); // no need to destroy the channels - stopped_cleanup() calls // cleanup_members(), which deletes them. } /// Set the command to execute. Call before execute(). /// Note: The command and the arguments _must_ be shell-escaped. /// Use g_shell_quote() or Glib::shell_quote(). Note that each argument /// must be escaped separately. void set_command(const std::string& command_exec, const std::string& command_args) { command_exec_ = command_exec; command_args_ = command_args; } /// Launch the command. bool execute(); /// Send SIGTERM(15) (terminate) to the child process. /// Use only after execute(). Using it after the command has exited has no effect. bool try_stop(hz::signal_t sig = hz::SIGNAL_SIGTERM); /// Send SIGKILL(9) (kill) to the child process. Same as /// try_stop(hz::SIGNAL_SIGKILL). /// Note that SIGKILL cannot be overridden in child process. bool try_kill(); /// Set a timeout (since call to this function) to terminate the child process, /// kill it or both (use 0 to ignore the parameter). /// The timeouts will be unset automatically when the command exits. /// This has an effect only if the command is running (after execute()). void set_stop_timeouts(int term_timeout_msec = 0, int kill_timeout_msec = 0); /// Unset the terminate / kill timeouts. This will stop the timeout counters. /// This has an effect only if the command is running (after execute()). void unset_stop_timeouts(); /// If stopped_cleanup_needed() returned true, call this. The command /// should be exited by this time. Must be called before the next execute(). void stopped_cleanup(); /// Returns true if command has stopped. /// Call repeatedly in a waiting function, after execute(). /// When it returns true, call stopped_cleanup(). bool stopped_cleanup_needed() { return (child_watch_handler_called_); } /// Check if the process is running. Note that if this returns false, it doesn't mean that /// the io channels have been closed or that the data may be read safely. Poll /// stopped_cleanup_needed() instead. bool is_running() const { return running_; } /// Call this before execution. There is a race-like condition - when the command /// outputs something, the io channel reads it from fd to its buffer and the event /// source callback is called. If the command dies, the io channel callback reads /// the remaining data to the channel buffer. /// Since the event source callbacks (which read from the buffer and empty it) /// happen rather sporadically (from the glib loop), the buffer may not get read and /// emptied at all (before the command exits). This is why it's necessary to have /// a buffer size which potentially can hold _all_ the command output. /// A way to fight this is to increase event source priority (which may not help). /// Another way is to delay the command exit so that the event source callback /// catches on and reads the buffer. // Use 0 to ignore the parameter. Call this before execute(). void set_buffer_sizes(int stdout_buffer_size = 0, int stderr_buffer_size = 0) { if (stdout_buffer_size) channel_stdout_buffer_size_ = stdout_buffer_size; // 100K by default if (stderr_buffer_size) channel_stderr_buffer_size_ = stderr_buffer_size; // 10K by default } /// If stdout_make_str_as_available_ is false, call this after stopped_cleanup(), /// before next execute(). If it's true, you may call this before the command has /// stopped, but it will decrease performance significantly. std::string get_stdout_str(bool clear_existing = false) { // debug_out_dump("app", str_stdout_); if (clear_existing) { std::string ret = str_stdout_; str_stdout_.clear(); return ret; } return str_stdout_; } /// See notes on get_stdout_str(). std::string get_stderr_str(bool clear_existing = false) { if (clear_existing) { std::string ret = str_stderr_; str_stderr_.clear(); return ret; } return str_stderr_; } /// Return execution time, in seconds. Call this after execute(). double get_execution_time() { gulong microsec = 0; return g_timer_elapsed(timer_, µsec); } /// Set exit status translator callback, disconnecting the old one. /// Call only before execute(). void set_exit_status_translator(exit_status_translator_func_t func, void* user_data) { translator_func_ = func; translator_func_data_ = user_data; } /// Set exit notifier callback, disconnecting the old one. /// You can poll stopped_cleanup_needed() instead of using this function. void set_exited_callback(exited_callback_func_t func, void* user_data) { exited_callback_ = func; exited_callback_data_ = user_data; } // these are sorta-private /// Channel type, for passing to callbacks enum channel_t { channel_type_stdout, channel_type_stderr }; // Callbacks (Note: These are called by the real callbacks) /// Child watch handler static void on_child_watch_handler(GPid arg_pid, int waitpid_status, gpointer data); /// Channel I/O handler static gboolean on_channel_io(GIOChannel* channel, GIOCondition cond, Cmdex* self, Cmdex::channel_t type); private: /// Clean up the member variables and shut down the channels if needed. void cleanup_members(); // default command and its args. std::strings, not ustrings. std::string command_exec_; /// Binary name to execute. NOT affected by cleanup_members(). std::string command_args_; /// Arguments that always go with the binary. NOT affected by cleanup_members(). bool running_; ///< If true, the child process is running now. NOT affected by cleanup_members(). int kill_signal_sent_; ///< If non-zero, the process has been sent this signal to terminate bool child_watch_handler_called_; ///< true after child_watch_handler callback, before stopped_cleanup(). GPid pid_; ///< Process ID. int in Unix, pointer in win32 int waitpid_status_; ///< After the command is stopped, before cleanup, this will be available (waitpid() status). GTimer* timer_; ///< Keeps track of elapsed time since command execution. Value is not used by this class, but may be handy. int event_source_id_term; ///< Timeout event source ID for SIGTERM. int event_source_id_kill; ///< Timeout event source ID for SIGKILL. int fd_stdout_; ///< stdout file descriptor int fd_stderr_; ///< stderr file descriptor GIOChannel* channel_stdout_; ///< stdout channel GIOChannel* channel_stderr_; ///< stderr channel int channel_stdout_buffer_size_; ///< stdout channel buffer size. NOT affected by cleanup_members(). int channel_stderr_buffer_size_; ///< stderr channel buffer size. NOT affected by cleanup_members(). int event_source_id_stdout_; ///< IO watcher event source ID for stdout int event_source_id_stderr_; ///< IO watcher event source ID for stderr std::string str_stdout_; ///< stdout data read during execution. NOT affected by cleanup_members(). std::string str_stderr_; ///< stderr data read during execution. NOT affected by cleanup_members(). // signals // convert command exit status to message string exit_status_translator_func_t translator_func_; ///< Exit status translator function. NOT affected by cleanup_members(). void* translator_func_data_; ///< Data to supply to the exit status translator function. // "command exited" signal callback. exited_callback_func_t exited_callback_; ///< Exit notifier function. NOT affected by cleanup_members(). void* exited_callback_data_; ///< Data to supply to the exit notifier function. }; #endif gsmartcontrol-1.1.4/src/applib/cmdex_sync.cpp000066400000000000000000000102011417717230600213130ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #include // g_usleep() #include "cmdex_sync.h" namespace { /// "Execution finished" signal static sigc::signal s_cmdex_sync_signal_execute_finish; /// Mutex for "Execution finished" signal static hz::SyncPolicyMtDefault::Mutex s_cmdex_sync_signal_execute_finish_mutex; } cmdex_signal_execute_finish_type cmdex_sync_signal_execute_finish() { return cmdex_signal_execute_finish_type(s_cmdex_sync_signal_execute_finish, new hz::SyncPolicyMtDefault::ScopedLock(s_cmdex_sync_signal_execute_finish_mutex)); } bool CmdexSync::execute() { set_error_msg(""); // clear old error if present bool slot_connected = !(signal_execute_tick.slots().begin() == signal_execute_tick.slots().end()); if (slot_connected && !signal_execute_tick.emit(status_starting)) return false; if (!cmdex_.execute()) { // try to execute debug_out_error("app", DBG_FUNC_MSG << "cmdex_.execute() failed.\n"); import_error(); // get error from cmdex and display warnings if needed // emit this for execution loggers cmdex_sync_signal_execute_finish()->emit(CmdexSyncCommandInfo(get_command_name(), get_command_args(), get_stdout_str(), get_stderr_str(), get_error_msg())); if (slot_connected) signal_execute_tick.emit(status_failed); return false; } bool stop_requested = false; // stop requested from tick function bool signals_sent = false; // stop signals sent while(!cmdex_.stopped_cleanup_needed()) { if (!stop_requested) { // running and no stop requested yet // call the tick function with "running" periodically. // if it returns false, try to stop. if (slot_connected && !signal_execute_tick.emit(status_running)) { debug_out_info("app", DBG_FUNC_MSG << "execute_tick slot returned false, trying to stop the program.\n"); stop_requested = true; } } if (stop_requested && !signals_sent) { // stop request received // send the stop request to the command if (!cmdex_.try_stop()) { // try sigterm. this returns false if it can't be done (no permissions, zombie) debug_out_warn("app", DBG_FUNC_MSG << "cmdex_.try_stop() returned false.\n"); } // set sigkill timeout to 3 sec (in case sigterm fails); won't do anything if already exited. cmdex_.set_stop_timeouts(0, forced_kill_timeout_msec); // import_error(); // don't need errors here - they will be available later anyway. signals_sent = true; } // alert the tick function if (stop_requested && slot_connected) { signal_execute_tick.emit(status_stopping); // ignore returned value here } // Without this, no event sources will be processed and the program will // hang waiting for the child to exit (the watch handler won't be called). // Note: If you have an idle callback, g_main_context_pending() will // always return true (until the idle callback returns false and unregisters itself). while(g_main_context_pending(NULL)) { g_main_context_iteration(NULL, false); } g_usleep(50*1000); // 50 msec. avoids 100% CPU usage. } // command exited, do a cleanup. cmdex_.stopped_cleanup(); import_error(); // get error from cmdex and display warnings if needed // emit this for execution loggers cmdex_sync_signal_execute_finish()->emit(CmdexSyncCommandInfo(get_command_name(), get_command_args(), get_stdout_str(), get_stderr_str(), get_error_msg())); if (slot_connected) signal_execute_tick.emit(status_stopped); // last call return true; } void CmdexSync::import_error() { cmdex_.errors_lock(); Cmdex::error_list_t errors = cmdex_.get_errors(false); // these are not clones hz::ErrorBase* e = 0; if (!errors.empty()) e = errors.back()->clone(); cmdex_.clear_errors(false); // and clear them cmdex_.errors_unlock(); if (e) { // if error is present, alert the user on_error_warn(e); } } /// @} gsmartcontrol-1.1.4/src/applib/cmdex_sync.h000066400000000000000000000211111417717230600207620ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef APP_CMDEX_SYNC_H #define APP_CMDEX_SYNC_H #include #include #include "hz/error.h" #include "hz/intrusive_ptr.h" #include "hz/process_signal.h" // hz::SIGNAL_* #include "hz/sync.h" #include "hz/sync_lock_ptr.h" #include "cmdex.h" /// Information about finished command (structure for storing it). /// Supports intrusive_ptr for memory-efficient storage. struct CmdexSyncCommandInfoCopy : public hz::intrusive_ptr_referenced { CmdexSyncCommandInfoCopy(const std::string& c, const std::string& p, const std::string& so, const std::string& se, const std::string& em) : command(c), parameters(p), std_output(so), std_error(se), error_msg(em) { } std::string command; ///< Executed command std::string parameters; ///< Command parameters std::string std_output; ///< Stdout data std::string std_error; ///< Stderr data std::string error_msg; ///< Execution error message }; /// A reference-counting pointer to CmdexSyncCommandInfoCopy typedef hz::intrusive_ptr CmdexSyncCommandInfoRefPtr; /// Information about a finished command. struct CmdexSyncCommandInfo { CmdexSyncCommandInfo(const std::string& c, const std::string& p, const std::string& so, const std::string& se, const std::string& em) : command(c), parameters(p), std_output(so), std_error(se), error_msg(em) { } /// Make a copy of this structure for storage. CmdexSyncCommandInfoRefPtr copy() const { return new CmdexSyncCommandInfoCopy(command, parameters, std_output, std_error, error_msg); } const std::string& command; ///< Executed command const std::string& parameters; ///< Command parameters const std::string& std_output; ///< Stdout data const std::string& std_error; ///< Stderr data const std::string& error_msg; ///< Execution error message }; /// cmdex_sync_signal_execute_finish() return type, mt-synchronized signal. typedef hz::sync_lock_ptr< sigc::signal&, hz::SyncPolicyMtDefault::ScopedLock > cmdex_signal_execute_finish_type; /// This signal is emitted every time execute() finishes. cmdex_signal_execute_finish_type cmdex_sync_signal_execute_finish(); /// Synchronous Cmdex (command executor) with ticking support. class CmdexSync : public hz::intrusive_ptr_referenced, public sigc::trackable { public: /// Constructor CmdexSync(const std::string& command_exec, const std::string& command_args) { construct(); this->set_command(command_exec, command_args); } /// Constructor CmdexSync() { construct(); } /// Virtual destructor virtual ~CmdexSync() { } /// Set command to execute and its parameters void set_command(const std::string& command_name, const std::string& command_args) { cmdex_.set_command(command_name, command_args); // keep a copy locally to avoid locking on get() every time command_name_ = command_name; command_args_ = command_args; } /// Get command to execute std::string get_command_name() const { return command_name_; } /// Get command arguments std::string get_command_args() const { return command_args_; } /// Execute the command. The function will return only after the command exits. /// Calls signal_execute_tick signal repeatedly while doing stuff. /// Note: If the command _was_ executed, but there was an error, /// this will return true. Check get_error_msg() for emptiness. /// \c return false if failed to execute, true otherwise. virtual bool execute(); /// Set timeout (in ms) to send SIGKILL after sending SIGTERM. /// Used if manual stop was requested through ticker. void set_forced_kill_timeout(int timeout_msec) { forced_kill_timeout_msec = timeout_msec; } /// Try to stop the process. Call this from ticker slot while executing. bool try_stop(hz::signal_t sig = hz::SIGNAL_SIGTERM) { return cmdex_.try_stop(sig); } /// Same as try_stop(hz::SIGNAL_SIGKILL). bool try_kill() { return cmdex_.try_kill(); } /// Set a timeout (since call to this function) to terminate, kill or both (use 0 to ignore the parameter). /// the timeouts will be unset automatically when the command exits. /// Call from ticker slot while executing. void set_stop_timeouts(int term_timeout_msec = 0, int kill_timeout_msec = 0) { cmdex_.set_stop_timeouts(term_timeout_msec, kill_timeout_msec); } /// Unset terminate / kill timeouts. This will stop the timeout counters. /// Call from ticker slot while executing. void unset_stop_timeouts() { cmdex_.unset_stop_timeouts(); } /// Check if the child process is running. See Cmdex::is_running(). /// Call from ticker slot while executing. bool is_running() const { return cmdex_.is_running(); } /// See Cmdex::set_buffer_sizes() for details. Call this before execute(). void set_buffer_sizes(int stdout_buffer_size = 0, int stderr_buffer_size = 0) { cmdex_.set_buffer_sizes(stdout_buffer_size, stderr_buffer_size); } /// See Cmdex::get_stdout_str() for details. std::string get_stdout_str(bool clear_existing = false) { return cmdex_.get_stdout_str(clear_existing); } /// See Cmdex::get_stderr_str() for details. std::string get_stderr_str(bool clear_existing = false) { return cmdex_.get_stderr_str(clear_existing); } /// See Cmdex::set_exit_status_translator() for details. void set_exit_status_translator(Cmdex::exit_status_translator_func_t func, void* user_data) { cmdex_.set_exit_status_translator(func, user_data); } /// Get command execution error message. If \c with_header /// is true, a header set using set_error_header() will be displayed first. std::string get_error_msg(bool with_header = false) const { if (with_header) return error_header_ + error_msg_; return error_msg_; } /// Set a message to display when running. %s in \c msg will be replaced by the command. void set_running_msg(const std::string& msg) { running_msg_ = msg; } /// Set error header string. See get_error_msg() void set_error_header(const std::string& msg) { error_header_ = msg; } /// Get error header string. See get_error_msg() std::string get_error_header() { return error_header_; } // ----------------- Signals /// Status flags for signal_execute_tick slots, along with possible return values. enum tick_status_t { status_starting, ///< Return status will indicate whether to proceed with the execution status_failed, ///< The execution failed status_running, ///< Return status will indicate whether to abort the execution status_stopping, ///< The child has been sent a signal status_stopped ///< The child exited }; /// This signal is emitted whenever something happens with the execution /// (the status is changed), and periodically while the process is running. sigc::signal signal_execute_tick; protected: /// Initialize the members (constructor calls this) void construct() { forced_kill_timeout_msec = 3 * 1000; // 3 sec default running_msg_ = "Running %s..."; // %s will be replaced by command basename set_error_header("An error occurred while executing the command:\n\n"); } /// Import the last error from cmdex_ and clear all errors there. virtual void import_error(); /// The warnings are already printed via debug_* in cmdex. /// Override if needed. virtual void on_error_warn(hz::ErrorBase* e) { if (e) { set_error_msg(e->get_message()); // this message will be displayed } } /// Set error message void set_error_msg(const std::string& error_msg) { error_msg_ = error_msg; } /// Get "running" message std::string get_running_msg() const { return running_msg_; } /// Get command executor object Cmdex& get_command_executor() { return cmdex_; } private: Cmdex cmdex_; ///< Command executor std::string command_name_; ///< Command name std::string command_args_; ///< Command arguments std::string running_msg_; ///< "Running" message (to show in the dialogs, etc...) int forced_kill_timeout_msec; ///< Kill timeout in ms. std::string error_msg_; ///< Execution error message std::string error_header_; ///< The error message may have this prepended to it. hz::ErrorBase* error_; ///< Error holder. }; #endif /// @} gsmartcontrol-1.1.4/src/applib/cmdex_sync_gui.cpp000066400000000000000000000141301417717230600221640ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #include // Gtk::Main #include #include "hz/fs_path.h" #include "hz/string_sprintf.h" #include "cmdex_sync_gui.h" bool CmdexSyncGui::execute() { this->create_running_dialog(); // create, but don't show. this->set_running_dialog_abort_mode(false); // reset and set the message return CmdexSync::execute(); } #define CMDEX_DIALOG_MESSAGE_TYPE Gtk::MESSAGE_OTHER #define CMDEX_DIALOG_HINT_TYPE Gdk::WINDOW_TYPE_HINT_DIALOG Gtk::MessageDialog* CmdexSyncGui::create_running_dialog(Gtk::Window* parent, const Glib::ustring& msg) { if (running_dialog_) return running_dialog_; if (!msg.empty()) set_running_msg(msg); // Construct the dialog so we can manipulate it before execution if (parent) { running_dialog_ = new Gtk::MessageDialog(*parent, "", false, CMDEX_DIALOG_MESSAGE_TYPE, Gtk::BUTTONS_CANCEL); } else { running_dialog_ = new Gtk::MessageDialog("", false, CMDEX_DIALOG_MESSAGE_TYPE, Gtk::BUTTONS_CANCEL); } running_dialog_->signal_response().connect(sigc::mem_fun(*this, &CmdexSyncGui::on_running_dialog_response)); running_dialog_->set_decorated(false); running_dialog_->set_deletable(false); running_dialog_->set_skip_pager_hint(true); running_dialog_->set_skip_taskbar_hint(true); running_dialog_->set_type_hint(Gdk::WindowTypeHint(CMDEX_DIALOG_HINT_TYPE)); running_dialog_->set_position(Gtk::WIN_POS_CENTER_ON_PARENT); // avoid running multiple programs in parallel (the dialogs can be overlap...). // this won't harm the tests - they don't involve long-running commands. running_dialog_->set_modal(true); return running_dialog_; } void CmdexSyncGui::show_hide_dialog(bool show) { if (running_dialog_) { if (show) { running_dialog_timer_.start(); // running_dialog_->show(); } else { running_dialog_->hide(); running_dialog_timer_.stop(); running_dialog_shown_ = false; } } } void CmdexSyncGui::update_dialog_show_timer() { double timeout = 2.; // 2 sec for normal dialogs if (running_dialog_abort_mode_) timeout = 0.4; // 0.4 sec for aborting... dialogs if (!running_dialog_shown_ && running_dialog_timer_.elapsed() > timeout) { // without first making it sensitive, the "whole label selected" problem may occur. running_dialog_->set_response_sensitive(Gtk::RESPONSE_CANCEL, true); running_dialog_->show(); // enable / disable the button. do this after show(), or else the label gets selected or the cursor gets visible. running_dialog_->set_response_sensitive(Gtk::RESPONSE_CANCEL, !running_dialog_abort_mode_); running_dialog_shown_ = true; } } void CmdexSyncGui::set_running_dialog_abort_mode(bool aborting) { if (!running_dialog_) return; if (aborting && !running_dialog_abort_mode_) { // hide it until another timeout passes. this way, we: // avoid quick show/hide flickering; // avoid a strange problem when sensitive but clear dialog appears; // make it show at center of parent. show_hide_dialog(false); running_dialog_->set_message("\n Aborting... "); // the sensitive button switching is done after show(), to avoid some visual // defects - cursor in label, selected label. show_hide_dialog(true); // this resets the timer running_dialog_abort_mode_ = true; } else if (!aborting) { std::string msg = hz::string_sprintf(get_running_msg().c_str(), hz::FsPath(this->get_command_name()).get_basename().c_str()); running_dialog_->set_message("\n " + msg + " "); // running_dialog_->set_response_sensitive(Gtk::RESPONSE_CANCEL, true); running_dialog_abort_mode_ = false; } } bool CmdexSyncGui::execute_tick_func(tick_status_t status) { if (status == status_starting) { if (execution_running_) return false; // already running, abort the new one (?) // If quit() was called during one of the manual iterations, and execute() // is called in a loop, we need to prevent any real execution past that point. if (Gtk::Main::iteration(false) && Gtk::Main::level() > 0) { return false; // try to abort execution } execution_running_ = true; should_abort_ = false; // show a dialog with "running..." and an Abort button this->show_hide_dialog(true); return true; // proceed with execution } if (status == status_failed) { // close the dialog this->show_hide_dialog(false); // show a dialog with an error. // Handled by sync_errors_warn(), nothing else is needed here. execution_running_ = false; return true; // return value is ignored here } if (status == status_running) { while (Gtk::Main::events_pending()) { // Gtk::Main::iteration() returns true if Gtk::Main::quit() has been called, or if there's no Main yet. // debug_out_dump("app", Gtk::Main::level() << "\n"); if (Gtk::Main::iteration() && Gtk::Main::level() > 0) { set_running_dialog_abort_mode(true); return false; // try to abort execution } } if (should_abort_) { should_abort_ = false; set_running_dialog_abort_mode(true); return false; // try to abort execution } // the dialog may be shown only after some time has passed, to avoid quick show/hide. // this enables it. this->update_dialog_show_timer(); return true; // continue execution } if (status == status_stopping) { if (Gtk::Main::iteration(false) && Gtk::Main::level() > 0) { return false; // we're exiting from the main loop, so return early } // show a dialog with "Aborting..." this->update_dialog_show_timer(); return true; // return value is ignored here } if (status == status_stopped) { // close the dialog. this->show_hide_dialog(false); // show error messages if needed (get_errors()). // Handled by sync_errors_warn(), nothing is needed here. execution_running_ = false; return true; // return value is ignored here } return true; // we shouldn't reach this } /// @} gsmartcontrol-1.1.4/src/applib/cmdex_sync_gui.h000066400000000000000000000064051417717230600216370ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef APP_CMDEX_SYNC_GUI_H #define APP_CMDEX_SYNC_GUI_H #include #include #include "hz/noncopyable.h" #include "cmdex_sync.h" /// Same as CmdexSync, but with GTK UI support. /// This one is noncopyable, because we can't copy the dialogs, etc... class CmdexSyncGui : public CmdexSync, public hz::noncopyable { public: /// Constructor CmdexSyncGui(const std::string& cmd, const std::string& cmdargs) : CmdexSync(cmd, cmdargs), execution_running_(false), should_abort_(false), running_dialog_(0), running_dialog_shown_(false), running_dialog_abort_mode_(false) { signal_execute_tick.connect(sigc::mem_fun(*this, &CmdexSyncGui::execute_tick_func)); } /// Constructor CmdexSyncGui() : execution_running_(false), should_abort_(false), running_dialog_(0), running_dialog_shown_(false), running_dialog_abort_mode_(false) { signal_execute_tick.connect(sigc::mem_fun(*this, &CmdexSyncGui::execute_tick_func)); } /// Destructor ~CmdexSyncGui() { delete running_dialog_; } // Reimplemented from CmdexSync virtual bool execute(); /// UI callbacks may use this to abort execution. void set_should_abort() { should_abort_ = true; } /// Create a "running" dialog or return already existing one. /// The dialog will be auto-created and displayed on execute(). /// You need the function only if you intend to modify it before execute(). Gtk::MessageDialog* create_running_dialog(Gtk::Window* parent = 0, const Glib::ustring& msg = ""); /// Return the "running" dialog Gtk::MessageDialog* get_running_dialog() { return running_dialog_; } /// Show or hide the "running" dialog. /// This actually shows the dialog only after some time has passed, /// to avoid very quick show/hide in case the command exits very quickly. void show_hide_dialog(bool show); /// This function is called from ticker function in running mode /// to show the dialog when requested time elapses. void update_dialog_show_timer(); /// Switch the dialog to "aborting..." mode void set_running_dialog_abort_mode(bool aborting); private: /// Dialog response callback. void on_running_dialog_response(int response_id) { // Try to abort if Cancel was clicked if (response_id == Gtk::RESPONSE_CANCEL) set_should_abort(); } /// This function is attached to CmdexSync::signal_execute_tick. bool execute_tick_func(tick_status_t status); bool execution_running_; ///< If true, the execution is still in progress bool should_abort_; ///< GUI callbacks may set this to abort the execution Gtk::MessageDialog* running_dialog_; ///< "Running" dialog bool running_dialog_shown_; ///< If true, the "running" dialog is visible bool running_dialog_abort_mode_; ///< If true, the "running" dialog is in "aborting..." mode. Glib::Timer running_dialog_timer_; ///< "Running" dialog show timer. }; #endif /// @} gsmartcontrol-1.1.4/src/applib/executor_factory.cpp000066400000000000000000000030671417717230600225600ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2011 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #include "hz/debug.h" #include "executor_factory.h" #include "smartctl_executor_gui.h" #include "cli_executors.h" ExecutorFactory::ExecutorFactory(bool use_gui, Gtk::Window* parent) : use_gui_(use_gui), parent_(parent) { } hz::intrusive_ptr ExecutorFactory::create_executor(ExecutorFactory::Type type) { switch (type) { case ExecutorSmartctl: { if (use_gui_) { SmartctlExecutorGuiRefPtr ex = SmartctlExecutorGuiRefPtr(new SmartctlExecutorGui()); ex->create_running_dialog(parent_); // dialog parent return ex; } return SmartctlExecutorRefPtr(new SmartctlExecutor()); } case ExecutorTwCli: { if (use_gui_) { TwCliExecutorGuiRefPtr ex = TwCliExecutorGuiRefPtr(new TwCliExecutorGui()); ex->create_running_dialog(parent_); // dialog parent return ex; } return TwCliExecutorRefPtr(new TwCliExecutor()); } case ExecutorArecaCli: { if (use_gui_) { ArecaCliExecutorGuiRefPtr ex = ArecaCliExecutorGuiRefPtr(new ArecaCliExecutorGui()); ex->create_running_dialog(parent_); // dialog parent return ex; } return ArecaCliExecutorRefPtr(new ArecaCliExecutor()); } } DBG_ASSERT(0); return hz::intrusive_ptr(); } /// @} gsmartcontrol-1.1.4/src/applib/executor_factory.h000066400000000000000000000026121417717230600222200ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2011 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef EXECUTOR_FACTORY_H #define EXECUTOR_FACTORY_H #include "cmdex_sync.h" #include "hz/intrusive_ptr.h" // Forward declaration namespace Gtk { class Window; } /// This class allows you to create new executors for different commands, /// without carrying the GUI/CLI stuff manually. class ExecutorFactory : public hz::intrusive_ptr_referenced { public: /// Executor type for create_executor() enum Type { ExecutorSmartctl, ExecutorTwCli, ExecutorArecaCli }; /// Constructor. If \c use_gui is true, specify \c parent for the GUI dialogs. ExecutorFactory(bool use_gui, Gtk::Window* parent = 0); /// Create a new executor instance according to \c type and the constructor parameters. hz::intrusive_ptr create_executor(Type type); private: bool use_gui_; ///< Whether to construct GUI executors or not. Gtk::Window* parent_; ///< Parent window for dialogs }; /// A reference-counting pointer to ExecutorFactory typedef hz::intrusive_ptr ExecutorFactoryRefPtr; #endif /// @} gsmartcontrol-1.1.4/src/applib/gui_utils.cpp000066400000000000000000000110661417717230600211750ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #include "app_pango_utils.h" // app_pango_strip_markup() #include "gui_utils.h" namespace { /// Show a message dialog inline void show_dialog(const std::string& message, const std::string& sec_message, Gtk::Window* parent, Gtk::MessageType type, bool sec_msg_markup) { // no markup, modal Gtk::MessageDialog dialog("\n" + message + (sec_message.empty() ? "\n" : ""), false, type, Gtk::BUTTONS_OK, true); if (!sec_message.empty()) dialog.set_secondary_text(sec_message, sec_msg_markup); if (parent) { dialog.set_transient_for(*parent); dialog.set_position(Gtk::WIN_POS_CENTER_ON_PARENT); } else { dialog.set_position(Gtk::WIN_POS_MOUSE); } dialog.run(); // blocks until the dialog is closed } } void gui_show_error_dialog(const std::string& message, Gtk::Window* parent) { show_dialog(message, "", parent, Gtk::MESSAGE_ERROR, false); } void gui_show_error_dialog(const std::string& message, const std::string& sec_message, Gtk::Window* parent, bool sec_msg_markup) { show_dialog(message, sec_message, parent, Gtk::MESSAGE_ERROR, sec_msg_markup); } void gui_show_warn_dialog(const std::string& message, Gtk::Window* parent) { show_dialog(message, "", parent, Gtk::MESSAGE_WARNING, false); } void gui_show_warn_dialog(const std::string& message, const std::string& sec_message, Gtk::Window* parent, bool sec_msg_markup) { show_dialog(message, sec_message, parent, Gtk::MESSAGE_WARNING, sec_msg_markup); } void gui_show_info_dialog(const std::string& message, Gtk::Window* parent) { show_dialog(message, "", parent, Gtk::MESSAGE_INFO, false); } void gui_show_info_dialog(const std::string& message, const std::string& sec_message, Gtk::Window* parent, bool sec_msg_markup) { show_dialog(message, sec_message, parent, Gtk::MESSAGE_INFO, sec_msg_markup); } // Returns false if Cancel was clicked. Puts the user-entered string into result otherwise. bool gui_show_text_entry_dialog(const std::string& title, const std::string& message, std::string& result, const std::string& default_str, Gtk::Window* parent) { return gui_show_text_entry_dialog(title, message, "", result, default_str, parent, false); } bool gui_show_text_entry_dialog(const std::string& title, const std::string& message, const std::string& sec_message, std::string& result, const std::string& default_str, Gtk::Window* parent, bool sec_msg_markup) { int response = 0; std::string input_str; { // the dialog hides at the end of scope Gtk::Dialog dialog(title, true); // modal dialog.set_resizable(false); dialog.set_skip_taskbar_hint(true); dialog.set_border_width(5); if (parent) { dialog.set_transient_for(*parent); dialog.set_position(Gtk::WIN_POS_CENTER_ON_PARENT); } else { dialog.set_position(Gtk::WIN_POS_MOUSE); } Gtk::Label main_label; main_label.set_markup("" + Glib::Markup::escape_text(message) + (sec_message.empty() ? "\n" : "") + ""); main_label.set_line_wrap(true); main_label.set_selectable(true); main_label.set_alignment(Gtk::ALIGN_START); Gtk::Label sec_label; if (sec_msg_markup) { sec_label.set_markup(sec_message); } else { sec_label.set_text(sec_message); } sec_label.set_line_wrap(true); sec_label.set_selectable(true); sec_label.set_alignment(Gtk::ALIGN_START); Gtk::Entry input_entry; input_entry.set_activates_default(true); Gtk::Box vbox; vbox.set_spacing(12); vbox.pack_start(main_label, false, false, 0); vbox.pack_start(sec_label, true, true, 0); vbox.pack_start(input_entry, true, true, 0); vbox.show_all(); dialog.get_action_area()->set_border_width(5); dialog.get_action_area()->set_spacing(6); dialog.get_vbox()->set_spacing(14); // as in MessageDialog dialog.get_vbox()->pack_start(vbox, false, false, 0); dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); Gtk::Button ok_button(Gtk::Stock::OK); ok_button.set_can_default(true); ok_button.show_all(); dialog.add_action_widget(ok_button, Gtk::RESPONSE_OK); ok_button.grab_default(); // make it the default widget response = dialog.run(); // blocks until the dialog is closed input_str = input_entry.get_text(); } if (response == Gtk::RESPONSE_OK) { result = input_str; return true; } return false; } /// @} gsmartcontrol-1.1.4/src/applib/gui_utils.h000066400000000000000000000043311417717230600206370ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef GUI_UTILS_H #define GUI_UTILS_H #include #include // These functions won't return until the dialogs are closed. // Messages must not contain any markup. /// Show an error dialog void gui_show_error_dialog(const std::string& message, Gtk::Window* parent = 0); /// Show an error dialog with a (possibly markupped) secondary message void gui_show_error_dialog(const std::string& message, const std::string& sec_message, Gtk::Window* parent = 0, bool sec_msg_markup = false); /// Show a warning dialog void gui_show_warn_dialog(const std::string& message, Gtk::Window* parent = 0); /// Show a warning dialog with a (possibly markupped) secondary message void gui_show_warn_dialog(const std::string& message, const std::string& sec_message, Gtk::Window* parent = 0, bool sec_msg_markup = false); /// Show an informational dialog void gui_show_info_dialog(const std::string& message, Gtk::Window* parent = 0); /// Show an informational dialog with a (possibly markupped) secondary message void gui_show_info_dialog(const std::string& message, const std::string& sec_message, Gtk::Window* parent = 0, bool sec_msg_markup = false); /// Show a text entry dialog. \c result is filled with the user-entered string on success. /// \return false if Cancel was clicked. bool gui_show_text_entry_dialog(const std::string& title, const std::string& message, std::string& result, const std::string& default_str, Gtk::Window* parent = 0); /// Show a text entry dialog with a (possibly markupped) secondary message. /// \c result is filled with the user-entered string on success. /// \return false if Cancel was clicked. bool gui_show_text_entry_dialog(const std::string& title, const std::string& message, const std::string& sec_message, std::string& result, const std::string& default_str, Gtk::Window* parent = 0, bool sec_msg_markup = false); #endif /// @} gsmartcontrol-1.1.4/src/applib/selftest.cpp000066400000000000000000000224621417717230600210240ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #include // std::max, std::min #include // std::floor #include "app_pcrecpp.h" #include "storage_property.h" #include "smartctl_parser.h" #include "selftest.h" // Returns estimated time of completion for the test. returns -1 if n/a or unknown. 0 is a valid value. int64_t SelfTest::get_remaining_seconds() const { int64_t total = get_min_duration_seconds(); if (total <= 0) return -1; // unknown double gran = (double(total) / 9.); // seconds per 10% // since remaining_percent_ may be manually set to 100, we limit from the above. double rem_seconds_at_last_change = std::min(double(total), gran * remaining_percent_ / 10.); double rem = rem_seconds_at_last_change - timer_.elapsed(); return std::max(int64_t(0), int64_t(std::floor(rem + 0.5))); // aka round. don't return negative values. } // a drive reports a constant "test duration during idle" capability. int64_t SelfTest::get_min_duration_seconds() const { if (!drive_) return -1; // n/a if (total_duration_ != -1) // cache return total_duration_; std::string prop_name; switch(type_) { case type_ioffline: prop_name = "iodc_total_time_length"; break; case type_short: prop_name = "short_total_time_length"; break; case type_long: prop_name = "long_total_time_length"; break; case type_conveyance: prop_name = "conveyance_total_time_length"; break; } StorageProperty p = drive_->lookup_property(prop_name, StorageProperty::section_data, StorageProperty::subsection_capabilities); // p stores it as uint64_t return (total_duration_ = (p.empty() ? 0 : static_cast(p.value_time_length))); } bool SelfTest::is_supported() const { if (!drive_) return false; if (type_ == type_ioffline) // disable this for now - it's unsupported. return false; std::string prop_name; switch(type_) { case type_ioffline: prop_name = "iodc_support"; break; case type_short: prop_name = "selftest_support"; break; case type_long: prop_name = "selftest_support"; break; // same for short and long case type_conveyance: prop_name = "conveyance_support"; break; } StorageProperty p = drive_->lookup_property(prop_name, StorageProperty::section_internal); return (!p.empty() && p.value_bool); } // start the test std::string SelfTest::start(hz::intrusive_ptr smartctl_ex) { this->clear(); // clear previous results if (!drive_) return "Invalid drive given."; if (drive_->get_test_is_active()) return "A test is already running on this drive."; if (!this->is_supported()) return get_test_name(type_) + " is unsupported by this drive."; std::string test_param; switch(type_) { case type_ioffline: test_param = "offline"; break; case type_short: test_param = "short"; break; case type_long: test_param = "long"; break; case type_conveyance: test_param = "conveyance"; break; // no default - this way we get warned by compiler if we're not listing all of them. } if (test_param.empty()) return "Invalid test specified"; std::string output; std::string error_msg = drive_->execute_device_smartctl("--test=" + test_param, smartctl_ex, output); if (!error_msg.empty()) // checks for empty output too return error_msg; if (!app_pcre_match("/^Drive command .* successful\\.\\nTesting has begun\\.$/mi", output)) { return "Sending command failed."; } // update our members // error_msg = this->update(smartctl_ex); // if (!error_msg.empty()) // update can error out too. // return error_msg; // Don't update here - the logs may not be updated this fast. // Better to wait several seconds and then call it manually. // Set up everything so that the caller won't have to. status_ = StorageSelftestEntry::status_in_progress; remaining_percent_ = 100; // set to 90 to avoid the 100->90 timer reset. this way we won't be looking at // "remaining 60sec" on 60sec test twice (5 seconds apart). Since the test starts // at 90% anyway, it's a good thing. last_seen_percent_ = 90; poll_in_seconds_ = 5; // first update() in 5 seconds timer_.start(); drive_->set_test_is_active(true); return std::string(); // everything ok } // abort test. std::string SelfTest::force_stop(hz::intrusive_ptr smartctl_ex) { if (!drive_) return "Invalid drive given."; if (!drive_->get_test_is_active()) return "No test is currently running on this drive."; // To abort immediate offline test, the device MUST have // "Abort Offline collection upon new command" capability, // any command (e.g. "--abort") will abort it. If it has "Suspend Offline...", // there's no way to abort such test. if (type_ == type_ioffline) { StorageProperty p = drive_->lookup_property("iodc_command_suspends", StorageProperty::section_internal); if (!p.empty() && p.value_bool) { // if empty, give a chance to abort anyway. return "Aborting this test is unsupported by the drive."; } // else, proceed as any other test } // To abort non-captive short, long and conveyance tests, use "--abort". std::string output; std::string error_msg = drive_->execute_device_smartctl("--abort", smartctl_ex, output); if (!error_msg.empty()) // checks for empty output too return error_msg; // this command prints success even if no test was running. if (!app_pcre_match("/^Self-testing aborted!$/mi", output)) { return "Sending command failed."; } // update our members error_msg = this->update(smartctl_ex); // the thing is, update() may fail to actually update the statuses, so // do it manually. if (status_ == StorageSelftestEntry::status_in_progress) { // update() couldn't do its job status_ = StorageSelftestEntry::status_aborted_by_host; remaining_percent_ = -1; last_seen_percent_ = -1; poll_in_seconds_ = -1; timer_.stop(); drive_->set_test_is_active(false); } if (!error_msg.empty()) // update can error out too. return error_msg; return std::string(); // everything ok } // update status variables. note: the returned error is an error in logic, // not an hw defect error. std::string SelfTest::update(hz::intrusive_ptr smartctl_ex) { if (!drive_) return "Invalid drive given."; std::string output; // std::string error_msg = drive_->execute_device_smartctl("--log=selftest", smartctl_ex, output); std::string error_msg = drive_->execute_device_smartctl("--capabilities", smartctl_ex, output); if (!error_msg.empty()) // checks for empty output too return error_msg; StorageAttribute::DiskType disk_type = drive_->get_is_hdd() ? StorageAttribute::DiskHDD : StorageAttribute::DiskSSD; SmartctlParser ps; if (!ps.parse_full(output, disk_type)) { // try to parse it return ps.get_error_msg(); } SmartctlParser::prop_list_t props = ps.get_properties(); // Note: Since the self-test log is sometimes late // and in undetermined order (sorting by hours is too rough), // we use the "self-test status" capability. StorageProperty p; for (SmartctlParser::prop_list_t::const_iterator iter = props.begin(); iter != props.end(); ++iter) { // if (iter->section != StorageProperty::section_data || iter->subsection != StorageProperty::subsection_selftest_log if (iter->section != StorageProperty::section_internal || iter->value_type != StorageProperty::value_type_selftest_entry || iter->value_selftest_entry.test_num != 0 || iter->generic_name != "last_selftest_status") continue; p = *iter; } if (p.empty()) return "The drive doesn't report the test status."; status_ = p.value_selftest_entry.status; bool active = (status_ == StorageSelftestEntry::status_in_progress); // Note that the test needs 90% to complete, not 100. It starts at 90% // and reaches 00% on completion. That's 9 pieces. if (active) { remaining_percent_ = p.value_selftest_entry.remaining_percent; if (remaining_percent_ != last_seen_percent_) { last_seen_percent_ = remaining_percent_; timer_.start(); // restart the timer } int64_t total = get_min_duration_seconds(); if (total <= 0) { // unknown poll_in_seconds_ = 30; // just a guess } else { // seconds per 10%. use double, because e.g. 60sec test gives silly values with int. double gran = (double(total) / 9.); // Add 1/10 for disk load delays, etc... . Limit to 15sec, in case of very quick tests. poll_in_seconds_ = std::max(int64_t(15), int64_t(gran / 3. + (gran / 10.))); // for long tests we don't want to make the user wait too much, so // we need to poll more frequently by the end, in case it's completed. if (type_ == type_long && remaining_percent_ == 10) poll_in_seconds_ = std::max(int64_t(1*60), int64_t(gran / 10.)); // that's 2 min for 180min extended test debug_out_dump("app", DBG_FUNC_MSG << "total: " << total << ", gran: " << gran << ", poll in: " << poll_in_seconds_ << ", remaining secs: " << get_remaining_seconds() << ", remaining %: " << int(remaining_percent_) << ", last seen %: " << int(last_seen_percent_) << ".\n"); } } else { remaining_percent_ = -1; last_seen_percent_ = -1; poll_in_seconds_ = -1; timer_.stop(); } drive_->set_test_is_active(active); return std::string(); // everything ok } /// @} gsmartcontrol-1.1.4/src/applib/selftest.h000066400000000000000000000100471417717230600204650ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef SELFTEST_H #define SELFTEST_H #include #include #include "hz/cstdint.h" #include "hz/intrusive_ptr.h" #include "storage_device.h" #include "cmdex_sync.h" /// SMART self-test runner. class SelfTest : public hz::intrusive_ptr_referenced { public: /// Test type enum test_t { type_ioffline, ///< Immediate offline, not supported type_short, ///< Short self-test type_long, ///< Extended (a.k.a. long) self-test type_conveyance ///< Conveyance self-test }; /// Get displayable name for a test type static std::string get_test_name(test_t t) { switch (t) { case type_ioffline: return "Immediate Offline Test"; case type_short: return "Short Self-test"; case type_long: return "Extended Self-test"; case type_conveyance: return "Conveyance Self-test"; }; return "[error]"; } /// Constructor. \c drive must have the capabilities present in its properties. SelfTest(StorageDeviceRefPtr drive, test_t type) : drive_(drive), type_(type) { clear(); } /// Clear results of previous test void clear() { status_ = StorageSelftestEntry::status_unknown; remaining_percent_ = -1; last_seen_percent_ = -1; total_duration_ = -1; poll_in_seconds_ = -1; } /// Check if the test is currently active bool is_active() const { return (status_ == StorageSelftestEntry::status_in_progress); } /// Get remaining time percent until the test completion. /// \return -1 if N/A or unknown. int8_t get_remaining_percent() const { return remaining_percent_; } /// Get estimated time of completion for the test. /// \return -1 if N/A or unknown. Note that 0 is a valid value. int64_t get_remaining_seconds() const; /// Get test type test_t get_test_type() const { return type_; } /// Get test status StorageSelftestEntry::status_t get_status() const { return status_; } /// Get the number of seconds after which the caller should call update(). int64_t get_poll_in_seconds() const { return poll_in_seconds_; } /// Get a constant "test duration during idle" capability drive's stored capabilities. -1 if N/A. int64_t get_min_duration_seconds() const; /// Gets the current test type support status from drive's stored capabilities. bool is_supported() const; /// Start the test. /// \return error message on error, empty string on success. std::string start(hz::intrusive_ptr smartctl_ex = 0); /// Abort the running test. /// \return error message on error, empty string on success. std::string force_stop(hz::intrusive_ptr smartctl_ex = 0); /// Update status variables. The user should call this every get_poll_in_seconds() seconds. /// \return error message on error, empty string on success. std::string update(hz::intrusive_ptr smartctl_ex = 0); private: StorageDeviceRefPtr drive_; ///< Drive to run the tests on test_t type_; ///< Test type // status variables: StorageSelftestEntry::status_t status_; ///< Current status of the test as reported by the drive int8_t remaining_percent_; ///< Remaining %. 0 means unknown, -1 means N/A. This is set to 100 on start. int8_t last_seen_percent_; ///< Last reported %, to detect changes in percentage (needed for timer update). mutable int64_t total_duration_; ///< Total duration needed for the test, as reported by the drive. Constant. This variable acts as a cache. int64_t poll_in_seconds_; ///< The user is asked to poll after this much seconds have passed. Glib::Timer timer_; ///< Counts time since the last percent change }; /// A reference-counting pointer to SelfTest typedef hz::intrusive_ptr SelfTestPtr; #endif /// @} gsmartcontrol-1.1.4/src/applib/smartctl_executor.cpp000066400000000000000000000106501417717230600227360ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #include // Glib::shell_quote() #include "smartctl_executor.h" #include "hz/fs_path.h" #include "hz/win32_tools.h" #include "rconfig/rconfig_mini.h" #include "app_pcrecpp.h" std::string get_smartctl_binary() { std::string smartctl_binary; rconfig::get_data("system/smartctl_binary", smartctl_binary); #ifdef _WIN32 // look in smartmontools installation directory. do { bool use_smt = false; rconfig::get_data("system/win32_search_smartctl_in_smartmontools", use_smt); if (!use_smt) break; std::string smt_regpath, smt_regpath_wow, smt_regkey, smt_smartctl; rconfig::get_data("system/win32_smartmontools_regpath", smt_regpath); rconfig::get_data("system/win32_smartmontools_regpath_wow", smt_regpath_wow); // same as above, but with WOW6432Node rconfig::get_data("system/win32_smartmontools_regkey", smt_regkey); rconfig::get_data("system/win32_smartmontools_smartctl_binary", smt_smartctl); if ((smt_regpath.empty() && smt_regpath_wow.empty()) || smt_regkey.empty() || smt_smartctl.empty()) break; std::string smt_inst_dir; hz::win32_get_registry_value_string(HKEY_LOCAL_MACHINE, smt_regpath, smt_regkey, smt_inst_dir); if (smt_inst_dir.empty()) { hz::win32_get_registry_value_string(HKEY_LOCAL_MACHINE, smt_regpath_wow, smt_regkey, smt_inst_dir); } if (smt_inst_dir.empty()) { debug_out_info("app", DBG_FUNC_MSG << "Smartmontools installation not found in \"HKLM\\" << smt_regpath << "\\" << smt_regkey << "\".\n"); break; } debug_out_info("app", DBG_FUNC_MSG << "Smartmontools installation found at \"" << smt_inst_dir << "\", using \"" << smt_smartctl << "\".\n"); hz::FsPath p(smt_inst_dir); p.append(smt_smartctl); if (!p.exists() || !p.is_file()) break; smartctl_binary = p.str(); } while (false); #endif return smartctl_binary; } std::string execute_smartctl(const std::string& device, const std::string& device_opts, const std::string& command_options, hz::intrusive_ptr smartctl_ex, std::string& smartctl_output) { #ifndef _WIN32 // win32 doesn't have slashes in devices names { std::string::size_type pos = device.rfind('/'); // find basename if (pos == std::string::npos) { debug_out_error("app", DBG_FUNC_MSG << "Invalid device name \"" << device << "\".\n"); return "Invalid device name specified."; } } #endif if (!smartctl_ex) // if it doesn't exist, create a default one smartctl_ex = new SmartctlExecutor(); // will be auto-deleted std::string smartctl_binary = get_smartctl_binary(); if (smartctl_binary.empty()) { debug_out_error("app", DBG_FUNC_MSG << "Smartctl binary is not set in config.\n"); return "Smartctl binary is not specified in configuration."; } std::string smartctl_def_options; rconfig::get_data("system/smartctl_options", smartctl_def_options); if (!smartctl_def_options.empty()) smartctl_def_options += " "; std::string device_specific_options = device_opts; if (!device_specific_options.empty()) device_specific_options += " "; smartctl_ex->set_command(Glib::shell_quote(smartctl_binary), smartctl_def_options + device_specific_options + command_options + " " + Glib::shell_quote(device)); if (!smartctl_ex->execute() || !smartctl_ex->get_error_msg().empty()) { debug_out_warn("app", DBG_FUNC_MSG << "Smartctl binary did not execute cleanly.\n"); smartctl_output = hz::string_trim_copy(hz::string_any_to_unix_copy(smartctl_ex->get_stdout_str())); // check if it's a device permission error. // Smartctl open device: /dev/sdb failed: Permission denied if (app_pcre_match("/Smartctl open device.+Permission denied/mi", smartctl_output)) { return "Permission denied while opening device."; } // smartctl_output = smartctl_ex->get_stdout_str(); return smartctl_ex->get_error_msg(); } // any_to_unix is needed for windows smartctl_output = hz::string_trim_copy(hz::string_any_to_unix_copy(smartctl_ex->get_stdout_str())); if (smartctl_output.empty()) { debug_out_error("app", DBG_FUNC_MSG << "Smartctl returned an empty output.\n"); return "Smartctl returned an empty output."; } return std::string(); } /// @} gsmartcontrol-1.1.4/src/applib/smartctl_executor.h000066400000000000000000000124101417717230600223770ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef SMARTCTL_EXECUTOR_H #define SMARTCTL_EXECUTOR_H #include "cmdex.h" #include "cmdex_sync.h" /// Smartctl executor template. template class SmartctlExecutorGeneric : public ExecutorSync { public: /// Constructor SmartctlExecutorGeneric(const std::string& cmd, const std::string& cmdargs) : ExecutorSync(cmd, cmdargs) { this->construct(); } /// Constructor SmartctlExecutorGeneric() { this->construct(); } /// Virtual destructor virtual ~SmartctlExecutorGeneric() { } protected: /// Called by constructors void construct() { ExecutorSync::get_command_executor().set_exit_status_translator(&SmartctlExecutorGeneric::translate_exit_status, NULL); this->set_error_header("An error occurred while executing smartctl:\n\n"); } enum { exit_cant_parse = 1 << 0, ///< Smartctl error code bit exit_open_failed = 1 << 1, ///< Smartctl error code bit exit_smart_failed = 1 << 2, ///< Smartctl error code bit exit_disk_failing = 1 << 3, ///< Smartctl error code bit exit_prefail_threshold = 1 << 4, ///< Smartctl error code bit exit_threshold_in_past = 1 << 5, ///< Smartctl error code bit exit_error_log = 1 << 6, ///< Smartctl error code bit exit_self_test_log = 1 << 7 ///< Smartctl error code bit }; /// Translate smartctl error code to a readable message static std::string translate_exit_status(int status, void* user_data) { const char* table[] = { "Command line did not parse.", "Device open failed, or device did not return an IDENTIFY DEVICE structure.", "Some SMART command to the disk failed, or there was a checksum error in a SMART data structure", "SMART status check returned \"DISK FAILING\"", "SMART status check returned \"DISK OK\" but some prefail Attributes are less than threshold.", "SMART status check returned \"DISK OK\" but we found that some (usage or prefail) Attributes have been less than threshold at some time in the past.", "The device error log contains records of errors.", "The device self-test log contains records of errors." }; std::string str; // check every bit for (unsigned int i = 0; i <= 7; i++) { if (status & (1 << i)) { if (!str.empty()) str += "\n"; // new-line-separate each entry str += table[i]; } } return str; } /// Import the last error from command executor and clear all errors there virtual void import_error() { Cmdex& cmdex = this->get_command_executor(); cmdex.errors_lock(); Cmdex::error_list_t errors = cmdex.get_errors(false); // these are not clones hz::ErrorBase* e = 0; // find the last relevant error. // note: const_reverse_iterator doesn't work on gcc 3, so don't do it. for (Cmdex::error_list_t::reverse_iterator iter = errors.rbegin(); iter != errors.rend(); ++iter) { // ignore iochannel errors, they may mask the real errors if ((*iter)->get_type() != "giochannel" && (*iter)->get_type() != "custom") { e = (*iter)->clone(); break; } } cmdex.clear_errors(false); // and clear them cmdex.errors_unlock(); if (e) { // if error is present, alert the user on_error_warn(e); } } /// This is called when an error occurs in command executor. /// Note: The warnings are already printed via debug_* in cmdex. virtual void on_error_warn(hz::ErrorBase* e) { if (!e) return; // import the error only if it's relevant. std::string error_type = e->get_type(); // accept all errors by default, except: // Treat most exit codes as non-errors. if (error_type == "exit") { int exit_code = 0; e->get_code(exit_code); // Ignore everyone except this. // Note that we don't treat exit_open_failed as failure because: // * It may be returned from a DVD that returns product info but has no disk inside; // * It may be returned from a usb flash drive with -d scsi; // * exit_cant_parse is returned when opening unsupported usb drives without -d scsi. if ( !(exit_code & exit_cant_parse) ) return; // ignore giochannel errors - higher level errors will be triggered, and they more user-friendly. } else if (error_type == "giochannel" || error_type == "custom") { return; } this->set_error_msg(e->get_message()); } }; /// Smartctl executor without GUI support typedef SmartctlExecutorGeneric SmartctlExecutor; /// A reference-counting pointer to SmartctlExecutor typedef hz::intrusive_ptr SmartctlExecutorRefPtr; /// Get smartctl binary (from config, etc...). Returns an empty string if not found. std::string get_smartctl_binary(); /// Execute smartctl on device \c device. /// \return error message on error, empty string on success. std::string execute_smartctl(const std::string& device, const std::string& device_opts, const std::string& command_options, hz::intrusive_ptr smartctl_ex, std::string& smartctl_output); #endif /// @} gsmartcontrol-1.1.4/src/applib/smartctl_executor_gui.h000066400000000000000000000014061417717230600232460ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef SMARTCTL_EXECUTOR_GUI_H #define SMARTCTL_EXECUTOR_GUI_H #include "hz/intrusive_ptr.h" #include "smartctl_executor.h" #include "cmdex_sync_gui.h" /// Smartctl executor with GUI support typedef SmartctlExecutorGeneric SmartctlExecutorGui; /// A reference-counting pointer to SmartctlExecutor typedef hz::intrusive_ptr SmartctlExecutorGuiRefPtr; #endif /// @} gsmartcontrol-1.1.4/src/applib/smartctl_executor_test.cpp000066400000000000000000000025651417717230600240030ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_unlicense.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib_tests /// \weakgroup applib_tests /// @{ #include #include // #include "smartctl_executor_gui.h" #include "smartctl_executor.h" /// Main function of the test int main(int argc, char** argv) { // Glib::thread_init(); Gtk::Main m(argc, argv); // NOTE: Don't use long options (e.g. --info). Use short ones (e.g. -i), // because long options may be unsupported on some platforms. // SmartctlExecutor ex("smartctl", "-i /dev/sda"); // SmartctlExecutorGui ex("ls", "-l --color=no -R /dev"); // SmartctlExecutorGui ex("lsa", "-1 --color=no /sys/block"); // SmartctlExecutorGui ex("../../../0test_binary.sh", ""); SmartctlExecutor ex("../../../0test_binary.sh", ""); ex.execute(); std::string out_str = ex.get_stdout_str(); // std::cout << "OUT:\n" << out_str << "\n\n"; std::cerr << "OUT SIZE: " << out_str.size() << "\n"; std::cerr << "STDERR:\n" << ex.get_stderr_str() << "\n"; std::cerr << "ERROR MSG:\n"; std::cerr << ex.get_error_msg(); // execute it second time ex.execute(); // m.run(); return 0; } /// @} gsmartcontrol-1.1.4/src/applib/smartctl_parser.cpp000066400000000000000000002677051417717230600224130ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #include // localeconv #include "hz/locale_tools.h" // ScopedCLocale, locale_c_get(). #include "hz/cstdint.h" // uint64_t #include "hz/string_algo.h" // string_* #include "hz/string_num.h" // string_is_numeric, number_to_string #include "hz/format_unit.h" // format_size #include "hz/debug.h" // debug_* #include "app_pcrecpp.h" #include "smartctl_parser.h" #include "storage_property_descr.h" #include "storage_property_colors.h" namespace { /// Get storage property by checksum error name (which corresponds to /// an output section). inline StorageProperty app_get_checksum_error_property(const std::string& name) { StorageProperty p; p.section = StorageProperty::section_data; if (name == "Attribute Data") { p.subsection = StorageProperty::subsection_attributes; p.set_name(name, "attribute_data_checksum_error"); } else if (name == "Attribute Thresholds") { p.subsection = StorageProperty::subsection_attributes; p.set_name(name, "attribute_thresholds_checksum_error"); } else if (name == "ATA Error Log") { p.subsection = StorageProperty::subsection_error_log; p.set_name(name, "ata_error_log_checksum_error"); } else if (name == "Self-Test Log") { p.subsection = StorageProperty::subsection_selftest_log; p.set_name(name, "selftest_log_checksum_error"); } p.readable_name = "Error in " + name + " structure"; p.reported_value = "checksum error"; p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; return p; } } SmartctlParser::SmartctlParser() : disk_type_(StorageAttribute::DiskAny) { } // Parse full "smartctl -x" output bool SmartctlParser::parse_full(const std::string& full, StorageAttribute::DiskType disk_type) { this->clear(); // clear previous data this->set_data_full(full); disk_type_ = disk_type; // -------------------- Fix the output so it doesn't interfere with proper parsing // perform any2unix std::string s = hz::string_trim_copy(hz::string_any_to_unix_copy(full)); if (s.empty()) { set_error_msg("Smartctl data is empty."); debug_out_warn("app", DBG_FUNC_MSG << "Empty string passed as an argument. Returning.\n"); return false; } // The first line may be a command, filter it out. e.g. // # smartctl -a /dev/sda // NO NEED: We ignore everything non-section (except version info). // Note: We ignore non-section lines, so we don't need any filtering here. // { // app_pcre_replace_once("/^# .*$/", "", s); // replace first only, on the first line only. // } // Checksum warnings are kind of randomly distributed, so // extract and remove them. { pcrecpp::RE re = app_pcre_re("/\\nWarning! SMART (.+) Structure error: invalid SMART checksum\\.$/mi"); std::string name; pcrecpp::StringPiece input(s); // position tracker while (re.FindAndConsume(&input, &name)) { add_property(app_get_checksum_error_property(hz::string_trim_copy(name))); } app_pcre_replace(re, "", s); // remove them from s. } // Remove some additional stuff which doesn't fit // Display this warning somewhere? (info section?) // Or not, these options don't do anything crucial - just some translation stuff. { app_pcre_replace("/\\n.*May need -F samsung or -F samsung2 enabled; see manual for details\\.$/mi", "", s); // remove from s } // The Warning: parts also screw up newlines sometimes (making double-newlines, // confusing for section separation). { pcrecpp::RE re = app_pcre_re("/^(Warning: ATA error count.*\\n)\\n/mi"); std::string match; if (app_pcre_match(re, s, &match)) { app_pcre_replace(re, match, s); // make one newline less } } // If the device doesn't support many things, the warnings aren't separated (for sections). // Fix that. This affects old smartctl only (at least 6.5 fixed the warnings). { pcrecpp::RE re1 = app_pcre_re("/^(Warning: device does not support Error Logging)$/mi"); pcrecpp::RE re2 = app_pcre_re("/^(Warning: device does not support Self Test Logging)$/mi"); pcrecpp::RE re3 = app_pcre_re("/^(Device does not support Selective Self Tests\\/Logging)$/mi"); pcrecpp::RE re4 = app_pcre_re("/^(Warning: device does not support SCT Commands)$/mi"); std::string match; if (app_pcre_match(re1, s, &match)) app_pcre_replace(re1, "\n" + match + "\n", s); // add extra newlines if (app_pcre_match(re2, s, &match)) app_pcre_replace(re2, "\n" + match + "\n", s); // add extra newlines if (app_pcre_match(re3, s, &match)) app_pcre_replace(re3, "\n" + match + "\n", s); // add extra newlines if (app_pcre_match(re4, s, &match)) app_pcre_replace(re4, "\n" + match + "\n", s); // add extra newlines } // Some errors get in the way of subsection detection and have little value, remove them. { // "ATA_READ_LOG_EXT (addr=0x00:0x00, page=0, n=1) failed: 48-bit ATA commands not implemented" // or "ATA_READ_LOG_EXT (addr=0x11:0x00, page=0, n=1) failed: scsi error aborted command" // in front of "Read GP Log Directory failed" and "Read SATA Phy Event Counters failed". pcrecpp::RE re1 = app_pcre_re("/^(ATA_READ_LOG_EXT \\([^)]+\\) failed: .*)$/mi"); // "SMART WRITE LOG does not return COUNT and LBA_LOW register" // in front of "SCT (Get) Error Recovery Control command failed" (scterc section) pcrecpp::RE re2= app_pcre_re("/^((?:Error )?SMART WRITE LOG does not return COUNT and LBA_LOW register)$/mi"); // "Read SCT Status failed: scsi error aborted command" // in front of "Read SCT Temperature History failed" and "SCT (Get) Error Recovery Control command failed" pcrecpp::RE re3= app_pcre_re("/^(Read SCT Status failed: .*)$/mi"); // "Unknown SCT Status format version 0, should be 2 or 3." pcrecpp::RE re4= app_pcre_re("/^(Unknown SCT Status format version .*)$/mi"); // "Read SCT Data Table failed: scsi error aborted command" pcrecpp::RE re5= app_pcre_re("/^(Read SCT Data Table failed: .*)$/mi"); // "Write SCT Data Table failed: Undefined error: 0" // in front of "Read SCT Temperature History failed" pcrecpp::RE re6= app_pcre_re("/^(Write SCT Data Table failed: .*)$/mi"); // "Unexpected SCT status 0x0000 (action_code=0, function_code=0)" // in front of "Read SCT Temperature History failed" pcrecpp::RE re7= app_pcre_re("/^(Unexpected SCT status .*\\))$/mi"); std::string match; if (app_pcre_match(re1, s, &match)) app_pcre_replace(re1, "", s); // add extra newlines if (app_pcre_match(re2, s, &match)) app_pcre_replace(re2, "", s); // add extra newlines if (app_pcre_match(re3, s, &match)) app_pcre_replace(re3, "", s); // add extra newlines if (app_pcre_match(re4, s, &match)) app_pcre_replace(re4, "", s); // add extra newlines if (app_pcre_match(re5, s, &match)) app_pcre_replace(re5, "", s); // add extra newlines if (app_pcre_match(re6, s, &match)) app_pcre_replace(re6, "", s); // add extra newlines if (app_pcre_match(re7, s, &match)) app_pcre_replace(re7, "", s); // add extra newlines } // ------------------- Parsing // version info std::string version, version_full; if (!parse_version(s, version, version_full)) { set_error_msg("Cannot extract smartctl version information."); debug_out_warn("app", DBG_FUNC_MSG << "Cannot extract version information. Returning.\n"); return false; } else { { StorageProperty p; p.set_name("Smartctl version", "smartctl_version", "Smartctl Version"); p.reported_value = version; p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; p.section = StorageProperty::section_info; // add to info section add_property(p); } { StorageProperty p; p.set_name("Smartctl version", "smartctl_version_full", "Smartctl Version"); p.reported_value = version_full; p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; p.section = StorageProperty::section_info; // add to info section add_property(p); } } if (!check_parsed_version(version, version_full)) { set_error_msg("Incompatible smartctl version."); debug_out_warn("app", DBG_FUNC_MSG << "Incompatible smartctl version. Returning.\n"); return false; } // sections std::string::size_type section_start_pos = 0, section_end_pos = 0, tmp_pos = 0; bool status = false; // true if at least one section was parsed // sections are started by // === START OF SECTION === while (section_start_pos != std::string::npos && (section_start_pos = s.find("=== START", section_start_pos)) != std::string::npos) { tmp_pos = s.find("\n", section_start_pos); // works with \r\n too. This may be npos if nothing follows the header. // trim is needed to remove potential \r in the end std::string section_header = hz::string_trim_copy(s.substr(section_start_pos, (tmp_pos == std::string::npos ? tmp_pos : (tmp_pos - section_start_pos)) )); std::string section_body_str; if (tmp_pos != std::string::npos) { section_end_pos = s.find("=== START", tmp_pos); // start of the next section section_body_str = hz::string_trim_copy(s.substr(tmp_pos, (section_end_pos == std::string::npos ? section_end_pos : section_end_pos - tmp_pos))); } status = parse_section(section_header, section_body_str) || status; section_start_pos = (tmp_pos == std::string::npos ? std::string::npos : section_end_pos); } if (!status) { set_error_msg("No ATA sections could be parsed."); debug_out_warn("app", DBG_FUNC_MSG << "No ATA sections could be parsed. Returning.\n"); return false; } return true; } // Supply output of "smartctl --version" here. // returns false on failure. Non-unix newlines in s are ok. bool SmartctlParser::parse_version(const std::string& s, std::string& version, std::string& version_full) { // e.g. // "smartctl version 5.37" // "smartctl 5.39" // "smartctl 5.39 2009-06-03 20:10" (cvs versions) // "smartctl 5.39 2009-08-08 r2873" (svn versions) if (!app_pcre_match("/^smartctl (?:version )?(([0-9][^ \\t\\n\\r]+)(?: [0-9 r:-]+)?)/mi", s, &version_full, &version)) { debug_out_error("app", DBG_FUNC_MSG << "No smartctl version information found in supplied string.\n"); return false; } hz::string_trim(version_full); return true; } // check that the version of smartctl output can be parsed with this parser. bool SmartctlParser::check_parsed_version(const std::string& version_str, const std::string& version_full_str) { // tested with 5.1-xx versions (1 - 18), and 5.[20 - 38]. // note: 5.1-11 (maybe others too) with scsi disk gives non-parsable output (why?). // 5.0-24, 5.0-36, 5.0-49 tested with data only, from smartmontool site. // can't fully test 5.0-xx, they don't support sata and I have only sata. const double minimum_req_version = 5.0; double version = 0; if (hz::string_is_numeric(version_str, version, false)) { if (version >= minimum_req_version) return true; } return false; } // convert e.g. "1,000,204,886,016 bytes" to 1.00 TB [931.51 GiB, 1000204886016 bytes]. // Note: this property is present since 5.33. std::string SmartctlParser::parse_byte_size(const std::string& str, uint64_t& bytes, bool extended) { // E.g. "500,107,862,016" bytes or "80'060'424'192 bytes" or "80 026 361 856 bytes". // French locale inserts 0xA0 as a separator (non-breaking space, _not_ a valid utf8 char). // Added '.'-separated too, just in case. // Smartctl uses system locale's thousands_sep explicitly. // When launching smartctl, we use LANG=C for it, but it works only on POSIX. // Also, loading smartctl output files from different locales doesn't really work. // debug_out_dump("app", "Size reported as: " << str << "\n"); std::vector to_replace; to_replace.push_back(" "); to_replace.push_back("'"); to_replace.push_back(","); to_replace.push_back("."); to_replace.push_back(std::string(1, 0xa0)); to_replace.push_back(std::string(1, 0xc2)); #ifdef _WIN32 // if current locale is C, then probably we didn't change it at application // startup, so set it now (temporarily). Otherwise, just use the current locale's // thousands separator. { std::string old_locale = hz::locale_c_get(); hz::ScopedCLocale loc("", old_locale == "C"); // set system locale if the current one is C struct lconv* lc = std::localeconv(); if (lc && lc->thousands_sep && *(lc->thousands_sep)) { to_replace.push_back(lc->thousands_sep); } } // the locale is restored here #endif to_replace.push_back("bytes"); std::string s = hz::string_replace_array_copy(hz::string_trim_copy(str), to_replace, ""); uint64_t v = 0; if (hz::string_is_numeric(s, v, false)) { bytes = v; return hz::format_size(v, true) + (extended ? " [" + hz::format_size(v, false) + ", " + hz::number_to_string(v) + " bytes]" : ""); } return std::string(); } // Parse the section part (with "=== .... ===" header) - info or data sections. bool SmartctlParser::parse_section(const std::string& header, const std::string& body) { if (app_pcre_match("/START OF INFORMATION SECTION/mi", header)) { return parse_section_info(body); } else if (app_pcre_match("/START OF READ SMART DATA SECTION/mi", header)) { return parse_section_data(body); // These sections provide information about actions performed. // You may encounter this if e.g. executing "smartctl -a -s on". // example contents: "SMART Enabled.". } else if (app_pcre_match("/START OF READ SMART DATA SECTION/mi", header)) { return true; // We don't parse this - it's parsed by the respective command issuer. } else if (app_pcre_match("/START OF ENABLE/DISABLE COMMANDS SECTION/mi", header)) { return true; // This is printed when executing "-t long", etc... . Parsed by respective command issuer. } else if (app_pcre_match("/START OF OFFLINE IMMEDIATE AND SELF-TEST SECTION/mi", header)) { return true; } debug_out_warn("app", DBG_FUNC_MSG << "Unknown section encountered.\n"); debug_out_dump("app", "---------------- Begin unknown section header dump ----------------\n"); debug_out_dump("app", header << "\n"); debug_out_dump("app", "----------------- End unknown section header dump -----------------\n"); return false; // unknown section } // ------------------------------------------------ INFO SECTION bool SmartctlParser::parse_section_info(const std::string& body) { this->set_data_section_info(body); StorageProperty::section_t section = StorageProperty::section_info; // split by lines. // e.g. Device Model: ST3500630AS pcrecpp::RE re = app_pcre_re("/^([^\\n]+): [ \\t]*(.*)$/miU"); // ungreedy std::vector lines; hz::string_split(body, '\n', lines, false); std::string name, value, warning_msg; // pcrecpp::StringPiece input(body); // position tracker bool expecting_warning_lines = false; // while (re.FindAndConsume(&input, &name, &value)) { for (size_t line_index = 0; line_index < lines.size(); ++line_index) { std::string line = hz::string_trim_copy(lines[line_index]); if (expecting_warning_lines) { if (!line.empty()) { warning_msg += "\n" + line; } else { expecting_warning_lines = false; StorageProperty p; p.section = section; p.set_name("Warning", "info_warning", "Warning"); p.reported_value = warning_msg; p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; add_property(p); warning_msg.clear(); } continue; } if (line.empty()) { continue; // empty lines are part of Info section } // Sometimes, we get this in the middle of Info section (separated by double newlines): /* ==> WARNING: A firmware update for this drive may be available, see the following Seagate web pages: http://knowledge.seagate.com/articles/en_US/FAQ/207931en http://knowledge.seagate.com/articles/en_US/FAQ/213891en */ if (app_pcre_match("/^==> WARNING: /mi", line)) { app_pcre_replace("^==> WARNING: ", "", line); warning_msg = hz::string_trim_copy(line); expecting_warning_lines = true; continue; } // This is not an ordinary name / value pair, so filter it out (we don't need it anyway). // Usually this happens when smart is unsupported or disabled. if (app_pcre_match("/mandatory SMART command failed/mi", line)) { continue; } // --get=all may cause these, ignore. // "Unexpected SCT status 0x0010 (action_code=4, function_code=2)" if (app_pcre_match("/^Unexpected SCT status/mi", line) // "Write SCT (Get) XXX Error Recovery Control Command failed: scsi error aborted command" || app_pcre_match("/^Write SCT \\(Get\\) XXX Error Recovery Control Command failed/mi", line) // "Write SCT (Get) Feature Control Command failed: scsi error aborted command" || app_pcre_match("/^Write SCT \\(Get\\) Feature Control Command failed/mi", line) // "Read SCT Status failed: scsi error aborted command" || app_pcre_match("/^Read SCT Status failed/mi", line) // "Read SMART Data failed: Input/output error" (just ignore this, the rest of the data seems fine) || app_pcre_match("/^Read SMART Data failed/mi", line) // "Unknown SCT Status format version 0, should be 2 or 3." || app_pcre_match("/^Unknown SCT Status format version/mi", line) // "Read SMART Thresholds failed: scsi error aborted command" || app_pcre_match("/^Read SMART Thresholds failed/mi", line) // " Enabled status cached by OS, trying SMART RETURN STATUS cmd." || app_pcre_match("/Enabled status cached by OS, trying SMART RETURN STATUS cmd/mi", line) || app_pcre_match("/^>> Terminate command early due to bad response to IEC mode page/mi", line) // on a flash drive // "scsiModePageOffset: response length too short, resp_len=4 offset=4 bd_len=0" || app_pcre_match("/^scsiModePageOffset: .+/mi", line) // on a flash drive ) { continue; } if (re.FullMatch(line, &name, &value)) { hz::string_trim(name); hz::string_trim(value); StorageProperty p; p.section = section; p.set_name(name); p.reported_value = value; parse_section_info_property(p); // set type and the typed value. may change generic_name too. add_property(p); } else { debug_out_warn("app", DBG_FUNC_MSG << "Unknown Info line encountered.\n"); debug_out_dump("app", "---------------- Begin unknown Info line ----------------\n"); debug_out_dump("app", line << "\n"); debug_out_dump("app", "----------------- End unknown Info line -----------------\n"); } } return true; } // Parse a component (one line) of the info section bool SmartctlParser::parse_section_info_property(StorageProperty& p) { // ---- Info if (p.section != StorageProperty::section_info) { set_error_msg("Internal parser error."); // set this so we have something to display debug_out_error("app", DBG_FUNC_MSG << "Called with non-info section!\n"); return false; } if (app_pcre_match("/^Model Family$/mi", p.reported_name)) { p.set_name(p.reported_name, "model_family", "Model Family"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^(?:Device Model|Device|Product)$/mi", p.reported_name)) { // "Device" and "Product" are from scsi/usb p.set_name(p.reported_name, "device_model", "Device Model"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^Vendor$/mi", p.reported_name)) { // From scsi/usb p.set_name(p.reported_name, "vendor", "Vendor"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^Revision$/mi", p.reported_name)) { // From scsi/usb p.set_name(p.reported_name, "revision", "Revision"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^Device type$/mi", p.reported_name)) { // From scsi/usb p.set_name(p.reported_name, "device_type", "Device Type"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^Compliance$/mi", p.reported_name)) { // From scsi/usb p.set_name(p.reported_name, "device_type", "Compliance"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^Serial Number$/mi", p.reported_name)) { p.set_name(p.reported_name, "serial_number", "Serial Number"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^LU WWN Device Id$/mi", p.reported_name)) { p.set_name(p.reported_name, "wwn_id", "World Wide Name"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^Add. Product Id$/mi", p.reported_name)) { p.set_name(p.reported_name, "add_product_id", "Additional Product ID"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^Firmware Version$/mi", p.reported_name)) { p.set_name(p.reported_name, "firmware_version", "Firmware Version"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^User Capacity$/mi", p.reported_name)) { p.set_name(p.reported_name, "capacity", "Capacity"); p.value_type = StorageProperty::value_type_integer; uint64_t v = 0; if ((p.readable_value = parse_byte_size(p.reported_value, v, true)).empty()) { p.readable_value = "[unknown]"; } else { p.value_integer = v; } } else if (app_pcre_match("/^Sector Sizes$/mi", p.reported_name)) { p.set_name(p.reported_name, "sector_sizes", "Sector Sizes"); p.value_type = StorageProperty::value_type_string; // prints 2 values (phys/logical, if they're different) p.value_string = p.reported_value; } else if (app_pcre_match("/^Sector Size$/mi", p.reported_name)) { p.set_name(p.reported_name, "sector_size", "Sector Size"); p.value_type = StorageProperty::value_type_string; // prints a single value (if it's not 512) p.value_string = p.reported_value; } else if (app_pcre_match("/^Logical block size$/mi", p.reported_name)) { // from scsi/usb p.set_name(p.reported_name, "logical_block_size", "Logical Block Size"); p.value_type = StorageProperty::value_type_string; // "512 bytes" p.value_string = p.reported_value; } else if (app_pcre_match("/^Rotation Rate$/mi", p.reported_name)) { p.set_name(p.reported_name, "rotation_rate", "Rotation Rate"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^Form Factor$/mi", p.reported_name)) { p.set_name(p.reported_name, "form_factor", "Form Factor"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^Device is$/mi", p.reported_name)) { p.set_name(p.reported_name, "in_smartctl_db", "In Smartctl Database"); p.value_type = StorageProperty::value_type_bool; p.value_bool = (!app_pcre_match("/Not in /mi", p.reported_value)); } else if (app_pcre_match("/^ATA Version is$/mi", p.reported_name)) { p.set_name(p.reported_name, "ata_version", "ATA Version"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^ATA Standard is$/mi", p.reported_name)) { p.set_name(p.reported_name, "ata_standard", "ATA Standard"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^SATA Version is$/mi", p.reported_name)) { p.set_name(p.reported_name, "sata_version", "SATA Version"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^Local Time is$/mi", p.reported_name)) { p.set_name(p.reported_name, "scan_time", "Scanned on"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^SMART support is$/mi", p.reported_name)) { // There are two different properties with this name - supported and enabled. // Don't put complete messages here - they change across smartctl versions. if (app_pcre_match("/Available - device has/mi", p.reported_value)) { p.set_name(p.reported_name, "smart_supported", "SMART Supported"); p.value_type = StorageProperty::value_type_bool; p.value_bool = true; } else if (app_pcre_match("/Enabled/mi", p.reported_value)) { p.set_name(p.reported_name, "smart_enabled", "SMART Enabled"); p.value_type = StorageProperty::value_type_bool; p.value_bool = true; } else if (app_pcre_match("/Disabled/mi", p.reported_value)) { p.set_name(p.reported_name, "smart_enabled", "SMART Enabled"); p.value_type = StorageProperty::value_type_bool; p.value_bool = false; } else if (app_pcre_match("/Unavailable/mi", p.reported_value)) { p.set_name(p.reported_name, "smart_supported", "SMART Supported"); p.value_type = StorageProperty::value_type_bool; p.value_bool = false; // this should be the last - when ambiguous state is detected, usually smartctl // retries with other methods and prints one of the above. } else if (app_pcre_match("/Ambiguous/mi", p.reported_value)) { p.set_name(p.reported_name, "smart_supported", "SMART Supported"); p.value_type = StorageProperty::value_type_bool; p.value_bool = true; // let's be optimistic - just hope that it doesn't hurt. } // "-g all" stuff } else if (app_pcre_match("/^AAM feature is$/mi", p.reported_name)) { p.set_name(p.reported_name, "aam_feature", "AAM Feature"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^AAM level is$/mi", p.reported_name)) { p.set_name(p.reported_name, "aam_level", "AAM Level"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^APM feature is$/mi", p.reported_name)) { p.set_name(p.reported_name, "apm_feature", "APM Feature"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^APM level is$/mi", p.reported_name)) { p.set_name(p.reported_name, "apm_level", "APM Level"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^Rd look-ahead is$/mi", p.reported_name)) { p.set_name(p.reported_name, "read_lookahead", "Read Look-Ahead"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^Write cache is$/mi", p.reported_name)) { p.set_name(p.reported_name, "write_cache", "Write Cache"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^Wt Cache Reorder$/mi", p.reported_name)) { p.set_name(p.reported_name, "write_cache_reorder", "Write Cache Reorder"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^DSN feature is$/mi", p.reported_name)) { p.set_name(p.reported_name, "dsn_feature", "DSN Feature"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^Power mode (?:was|is)$/mi", p.reported_name)) { p.set_name(p.reported_name, "power_mode", "Power Mode"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } else if (app_pcre_match("/^ATA Security is$/mi", p.reported_name)) { p.set_name(p.reported_name, "ata_security", "ATA Security"); p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; // These are some debug warnings from smartctl on usb flash drives } else if (app_pcre_match("/^scsiMode/mi", p.reported_name)) { p.show_in_ui = false; } else { debug_out_warn("app", DBG_FUNC_MSG << "Unknown property \"" << p.reported_name << "\"\n"); // this is not an error, just unknown attribute. treat it as string. // Don't highlight it with warning, it may just be a new smartctl feature. p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; } return true; } // ------------------------------------------------ DATA SECTION // Parse the Data section (without "===" header) bool SmartctlParser::parse_section_data(const std::string& body) { this->set_data_section_data(body); // perform any2unix // std::string s = hz::string_any_to_unix_copy(body); std::vector split_subsections; // subsections are separated by double newlines, except: // - "error log" subsection, which contains double-newline-separated blocks. // - "scttemp" subsection, which has 3 blocks. hz::string_split(body, "\n\n", split_subsections, true); bool status = false; // at least one subsection was parsed std::vector subsections; // merge "single " parts. For error log, each part begins with a double-space or "Error nn". // For scttemp, parts begin with // "SCT Temperature History Version" or // "Index " or // "Read SCT Temperature History failed". for (unsigned int i = 0; i < split_subsections.size(); ++i) { std::string sub = hz::string_trim_copy(split_subsections[i], "\t\n\r"); // don't trim space if (app_pcre_re("^ ").PartialMatch(sub) || app_pcre_re("^Error [0-9]+").PartialMatch(sub) || app_pcre_re("^SCT Temperature History Version").PartialMatch(sub) || app_pcre_re("^Index[ \t]+").PartialMatch(sub) || app_pcre_re("^Read SCT Temperature History failed").PartialMatch(sub) ) { if (!subsections.empty()) { subsections.back() += "\n\n" + sub; // append to previous part } else { debug_out_warn("app", DBG_FUNC_MSG << "Error Log's Error block, or SCT Temperature History, or SCT Index found without any data subsections present.\n"); } } else { // not an Error block, process as usual subsections.push_back(sub); } } // parse each subsection for (unsigned int i = 0; i < subsections.size(); ++i) { std::string sub = hz::string_trim_copy(subsections[i]); if (sub.empty()) continue; if (app_pcre_match("/^SMART overall-health self-assessment/mi", sub)) { status = parse_section_data_subsection_health(sub) || status; } else if (app_pcre_match("/^General SMART Values/mi", sub)) { status = parse_section_data_subsection_capabilities(sub) || status; } else if (app_pcre_match("/^SMART Attributes Data Structure/mi", sub)) { status = parse_section_data_subsection_attributes(sub) || status; } else if (app_pcre_match("/^General Purpose Log Directory Version/mi", sub) // -l directory || app_pcre_match("/^General Purpose Log Directory not supported/mi", sub) || app_pcre_match("/^General Purpose Logging \\(GPL\\) feature set supported/mi", sub) || app_pcre_match("/^Read GP Log Directory failed/mi", sub) || app_pcre_match("/^Log Directories not read due to '-F nologdir' option/mi", sub) || app_pcre_match("/^Read SMART Log Directory failed/mi", sub) || app_pcre_match("/^SMART Log Directory Version/mi", sub) ) { // old smartctl status = parse_section_data_subsection_directory_log(sub) || status; } else if (app_pcre_match("/^SMART Error Log Version/mi", sub) // -l error || app_pcre_match("/^SMART Extended Comprehensive Error Log Version/mi", sub) // -l xerror || app_pcre_match("/^Warning: device does not support Error Logging/mi", sub) // -l error || app_pcre_match("/^SMART Error Log not supported/mi", sub) // -l error || app_pcre_match("/^Read SMART Error Log failed/mi", sub) ) { // -l error status = parse_section_data_subsection_error_log(sub) || status; } else if (app_pcre_match("/^SMART Extended Comprehensive Error Log \\(GP Log 0x03\\) not supported/mi", sub) // -l xerror || app_pcre_match("/^SMART Extended Comprehensive Error Log size (.*) not supported/mi", sub) || app_pcre_match("/^Read SMART Extended Comprehensive Error Log failed/mi", sub) ) { // -l xerror // These are printed with "-l xerror,error" if falling back to "error". They're in their own sections, ignore them. // We don't support showing these messages. status = false; } else if (app_pcre_match("/^SMART Self-test log/mi", sub) // -l selftest || app_pcre_match("/^SMART Extended Self-test Log Version/mi", sub) // -l xselftest || app_pcre_match("/^Warning: device does not support Self Test Logging/mi", sub) // -l selftest || app_pcre_match("/^Read SMART Self-test Log failed/mi", sub) // -l selftest || app_pcre_match("/^SMART Self-test Log not supported/mi", sub)) { // -l selftest status = parse_section_data_subsection_selftest_log(sub) || status; } else if (app_pcre_match("/^SMART Extended Self-test Log \\(GP Log 0x07\\) not supported/mi", sub) // -l xselftest || app_pcre_match("/^SMART Extended Self-test Log size [0-9-]+ not supported/mi", sub) // -l xselftest || app_pcre_match("/^Read SMART Extended Self-test Log failed/mi", sub) ) { // -l xselftest // These are printed with "-l xselftest,selftest" if falling back to "selftest". They're in their own sections, ignore them. // We don't support showing these messages. status = false; } else if (app_pcre_match("/^SMART Selective self-test log data structure/mi", sub) || app_pcre_match("/^Device does not support Selective Self Tests\\/Logging/mi", sub) || app_pcre_match("/^Selective Self-tests\\/Logging not supported/mi", sub) || app_pcre_match("/^Read SMART Selective Self-test Log failed/mi", sub) ) { status = parse_section_data_subsection_selective_selftest_log(sub) || status; } else if (app_pcre_match("/^SCT Status Version/mi", sub) // "SCT Commands not supported" // "SCT Commands not supported if ATA Security is LOCKED" // "Error unknown SCT Temperature History Format Version (3), should be 2." // "Another SCT command is executing, abort Read Data Table" || app_pcre_match("/^SCT Commands not supported/mi", sub) || app_pcre_match("/^SCT Data Table command not supported/mi", sub) || app_pcre_match("/^Error unknown SCT Temperature History Format Version/mi", sub) || app_pcre_match("/^Another SCT command is executing, abort Read Data Table/mi", sub) || app_pcre_match("/^Warning: device does not support SCT Commands/mi", sub) ) { // old smartctl status = parse_section_data_subsection_scttemp_log(sub) || status; } else if (app_pcre_match("/^SCT Error Recovery Control/mi", sub) // Can be the same "SCT Commands not supported" as scttemp. // "Another SCT command is executing, abort Error Recovery Control" || app_pcre_match("/^SCT Error Recovery Control command not supported/mi", sub) || app_pcre_match("/^SCT \\(Get\\) Error Recovery Control command failed/mi", sub) || app_pcre_match("/^Another SCT command is executing, abort Error Recovery Control/mi", sub) || app_pcre_match("/^Warning: device does not support SCT \\(Get\\) Error Recovery Control/mi", sub) ) { // old smartctl status = parse_section_data_subsection_scterc_log(sub) || status; } else if (app_pcre_match("/^Device Statistics \\([^)]+\\)$/mi", sub) // -l devstat || app_pcre_match("/^Device Statistics \\([^)]+\\) not supported/mi", sub) || app_pcre_match("/^Read Device Statistics page (?:.+) failed/mi", sub) ) { status = parse_section_data_subsection_devstat(sub) || status; // "Device Statistics (GP Log 0x04) supported pages" } else if (app_pcre_match("/^Device Statistics \\([^)]+\\) supported pages/mi", sub) ) { // not sure where it came from // We don't support this section. status = false; } else if (app_pcre_match("/^SATA Phy Event Counters/mi", sub) // -l sataphy || app_pcre_match("/^SATA Phy Event Counters \\(GP Log 0x11\\) not supported/mi", sub) || app_pcre_match("/^SATA Phy Event Counters with [0-9-]+ sectors not supported/mi", sub) || app_pcre_match("/^Read SATA Phy Event Counters failed/mi", sub) ) { status = parse_section_data_subsection_sataphy(sub) || status; } else { debug_out_warn("app", DBG_FUNC_MSG << "Unknown Data subsection encountered.\n"); debug_out_dump("app", "---------------- Begin unknown section dump ----------------\n"); debug_out_dump("app", sub << "\n"); debug_out_dump("app", "----------------- End unknown section dump -----------------\n"); } } return status; } // -------------------- Health bool SmartctlParser::parse_section_data_subsection_health(const std::string& sub) { // Health section data (--info and --get=all): /* Model Family: Hitachi/HGST Travelstar 5K750 Device Model: Hitachi HTS547550A9E384 Firmware Version: JE3OA40J User Capacity: 500,107,862,016 bytes [500 GB] Sector Sizes: 512 bytes logical, 4096 bytes physical Rotation Rate: 5400 rpm Form Factor: 2.5 inches Device is: In smartctl database [for details use: -P show] */ StorageProperty pt; // template for easy copying pt.section = StorageProperty::section_data; pt.subsection = StorageProperty::subsection_health; std::string name, value; if (app_pcre_match("/^([^:\\n]+):[ \\t]*(.*)$/mi", sub, &name, &value)) { hz::string_trim(name); hz::string_trim(value); // only one attribute in this section if (app_pcre_match("/SMART overall-health self-assessment/mi", name)) { pt.set_name(name, "overall_health", "Overall Health Self-Assessment Test"); pt.reported_value = value; pt.value_type = StorageProperty::value_type_string; pt.value_string = pt.reported_value; add_property(pt); } return true; } return false; } // -------------------- Capabilities bool SmartctlParser::parse_section_data_subsection_capabilities(const std::string& sub_initial) { // Capabilities section data: /* General SMART Values: Offline data collection status: (0x82) Offline data collection activity was completed without error. Auto Offline Data Collection: Enabled. Self-test execution status: ( 0) The previous self-test routine completed without error or no self-test has ever been run. Total time to complete Offline data collection: ( 45) seconds. Offline data collection capabilities: (0x5b) SMART execute Offline immediate. Auto Offline data collection on/off support. Suspend Offline collection upon new command. Offline surface scan supported. Self-test supported. No Conveyance Self-test supported. Selective Self-test supported. SMART capabilities: (0x0003) Saves SMART data before entering power-saving mode. Supports SMART auto save timer. Error logging capability: (0x01) Error logging supported. General Purpose Logging supported. Short self-test routine recommended polling time: ( 2) minutes. Extended self-test routine recommended polling time: ( 152) minutes. SCT capabilities: (0x003d) SCT Status supported. SCT Error Recovery Control supported. SCT Feature Control supported. SCT Data Table supported. */ StorageProperty pt; // template for easy copying pt.section = StorageProperty::section_data; pt.subsection = StorageProperty::subsection_capabilities; std::string sub = sub_initial; // Fix some bugs in smartctl output (pre-5.39-final versions): // There is a stale newline in "is in a Vendor Specific state\n.\n" and // "is in a Reserved state\n.\n". // app_pcre_replace("/\\n\\.$/mi", ".", &sub); app_pcre_replace("/(is in a Vendor Specific state)\\n\\.$/mi", "\\1.", sub); app_pcre_replace("/(is in a Reserved state)\\n\\.$/mi", "\\1.", sub); // split to lines and merge them into blocks std::vector lines, blocks; hz::string_split(sub, '\n', lines, true); bool partial = false; for(unsigned int i = 0; i < lines.size(); ++i) { std::string line = lines[i]; if (line.empty() || app_pcre_match("/General SMART Values/mi", line)) // skip the non-informative lines continue; line += "\n"; // avoid joining lines without separator. this will get stripped anyway. if (line.find_first_of(" \t") != 0 && !partial) { // new blocks don't start with whitespace blocks.push_back(std::string()); // new block blocks.back() += line; if (line.find(":") == std::string::npos) partial = true; // if the name spans several lines (they all start with non-whitespace) continue; } if (partial && line.find(":") != std::string::npos) partial = false; if (blocks.empty()) { debug_out_error("app", DBG_FUNC_MSG << "Non-block related line found!\n"); blocks.push_back(std::string()); // avoid segfault } blocks.back() += line; } // parse each block pcrecpp::RE re = app_pcre_re("/([^:]*):\\s*\\(([^)]+)\\)\\s*(.*)/ms"); bool cap_found = false; // found at least one capability for(unsigned int i = 0; i < blocks.size(); ++i) { std::string block = hz::string_trim_copy(blocks[i]); std::string name_orig, numvalue_orig, strvalue_orig; if (!re.FullMatch(block, &name_orig, &numvalue_orig, &strvalue_orig)) { debug_out_error("app", DBG_FUNC_MSG << "Block " << i << " cannot be parsed.\n"); debug_out_dump("app", "---------------- Begin unparsable block dump ----------------\n"); debug_out_dump("app", block << "\n"); debug_out_dump("app", "----------------- End unparsable block dump -----------------\n"); continue; } // flatten: std::string name = hz::string_trim_copy(hz::string_remove_adjacent_duplicates_copy( hz::string_replace_chars_copy(name_orig, "\t\n", ' '), ' ')); std::string strvalue = hz::string_trim_copy(hz::string_remove_adjacent_duplicates_copy( hz::string_replace_chars_copy(strvalue_orig, "\t\n", ' '), ' ')); int numvalue = -1; if (!hz::string_is_numeric(hz::string_trim_copy(numvalue_orig), numvalue, false)) { // this will autodetect number base. debug_out_warn("app", DBG_FUNC_MSG << "Numeric value: \"" << numvalue_orig << "\" cannot be parsed as number.\n"); } // debug_out_dump("app", "name: \"" << name << "\"\n\tnumvalue: \"" << numvalue // << "\"\n\tstrvalue: \"" << strvalue << "\"\n\n"); // Time length properties if (hz::string_erase_right_copy(strvalue, ".") == "minutes" || hz::string_erase_right_copy(strvalue, ".") == "seconds") { // const int numvalue_unmod = numvalue; if (hz::string_erase_right_copy(strvalue, ".") == "minutes") numvalue *= 60; // convert to seconds // add as a time property StorageProperty p(pt); p.set_name(name); p.reported_value = numvalue_orig + " | " + strvalue_orig; // well, not really as reported, but still... p.value_type = StorageProperty::value_type_time_length; p.value_time_length = numvalue; // always in seconds // Set some generic names on the recognized ones parse_section_data_internal_capabilities(p); add_property(p); cap_found = true; // StorageCapability properties (capabilities are flag lists) } else { StorageProperty p(pt); p.set_name(name); p.reported_value = numvalue_orig + " | " + strvalue_orig; // well, not really as reported, but still... p.value_type = StorageProperty::value_type_capability; p.value_capability.reported_flag_value = numvalue_orig; p.value_capability.flag_value = static_cast(numvalue); // full flag value p.value_capability.reported_strvalue = strvalue_orig; // split capability lines into a vector. every flag sentence ends with "." hz::string_split(strvalue, '.', p.value_capability.strvalues, true); for (StorageCapability::strvalue_list_t::iterator iter = p.value_capability.strvalues.begin(); iter != p.value_capability.strvalues.end(); ++iter) { hz::string_trim(*iter); } // find some special capabilities we're interested in and add them. p is unmodified. parse_section_data_internal_capabilities(p); add_property(p); cap_found = true; } } if (!cap_found) set_error_msg("No capabilities found in Capabilities section."); return cap_found; } // Check the capabilities for internal properties we can use. bool SmartctlParser::parse_section_data_internal_capabilities(StorageProperty& cap) { // Some special capabilities we're interested in. // Note: Smartctl gradually changed spelling Off-line to Offline in some messages. // Also, some capitalization was changed (so the regexps are caseless). // "Offline data collection not supported." (at all) - we don't need to check this, // because we look for immediate/automatic anyway. // "was never started", "was completed without error", "is in progress", // "was suspended by an interrupting command from host", etc... pcrecpp::RE re_offline_status = app_pcre_re("/^(Off-?line data collection) activity (?:is|was) (.*)$/mi"); // "Enabled", "Disabled". May not show up on older smartctl (< 5.1.10), so no way of knowing there. pcrecpp::RE re_offline_enabled = app_pcre_re("/^(Auto Off-?line Data Collection):[ \\t]*(.*)$/mi"); pcrecpp::RE re_offline_immediate = app_pcre_re("/^(SMART execute Off-?line immediate)$/mi"); // "No Auto Offline data collection support.", "Auto Offline data collection on/off support.". pcrecpp::RE re_offline_auto = app_pcre_re("/^(No |)(Auto Off-?line data collection (?:on\\/off )?support)$/mi"); // Same as above (smartctl <= 5.1-18). "No Automatic timer ON/OFF support." pcrecpp::RE re_offline_auto2 = app_pcre_re("/^(No |)(Automatic timer ON\\/OFF support)$/mi"); pcrecpp::RE re_offline_suspend = app_pcre_re("/^(?:Suspend|Abort) (Off-?line collection upon new command)$/mi"); pcrecpp::RE re_offline_surface = app_pcre_re("/^(No |)(Off-?line surface scan supported)$/mi"); pcrecpp::RE re_selftest_support = app_pcre_re("/^(No |)(Self-test supported)$/mi"); pcrecpp::RE re_conv_selftest_support = app_pcre_re("/^(No |)(Conveyance Self-test supported)$/mi"); pcrecpp::RE re_selective_selftest_support = app_pcre_re("/^(No |)(Selective Self-test supported)$/mi"); pcrecpp::RE re_sct_status = app_pcre_re("/^(SCT Status supported)$/mi"); pcrecpp::RE re_sct_control = app_pcre_re("/^(SCT Feature Control supported)$/mi"); // means can change logging interval pcrecpp::RE re_sct_data = app_pcre_re("/^(SCT Data Table supported)$/mi"); // these are matched on name pcrecpp::RE re_offline_status_group = app_pcre_re("/^(Off-?line data collection status)/mi"); pcrecpp::RE re_offline_time = app_pcre_re("/^(Total time to complete Off-?line data collection)/mi"); pcrecpp::RE re_offline_cap_group = app_pcre_re("/^(Off-?line data collection capabilities)/mi"); pcrecpp::RE re_smart_cap_group = app_pcre_re("/^(SMART capabilities)/mi"); pcrecpp::RE re_error_log_cap_group = app_pcre_re("/^(Error logging capability)/mi"); pcrecpp::RE re_sct_cap_group = app_pcre_re("/^(SCT capabilities)/mi"); pcrecpp::RE re_selftest_status = app_pcre_re("/^Self-test execution status/mi"); pcrecpp::RE re_selftest_short_time = app_pcre_re("/^(Short self-test routine recommended polling time)/mi"); pcrecpp::RE re_selftest_long_time = app_pcre_re("/^(Extended self-test routine recommended polling time)/mi"); pcrecpp::RE re_conv_selftest_time = app_pcre_re("/^(Conveyance self-test routine recommended polling time)/mi"); if (cap.section != StorageProperty::section_data || cap.subsection != StorageProperty::subsection_capabilities) { debug_out_error("app", DBG_FUNC_MSG << "Non-capability property passed.\n"); return false; } // Name the capability groups for easy matching when setting descriptions if (cap.value_type == StorageProperty::value_type_capability) { if (re_offline_status_group.PartialMatch(cap.reported_name)) { cap.generic_name = "offline_status_group"; } else if (re_offline_cap_group.PartialMatch(cap.reported_name)) { cap.generic_name = "offline_cap_group"; } else if (re_smart_cap_group.PartialMatch(cap.reported_name)) { cap.generic_name = "smart_cap_group"; } else if (re_error_log_cap_group.PartialMatch(cap.reported_name)) { cap.generic_name = "error_log_cap_group"; } else if (re_sct_cap_group.PartialMatch(cap.reported_name)) { cap.generic_name = "sct_cap_group"; } else if (re_selftest_status.PartialMatch(cap.reported_name)) { cap.generic_name = "last_selftest_cap_group"; } } // Last self-test status if (re_selftest_status.PartialMatch(cap.reported_name)) { // The last self-test status. break up into pieces. StorageProperty p; p.section = StorageProperty::section_internal; p.set_name("last_selftest_status"); p.value_type = StorageProperty::value_type_selftest_entry; p.value_selftest_entry.test_num = 0; p.value_selftest_entry.remaining_percent = -1; // unknown or n/a // check for lines in capability vector for (StorageCapability::strvalue_list_t::const_iterator iter = cap.value_capability.strvalues.begin(); iter != cap.value_capability.strvalues.end(); ++iter) { std::string value; if (app_pcre_match("/^([0-9]+)% of test remaining/mi", *iter, &value)) { uint8_t v = 0; if (hz::string_is_numeric(value, v)) p.value_selftest_entry.remaining_percent = v; } else if (app_pcre_match("/^(The previous self-test routine completed without error or no .*)/mi", *iter, &value)) { p.value_selftest_entry.status_str = value; p.value_selftest_entry.status = StorageSelftestEntry::status_completed_no_error; } else if (app_pcre_match("/^(The self-test routine was aborted by the host)/mi", *iter, &value)) { p.value_selftest_entry.status_str = value; p.value_selftest_entry.status = StorageSelftestEntry::status_aborted_by_host; } else if (app_pcre_match("/^(The self-test routine was interrupted by the host with a hard.*)/mi", *iter, &value)) { p.value_selftest_entry.status_str = value; p.value_selftest_entry.status = StorageSelftestEntry::status_interrupted; } else if (app_pcre_match("/^(A fatal error or unknown test error occurred while the device was executing its .*)/mi", *iter, &value)) { p.value_selftest_entry.status_str = value; p.value_selftest_entry.status = StorageSelftestEntry::status_fatal_or_unknown; } else if (app_pcre_match("/^(The previous self-test completed having a test element that failed and the test element that failed is not known)/mi", *iter, &value)) { p.value_selftest_entry.status_str = value; p.value_selftest_entry.status = StorageSelftestEntry::status_compl_unknown_failure; } else if (app_pcre_match("/^(The previous self-test completed having the electrical element of the test failed)/mi", *iter, &value)) { p.value_selftest_entry.status_str = value; p.value_selftest_entry.status = StorageSelftestEntry::status_compl_electrical_failure; } else if (app_pcre_match("/^(The previous self-test completed having the servo .*)/mi", *iter, &value)) { p.value_selftest_entry.status_str = value; p.value_selftest_entry.status = StorageSelftestEntry::status_compl_servo_failure; } else if (app_pcre_match("/^(The previous self-test completed having the read element of the test failed)/mi", *iter, &value)) { p.value_selftest_entry.status_str = value; p.value_selftest_entry.status = StorageSelftestEntry::status_compl_read_failure; } else if (app_pcre_match("/^(The previous self-test completed having a test element that failed and the device is suspected of having handling damage)/mi", *iter, &value)) { p.value_selftest_entry.status_str = value; p.value_selftest_entry.status = StorageSelftestEntry::status_compl_handling_damage; // samsung bug (?), as per smartctl sources. } else if (app_pcre_match("/^(The previous self-test routine completed with unknown result or self-test .*)/mi", *iter, &value)) { p.value_selftest_entry.status_str = value; p.value_selftest_entry.status = StorageSelftestEntry::status_compl_unknown_failure; // we'll use this again (correct?) } else if (app_pcre_match("/^(Self-test routine in progress)/mi", *iter, &value)) { p.value_selftest_entry.status_str = value; p.value_selftest_entry.status = StorageSelftestEntry::status_in_progress; } else if (app_pcre_match("/^(Reserved)/mi", *iter, &value)) { p.value_selftest_entry.status_str = value; p.value_selftest_entry.status = StorageSelftestEntry::status_reserved; } } add_property(p); return true; } // Check the time-related ones first. // Note: We only modify the existing property here! // Section is unmodified. if (cap.value_type == StorageProperty::value_type_time_length) { if (re_offline_time.PartialMatch(cap.reported_name)) { cap.generic_name = "iodc_total_time_length"; } else if (re_selftest_short_time.PartialMatch(cap.reported_name)) { cap.generic_name = "short_total_time_length"; } else if (re_selftest_long_time.PartialMatch(cap.reported_name)) { cap.generic_name = "long_total_time_length"; } else if (re_conv_selftest_time.PartialMatch(cap.reported_name)) { cap.generic_name = "conveyance_total_time_length"; } return true; } // Extract subcapabilities from capability vectors and assign to "internal" section. if (cap.value_type == StorageProperty::value_type_capability) { // check for lines in capability vector for (StorageCapability::strvalue_list_t::const_iterator iter = cap.value_capability.strvalues.begin(); iter != cap.value_capability.strvalues.end(); ++iter) { // debug_out_dump("app", "Looking for internal capability in: \"" << (*iter) << "\"\n"); StorageProperty p; p.section = StorageProperty::section_internal; // Note: We don't set reported_value on internal properties. std::string name, value; if (re_offline_status.PartialMatch(*iter, &name, &value)) { p.set_name(name, "odc_status"); p.value_type = StorageProperty::value_type_string; p.value_string = hz::string_trim_copy(value); } else if (re_offline_enabled.PartialMatch(*iter, &name, &value)) { p.set_name(name, "aodc_enabled"); p.value_type = StorageProperty::value_type_bool; p.value_bool = (hz::string_trim_copy(value) == "Enabled"); } else if (re_offline_immediate.PartialMatch(*iter, &name)) { p.set_name(name, "iodc_support"); p.value_type = StorageProperty::value_type_bool; p.value_bool = true; } else if (re_offline_auto.PartialMatch(*iter, &value, &name) || re_offline_auto2.PartialMatch(*iter, &value, &name)) { p.set_name(name, "aodc_support", "Automatic Offline Data Collection toggle support"); p.value_type = StorageProperty::value_type_bool; p.value_bool = (hz::string_trim_copy(value) != "No"); } else if (re_offline_suspend.PartialMatch(*iter, &value, &name)) { p.set_name(name, "iodc_command_suspends", "Offline Data Collection suspends upon new command"); p.value_type = StorageProperty::value_type_bool; p.value_bool = (hz::string_trim_copy(value) == "Suspend"); } else if (re_offline_surface.PartialMatch(*iter, &value, &name)) { p.set_name(name, "odc_surface_scan_support"); p.value_type = StorageProperty::value_type_bool; p.value_bool = (hz::string_trim_copy(value) != "No"); } else if (re_selftest_support.PartialMatch(*iter, &value, &name)) { p.set_name(name, "selftest_support"); p.value_type = StorageProperty::value_type_bool; p.value_bool = (hz::string_trim_copy(value) != "No"); } else if (re_conv_selftest_support.PartialMatch(*iter, &value, &name)) { p.set_name(name, "conveyance_support"); p.value_type = StorageProperty::value_type_bool; p.value_bool = (hz::string_trim_copy(value) != "No"); } else if (re_selective_selftest_support.PartialMatch(*iter, &value, &name)) { p.set_name(name, "selective_selftest_support"); p.value_type = StorageProperty::value_type_bool; p.value_bool = (hz::string_trim_copy(value) != "No"); } else if (re_sct_status.PartialMatch(*iter, &name)) { p.set_name(name, "sct_status_support"); p.value_type = StorageProperty::value_type_bool; p.value_bool = true; } else if (re_sct_control.PartialMatch(*iter, &name)) { p.set_name(name, "sct_control_support"); p.value_type = StorageProperty::value_type_bool; p.value_bool = true; } else if (re_sct_data.PartialMatch(*iter, &name)) { p.set_name(name, "sct_data_support"); p.value_type = StorageProperty::value_type_bool; p.value_bool = true; } if (!p.empty()) add_property(p); } return true; } debug_out_error("app", DBG_FUNC_MSG << "Capability property has invalid type \"" << StorageProperty::get_value_type_name(cap.value_type) << "\".\n"); return false; } // -------------------- Attributes bool SmartctlParser::parse_section_data_subsection_attributes(const std::string& sub) { StorageProperty pt; // template for easy copying pt.section = StorageProperty::section_data; pt.subsection = StorageProperty::subsection_attributes; // split to lines std::vector lines; hz::string_split(sub, '\n', lines, true); // Format notes: // * Before 5.1-14, no UPDATED column was present in "old" format. // * Most, but not all attribute names are with underscores. However, I encountered one // named "Head flying hours" and there are slashes sometimes as well. // So, parse until we encounter the next column. Supported in Old format only. // * SSD drives may show "---" in value/worst/threshold fields. // "old" format (used in -a): /* SMART Attributes Data Structure revision number: 4 Vendor Specific SMART Attributes with Thresholds: ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE 5 Reallocated_Sector_Ct 0x0032 100 100 --- Old_age Always - 0 9 Power_On_Hours 0x0032 253 100 --- Old_age Always - 1720 */ // "brief" format (used in -x): /* SMART Attributes Data Structure revision number: 16 Vendor Specific SMART Attributes with Thresholds: ID# ATTRIBUTE_NAME FLAGS VALUE WORST THRESH FAIL RAW_VALUE 1 Raw_Read_Error_Rate PO-R-- 100 100 062 - 0 2 Throughput_Performance P-S--- 197 197 040 - 160 194 Temperature_Celsius -O---- 222 222 000 - 27 (Min/Max 12/48) ||||||_ K auto-keep |||||__ C event count ||||___ R error rate |||____ S speed/performance ||_____ O updated online |______ P prefailure warning */ enum { FormatStyleOld, FormatStyleNoUpdated, // old format without UPDATED column FormatStyleBrief }; bool attr_found = false; // at least one attribute was found int attr_format_style = FormatStyleOld; std::string space_re = "[ \\t]+"; std::string old_flag_re = "(0x[a-fA-F0-9]+)"; std::string brief_flag_re = "([A-Z+-]{2,})"; // We allow name with spaces only in the old format, not in brief. // This has to do with the name end detection - it's either 0x (flag's start) in the old format, // or a space in the brief format. std::string old_base_re = "[ \\t]*([0-9]+) ([^ \\t\\n]+(?:[^0-9\\t\\n]+)*)" + space_re + old_flag_re + space_re; // ID / name / flag std::string brief_base_re = "[ \\t]*([0-9]+) ([^ \\t\\n]+)" + space_re + brief_flag_re + space_re; // ID / name / flag std::string vals_re = "([0-9-]+)" + space_re + "([0-9-]+)" + space_re + "([0-9-]+)" + space_re; // value / worst / threshold std::string type_re = "([^ \\t\\n]+)" + space_re; std::string updated_re = "([^ \\t\\n]+)" + space_re; std::string failed_re = "([^ \\t\\n]+)" + space_re; std::string raw_re = "(.+)[ \\t]*"; pcrecpp::RE re_old_up = app_pcre_re("/" + old_base_re + vals_re + type_re + updated_re + failed_re + raw_re + "/mi"); pcrecpp::RE re_old_noup = app_pcre_re("/" + old_base_re + vals_re + type_re + failed_re + raw_re + "/mi"); pcrecpp::RE re_brief = app_pcre_re("/" + brief_base_re + vals_re + failed_re + raw_re + "/mi"); pcrecpp::RE re_flag_descr = app_pcre_re("/^[\\t ]+\\|/mi"); for(std::size_t i = 0; i < lines.size(); ++i) { const std::string& line = lines[i]; // skip the non-informative lines if (line.empty() || app_pcre_match("/SMART Attributes with Thresholds/mi", line)) continue; if (app_pcre_match("/ATTRIBUTE_NAME/mi", line)) { // detect format type if (!app_pcre_match("/WHEN_FAILED/mi", line)) { attr_format_style = FormatStyleBrief; } else if (!app_pcre_match("/UPDATED/mi", line)) { attr_format_style = FormatStyleNoUpdated; } continue; // we don't need this line } if (re_flag_descr.PartialMatch(line)) { continue; // skip flag description lines } if (app_pcre_match("/Data Structure revision number/mi", line)) { pcrecpp::RE re = app_pcre_re("/^([^:\\n]+):[ \\t]*(.*)$/mi"); std::string name, value; if (re.PartialMatch(line, &name, &value)) { hz::string_trim(name); hz::string_trim(value); int64_t value_num = 0; hz::string_is_numeric(value, value_num, false); StorageProperty p(pt); p.set_name(name, "data_structure_version"); p.reported_value = value; p.value_type = StorageProperty::value_type_integer; p.value_integer = value_num; add_property(p); attr_found = true; } } else { // A line in attribute table std::string id, name, flag, value, worst, threshold, attr_type, update_type, when_failed, raw_value; bool matched = true; if (attr_format_style == FormatStyleOld) { if (!re_old_up.FullMatch(line, &id, &name, &flag, &value, &worst, &threshold, &attr_type, &update_type, &when_failed, &raw_value)) { matched = false; debug_out_warn("app", DBG_FUNC_MSG << "Cannot parse attribute line.\n"); } } else if (attr_format_style == FormatStyleNoUpdated) { if (!re_old_noup.FullMatch(line, &id, &name, &flag, &value, &worst, &threshold, &attr_type, &when_failed, &raw_value)) { matched = false; debug_out_warn("app", DBG_FUNC_MSG << "Cannot parse attribute line.\n"); } } else if (attr_format_style == FormatStyleBrief) { if (!re_brief.FullMatch(line, &id, &name, &flag, &value, &worst, &threshold, &when_failed, &raw_value)) { matched = false; debug_out_warn("app", DBG_FUNC_MSG << "Cannot parse attribute line.\n"); } } if (!matched) { debug_out_dump("app", "------------ Begin unparsable attribute line dump ------------\n"); debug_out_dump("app", line << "\n"); debug_out_dump("app", "------------- End unparsable attribute line dump -------------\n"); continue; // continue to the next line } StorageAttribute a; hz::string_is_numeric(hz::string_trim_copy(id), a.id, true, 10); a.flag = hz::string_trim_copy(flag); uint8_t norm_value = 0, worst_value = 0, threshold_value = 0; if (hz::string_is_numeric(hz::string_trim_copy(value), norm_value, true, 10)) { a.value = norm_value; } if (hz::string_is_numeric(hz::string_trim_copy(worst), worst_value, true, 10)) { a.worst = worst_value; } if (hz::string_is_numeric(hz::string_trim_copy(threshold), threshold_value, true, 10)) { a.threshold = threshold_value; } if (attr_format_style == FormatStyleBrief) { a.attr_type = app_pcre_match("/P/", a.flag) ? StorageAttribute::attr_type_prefail : StorageAttribute::attr_type_oldage; } else { if (attr_type == "Pre-fail") { a.attr_type = StorageAttribute::attr_type_prefail; } else if (attr_type == "Old_age") { a.attr_type = StorageAttribute::attr_type_oldage; } else { a.attr_type = StorageAttribute::attr_type_unknown; } } if (attr_format_style == FormatStyleBrief) { a.update_type = app_pcre_match("/O/", a.flag) ? StorageAttribute::update_type_always : StorageAttribute::update_type_offline; } else { if (update_type == "Always") { a.update_type = StorageAttribute::update_type_always; } else if (update_type == "Offline") { a.update_type = StorageAttribute::update_type_offline; } else { a.update_type = StorageAttribute::update_type_unknown; } } a.when_failed = StorageAttribute::fail_time_unknown; hz::string_trim(when_failed); if (when_failed == "-") { a.when_failed = StorageAttribute::fail_time_none; } else if (when_failed == "In_the_past" || when_failed == "Past") { // the second one if from brief format a.when_failed = StorageAttribute::fail_time_past; } else if (when_failed == "FAILING_NOW" || when_failed == "NOW") { // the second one if from brief format a.when_failed = StorageAttribute::fail_time_now; } a.raw_value = hz::string_trim_copy(raw_value); hz::string_is_numeric(hz::string_trim_copy(raw_value), a.raw_value_int, false); // same as raw_value, but parsed as int. StorageProperty p(pt); p.set_name(hz::string_trim_copy(name)); p.reported_value = line; // use the whole line here p.value_type = StorageProperty::value_type_attribute; p.value_attribute = a; add_property(p); attr_found = true; } } if (!attr_found) set_error_msg("No attributes found in Attributes section."); return attr_found; } bool SmartctlParser::parse_section_data_subsection_directory_log(const std::string& sub) { StorageProperty pt; // template for easy copying pt.section = StorageProperty::section_data; pt.subsection = StorageProperty::subsection_directory_log; // Directory log contains: /* General Purpose Log Directory Version 1 SMART Log Directory Version 1 [multi-sector log support] Address Access R/W Size Description 0x00 GPL,SL R/O 1 Log Directory 0x01 SL R/O 1 Summary SMART error log 0x02 SL R/O 5 Comprehensive SMART error log 0x03 GPL R/O 6 Ext. Comprehensive SMART error log 0x04 GPL,SL R/O 8 Device Statistics log 0x06 SL R/O 1 SMART self-test log 0x07 GPL R/O 1 Extended self-test log 0x09 SL R/W 1 Selective self-test log 0x0a GPL R/W 8 Device Statistics Notification */ bool data_found = false; // true if something was found. // the whole subsection { StorageProperty p(pt); p.set_name("General Purpose Log Directory", "directory_log"); p.reported_value = sub; p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; add_property(p); data_found = true; } // supported / unsupported { StorageProperty p(pt); p.set_name("General Purpose Log Directory supported", "directory_log_supported"); // p.reported_value; // nothing p.value_type = StorageProperty::value_type_bool; p.value_bool = !app_pcre_match("/General Purpose Log Directory not supported/mi", sub); add_property(p); data_found = true; } return data_found; } bool SmartctlParser::parse_section_data_subsection_error_log(const std::string& sub) { StorageProperty pt; // template for easy copying pt.section = StorageProperty::section_data; pt.subsection = StorageProperty::subsection_error_log; // Note: The format of this section was changed somewhere between 5.0-x and 5.30. // The old format is doesn't really give any useful info, and whatever's left is somewhat // parsable by this parser. Can't really improve that. // Also, type (e.g. UNC) is not always present (depends on the drive I guess). // Sample "-l xerror" output: /* SMART Extended Comprehensive Error Log Version: 1 (1 sectors) Device Error Count: 1 CR = Command Register FEATR = Features Register COUNT = Count (was: Sector Count) Register LBA_48 = Upper bytes of LBA High/Mid/Low Registers ] ATA-8 LH = LBA High (was: Cylinder High) Register ] LBA LM = LBA Mid (was: Cylinder Low) Register ] Register LL = LBA Low (was: Sector Number) Register ] DV = Device (was: Device/Head) Register DC = Device Control Register ER = Error register ST = Status register Powered_Up_Time is measured from power on, and printed as DDd+hh:mm:SS.sss where DD=days, hh=hours, mm=minutes, SS=sec, and sss=millisec. It "wraps" after 49.710 days. Error 1 [0] occurred at disk power-on lifetime: 1 hours (0 days + 1 hours) When the command that caused the error occurred, the device was active or idle. After command completion occurred, registers were: ER -- ST COUNT LBA_48 LH LM LL DV DC -- -- -- == -- == == == -- -- -- -- -- 02 -- 51 00 00 00 00 00 00 00 00 00 00 Error: TK0NF Commands leading to the command that caused the error were: CR FEATR COUNT LBA_48 LH LM LL DV DC Powered_Up_Time Command/Feature_Name -- == -- == -- == == == -- -- -- -- -- --------------- -------------------- 10 00 00 00 01 00 00 00 00 03 34 e0 ff 00:00:17.305 RECALIBRATE [OBS-4] 10 00 00 00 01 00 00 00 00 03 34 e0 08 00:00:17.138 RECALIBRATE [OBS-4] 91 40 00 01 3f 00 00 01 00 03 34 af 08 00:00:17.138 INITIALIZE DEVICE PARAMETERS [OBS-6] c4 00 40 00 00 00 00 3f 00 00 00 e0 04 00:00:16.934 READ MULTIPLE c4 00 40 00 01 00 00 3f 00 00 00 e0 00 00:00:07.959 READ MULTIPLE */ bool data_found = false; // Error log version { // "SMART Error Log Version: 1" // "SMART Extended Comprehensive Error Log Version: 1 (1 sectors)" pcrecpp::RE re = app_pcre_re("/^(SMART (Extended Comprehensive )?Error Log Version): ([0-9]+).*?$/mi"); std::string name, value; if (re.PartialMatch(sub, &name, &value)) { hz::string_trim(name); hz::string_trim(value); StorageProperty p(pt); p.set_name(name, "error_log_version"); p.reported_value = value; p.value_type = StorageProperty::value_type_integer; int64_t value_num = 0; hz::string_is_numeric(value, value_num, false); p.value_integer = value_num; add_property(p); data_found = true; } } // Error log support { pcrecpp::RE re = app_pcre_re("/^(Warning: device does not support Error Logging)|(SMART Error Log not supported)$/mi"); if (re.PartialMatch(sub)) { StorageProperty p(pt); p.set_name("error_log_unsupported"); p.readable_name = "Warning"; p.readable_value = "Device does not support error logging"; add_property(p); } } // Error log entry count { // note: these represent the same information pcrecpp::RE re1 = app_pcre_re("/^(?:ATA|Device) Error Count:[ \\t]*([0-9]+)/mi"); pcrecpp::RE re2 = app_pcre_re("/^No Errors Logged$/mi"); std::string value; if (re1.PartialMatch(sub, &value) || re2.PartialMatch(sub)) { hz::string_trim(value); StorageProperty p(pt); p.set_name("ATA Error Count", "error_log_error_count"); p.reported_value = value; p.value_type = StorageProperty::value_type_integer; int64_t value_num = 0; if (!re2.PartialMatch(sub)) { // if no errors, when value should be zero. otherwise, this: hz::string_is_numeric(value, value_num, false); } p.value_integer = value_num; add_property(p); data_found = true; } } // individual errors { // split by blocks // "Error 1 [0] occurred at disk power-on lifetime: 1 hours (0 days + 1 hours)" // "Error 25 occurred at disk power-on lifetime: 14799 hours" pcrecpp::RE re_block = app_pcre_re("/^((Error[ \\t]*([0-9]+))[ \\t]*(?:\\[[0-9]+\\][ \\t])?occurred at disk power-on lifetime:[ \\t]*([0-9]+) hours(?:[^\\n]*)?.*(?:\\n(?: |\\n ).*)*)/mi"); // " When the command that caused the error occurred, the device was active or idle." // Note: For "in an unknown state" - remove first two words. pcrecpp::RE re_state = app_pcre_re("/occurred, the device was[ \\t]*(?: in)?(?: an?)?[ \\t]+([^.\\n]*)\\.?/mi"); // " 84 51 2c 71 cd 3f e6 Error: ICRC, ABRT 44 sectors at LBA = 0x063fcd71 = 104844657" // " 40 51 00 f5 41 61 e0 Error: UNC at LBA = 0x006141f5 = 6373877" // " 02 -- 51 00 00 00 00 00 00 00 00 00 00 Error: TK0NF" pcrecpp::RE re_type = app_pcre_re("/[ \\t]+Error:[ \\t]*([ ,a-z0-9]+)(?:[ \\t]+((?:[0-9]+|at )[ \\t]*.*))?$/mi"); std::string block, name, value_num, value_time; pcrecpp::StringPiece input(sub); // position tracker while (re_block.FindAndConsume(&input, &block, &name, &value_num, &value_time)) { hz::string_trim(block); hz::string_trim(value_num); hz::string_trim(value_time); // debug_out_dump("app", "\nBLOCK -------------------------------\n" << block); std::string state, etypes_str, emore; re_state.PartialMatch(block, &state); re_type.PartialMatch(block, &etypes_str, &emore); StorageProperty p(pt); p.set_name(hz::string_trim_copy(name)); // "Error 6" p.reported_value = block; p.value_type = StorageProperty::value_type_error_block; hz::string_is_numeric(value_num, p.value_error_block.error_num, false); hz::string_is_numeric(value_time, p.value_error_block.lifetime_hours, false); std::vector etypes; hz::string_split(etypes_str, ",", etypes, true); for (std::vector::iterator iter = etypes.begin(); iter != etypes.end(); ++iter) { hz::string_trim(*iter); } p.value_error_block.device_state = hz::string_trim_copy(state); p.value_error_block.reported_types = etypes; p.value_error_block.type_more_info = hz::string_trim_copy(emore); add_property(p); data_found = true; } } // the whole subsection { StorageProperty p(pt); p.set_name("SMART Error Log", "error_log"); p.reported_value = sub; p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; add_property(p); data_found = true; } // We may further split this subsection by Error blocks, but it's unnecessary - // the data is too advanced to be of any use if parsed. return data_found; } // -------------------- Selftest Log bool SmartctlParser::parse_section_data_subsection_selftest_log(const std::string& sub) { StorageProperty pt; // template for easy copying pt.section = StorageProperty::section_data; pt.subsection = StorageProperty::subsection_selftest_log; // Self-test log contains: // * structure revision number // * a list of current / previous tests performed, with each having: // num (the higher - the older). // test_description (Extended offline / Short offline / Conveyance offline / ... ?) // status (completed without error, interrupted (reason), aborted, fatal or unknown error, ?) // remaining % (this will be 00% for completed, and may be > 0 for interrupted). // lifetime (hours) - int. // LBA_of_first_error - "-" or int ? /* SMART Extended Self-test Log Version: 1 (1 sectors) Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error # 1 Extended offline Completed without error 00% 43116 - # 2 Extended offline Completed without error 00% 29867 - # 3 Extended offline Completed without error 00% 19477 - */ bool data_found = false; // true if something was found. // The whole subsection { StorageProperty p(pt); p.set_name("SMART Self-Test Log", "selftest_log"); p.reported_value = sub; p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; add_property(p); data_found = true; } // Self-test log support { pcrecpp::RE re = app_pcre_re("/^(Warning: device does not support Self Test Logging)|(SMART Self-test Log not supported)$/mi"); if (re.PartialMatch(sub)) { StorageProperty p(pt); p.set_name("selftest_log_unsupported"); p.readable_name = "Warning"; p.readable_value = "Device does not support self-test logging"; add_property(p); } } // Self-test log version { // SMART Self-test log structure revision number 1 // SMART Extended Self-test Log Version: 1 (1 sectors) pcrecpp::RE re1 = app_pcre_re("/(SMART Self-test log structure[^\\n0-9]*)([^ \\n]+)[ \\t]*$/mi"); pcrecpp::RE re1_ex = app_pcre_re("/(SMART Extended Self-test Log Version: ([0-9]+).*$/mi"); // older smartctl (pre 5.1-16) pcrecpp::RE re2 = app_pcre_re("/(SMART Self-test log, version number[^\\n0-9]*)([^ \\n]+)[ \\t]*$/mi"); std::string name, value; if (re1.PartialMatch(sub, &name, &value) || re1_ex.PartialMatch(sub, &name, &value) || re2.PartialMatch(sub, &name, &value)) { hz::string_trim(value); StorageProperty p(pt); p.set_name(hz::string_trim_copy(name), "selftest_log_version"); p.reported_value = value; p.value_type = StorageProperty::value_type_integer; int64_t value_num = 0; hz::string_is_numeric(value, value_num, false); p.value_integer = value_num; add_property(p); data_found = true; } } int64_t test_count = 0; // type is of p.value_integer // individual entries { // split by columns. // num, type, status, remaining, hours, lba (optional). pcrecpp::RE re = app_pcre_re("/^(#[ \\t]*([0-9]+)[ \\t]+(\\S+(?: \\S+)*) [ \\t]*(\\S.*) [ \\t]*([0-9]+%) [ \\t]*([0-9]+)[ \\t]*((?: [ \\t]*\\S.*)?))$/mi"); std::string line, num, type, status_str, remaining, hours, lba; pcrecpp::StringPiece input(sub); // position tracker while (re.FindAndConsume(&input, &line, &num, &type, &status_str, &remaining, &hours, &lba)) { hz::string_trim(num); StorageProperty p(pt); p.set_name("Self-test entry " + num); p.reported_value = hz::string_trim_copy(line); p.value_type = StorageProperty::value_type_selftest_entry; hz::string_is_numeric(num, p.value_selftest_entry.test_num, false); hz::string_is_numeric(hz::string_trim_copy(remaining), p.value_selftest_entry.remaining_percent, false); hz::string_is_numeric(hz::string_trim_copy(hours), p.value_selftest_entry.lifetime_hours, false); p.value_selftest_entry.type = hz::string_trim_copy(type); p.value_selftest_entry.lba_of_first_error = hz::string_trim_copy(lba); // old smartctls didn't print anything for lba if none, but newer ones print "-". normalize. if (p.value_selftest_entry.lba_of_first_error == "") p.value_selftest_entry.lba_of_first_error = "-"; hz::string_trim(status_str); StorageSelftestEntry::status_t status = StorageSelftestEntry::status_unknown; // don't match end - some of them are not complete here if (app_pcre_match("/^Completed without error/mi", status_str)) { status = StorageSelftestEntry::status_completed_no_error; } else if (app_pcre_match("/^Aborted by host/mi", status_str)) { status = StorageSelftestEntry::status_aborted_by_host; } else if (app_pcre_match("/^Interrupted \\(host reset\\)/mi", status_str)) { status = StorageSelftestEntry::status_interrupted; } else if (app_pcre_match("/^Fatal or unknown error/mi", status_str)) { status = StorageSelftestEntry::status_fatal_or_unknown; } else if (app_pcre_match("/^Completed: unknown failure/mi", status_str)) { status = StorageSelftestEntry::status_compl_unknown_failure; } else if (app_pcre_match("/^Completed: electrical failure/mi", status_str)) { status = StorageSelftestEntry::status_compl_electrical_failure; } else if (app_pcre_match("/^Completed: servo\\/seek failure/mi", status_str)) { status = StorageSelftestEntry::status_compl_servo_failure; } else if (app_pcre_match("/^Completed: read failure/mi", status_str)) { status = StorageSelftestEntry::status_compl_read_failure; } else if (app_pcre_match("/^Completed: handling damage/mi", status_str)) { status = StorageSelftestEntry::status_compl_handling_damage; } else if (app_pcre_match("/^Self-test routine in progress/mi", status_str)) { status = StorageSelftestEntry::status_in_progress; } else if (app_pcre_match("/^Unknown\\/reserved test status/mi", status_str)) { status = StorageSelftestEntry::status_reserved; } p.value_selftest_entry.status_str = status_str; p.value_selftest_entry.status = status; add_property(p); data_found = true; ++test_count; } } // number of tests. // Note: "No self-tests have been logged" is sometimes absent, so don't rely on it. { StorageProperty p(pt); p.set_name("Number of entries in self-test log", "selftest_num_entries"); // p.reported_value; // nothing p.value_type = StorageProperty::value_type_integer; p.value_integer = test_count; add_property(p); data_found = true; } return data_found; } // -------------------- Selective Selftest Log bool SmartctlParser::parse_section_data_subsection_selective_selftest_log(const std::string& sub) { StorageProperty pt; // template for easy copying pt.section = StorageProperty::section_data; pt.subsection = StorageProperty::subsection_selective_selftest_log; // Selective self-test log contains: /* SMART Selective self-test log data structure revision number 1 SPAN MIN_LBA MAX_LBA CURRENT_TEST_STATUS 1 0 0 Not_testing 2 0 0 Not_testing 3 0 0 Not_testing 4 0 0 Not_testing 5 0 0 Not_testing Selective self-test flags (0x0): After scanning selected spans, do NOT read-scan remainder of disk. If Selective self-test is pending on power-up, resume after 0 minute delay. */ bool data_found = false; // true if something was found. // the whole subsection { StorageProperty p(pt); p.set_name("SMART Selective self-test log", "selective_selftest_log"); p.reported_value = sub; p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; add_property(p); data_found = true; } // supported / unsupported { StorageProperty p(pt); p.set_name("Selective self-tests supported", "selective_selftest_supported"); // p.reported_value; // nothing p.value_type = StorageProperty::value_type_bool; p.value_bool = !app_pcre_match("/Device does not support Selective Self Tests\\/Logging/mi", sub); add_property(p); data_found = true; } return data_found; } bool SmartctlParser::parse_section_data_subsection_scttemp_log(const std::string& sub) { StorageProperty pt; // template for easy copying pt.section = StorageProperty::section_data; pt.subsection = StorageProperty::subsection_temperature_log; // scttemp log contains: /* SCT Status Version: 3 SCT Version (vendor specific): 258 (0x0102) SCT Support Level: 1 Device State: Active (0) Current Temperature: 39 Celsius Power Cycle Min/Max Temperature: 25/39 Celsius Lifetime Min/Max Temperature: 17/50 Celsius Under/Over Temperature Limit Count: 0/0 Vendor specific: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 SCT Temperature History Version: 2 Temperature Sampling Period: 1 minute Temperature Logging Interval: 1 minute Min/Max recommended Temperature: 0/60 Celsius Min/Max Temperature Limit: -41/85 Celsius Temperature History Size (Index): 478 (361) Index Estimated Time Temperature Celsius 362 2017-08-29 08:43 38 ******************* ... ..(119 skipped). .. ******************* 4 2017-08-29 10:43 38 ******************* 5 2017-08-29 10:44 39 ******************** ... ..( 91 skipped). .. ******************** 97 2017-08-29 12:16 39 ******************** 98 2017-08-29 12:17 ? - 99 2017-08-29 12:18 25 ****** */ bool data_found = false; // true if something was found. // the whole subsection { StorageProperty p(pt); p.set_name("SCT temperature log", "scttemp_log"); p.reported_value = sub; p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; add_property(p); data_found = true; } // supported / unsupported { StorageProperty p(pt); p.set_name("SCT commands unsupported", "sct_unsupported"); // p.reported_value; // nothing p.value_type = StorageProperty::value_type_bool; p.value_bool = app_pcre_match("/(SCT Commands not supported)|(SCT Data Table command not supported)/mi", sub); add_property(p); data_found = true; } // Find current temperature { std::string name, value; if (app_pcre_match("/^(Current Temperature):[ \\t]+(.*) Celsius$/mi", sub, &name, &value)) { StorageProperty p; p.section = StorageProperty::section_data; p.subsection = StorageProperty::subsection_temperature_log; p.set_name("Current Temperature", "sct_temperature_celsius"); p.value_type = StorageProperty::value_type_integer; p.reported_value = value; hz::string_is_numeric(value, p.value_integer); add_property(p); } } return data_found; } bool SmartctlParser::parse_section_data_subsection_scterc_log(const std::string& sub) { StorageProperty pt; // template for easy copying pt.section = StorageProperty::section_data; pt.subsection = StorageProperty::subsection_erc_log; // scterc log contains: /* SCT Error Recovery Control: Read: 70 (7.0 seconds) Write: 70 (7.0 seconds) */ bool data_found = false; // true if something was found. // the whole subsection { StorageProperty p(pt); p.set_name("SCT ERC log", "scterc_log"); p.reported_value = sub; p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; add_property(p); data_found = true; } // supported / unsupported { StorageProperty p(pt); p.set_name("SCT ERC supported", "sct_erc_supported"); // p.reported_value; // nothing p.value_type = StorageProperty::value_type_bool; p.value_bool = !app_pcre_match("/SCT Error Recovery Control command not supported/mi", sub); add_property(p); data_found = true; } return data_found; } bool SmartctlParser::parse_section_data_subsection_devstat(const std::string& sub) { StorageProperty pt; // template for easy copying pt.section = StorageProperty::section_data; pt.subsection = StorageProperty::subsection_devstat; // devstat log contains: /* Device Statistics (GP Log 0x04) Page Offset Size Value Flags Description 0x01 ===== = = === == General Statistics (rev 1) == 0x01 0x008 4 569 -D- Lifetime Power-On Resets 0x01 0x010 4 6360 -D- Power-on Hours 0x01 0x018 6 17887792526 -D- Logical Sectors Written 0x01 0x020 6 51609191 -D- Number of Write Commands 0x01 0x028 6 17634698564 -D- Logical Sectors Read 0x01 0x030 6 179799274 -D- Number of Read Commands 0x01 0x038 6 1421163520 -D- Date and Time TimeStamp 0x01 0x048 2 202 ND- Workload Utilization 0x03 ===== = = === == Rotating Media Statistics (rev 1) == 0x03 0x008 4 6356 -D- Spindle Motor Power-on Hours 0x03 0x010 4 6356 -D- Head Flying Hours |||_ C monitored condition met ||__ D supports DSN |___ N normalized value */ // Old (6.3) format: /* Page Offset Size Value Description 1 ===== = = == General Statistics (rev 2) == 1 0x008 4 2 Lifetime Power-On Resets 1 0x018 6 1480289770 Logical Sectors Written 1 0x020 6 28939977 Number of Write Commands 1 0x028 6 3331436 Logical Sectors Read 1 0x030 6 122181 Number of Read Commands 1 0x038 6 12715200000 Date and Time TimeStamp 7 ===== = = == Solid State Device Statistics (rev 1) == 7 0x008 1 1~ Percentage Used Endurance Indicator |_ ~ normalized value */ enum { FormatStyleNoFlags, // 6.3 and older FormatStyleCurrent, // 6.5 }; // supported / unsupported bool supported = true; { StorageProperty p(pt); p.set_name("Device statistics supported", "devstat_supported"); // p.reported_value; // nothing p.value_type = StorageProperty::value_type_bool; p.value_bool = !app_pcre_match("/Device Statistics \\(GP\\/SMART Log 0x04\\) not supported/mi", sub); supported = p.value_bool; add_property(p); } if (!supported) { return false; } bool entries_found = false; // at least one entry was found // split to lines std::vector lines; hz::string_split(sub, '\n', lines, true); std::string space_re = "[ \\t]+"; std::string flag_re = "([A-Z=-]{3,})"; // Page Offset Size Value Flags Description pcrecpp::RE line_re = app_pcre_re("/[ \\t]*([0-9a-z]+)" + space_re + "([0-9a-z=]+)" + space_re + "([0-9=]+)" + space_re + "([0-9=-]+)" + space_re + flag_re + space_re + "(.+)/mi"); // Page Offset Size Value Description pcrecpp::RE line_re_noflags = app_pcre_re("/[ \\t]*([0-9a-z]+)" + space_re + "([0-9a-z=]+)" + space_re + "([0-9=]+)" + space_re + "([0-9=~-]+)" + space_re + "(.+)/mi"); // flag description lines pcrecpp::RE re_flag_descr = app_pcre_re("/^[\\t ]+\\|/mi"); int devstat_format_style = FormatStyleCurrent; for(unsigned int i = 0; i < lines.size(); ++i) { const std::string& line = lines[i]; // skip the non-informative lines // "Device Statistics (GP Log 0x04)" // "Device Statistics (SMART Log 0x04)" // "ATA_SMART_READ_LOG failed: Undefined error: 0" // "Read Device Statistics page 0x00 failed" // "Read Device Statistics pages 0x00-0x07 failed" if (line.empty() || app_pcre_match("/^Device Statistics \\((?:GP|SMART) Log 0x04\\)/mi", line) || app_pcre_match("/^ATA_SMART_READ_LOG failed:/mi", line) || app_pcre_match("/^Read Device Statistics page (?:.+) failed/mi", line) || app_pcre_match("/^Read Device Statistics pages (?:.+) failed/mi", line) ) { continue; } // Table header if (app_pcre_match("/^Page[\\t ]+Offset[\\t ]+Size/mi", line)) { // detect format type if (!app_pcre_match("/[\\t ]+Flags[\\t ]+/mi", line)) { devstat_format_style = FormatStyleNoFlags; } continue; // we don't need this line } if (re_flag_descr.PartialMatch(line)) { // " |||_ C monitored condition met", etc... continue; // skip flag description lines } std::string page, offset, size, value, flags, description; bool matched = false; if (devstat_format_style == FormatStyleCurrent) { if (line_re.FullMatch(line, &page, &offset, &size, &value, &flags, &description)) { matched = true; } } else if (devstat_format_style == FormatStyleNoFlags) { if (line_re_noflags.FullMatch(line, &page, &offset, &size, &value, &description)) { matched = true; flags = "---"; // to keep consistent with the Current format if (!value.empty() && value[value.size() - 1] == '~') { // normalized flags = "N--"; value.resize(value.size() - 1); } } } if (!matched) { debug_out_warn("app", DBG_FUNC_MSG << "Cannot parse devstat line.\n"); debug_out_dump("app", "------------ Begin unparsable devstat line dump ------------\n"); debug_out_dump("app", line << "\n"); debug_out_dump("app", "------------- End unparsable devstat line dump -------------\n"); continue; // continue to the next line } StorageStatistic st; st.is_header = (hz::string_trim_copy(value) == "="); st.flags = st.is_header ? std::string() : hz::string_trim_copy(flags); st.value = st.is_header ? std::string() : hz::string_trim_copy(value); hz::string_is_numeric(st.value, st.value_int, false); hz::string_is_numeric(page, st.page, false, 16); hz::string_is_numeric(offset, st.offset, false, 16); if (st.is_header) { description = hz::string_trim_copy(hz::string_trim_copy(description, "=")); } StorageProperty p(pt); p.set_name(hz::string_trim_copy(description)); p.reported_value = line; // use the whole line here p.value_type = StorageProperty::value_type_statistic; p.value_statistic = st; add_property(p); entries_found = true; } if (!entries_found) set_error_msg("No entries found in Statistics section."); return entries_found; } bool SmartctlParser::parse_section_data_subsection_sataphy(const std::string& sub) { StorageProperty pt; // template for easy copying pt.section = StorageProperty::section_data; pt.subsection = StorageProperty::subsection_phy_log; // sataphy log contains: /* SATA Phy Event Counters (GP Log 0x11) ID Size Value Description 0x0001 2 0 Command failed due to ICRC error 0x0002 2 0 R_ERR response for data FIS 0x0003 2 0 R_ERR response for device-to-host data FIS 0x0004 2 0 R_ERR response for host-to-device data FIS 0x0005 2 0 R_ERR response for non-data FIS 0x0006 2 0 R_ERR response for device-to-host non-data FIS 0x0007 2 0 R_ERR response for host-to-device non-data FIS 0x0008 2 0 Device-to-host non-data FIS retries 0x0009 2 1 Transition from drive PhyRdy to drive PhyNRdy */ bool data_found = false; // true if something was found. // the whole subsection { StorageProperty p(pt); p.set_name("SATA Phy log", "sataphy_log"); p.reported_value = sub; p.value_type = StorageProperty::value_type_string; p.value_string = p.reported_value; add_property(p); data_found = true; } // supported / unsupported { StorageProperty p(pt); p.set_name("SATA Phy log supported", "sataphy_supported"); // p.reported_value; // nothing p.value_type = StorageProperty::value_type_bool; p.value_bool = !app_pcre_match("/SATA Phy Event Counters \\(GP Log 0x11\\) not supported/mi", sub) && !app_pcre_match("/SATA Phy Event Counters with [0-9-]+ sectors not supported/mi", sub); add_property(p); data_found = true; } return data_found; } // adds a property into property list, looks up and sets its description. // Yes, there's no place for this in the Parser, but whatever... void SmartctlParser::add_property(StorageProperty p) { storage_property_autoset_description(p, disk_type_); storage_property_autoset_warning(p); storage_property_autoset_warning_descr(p); // append warning to description properties_.push_back(p); } /// @} gsmartcontrol-1.1.4/src/applib/smartctl_parser.h000066400000000000000000000116261417717230600220450ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef SMARTCTL_PARSER_H #define SMARTCTL_PARSER_H #include #include #include "storage_property.h" /// Smartctl parser. /// Note: ALL parse_* functions (except parse_full() and parse_version()) /// expect data in unix-newline format! class SmartctlParser { public: /// Property list typedef std::vector prop_list_t; /// Constructor SmartctlParser(); /// Parse full "smartctl -x" output bool parse_full(const std::string& s, StorageAttribute::DiskType disk_type); /// Supply any output of smartctl here, the smartctl version will be retrieved. static bool parse_version(const std::string& s, std::string& version, std::string& version_full); /// Check that the version of smartctl output can be parsed with this parser. static bool check_parsed_version(const std::string& version_str, const std::string& version_full_str); /// Convert e.g. "1,000,204,886,016 bytes" to 1.00 TiB [931.51 GB, 1000204886016 bytes] static std::string parse_byte_size(const std::string& str, uint64_t& bytes, bool extended); // You don't really need to call these functions, use the ones above. /// Parse the section part (with "=== .... ===" header) - info or data sections. bool parse_section(const std::string& header, const std::string& body); /// Parse the info section (without "===" header). /// This includes --info and --get=all. bool parse_section_info(const std::string& body); /// Parse a component (one line) of the info section bool parse_section_info_property(StorageProperty& p); /// Parse the Data section (without "===" header) bool parse_section_data(const std::string& body); /// Parse subsections of Data section bool parse_section_data_subsection_health(const std::string& sub); bool parse_section_data_subsection_capabilities(const std::string& sub); bool parse_section_data_subsection_attributes(const std::string& sub); bool parse_section_data_subsection_directory_log(const std::string& sub); bool parse_section_data_subsection_error_log(const std::string& sub); bool parse_section_data_subsection_selftest_log(const std::string& sub); bool parse_section_data_subsection_selective_selftest_log(const std::string& sub); bool parse_section_data_subsection_scttemp_log(const std::string& sub); bool parse_section_data_subsection_scterc_log(const std::string& sub); bool parse_section_data_subsection_devstat(const std::string& sub); bool parse_section_data_subsection_sataphy(const std::string& sub); /// Check the capabilities for internal properties we can use. bool parse_section_data_internal_capabilities(StorageProperty& cap); /// Clear parsed data void clear() { data_full_.clear(); data_section_info_.clear(); data_section_data_.clear(); error_msg_.clear(); properties_.clear(); } /// Get "full" data, as passed to parse_full(). std::string get_data_full() const { return data_full_; } /* std::string get_data_section_info() const { return data_section_info_; } std::string get_data_section_data() const { return data_section_data_; } */ /// Get parse error message. Call this only if parsing doesn't succeed, /// to get a friendly error message. std::string get_error_msg() const { return "Cannot parse smartctl output: " + error_msg_; } /// Get parse result properties const prop_list_t& get_properties() const { return properties_; } private: /// Add a property into property list, look up and set its description void add_property(StorageProperty p); /// Set "full" data ("smartctl -x" output) void set_data_full(const std::string& s) { data_full_ = s; } /// Set "info" section data ("smartctl -i" output, or the first part of "smartctl -x" output) void set_data_section_info(const std::string& s) { data_section_info_ = s; } /// Parse "data" section data (the second part of "smartctl -x" output). void set_data_section_data(const std::string& s) { data_section_data_ = s; } /// Set error message void set_error_msg(const std::string& s) { error_msg_ = s; } prop_list_t properties_; ///< Parsed data properties std::string data_full_; ///< full data, filled by parse_full() std::string data_section_info_; ///< "info" section data, filled by parse_section_info() std::string data_section_data_; ///< "data" section data, filled by parse_section_data() std::string error_msg_; ///< This will be filled with some displayable message on error StorageAttribute::DiskType disk_type_; ///< Disk type (HDD, SSD) }; #endif /// @} gsmartcontrol-1.1.4/src/applib/smartctl_parser_test.cpp000066400000000000000000000035041417717230600234330ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_unlicense.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib_tests /// \weakgroup applib_tests /// @{ #undef HZ_USE_LIBDEBUG #define HZ_USE_LIBDEBUG 0 // enable libdebug emulation through std::cerr #undef HZ_EMULATE_LIBDEBUG #define HZ_EMULATE_LIBDEBUG 1 #include #include #include "libdebug/libdebug.h" #include "hz/fs_file.h" #include "storage_property.h" #include "smartctl_parser.h" /// Main function of the test int main(int argc, char** argv) { if (argc < 2) { std::cout << "Usage: " << argv[0] << " \n"; return EXIT_FAILURE; } debug_register_domain("app"); std::string file_str = argv[1]; hz::File file(file_str); std::string contents; if (!file.get_contents(contents)) { debug_out_error("app", file.get_error_locale() << "\n"); return EXIT_FAILURE; } SmartctlParser sp; if (!sp.parse_full(contents, StorageAttribute::DiskAny)) { debug_out_error("app", "Cannot parse file contents: " << sp.get_error_msg() << "\n"); return EXIT_FAILURE; } const SmartctlParser::prop_list_t& props = sp.get_properties(); for(unsigned int i = 0; i < props.size(); ++i) { debug_out_dump("app", props[i] << "\n"); } return EXIT_SUCCESS; } /* #include "app_pcrecpp.h" int main() { pcrecpp::RE re("^ab\\/c?de$"); std::cerr << re.PartialMatch("ab/de") << "\n"; std::cerr << app_pcre_match("^abcd", "abcde") << "\n"; std::cerr << app_pcre_match("/^abcd/", "abcde") << "\n"; std::cerr << app_pcre_match("/^abcd/i", "Abcde") << "\n"; std::cerr << app_pcre_match("/^ab.*de$/mis", "abc\\nDe") << "\n"; } */ /// @} gsmartcontrol-1.1.4/src/applib/spawn_test.cpp000066400000000000000000000041521417717230600213560ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_unlicense.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib_tests /// \weakgroup applib_tests /// @{ #include #include /// Main function of the test int main(int argc, char** argv) { // g_thread_init(NULL); GPid pid; int fd_stdout = 0, fd_stderr = 0; std::string cmd = "iexplore"; // std::vector child_argv = Glib::shell_parse_argv(cmd); gchar* curr_dir = g_get_current_dir(); int argcp = 0; // number of args gchar** argvp = 0; // args vector GError* shell_error = 0; g_shell_parse_argv(cmd.c_str(), &argcp, &argvp, &shell_error); GError* spawn_error = 0; g_spawn_async_with_pipes(curr_dir, argvp, NULL, GSpawnFlags(G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD), NULL, NULL, // child setup function &pid, 0, &fd_stdout, &fd_stderr, &spawn_error); g_free(curr_dir); g_strfreev(argvp); #ifdef _WIN32 GIOChannel* channel_stdout = g_io_channel_win32_new_fd(fd_stdout); GIOChannel* channel_stderr = g_io_channel_win32_new_fd(fd_stderr); #else GIOChannel* channel_stdout = g_io_channel_unix_new(fd_stdout); GIOChannel* channel_stderr = g_io_channel_unix_new(fd_stderr); #endif // blocking writes if the pipe is full helps for small-pipe systems (see man 7 pipe). int channel_flags = ~G_IO_FLAG_NONBLOCK; if (channel_stdout) { g_io_channel_set_encoding(channel_stdout, NULL, 0); // binary IO g_io_channel_set_flags(channel_stdout, GIOFlags(g_io_channel_get_flags(channel_stdout) & channel_flags), 0); g_io_channel_set_buffer_size(channel_stdout, 10000); } if (channel_stderr) { g_io_channel_set_encoding(channel_stderr, NULL, 0); // binary IO g_io_channel_set_flags(channel_stderr, GIOFlags(g_io_channel_get_flags(channel_stderr) & channel_flags), 0); g_io_channel_set_buffer_size(channel_stderr, 10000); } g_main_loop_run(g_main_loop_new(NULL, false)); return 0; } /// @} gsmartcontrol-1.1.4/src/applib/storage_detector.cpp000066400000000000000000000116421417717230600225260ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #include #include "hz/hz_config.h" // CONFIG_* #include "hz/debug.h" #include "app_pcrecpp.h" #include "smartctl_executor.h" #include "storage_detector.h" #include "storage_detector_linux.h" #include "storage_detector_win32.h" #include "storage_detector_other.h" std::string StorageDetector::detect(std::vector& drives, ExecutorFactoryRefPtr ex_factory) { debug_out_info("app", DBG_FUNC_MSG << "Starting drive detection.\n"); std::vector all_detected; std::string error_msg; // Try each one and move to next if it fails. #if defined CONFIG_KERNEL_LINUX error_msg = detect_drives_linux(all_detected, ex_factory); // linux /proc/partitions as fallback. #elif defined CONFIG_KERNEL_FAMILY_WINDOWS error_msg = detect_drives_win32(all_detected, ex_factory); // win32 #else // freebsd, etc... error_msg = detect_drives_other(all_detected, ex_factory); // bsd, etc... . scans /dev. #endif if (all_detected.empty()) { debug_out_warn("app", DBG_FUNC_MSG << "Cannot detect drives: None of the drive detection methods returned any drives.\n"); return error_msg; // last error message should be ok. } for (std::vector::iterator iter = all_detected.begin(); iter != all_detected.end(); ++iter) { StorageDeviceRefPtr drive = *iter; // try to match against patterns // for (unsigned int i = 0; i < match_patterns_.size(); i++) { // try to match against general filter // if (!app_pcre_match(match_patterns_[i], dev)) // continue; // matched, check the blacklist bool blacked = false; for (unsigned int j = 0; j < blacklist_patterns_.size(); j++) { if (app_pcre_match(blacklist_patterns_[j], drive->get_device())) { // matched the blacklist too blacked = true; break; } } debug_out_info("app", "Found device: " << drive->get_device_with_type() << ".\n"); if (!blacked) { drives.push_back(drive); // break; // no need to match other patters, go to next device } else { // blacklisted debug_out_info("app", "Device " << drive->get_device_with_type() << " is blacklisted, ignoring.\n"); // break; // go to next device } // } } // Sort the drives, because their order is not quite defined. // TODO Sort using natural sort std::sort(drives.begin(), drives.end()); debug_out_info("app", DBG_FUNC_MSG << "Drive detection finished.\n"); return std::string(); } std::string StorageDetector::fetch_basic_data(std::vector& drives, ExecutorFactoryRefPtr ex_factory, bool return_first_error) { fetch_data_errors_.clear(); fetch_data_error_outputs_.clear(); hz::intrusive_ptr smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl); for (unsigned int i = 0; i < drives.size(); ++i) { StorageDeviceRefPtr drive = drives[i]; debug_out_info("app", "Retrieving basic information about the device...\n"); smartctl_ex->set_running_msg("Running %s on " + drive->get_device_with_type() + "..."); // don't show any errors here - we don't want a screen flood. // no need for gui-based executors here, we already show the message in // iconview background (if called from main window) std::string error_msg; if (drive->get_info_output().empty()) { // if not fetched during detection error_msg = drive->fetch_basic_data_and_parse(smartctl_ex); } // normally we skip drives with errors - possibly scsi, etc... if (return_first_error && !error_msg.empty()) return error_msg; if (!error_msg.empty()) { // use original executor error if present (permits matches by our users). // if (!smartctl_ex->get_error_msg().empty()) // error_msg = smartctl_ex->get_error_msg(); fetch_data_errors_.push_back(error_msg); fetch_data_error_outputs_.push_back(smartctl_ex->get_stdout_str()); } debug_out_dump("app", "Device information for " << drive->get_device() << " (type: \"" << drive->get_type_argument() << "\"):\n" << "\tModel: " << drive->get_model_name() << "\n" << "\tDetected type: " << StorageDevice::get_type_readable_name(drive->get_detected_type()) << "\n" << "\tSMART status: " << StorageDevice::get_status_name(drive->get_smart_status()) << "\n" ); } return std::string(); } std::string StorageDetector::detect_and_fetch_basic_data(std::vector& put_drives_here, ExecutorFactoryRefPtr ex_factory) { std::string error_msg = detect(put_drives_here, ex_factory); if (error_msg.empty()) fetch_basic_data(put_drives_here, ex_factory, false); // ignore its errors, there may be plenty of them. return error_msg; } /// @} gsmartcontrol-1.1.4/src/applib/storage_detector.h000066400000000000000000000053021417717230600221670ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef STORAGE_DETECTOR_H #define STORAGE_DETECTOR_H #include #include #include "hz/intrusive_ptr.h" #include "storage_device.h" #include "cmdex_sync.h" #include "executor_factory.h" /// Storage detector - detects available drives in the system. class StorageDetector { public: /// Constructor StorageDetector() { // match_patterns_.push_back("/.+/"); // accept anything, the scanner picks up disks only anyway. } /// Detects a list of drives. Returns detection error message if error occurs. std::string detect(std::vector& put_drives_here, ExecutorFactoryRefPtr ex_factory); /// For each drive, fetch basic data and parse it. /// If \c return_first_error is true, the function returns on the first error. /// \return An empty string. Or, if return_first_error is true, the first error that occurs. std::string fetch_basic_data(std::vector& drives, ExecutorFactoryRefPtr ex_factory, bool return_first_error = false); /// Run detect() and fetch_basic_data(). /// \return An error if such occurs. std::string detect_and_fetch_basic_data(std::vector& put_drives_here, ExecutorFactoryRefPtr ex_factory); // void add_match_patterns(std::vector& patterns) // { // match_patterns_.insert(match_patterns_.end(), patterns.begin(), patterns.end()); // } /// Add device patterns to drive detection blacklist void add_blacklist_patterns(std::vector& patterns) { blacklist_patterns_.insert(blacklist_patterns_.end(), patterns.begin(), patterns.end()); } /// Get all errors produced by fetch_basic_data(). const std::vector& get_fetch_data_errors() const { return fetch_data_errors_; } /// Get command output for each error in get_fetch_data_errors(). const std::vector& get_fetch_data_error_outputs() const { return fetch_data_error_outputs_; } private: // std::vector match_patterns_; ///< First each file is matched against these std::vector blacklist_patterns_; ///< If a device matches these, it's ignored. std::vector fetch_data_errors_; ///< Errors that have occurred std::vector fetch_data_error_outputs_; ///< Corresponding command outputs to fetch_data_errors_ }; #endif /// @} gsmartcontrol-1.1.4/src/applib/storage_detector_helpers.h000066400000000000000000000155001417717230600237120ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2011 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef STORAGE_DETECTOR_HELPERS_H #define STORAGE_DETECTOR_HELPERS_H #include #include #include #include "executor_factory.h" #include "storage_device.h" #include "rconfig/rconfig_mini.h" #include "app_pcrecpp.h" /// Find and execute tw_cli with specified options, return its output through \c output. /// \return error message inline std::string execute_tw_cli(ExecutorFactoryRefPtr ex_factory, const std::string& command_options, std::string& output) { hz::intrusive_ptr executor = ex_factory->create_executor(ExecutorFactory::ExecutorTwCli); std::string binary; rconfig::get_data("system/tw_cli_binary", binary); if (binary.empty()) { debug_out_error("app", DBG_FUNC_MSG << "tw_cli binary is not set in config.\n"); return "tw_cli binary is not specified in configuration."; } std::vector binaries; // binaries to try // Note: tw_cli is automatically added to PATH in windows, no need to look for it. binaries.push_back(binary); #ifdef CONFIG_KERNEL_LINUX // tw_cli may be named tw_cli.x86 or tw_cli.x86_64 in linux binaries.push_back(binary + ".x86_64"); // try this first binaries.push_back(binary + ".x86"); #endif for (std::size_t i = 0; i < binaries.size(); ++i) { executor->set_command(Glib::shell_quote(binaries.at(i)), command_options); if (!executor->execute() || !executor->get_error_msg().empty()) { debug_out_warn("app", DBG_FUNC_MSG << "Error while executing tw_cli binary.\n"); } else { break; // found it } } // any_to_unix is needed for windows output = hz::string_trim_copy(hz::string_any_to_unix_copy(executor->get_stdout_str())); if (output.empty()) { debug_out_error("app", DBG_FUNC_MSG << "tw_cli returned an empty output.\n"); return "tw_cli returned an empty output."; } return std::string(); } /// Get the drives on a 3ware controller using tw_cli. /// Note that the drives are inserted in the order they are detected. inline std::string tw_cli_get_drives(const std::string& dev, int controller, std::vector& drives, ExecutorFactoryRefPtr ex_factory, bool use_tw_cli_dev) { debug_out_info("app", "Getting available 3ware drives (ports) for controller " << controller << " through tw_cli...\n"); std::string output; std::string error = execute_tw_cli(ex_factory, hz::string_sprintf("/c%d show all", controller), output); if (!error.empty()) { return error; } // split to lines std::vector lines; hz::string_split(output, '\n', lines, true); // Note that the ports may be printed in any order. We sort the drives themselves in the end. pcrecpp::RE port_re = app_pcre_re("/^p([0-9]+)[ \\t]+([^\\t\\n]+)/mi"); for (std::size_t i = 0; i < lines.size(); ++i) { std::string port_str, status; if (port_re.PartialMatch(hz::string_trim_copy(lines.at(i)), &port_str, &status)) { if (status != "NOT-PRESENT") { int port = -1; hz::string_is_numeric(port_str, port); if (port != -1) { if (use_tw_cli_dev) { // use "tw_cli/cx/py" device drives.push_back(StorageDeviceRefPtr(new StorageDevice("tw_cli/c" + hz::number_to_string(controller) + "/p" + hz::number_to_string(port)))); } else { drives.push_back(StorageDeviceRefPtr(new StorageDevice(dev, "3ware," + hz::number_to_string(port)))); } debug_out_info("app", "Added 3ware drive " << drives.back()->get_device_with_type() << ".\n"); } } } } return std::string(); } /// Return 3ware SCSI host numbers (same as /c switch to tw_cli). /// \return error string on error inline std::string tw_cli_get_controllers(ExecutorFactoryRefPtr ex_factory, std::vector& controllers) { debug_out_info("app", "Getting available 3ware controllers through tw_cli...\n"); std::string output; std::string error = execute_tw_cli(ex_factory, "show", output); if (!error.empty()) { return error; } // split to lines std::vector lines; hz::string_split(output, '\n', lines, true); pcrecpp::RE controller_re = app_pcre_re("/^c([0-9]+)[ \\t]+/mi"); for (std::size_t i = 0; i < lines.size(); ++i) { std::string controller_str; if (controller_re.PartialMatch(hz::string_trim_copy(lines.at(i)), &controller_str)) { int controller = -1; hz::string_is_numeric(controller_str, controller); if (controller != -1) { debug_out_info("app", "Found 3ware controller " << controller << ".\n"); controllers.push_back(controller); } } } // Sort them. This affects only the further detection order, since the drives // are sorted in the end anyway. std::sort(controllers.begin(), controllers.end()); return std::string(); } /// Get number of ports by sequentially running smartctl on each port, until /// one of the gives an error. \c type contains a printf-formatted string with %d. /// \return an error message on error. inline std::string smartctl_scan_drives_sequentially(const std::string& dev, const std::string& type, int from, int to, std::vector& drives, ExecutorFactoryRefPtr ex_factory, std::string& last_output) { hz::intrusive_ptr smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl); for (int i = from; i <= to; ++i) { std::string type_arg = hz::string_sprintf(type.c_str(), i); StorageDeviceRefPtr drive(new StorageDevice(dev, type_arg)); // This will generate an error if smartctl doesn't return 0, which is what happens // with non-populated ports. // Sometimes the output contains: // "Read Device Identity failed: Input/output error" // or // "Read Device Identity failed: empty IDENTIFY data" std::string error_msg = drive->fetch_basic_data_and_parse(smartctl_ex); last_output = drive->get_info_output(); // If we've reached smartctl port limit (older versions may have smaller limits), abort. if (app_pcre_match("/VALID ARGUMENTS ARE/mi", last_output)) { break; } // If we couldn't open the device, it means there is no such controller at specified device // and scanning the ports is useless. if (app_pcre_match("/No .* controller found/mi", last_output) || app_pcre_match("/Smartctl open device: .* failed: No such device/mi", last_output) ) { break; } if (!error_msg.empty()) { debug_out_info("app", "Smartctl returned with an error: " << error_msg << "\n"); debug_out_dump("app", "Skipping drive " << drive->get_device_with_type() << " due to smartctl error.\n"); } else { drives.push_back(drive); debug_out_info("app", "Added drive " << drive->get_device_with_type() << ".\n"); } } return std::string(); } #endif /// @} gsmartcontrol-1.1.4/src/applib/storage_detector_linux.cpp000066400000000000000000001221071417717230600237440ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #include "hz/hz_config.h" // CONFIG_* #if defined CONFIG_KERNEL_LINUX #include // std::find #include // std::fgets(), std::FILE #include // ENXIO #include #include #include #include // std::pair #include "hz/debug.h" #include "hz/fs_path_utils.h" #include "hz/fs_file.h" #include "rconfig/rconfig_mini.h" #include "app_pcrecpp.h" #include "storage_detector_linux.h" #include "storage_detector_helpers.h" namespace { // Linux 2.6 with udev. Scan /dev/disk/by-id - the directory entries there // are symlinks to respective /dev devices. Some devices have multiple // links pointing to them, so unique filter is needed. /* Sample listing: ------------------------------------------------------------ # ls -1 /dev/disk/by-id ata-ST31000340AS_9QJ0FFG7 ata-ST31000340AS_9QJ0FFG7-part1 ata-ST31000340AS_9QJ0FFG7-part2 ata-ST3500630AS_9QG0R38D ata-ST3500630AS_9QG0R38D-part1 scsi-SATA_ST31000340AS_9QJ0FFG7 scsi-SATA_ST31000340AS_9QJ0FFG7-part1 scsi-SATA_ST31000340AS_9QJ0FFG7-part2 scsi-SATA_ST3500630AS_9QG0R38D scsi-SATA_ST3500630AS_9QG0R38D-part1 */ /* // We don't use udev anymore - not all distros have it, and e.g. Ubuntu // has it all wrong (two symlinks (sda, sdb) pointing both to sdb). inline std::string detect_drives_linux_udev_byid(std::vector& devices) { debug_out_info("app", DBG_FUNC_MSG << "Detecting through device scan directory /dev/disk/by-id...\n"); std::string dev_dir; // this defaults to "/dev/disk/by-id" if (!rconfig::get_data("system/linux_udev_byid_path", dev_dir) || dev_dir.empty()) { debug_out_warn("app", DBG_FUNC_MSG << "Device scan directory path is not set.\n"); return "Device scan directory path is not set."; } hz::Dir dir(dev_dir); std::vector all_devices; if (!dir.list(all_devices, false)) { // this outputs to debug too. std::string error_msg = dir.get_error_utf8(); if (!dir.exists()) { debug_out_warn("app", DBG_FUNC_MSG << "Device scan directory doesn't exist.\n"); } else { debug_out_error("app", DBG_FUNC_MSG << "Cannot list directory entries.\n"); } return error_msg; } std::vector blacklist; blacklist.push_back("/-part[0-9]+$/"); // filter-out the ones with "partN" in them for (unsigned int i = 0; i < all_devices.size(); ++i) { std::string entry = all_devices[i]; if (entry == "." || entry == "..") continue; // platform blacklist bool blacked = false; for (std::vector::const_iterator iter = blacklist.begin(); iter != blacklist.end(); ++iter) { if (app_pcre_match(*iter, entry)) { blacked = true; break; } } if (blacked) continue; hz::FsPath path = dev_dir + hz::DIR_SEPARATOR_S + all_devices[i]; // those are usually relative links, so find out where they are pointing to. std::string link_dest; if (path.get_link_destination(link_dest)) { path = (hz::path_is_absolute(link_dest) ? link_dest : (dev_dir + hz::DIR_SEPARATOR_S + link_dest)); path.compress(); // compress in case there are ../-s. } // skip number-ended devices, in case we couldn't filter out the scan directory correctly // (it's user-settable after all). // if (app_pcre_match("/[0-9]+$/", path.str())) // continue; if (std::find(devices.begin(), devices.end(), path.str()) == devices.end()) // there may be duplicates devices.push_back(path.str()); } return std::string(); } */ /// Cache of file->contents, caches read files. std::map s_read_file_cache; /// Clear the read file cache. inline void clear_read_file_cache() { s_read_file_cache.clear(); } /// Procfs files don't support SEEK_END or ftello() (I think). They can't /// be read through hz::File::get_contents, so use this function instead. inline bool read_proc_file(hz::File& file, std::string& contents) { if (s_read_file_cache.find(file.get_path()) != s_read_file_cache.end()) { contents = s_read_file_cache[file.get_path()]; return true; } if (!file.open("rb")) // closed automatically return false; // the error message is in File itself. std::FILE* fp = file.get_handle(); if (!fp) return false; char line[256]; while (std::fgets(line, static_cast(sizeof(line)), fp) != NULL) { if (*line != '\0') contents += line; // line contains the terminating newline as well } s_read_file_cache[file.get_path()] = contents; debug_begin(); // avoiding printing prefix on every line debug_out_dump("app", DBG_FUNC_MSG << "File contents (\"" << file.get_path() << "\"):\n" << contents << "\n"); debug_end(); return true; } /// Same as read_proc_file(), but returns a vector of lines inline bool read_proc_file_lines(hz::File& file, std::vector& lines) { std::string contents; bool status = read_proc_file(file, contents); if (status) { hz::string_split(contents, '\n', lines, true); } return status; } /// Read /proc/partitions file. Return error message on error. inline std::string read_proc_partitions_file(std::vector& lines) { std::string path; if (!rconfig::get_data("system/linux_proc_partitions_path", path) || path.empty()) { debug_out_warn("app", DBG_FUNC_MSG << "Partitions file path is not set.\n"); return "Partitions file path is not set."; } hz::File file(path); if (!read_proc_file_lines(file, lines)) { // this outputs to debug too std::string error_msg = file.get_error_utf8(); // save before calling other file functions if (!file.exists()) { debug_out_warn("app", DBG_FUNC_MSG << "Partitions file doesn't exist.\n"); } else { debug_out_error("app", DBG_FUNC_MSG << "Partitions file exists but cannot be read.\n"); } return error_msg; } return std::string(); } /// Read /proc/devices file. Return error message on error. inline std::string read_proc_devices_file(std::vector& lines) { std::string path; if (!rconfig::get_data("system/linux_proc_devices_path", path) || path.empty()) { debug_out_warn("app", DBG_FUNC_MSG << "Devices file path is not set.\n"); return "Devices file path is not set."; } hz::File file(path); if (!read_proc_file_lines(file, lines)) { // this outputs to debug too std::string error_msg = file.get_error_utf8(); // save before calling other file functions if (!file.exists()) { debug_out_warn("app", DBG_FUNC_MSG << "Devices file doesn't exist.\n"); } else { debug_out_error("app", DBG_FUNC_MSG << "Devices file exists but cannot be read.\n"); } return error_msg; } return std::string(); } /// Read /proc/scsi/scsi file. Return error message on error. /// \c vendors_models is filled with (scsi host #, trimmed vendors line) pairs. /// Note that scsi host # is not unique. inline std::string read_proc_scsi_scsi_file(std::vector< std::pair >& vendors_models) { std::string path; if (!rconfig::get_data("system/linux_proc_scsi_scsi_path", path) || path.empty()) { debug_out_warn("app", DBG_FUNC_MSG << "SCSI file path is not set.\n"); return "SCSI file path is not set."; } hz::File file(path); std::vector lines; if (!read_proc_file_lines(file, lines)) { // this outputs to debug too std::string error_msg = file.get_error_utf8(); // save before calling other file functions if (!file.exists()) { debug_out_warn("app", DBG_FUNC_MSG << "SCSI file doesn't exist.\n"); } else { debug_out_error("app", DBG_FUNC_MSG << "SCSI file exists but cannot be read.\n"); } return error_msg; } int last_scsi_host = -1; pcrecpp::RE host_re = app_pcre_re("^Host: scsi([0-9]+)"); for (std::size_t i = 0; i < lines.size(); ++i) { std::string trimmed = hz::string_trim_copy(lines[i]); // The format of this file is scsi host number on one line, vendor on another, // some other info on the third. // debug_out_error("app", "SCSI Line: " << hz::string_trim_copy(lines[i]) << "\n"); std::string scsi_host_str; if (host_re.PartialMatch(trimmed, &scsi_host_str)) { // debug_out_dump("app", "SCSI Host " << scsi_host_str << " found.\n"); hz::string_is_numeric(scsi_host_str, last_scsi_host); } else if (last_scsi_host != -1 && app_pcre_match("/Vendor: /i", trimmed)) { vendors_models.push_back(std::make_pair(last_scsi_host, trimmed)); } } return std::string(); } /// host chan id lun type opens qdepth busy online /// Read /proc/scsi/sg/devices file. Return error message on error. /// \c sg_entries is filled with lines parsed as ints. /// Each line index corresponds to N in /dev/sgN. inline std::string read_proc_scsi_sg_devices_file(std::vector< std::vector >& sg_entries) { std::string path; if (!rconfig::get_data("system/linux_proc_scsi_sg_devices_path", path) || path.empty()) { debug_out_warn("app", DBG_FUNC_MSG << "Sg devices file path is not set.\n"); return "Sg devices path is not set."; } hz::File file(path); std::vector lines; if (!read_proc_file_lines(file, lines)) { // this outputs to debug too std::string error_msg = file.get_error_utf8(); // save before calling other file functions if (!file.exists()) { debug_out_warn("app", DBG_FUNC_MSG << "Sg devices file doesn't exist.\n"); } else { debug_out_error("app", DBG_FUNC_MSG << "Sg devices file exists but cannot be read.\n"); } return error_msg; } pcrecpp::RE parse_re = app_pcre_re("^([0-9-]+)\\s+([0-9-]+)\\s+([0-9-]+)\\s+([0-9-]+)\\s+([0-9-]+)\\s+([0-9-]+)\\s+([0-9-]+)\\s+([0-9-]+)\\s+([0-9-]+)"); for (std::size_t i = 0; i < lines.size(); ++i) { std::string trimmed = hz::string_trim_copy(lines[i]); std::vector line(9); if (parse_re.PartialMatch(trimmed, &line[0], &line[1], &line[2], &line[3], &line[4], &line[5], &line[6], &line[7], &line[8])) { std::vector line_num(line.size(), -1); for (std::size_t j = 0; j < line_num.size(); ++j) { hz::string_is_numeric(line[j], line_num[j]); } sg_entries.resize(i+1); // maintain the line indices sg_entries[i] = line_num; } else { debug_out_warn("app", DBG_FUNC_MSG << "Sg devices line offset " << i << " has invalid format.\n"); } } return std::string(); } /**
Linux (tested with 2.4 and 2.6) /proc/partitions. Parses the file, appends /dev to each entry.
Note that file format changed from 2.4 to 2.6 (some statistics fields moved to another file).
No /proc/partitions on freebsd, solaris or osx, afaik.

Sample 1 (2.4, devfs, with statistics):
------------------------------------------------------------
# cat /proc/partitions
major minor  #blocks  name     rio rmerge rsect ruse wio wmerge wsect wuse running use aveq

3     0   60051600 ide/host0/bus0/target0/lun0/disc 72159 136746 1668720 467190 383039 658435 8342136 1659840 -429 17038808 22703194
3     1    1638598 ide/host0/bus0/target0/lun0/part1 0 0 0 0 0 0 0 0 0 0 0
3     2    1028160 ide/host0/bus0/target0/lun0/part2 0 0 0 0 0 0 0 0 0 0 0
3     3    3092512 ide/host0/bus0/target0/lun0/part3 0 0 0 0 0 0 0 0 0 0 0
3     4          1 ide/host0/bus0/target0/lun0/part4 0 0 0 0 0 0 0 0 0 0 0
3     5     514048 ide/host0/bus0/target0/lun0/part5 1458 7877 74680 9070 2140 18285 166272 42490 0 10100 52000
------------------------------------------------------------

Sample 2 (2.6):
------------------------------------------------------------
# cat /proc/partitions
major minor  #blocks  name

	1     0       4096 ram0
	8     0  156290904 sda
	8     1   39070048 sda1
	8     2       7560 sda2
	8     3          1 sda3
------------------------------------------------------------

Sample 3 (2.6 on nokia n8xx, spaces may be missing):
------------------------------------------------------------
# cat /proc/partitions
major minor  #blocks  name

31  0     128 mtdblock0
31  1     384 mtdblock1
31  2    2048 mtdblock2
31  3    2048 mtdblock3
31  4  257536 mtdblock4
254 0 3932160 mmcblk0
254 1 3928064 mmcblk0p1
254 8 1966080 mmcblk1
254 9 2007032 mmcblk1p1
*/ inline std::string detect_drives_linux_proc_partitions(std::vector& drives, ExecutorFactoryRefPtr ex_factory) { debug_out_info("app", DBG_FUNC_MSG << "Detecting drives through partitions file (/proc/partitions by default; set \"system/linux_proc_partitions_path\" config key to override).\n"); std::vector lines; std::string error_msg = read_proc_partitions_file(lines); if (!error_msg.empty()) { return error_msg; } std::vector blacklist; blacklist.push_back("/d[a-z][0-9]+$/"); // sda1, hdb2 - partitions. twa0 and twe1 are drives, not partitions. blacklist.push_back("/ram[0-9]+$/"); // ramdisks? blacklist.push_back("/loop[0-9]*$/"); // not sure if loop devices go there, but anyway... blacklist.push_back("/part[0-9]+$/"); // devfs had them blacklist.push_back("/p[0-9]+$/"); // partitions are usually marked this way blacklist.push_back("/md[0-9]*$/"); // linux software raid blacklist.push_back("/dm-[0-9]*$/"); // linux device mapper std::vector devices; for (std::size_t i = 0; i < lines.size(); ++i) { std::string line = hz::string_trim_copy(lines[i]); if (line.empty() || app_pcre_match("/^major/", line)) // file header continue; std::string dev; if (!app_pcre_match("/^[ \\t]*[^ \\t\\n]+[ \\t]+[^ \\t\\n]+[ \\t]+[^ \\t\\n]+[ \\t]+([^ \\t\\n]+)/", line, &dev) || dev.empty()) { debug_out_warn("app", DBG_FUNC_MSG << "Cannot parse line \"" << line << "\".\n"); continue; } // platform blacklist bool blacked = false; for (std::vector::const_iterator iter = blacklist.begin(); iter != blacklist.end(); ++iter) { if (app_pcre_match(*iter, dev)) { blacked = true; break; } } if (blacked) continue; std::string path = "/dev/" + dev; // let's just hope the it's /dev. if (std::find(devices.begin(), devices.end(), path) == devices.end()) { // there may be duplicates devices.push_back(path); } } hz::intrusive_ptr smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl); for (std::size_t i = 0; i < devices.size(); ++i) { StorageDeviceRefPtr drive(new StorageDevice(devices.at(i))); drive->fetch_basic_data_and_parse(smartctl_ex); // 3ware controllers also export themselves as sd*. Smartctl detects that, // so we can avoid adding them. Older smartctl (5.38) prints "AMCC", newer one // prints "AMCC/3ware controller". It's better to search it this way. if (app_pcre_match("/try adding '-d 3ware,N'/im", drive->get_info_output())) { debug_out_dump("app", "Drive " << drive->get_device_with_type() << " seems to be a 3ware controller, ignoring.\n"); } else { drives.push_back(drive); debug_out_info("app", "Added drive " << drive->get_device_with_type() << ".\n"); } } return std::string(); } /**
Detect drives behind 3ware RAID controller.

3ware Linux (3w-9xxx, 3w-xxxx, 3w-sas drivers):
Call as: smartctl -i -d 3ware,[0-127] /dev/twa[0-15]  (or twe[0-15], or twl[0-15])
Use twe* for [678]xxx series (), twa* for 9xxx series, twl* for 9750 series.
Note: twe* devices are limited to [0-15] ports (not sure about this).
Note: /dev/tw* devices may not exist, they are created by smartctl on the first run.
Note: for twe*, /dev/sda may also exist (to be used with -d 3ware,N), we should
	somehow detect and ignore them.
Note: when specifying non-existent port, either a "Device Read Identity Failed"
	error, or a "blank" info may be returned.

Detection:
Grep /proc/devices for "twa", "twe" or "twl" (e.g. "251 twa"). Use this for /dev/tw* part.
Grep /proc/scsi/scsi for AMCC or 3ware (LSI too?), use number of matched lines N
	for /dev/tw*[0, N-1].
For detecting the number of ports, use "tw_cli /cK show all", K being the controller
	scsi number, which is displayed as scsiK in the scsi file.
	If there's no tw_cli, we'll have to scan all the ports (up to supported maximum).
	This is too slow however, so scan only 24.

Implementation notes: it seems that twe uses "3ware" and twa uses "AMCC"
(not sure about twl).
We can't handle a situation with mixed twa/twe/twl systems, since we don't know
how they will be ordered for tw_cli.
*/ inline std::string detect_drives_linux_3ware(std::vector& drives, ExecutorFactoryRefPtr ex_factory) { debug_out_info("app", DBG_FUNC_MSG << "Detecting drives behind 3ware controller(s)...\n"); std::vector lines; std::string error_msg = read_proc_devices_file(lines); if (!error_msg.empty()) { return error_msg; } bool twa_found = false; bool twe_found = false; bool twl_found = false; // Check /proc/devices for twa or twe for (std::size_t i = 0; i < lines.size(); ++i) { std::string dev; if (app_pcre_match("/^[ \\t]*[0-9]+[ \\t]+(tw[ael])(?:[ \\t]*|$)/", hz::string_trim_copy(lines[i]), &dev)) { debug_out_dump("app", DBG_FUNC_MSG << "Found 3ware " << dev << " entry in devices file.\n"); if (dev == "twa") { twa_found = true; } else if (dev == "twe") { twe_found = true; } else if (dev == "twl") { twl_found = true; } else { DBG_ASSERT(0); // error in regexp? } } } if (!twa_found && !twe_found && !twl_found) { debug_out_info("app", DBG_FUNC_MSG << "No 3ware-specific entries found in devices file.\n"); return std::string(); // no controllers } lines.clear(); debug_out_dump("app", DBG_FUNC_MSG << "Checking scsi file for 3ware controllers.\n"); std::vector< std::pair > vendors_models; error_msg = read_proc_scsi_scsi_file(vendors_models); if (!error_msg.empty()) { return error_msg; } std::set controller_hosts; // scsi hosts (controllers) found for tw* std::map device_numbers; // device base (e.g. twa) -> number of times found for (std::size_t i = 0; i < vendors_models.size(); ++i) { // twe: 3ware, twa: AMCC, twl: LSI. std::string vendor; if (!app_pcre_match("/Vendor: (AMCC)|(3ware)|(LSI) /i", vendors_models[i].second, &vendor)) { continue; // not a supported controller } int host_num = vendors_models[i].first; debug_out_dump("app", "Found LSI/AMCC/3ware controller in SCSI file, SCSI host " << host_num << ".\n"); // Skip additional adapters with the same host, since they are the same adapters // with different LUNs. if (controller_hosts.find(host_num) != controller_hosts.end()) { debug_out_dump("app", "Skipping adapter with SCSI host " << host_num << ", host already found.\n"); continue; } controller_hosts.insert(host_num); std::string dev_base = "twe"; if (twa_found) { dev_base = "twa"; } else if (twl_found) { dev_base = "twl"; } // If there are several different tw* devices present (like 1 twa and 1 twe), we // use the vendor name to differentiate them. if (int(twa_found) + int(twe_found) + int(twl_found) > 1) { if (twa_found && hz::string_to_lower_copy(vendor) == "amcc") { dev_base = "twa"; } else if (twe_found && hz::string_to_lower_copy(vendor) == "3ware") { dev_base = "twe"; } else if (twl_found && hz::string_to_lower_copy(vendor) == "lsi") { dev_base = "twl"; } // else we default to twl, twa, twe (in this order) } // We can't map twaX to scsiY, so lets assume the relative order is the same. std::string dev = std::string("/dev/") + dev_base + hz::number_to_string(device_numbers[dev_base]); ++device_numbers[dev_base]; error_msg = tw_cli_get_drives(dev, vendors_models[i].first, drives, ex_factory, false); if (!error_msg.empty()) { // no tw_cli int max_ports = rconfig::get_data("system/linux_3ware_max_scan_port"); max_ports = std::max(0, std::min(max_ports, 127)); // Sanity check debug_out_dump("app", "Starting brute-force port scan on 0-" << max_ports << " ports, device \"" << dev << "\". Change the maximum by setting \"system/linux_3ware_max_scan_port\" config key.\n"); std::string last_output; error_msg = smartctl_scan_drives_sequentially(dev, "3ware,%d", 0, max_ports, drives, ex_factory, last_output); debug_out_dump("app", "Brute-force port scan finished.\n"); } if (!error_msg.empty()) { debug_out_warn("app", DBG_FUNC_MSG << "Couldn't get the drives on ports of LSI/AMCC/3ware controller: " << error_msg << "\n"); } } if (controller_hosts.empty()) { debug_out_warn("app", DBG_FUNC_MSG << "AMCC/LSI/3ware entry found in devices file, but SCSI file contains no known entries.\n"); } return error_msg; } /**
Detect drives behind Adaptec RAID controller (aacraid driver).
Tested using Adaptec RAID 5805 (SAS / SATA controller).
Uses /dev/sgN devices (with -d sat for SATA and -d scsi (default) for SCSI).

Adaptec Linux detection strategy:
Check /proc/devices for "aac"; if it's there, continue.
Check /proc/scsi/scsi for "Vendor: Adaptec"; get its host (e.g. scsi6).
Check which lines correspond to e.g. host 6 in /proc/scsi/sg/devices; the
line order (e.g. fourth and fifth lines) correspond to /dev/sg* device (e.g.
/dev/sg3 and /dev/sg4).
Note: This will get us at least 2 unusable devices - /dev/sdc (the volume)
and /dev/sg3 (the controller). I think the controller can be filtered out
using "id > 0" requirement (the third column of /proc/scsi/sg/devices.
Try "-d sat" by default. If it fails ("Device Read Identity Failed:" ? not
sure how to detect the failure), fall back to "-d scsi".
*/ inline std::string detect_drives_linux_adaptec(std::vector& drives, ExecutorFactoryRefPtr ex_factory) { debug_out_info("app", DBG_FUNC_MSG << "Detecting drives behind Adaptec controller(s)...\n"); std::vector lines; std::string error_msg = read_proc_devices_file(lines); if (!error_msg.empty()) { return error_msg; } bool aac_found = false; // Check /proc/devices for "aac" for (std::size_t i = 0; i < lines.size(); ++i) { if (app_pcre_match("/^[ \\t]*[0-9]+[ \\t]+aac(?:[ \\t]*|$)/", hz::string_trim_copy(lines[i]))) { debug_out_dump("app", DBG_FUNC_MSG << "Found aac entry in devices file.\n"); aac_found = true; } } if (!aac_found) { debug_out_info("app", DBG_FUNC_MSG << "No Adaptec-specific entries found in devices file.\n"); return std::string(); // no controllers } lines.clear(); debug_out_dump("app", DBG_FUNC_MSG << "Checking scsi file for Adaptec controllers.\n"); std::vector< std::pair > vendors_models; error_msg = read_proc_scsi_scsi_file(vendors_models); if (!error_msg.empty()) { return error_msg; } std::vector< std::vector > sg_entries; error_msg = read_proc_scsi_sg_devices_file(sg_entries); if (!error_msg.empty()) { return error_msg; } hz::intrusive_ptr smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl); std::set controller_hosts; for (std::size_t i = 0; i < vendors_models.size(); ++i) { if (!app_pcre_match("/Vendor: Adaptec /i", vendors_models[i].second)) { continue; // not a supported controller } int host_num = vendors_models[i].first; debug_out_dump("app", "Found Adaptec controller in SCSI file, SCSI host " << host_num << ".\n"); // Skip additional adapters with the same host, since they are the same adapters // with different LUNs. if (controller_hosts.find(host_num) != controller_hosts.end()) { debug_out_dump("app", "Skipping adapter with SCSI host " << host_num << ", host already found.\n"); continue; } controller_hosts.insert(host_num); for (std::size_t sg_num = 0; sg_num < sg_entries.size(); ++sg_num) { if (sg_entries[sg_num].size() < 3) { // We need at least 3 columns in that file continue; } if (sg_entries[sg_num][0] != host_num || sg_entries[sg_num][2] <= 0) { // Different scsi host, or scsi id is 0 (the controller, probably) continue; } std::string dev = std::string("/dev/sg") + hz::number_to_string(sg_num); StorageDeviceRefPtr drive(new StorageDevice(dev, std::string("sat"))); std::string local_error_msg = drive->fetch_basic_data_and_parse(smartctl_ex); std::string output = drive->get_info_output(); // Note: Not sure about this, have to check with real SAS drives if (app_pcre_match("/Device Read Identity Failed/mi", output)) { // "-d sat" didn't work, default back to smartctl's "-d scsi" drive->clear_fetched(); drive->set_type_argument(""); local_error_msg = drive->fetch_basic_data_and_parse(smartctl_ex); } if (!local_error_msg.empty()) { debug_out_info("app", "Smartctl returned with an error: " << local_error_msg << "\n"); debug_out_dump("app", "Skipping drive " << drive->get_device_with_type() << ".\n"); } else { drives.push_back(drive); debug_out_info("app", "Added drive " << drive->get_device_with_type() << ".\n"); } } } if (controller_hosts.empty()) { debug_out_warn("app", DBG_FUNC_MSG << "Adaptec entry found in devices file, but SCSI file contains no known entries.\n"); } return error_msg; } /**
Areca Linux (arcmsr driver):
Call as:
	- smartctl -i -d areca,N /dev/sg0  (N is [1,24]).
	- smartctl -i -d areca,N/E /dev/sg0  (N is [1,128], E is [1,8]).
The models that have "ix" (case-insensitive) in their model names
have the enclosures and require the N,E syntax.
Note: Using N/E syntax with non-enclosure cards seems to work
regardless of the value of E.

Detection:
	There don't seem to be any entries in /proc/devices.
	Check /proc/scsi/scsi for "Vendor: Areca".
	(Alternatively, /proc/scsi/sg/device_strs should contain "Areca       RAID controller").
Detect SCSI host (N in hostN below):
	It's N of scsiN in /proc/scsi/sg/device_strs of the entry with "Vendor: Areca",
	which has type 3 in /proc/scsi/sg/devices (to filter out logical volumes) and id 16.
	The line number in /proc/scsi/sg/devices is X in /dev/sgX.
Check /sys/bus/scsi/devices/hostN/scsi_host/hostN/host_fw_hd_channels, set
	its contents as the number of ports. If not present, use 24 (maximum).
	No-enclosure models only, since we have no info yet for the enclosure ones.
Probe each port for a valid drive: If the output contains "Device Read Identity Failed"
	or "empty IDENTIFY data", then there is no drive on that port (or Areca has an
	old firmware, nothing we can do there).
Notification: If /sys/bus/scsi/devices/hostN/scsi_host/hostN/host_fw_version
	is older than "V1.46 2009-01-06", (1.51 for enclosure-having cards) notify the user
	(maybe its better to grep the smartctl output for that on port 0?). NOT IMPLEMENTED YET.
*/ inline std::string detect_drives_linux_areca(std::vector& drives, ExecutorFactoryRefPtr ex_factory) { debug_out_info("app", DBG_FUNC_MSG << "Detecting drives behind Areca controller(s)...\n"); std::vector< std::pair > vendors_models; std::string error_msg = read_proc_scsi_scsi_file(vendors_models); if (!error_msg.empty()) { return error_msg; } std::map controller_hosts; // controller # -> has_enclosure for (std::size_t i = 0; i < vendors_models.size(); ++i) { std::string vendor_line = vendors_models[i].second; if (!app_pcre_match("/Vendor: Areca /i", vendor_line)) { continue; // not a supported controller } int host_num = vendors_models[i].first; debug_out_dump("app", "Found Areca controller in SCSI file, SCSI host " << host_num << ".\n"); // Skip additional adapters with the same host (they are volumes) if (controller_hosts.find(host_num) != controller_hosts.end()) { debug_out_dump("app", "Skipping adapter with SCSI host " << host_num << ", host already found.\n"); continue; } // Check if it contains the "ix" string in its model name. It may not be exactly // a suffix (there may be "-VOL#00" or something like that appended as well). bool has_enclosure = app_pcre_match("/Model:.+ix.+Rev:/i", vendor_line); debug_out_dump("app", DBG_FUNC_MSG << "Areca controller (SCSI host " << host_num << ") seems to " << (has_enclosure ? "have enclosure(s)" : "have no enclosures") << ".\n"); controller_hosts[host_num] = has_enclosure; } if (controller_hosts.empty()) { debug_out_info("app", DBG_FUNC_MSG << "No Areca-specific entries found in SCSI file.\n"); return error_msg; } std::vector< std::vector > sg_entries; error_msg = read_proc_scsi_sg_devices_file(sg_entries); if (!error_msg.empty()) { return error_msg; } hz::intrusive_ptr smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl); for (std::map::iterator iter = controller_hosts.begin(); iter != controller_hosts.end(); ++iter) { const int host_num = iter->first; const bool has_enclosure = iter->second; for (std::size_t sg_num = 0; sg_num < sg_entries.size(); ++sg_num) { if (sg_entries[sg_num].size() < 5) { // We need at least 5 columns in that file continue; } const int type = sg_entries[sg_num][4]; // type 3 is Areca controller const int id = sg_entries[sg_num][2]; // id should be 16 (as per smartmontools) if (sg_entries[sg_num][0] != host_num || id != 16 || type != 3) { continue; } if (has_enclosure) { // TODO We have no information on what "/sys/bus/scsi/devices/host%d/scsi_host/host%d/host_fw_hd_channels" // contains in case of enclosure-having cards. int max_enclosures = rconfig::get_data("system/linux_areca_enc_max_enclosure"); max_enclosures = std::max(1, std::min(8, max_enclosures)); // 1-8 int max_ports = rconfig::get_data("system/linux_areca_enc_max_scan_port"); max_ports = std::max(1, std::min(128, max_ports)); // 1-128 sanity check std::string dev = std::string("/dev/sg") + hz::number_to_string(sg_num); debug_out_dump("app", "Starting brute-force port/enclosure scan on 1-" << max_ports << " ports, 1-" << max_enclosures << " enclosures, device \"" << dev << "\". Change the maximums by setting \"system/linux_areca_enc_max_scan_port\" and \"system/linux_areca_enc_max_enclosure\" config keys.\n"); std::string last_output; for (int enclosure_no = 1; enclosure_no < max_enclosures; ++enclosure_no) { error_msg = smartctl_scan_drives_sequentially(dev, "areca,%d/" + hz::number_to_string(enclosure_no), 1, max_ports, drives, ex_factory, last_output); } debug_out_dump("app", "Brute-force port/enclosure scan finished.\n"); } else { int max_ports = 0; // Read the number of ports. std::string ports_path = hz::string_sprintf("/sys/bus/scsi/devices/host%d/scsi_host/host%d/host_fw_hd_channels", host_num, host_num); hz::File ports_file(ports_path); std::string ports_file_contents; if (!read_proc_file(ports_file, ports_file_contents)) { debug_out_warn("app", DBG_FUNC_MSG << "Couldn't read the number of ports on Areca controller (\"" << ports_path << "\"): " << ports_file.get_error_utf8() << ", trying manually.\n"); } else { hz::string_is_numeric(hz::string_trim_copy(ports_file_contents), max_ports); debug_out_dump("app", DBG_FUNC_MSG << "Detected " << max_ports << " ports, through \"" << ports_path << "\".\n"); } if (max_ports == 0) { max_ports = rconfig::get_data("system/linux_areca_noenc_max_scan_port"); } max_ports = std::max(1, std::min(24, max_ports)); // 1-24 sanity check std::string dev = std::string("/dev/sg") + hz::number_to_string(sg_num); debug_out_dump("app", "Starting brute-force port scan on 1-" << max_ports << " ports, device \"" << dev << "\". Change the maximum by setting \"system/linux_areca_noenc_max_scan_port\" config key.\n"); std::string last_output; error_msg = smartctl_scan_drives_sequentially(dev, "areca,%d", 1, max_ports, drives, ex_factory, last_output); debug_out_dump("app", "Brute-force port scan finished.\n"); } if (!error_msg.empty()) { debug_out_warn("app", DBG_FUNC_MSG << "Couldn't get the drives on ports of Areca controller: " << error_msg << "\n"); } } } return error_msg; } /**
Detect drives behind HP RAID controller (cciss driver).
(aka CCISS (HP (Compaq) Smart Array Controller).

Note: hpsa/hpahcisr have a different method of detection.

Call as: smartctl -a -d cciss,[0-127] /dev/cciss/c0d0
	With the cciss drivers , for the path /dev/cciss/c0d0, c0 means controler 0 and d0 means LUN 1.
	So smarctl provides the same values for c0d0 and c0d1. There are just two logical drives
	on the same Smart Array controler.
To detect controller presence: cat /proc/devices, contains "cciss0"
	for controller 0.
	/proc/scsi/scsi contains no entries for it.
/dev/cciss/c0d0p1 is the first ordinary partition (used in mount, etc...).

The port limit had a brief regression in 5.39.x (limited to 15).
5.39 doesn't seem to work at all (prereleases work with P400 controller)?
	Possibly a 64-bit-only issue?
There is a cciss_vol_status utility but it doesn't report anything except "OK" status.
It may not return the "SMART Health Status: OK" line in smartctl -i for some reason?
	(It returns it in -a though, and in some freebsd output I found).
If no /dev/cciss exists, you may need to run "cd /dev; ./MAKEDEV cciss".

Detection:
	Grep /proc/devices for "cciss([0-9]+)" where the number is the controller #.
	Run "smartctl -i -d cciss,[0-127] /dev/cciss/cNd0" (where N is the controller #),
		until "No such device or address" or "VALID ARGUMENTS ARE" is encountered in the output.
	Note: We're not sure how to differentiate the outputs of free / non-existent ports,
		so scan them until 15, just in case.
*/ inline std::string detect_drives_linux_cciss(std::vector& drives, ExecutorFactoryRefPtr ex_factory) { debug_out_info("app", DBG_FUNC_MSG << "Detecting drives behind HP RAID (CCISS) controller(s)...\n"); std::vector lines; std::string error_msg = read_proc_devices_file(lines); if (!error_msg.empty()) { return error_msg; } std::vector controllers; // Check /proc/devices for ccissX (where X is the controller #). for (std::size_t i = 0; i < lines.size(); ++i) { std::string controller_no_str; if (app_pcre_match("/^[ \\t]*[0-9]+[ \\t]+cciss([0-9]+)(?:[ \\t]*|$)/", hz::string_trim_copy(lines[i]), &controller_no_str)) { debug_out_dump("app", DBG_FUNC_MSG << "Found cciss" << controller_no_str << " entry in devices file.\n"); int controller_no = 0; hz::string_is_numeric(controller_no_str, controller_no); controllers.push_back(controller_no); } } if (controllers.empty()) { debug_out_info("app", DBG_FUNC_MSG << "No cciss-specific entries found in devices file.\n"); return std::string(); // no controllers } hz::intrusive_ptr smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl); for (std::size_t controller_index = 0; controller_index < controllers.size(); ++controller_index) { int controller_no = controllers.at(controller_index); std::string dev = std::string("/dev/cciss/c") + hz::number_to_string(controller_no) + "d0"; const int max_port = 127; debug_out_dump("app", "Starting brute-force port scan on 1-" << max_port << " ports, device \"" << dev << "\".\n"); for (int port = 0; port <= max_port; ++port) { StorageDeviceRefPtr drive(new StorageDevice(dev, std::string("cciss,") + hz::number_to_string(port))); std::string local_error_msg = drive->fetch_basic_data_and_parse(smartctl_ex); std::string output = drive->get_info_output(); if (!local_error_msg.empty()) { debug_out_info("app", "Smartctl returned with an error: " << local_error_msg << "\n"); } if (app_pcre_match("/VALID ARGUMENTS ARE/mi", output)) { // smartctl doesn't support this many ports, return. debug_out_dump("app", "Reached smartctl port limit with port " << port << ", stopping port scan.\n"); break; } if (app_pcre_match("/No such device or address/mi", output) && port > 15) { // we've reached the controller port limit debug_out_dump("app", "Reached controller port limit with port " << port << ", stopping port scan.\n"); break; } if (local_error_msg.empty()) { drives.push_back(drive); debug_out_info("app", "Added drive " << drive->get_device_with_type() << ".\n"); } else { debug_out_dump("app", "Skipping drive " << drive->get_device_with_type() << " due to smartctl error.\n"); } } debug_out_dump("app", "Brute-force port scan finished.\n"); } return error_msg; } /**
Detect drives behind HP RAID controller (hpsa / hpahcisr drivers).

May need "-d sat+cciss,N" for SATA, but is usually auto-detected.

smartctl -a -d cciss,0 /dev/sg2    (hpsa or hpahcisr drivers under Linux)
("lsscsi -g" is helpful in determining which scsi generic device node corresponds to which device.)
Use the nodes corresponding to the RAID controllers, not the nodes corresponding to logical drives.
No entry in /proc/devices.
/dev/sda also seems to work?

Detection:
	Check /proc/scsi for "Vendor: HP", and NOT "Model: LOGICAL VOLUME". Take its scsi host number,
		match against /proc/scsi/sg/devices (first column). The matched line number
		is the one to use in /dev/sgN.
	Run smartctl -i -d cciss,[0-127] /dev/cciss/cNd0
		until "No such device or address" or "VALID ARGUMENTS ARE" is encountered in output.
*/ inline std::string detect_drives_linux_hpsa(std::vector& drives, ExecutorFactoryRefPtr ex_factory) { debug_out_info("app", DBG_FUNC_MSG << "Detecting drives behind HP RAID (hpsa/hpahcisr) controller(s)...\n"); std::vector< std::pair > vendors_models; std::string error_msg = read_proc_scsi_scsi_file(vendors_models); if (!error_msg.empty()) { return error_msg; } std::vector< std::vector > sg_entries; error_msg = read_proc_scsi_sg_devices_file(sg_entries); if (!error_msg.empty()) { return error_msg; } hz::intrusive_ptr smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl); std::set controller_hosts; for (std::size_t i = 0; i < vendors_models.size(); ++i) { if (!app_pcre_match("/Vendor: HP /i", vendors_models[i].second) || app_pcre_match("/LOGICAL VOLUME/i", vendors_models[i].second)) { continue; // not a supported controller, or a logical drive. } int host_num = vendors_models[i].first; debug_out_dump("app", "Found HP controller in SCSI file, SCSI host " << host_num << ".\n"); // Skip additional adapters with the same host (not sure if this is needed, but it won't hurt). if (controller_hosts.find(host_num) != controller_hosts.end()) { debug_out_dump("app", "Skipping adapter with SCSI host " << host_num << ", host already found.\n"); continue; } controller_hosts.insert(host_num); for (std::size_t sg_num = 0; sg_num < sg_entries.size(); ++sg_num) { if (sg_entries[sg_num].size() < 3) { // We need at least 3 columns in that file continue; } if (sg_entries[sg_num][0] != host_num) { // Different scsi host continue; } std::string dev = std::string("/dev/sg") + hz::number_to_string(sg_num); const int max_port = 127; debug_out_dump("app", "Starting brute-force port scan on 0-" << max_port << " ports, device \"" << dev << "\".\n"); for (int port = 0; port <= max_port; ++port) { StorageDeviceRefPtr drive(new StorageDevice(dev, std::string("cciss,") + hz::number_to_string(port))); std::string local_error_msg = drive->fetch_basic_data_and_parse(smartctl_ex); std::string output = drive->get_info_output(); if (app_pcre_match("/No such device or address/mi", output) || app_pcre_match("/VALID ARGUMENTS ARE/mi", output)) { // We reached the controller port limit, or smartctl-supported port limit. debug_out_dump("app", "Reached controller or smartctl port limit with port " << port << ", stopping port scan.\n"); break; } if (!local_error_msg.empty()) { debug_out_info("app", "Smartctl returned with an error: " << local_error_msg << "\n"); debug_out_dump("app", "Skipping drive " << drive->get_device_with_type() << " due to smartctl error.\n"); } else { drives.push_back(drive); debug_out_info("app", "Added drive " << drive->get_device_with_type() << ".\n"); } } debug_out_dump("app", "Brute-force port scan finished.\n"); } } if (controller_hosts.empty()) { debug_out_info("app", DBG_FUNC_MSG << "No hpsa/hpahcisr-specific entries found in Sg devices file.\n"); } return error_msg; } } // anon ns std::string detect_drives_linux(std::vector& drives, ExecutorFactoryRefPtr ex_factory) { clear_read_file_cache(); std::vector error_msgs; std::string error_msg; // Disable by-id detection - it's unreliable on broken systems. // For example, on Ubuntu 8.04, /dev/disk/by-id contains two device // links for two drives, but both point to the same sdb (instead of // sda and sdb). Plus, there are no "*-partN" files (not that we need them). // error_msg = detect_drives_linux_udev_byid(devices); // linux udev error_msg = detect_drives_linux_proc_partitions(drives, ex_factory); if (!error_msg.empty()) { error_msgs.push_back(error_msg); } error_msg = detect_drives_linux_3ware(drives, ex_factory); if (!error_msg.empty()) { error_msgs.push_back(error_msg); } error_msg = detect_drives_linux_areca(drives, ex_factory); if (!error_msg.empty()) { error_msgs.push_back(error_msg); } error_msg = detect_drives_linux_adaptec(drives, ex_factory); if (!error_msg.empty()) { error_msgs.push_back(error_msg); } error_msg = detect_drives_linux_cciss(drives, ex_factory); if (!error_msg.empty()) { error_msgs.push_back(error_msg); } error_msg = detect_drives_linux_hpsa(drives, ex_factory); if (!error_msg.empty()) { error_msgs.push_back(error_msg); } return hz::string_join(error_msgs, "\n"); } #endif // CONFIG_KERNEL_LINUX /// @} gsmartcontrol-1.1.4/src/applib/storage_detector_linux.h000066400000000000000000000013741417717230600234130ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef STORAGE_DETECTOR_LINUX_H #define STORAGE_DETECTOR_LINUX_H #include "hz/hz_config.h" // CONFIG_* #if defined CONFIG_KERNEL_LINUX #include #include #include "executor_factory.h" #include "storage_device.h" /// Detect drives in Linux std::string detect_drives_linux(std::vector& drives, ExecutorFactoryRefPtr ex_factory); #endif #endif /// @} gsmartcontrol-1.1.4/src/applib/storage_detector_other.cpp000066400000000000000000000242171417717230600237310ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #include "hz/hz_config.h" // CONFIG_* #if !defined CONFIG_KERNEL_LINUX && !defined CONFIG_KERNEL_FAMILY_WINDOWS #include // std::sort #if defined CONFIG_KERNEL_OPENBSD || defined CONFIG_KERNEL_NETBSD #include // getrawpartition() #endif #include "hz/debug.h" #include "hz/fs_path_utils.h" #include "hz/fs_path.h" #include "hz/fs_file.h" #include "hz/fs_dir.h" #include "rconfig/rconfig_mini.h" #include "app_pcrecpp.h" #include "storage_detector_other.h" std::string detect_drives_other(std::vector& drives, ExecutorFactoryRefPtr ex_factory) { debug_out_info("app", DBG_FUNC_MSG << "Detecting drives through /dev...\n"); std::vector devices; std::string sdev_config_path; #if defined CONFIG_KERNEL_SOLARIS sdev_config_path = "system/solaris_dev_path"; #else // other unixes sdev_config_path = "system/unix_sdev_path"; #endif std::string dev_dir; // defaults to /dev for freebsd, /dev/rdsk for solaris. if (!rconfig::get_data(sdev_config_path, dev_dir) || dev_dir.empty()) { debug_out_warn("app", DBG_FUNC_MSG << "Device directory path is not set.\n"); return "Device directory path is not set."; } hz::Dir dir(dev_dir); std::vector all_devices; if (!dir.list(all_devices, false, hz::DirSortNone())) { // this outputs to debug too. std::string error_msg = dir.get_error_utf8(); if (!dir.exists()) { debug_out_warn("app", DBG_FUNC_MSG << "Device directory doesn't exist.\n"); } else { debug_out_error("app", DBG_FUNC_MSG << "Cannot list directory entries.\n"); } return error_msg; } // platform whitelist std::vector whitelist; #if defined CONFIG_KERNEL_FREEBSD || defined CONFIG_KERNEL_DRAGONFLY // FreeBSD example device names: // * /dev/ad0 - ide disk 0. /dev/ad0a - non-slice dos partition 1. // * /dev/da1s1e - scsi disk 1 (second disk), slice 1 (aka dos partition 1), // bsd partition e (multiple bsd partitions may be inside one dos partition). // * /dev/acd0 - first cdrom. /dev/acd0c - ?. // Info: http://www.freebsd.org/doc/en/books/handbook/disks-naming.html // Of two machines I had access to, freebsd 6.3 had only real devices, // while freebsd 4.10 had lots of dummy ones (ad1, ad2, ad3, da0, da1, da2, da3, sa0, ...). whitelist.push_back("/^ad[0-9]+$/"); // adN without suffix - fbsd ide whitelist.push_back("/^da[0-9]+$/"); // daN without suffix - fbsd scsi, usb whitelist.push_back("/^ada[0-9]+$/"); // adaN without suffix - fbsd ata cam // whitelist.push_back("/ ^sa[0-9]+$/"); // saN without suffix - fbsd scsi tape // whitelist.push_back("/^ast[0-9]+$/"); // astN without suffix - fbsd ide tape whitelist.push_back("/^aacd[0-9]+$/"); // fbsd adaptec raid whitelist.push_back("/^mlxd[0-9]+$/"); // fbsd mylex raid whitelist.push_back("/^mlyd[0-9]+$/"); // fbsd mylex raid whitelist.push_back("/^amrd[0-9]+$/"); // fbsd AMI raid whitelist.push_back("/^idad[0-9]+$/"); // fbsd compaq raid whitelist.push_back("/^twed[0-9]+$/"); // fbsd 3ware raid // these are checked by smartctl, but they are not mentioned in freebsd docs: whitelist.push_back("/^tw[ae][0-9]+$/"); // fbsd 3ware raid // unused: acd - ide cdrom, cd - scsi cdrom, mcd - mitsumi cdrom, scd - sony cdrom, // fd - floppy, fla - diskonchip flash. #elif defined CONFIG_KERNEL_SOLARIS // /dev/rdsk contains "raw" physical char devices, as opposed to // "filesystem" block devices in /dev/dsk. smartctl needs rdsk. // x86: // /dev/rdsk/c0t0d0p0:1 // Where c0 is the controller number. // t0 is the target (SCSI ID number) (omit for ATAPI) // d0 is always 0 for SCSI, the drive # for ATAPI // p0 is the partition (p0 is the entire disk, or p1 - p4) // :1 is the logical drive (c - z or 1 - 24) (that is, logical partitions inside extended partition) // /dev/rdsk/c0d0p2:1 // where p2 means the extended partition (type 0x05) is partition 2 (out of 1-4) and // ":1" is the 2nd extended partition (:0 would be the first extended partition). -- not sure about this! // p0 whole physical disk // p1 - p4 Four primary fdisk partitions // p5 - p30 26 logical drives in extended DOS partition (not implemented by Solaris) // s0 - s15 16 slices in the Solaris FDISK partition (SPARC has only 8) // On Solaris, by convention, s2 is the whole Solaris partition. // SPARC: There are no *p* devices on sparc afaik, only slices. By convention, // s2 is used as a "whole" disk there (but not with EFI partitions!). // So, c0d0s2 is the whole disk there. // x86 has both c0d0s2 and c0d0p2, where s2 is a whole solaris partition. // NOTE: It seems that smartctl searches for s0 (aka root partition) in the end. I'm not sure // what implications this has for x86, but we replicate this behaviour. // Note: Cdroms are ATAPI, so they pose as SCSI, as opposed to IDE hds. // TODO: No idea how to implement /dev/rmt (scsi tape devices), // I have no files in that directory. whitelist.push_back("/^c[0-9]+(?:t[0-9]+)?d[0-9]+s0$/"); #elif defined CONFIG_KERNEL_OPENBSD || defined CONFIG_KERNEL_NETBSD // OpenBSD / NetBSD have /dev/wdNc for IDE/ATA, /dev/sdNc for SCSI disk, // /dev/stNc for scsi tape. N is [0-9]+. "c" means "whole disk" (not sure about // different architectures though). There are no "sdN" devices, only "sdNP". // Another manual says that wd0d would be a whole disk, while wd0c is its // bsd part only (on x86) (only on netbsd?). Anyway, getrawpartition() gives // us the letter we need. // There is no additional level of names for BSD subpartitions (unlike freebsd). // cd0a is cdrom. // Dummy devices are present. // Note: This detection may take a while (probably due to open() check). char whole_part = 'a' + getrawpartition(); // from bsd's util.h whitelist.push_back(hz::string_sprintf("/^wd[0-9]+%c$/", whole_part)); whitelist.push_back(hz::string_sprintf("/^sd[0-9]+%c$/", whole_part)); whitelist.push_back(hz::string_sprintf("/^st[0-9]+%c$/", whole_part)); #elif defined CONFIG_KERNEL_DARWIN // Darwin has /dev/disk0, /dev/disk0s1, etc... // Only real devices are present, so no need for additional checks. whitelist.push_back("/^disk[0-9]+$/"); #elif defined CONFIG_KERNEL_QNX // QNX has /dev/hd0, /dev/hd0t78 (partition?). // Afaik, IDE and SCSI have the same prefix. fd for floppy, cd for cdrom. // Not sure about the tapes. // Only real devices are present, so no need for additional checks. whitelist.push_back("/^hd[0-9]+$/"); #endif // unix platforms std::vector matched_devices; for (std::size_t i = 0; i < all_devices.size(); ++i) { std::string entry = all_devices[i]; if (entry == "." || entry == "..") continue; bool matched = false; for (std::vector::const_iterator iter = whitelist.begin(); iter != whitelist.end(); ++iter) { if (app_pcre_match(*iter, entry)) { matched = true; break; } } if (!matched) continue; hz::FsPath path = dev_dir + hz::DIR_SEPARATOR_S + all_devices[i]; // In case these are links, trace them to originals. // We don't replace /dev files with real devices - it leads to really bad paths (pci ids for solaris, etc...). std::string link_dest; hz::FsPath real_dev_path; if (path.get_link_destination(link_dest)) { real_dev_path = (hz::path_is_absolute(link_dest) ? link_dest : (dev_dir + hz::DIR_SEPARATOR_S + link_dest)); real_dev_path.compress(); // compress in case there are ../-s. // solaris has dangling links for non-existant devices, so filter them out. if (!real_dev_path.exists()) continue; } matched_devices.push_back(path.str()); } // List ones who need dummy device filtering #if defined CONFIG_KERNEL_FREEBSD || defined CONFIG_KERNEL_DRAGONFLY \ || defined CONFIG_KERNEL_OPENBSD || defined CONFIG_KERNEL_NETBSD // Since we may have encountered dummy devices, we should check // if they really exist. Unfortunately, this involves opening them, which // is not good with some media (e.g. it may hang on audio cd (freebsd, maybe others), etc...). // That's why we don't whitelist cd devices. Let's hope usb and others are // ok with this. Dummy devices give errno ENXIO (6, Device not configured) on *bsd. // In Linux ENXIO is (6, No such device or address). // Don't do this on solaris - we can't distinguish between cdroms and hds there. // If there are less than 4 devices, they are probably not dummy (newer freebsd). bool open_needed = (matched_devices.size() >= 4); if (open_needed) { debug_out_info("app", DBG_FUNC_MSG << "Number of matched devices is " << matched_devices.size() << ", will try to filter non-existent ones out.\n"); } else { debug_out_info("app", DBG_FUNC_MSG << "Number of matched devices is " << matched_devices.size() << ", no need for filtering them out.\n"); } for (std::size_t i = 0; i < matched_devices.size(); ++i) { hz::File dev_file(matched_devices[i]); // we check errno because OS may deny us for some other reason on valid devices. if (open_needed && !dev_file.open("rb") && dev_file.get_errno() == ENXIO) { debug_out_dump("app", DBG_FUNC_MSG << "Device \"" << dev_file.str() << "\" failed to open, ignoring.\n"); continue; } if (open_needed) debug_out_info("app", DBG_FUNC_MSG << "Device \"" << dev_file.str() << "\" opened successfully, adding to device list.\n"); devices.push_back(dev_file.str()); } #else // not *BSD for (std::size_t i = 0; i < matched_devices.size(); ++i) { debug_out_info("app", DBG_FUNC_MSG << "Device \"" << matched_devices[i] << "\" matched the whitelist, adding to device list.\n"); devices.push_back(matched_devices[i]); } #endif // TODO Sort using natural sort std::sort(devices.begin(), devices.end()); for (std::size_t i = 0; i < devices.size(); ++i) { drives.push_back(StorageDeviceRefPtr(new StorageDevice(devices.at(i)))); } return std::string(); } #endif // !defined CONFIG_KERNEL_LINUX && !defined CONFIG_KERNEL_FAMILY_WINDOWS /// @} gsmartcontrol-1.1.4/src/applib/storage_detector_other.h000066400000000000000000000015301417717230600233670ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef STORAGE_DETECTOR_OTHER_H #define STORAGE_DETECTOR_OTHER_H #include "hz/hz_config.h" // CONFIG_* #if !defined CONFIG_KERNEL_LINUX && !defined CONFIG_KERNEL_FAMILY_WINDOWS #include #include #include "executor_factory.h" #include "storage_device.h" /// Detect drives in FreeBSD, Solaris, etc... (all except Linux and Windows). std::string detect_drives_other(std::vector& drives, ExecutorFactoryRefPtr ex_factory); #endif #endif /// @} gsmartcontrol-1.1.4/src/applib/storage_detector_test.cpp000066400000000000000000000025151417717230600235640ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_unlicense.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib_tests /// \weakgroup applib_tests /// @{ #include #include "storage_device.h" #include "storage_detector.h" #include "gsc_settings.h" // in src directory /// Main function of the test int main() { // These settings contain device search paths, smartctl binary, etc... init_default_settings(); std::vector drives; // std::vector match_patterns; std::vector blacklist_patterns; // additional parameters StorageDetector sd; // sd.add_match_patterns(match_patterns); sd.add_blacklist_patterns(blacklist_patterns); ExecutorFactoryRefPtr ex_factory(new ExecutorFactory(false)); std::string error_msg = sd.detect_and_fetch_basic_data(drives, ex_factory); if (!error_msg.empty()) { std::cerr << error_msg << "\n"; } else { for (unsigned int i = 0; i < drives.size(); ++i) { std::cerr << drives[i]->get_device_with_type() << " (" << StorageDevice::get_type_readable_name(drives[i]->get_detected_type()) << ")\n"; } } return 0; } /// @} gsmartcontrol-1.1.4/src/applib/storage_detector_win32.cpp000066400000000000000000001014121417717230600235430ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #include "hz/hz_config.h" // CONFIG_* #if defined CONFIG_KERNEL_FAMILY_WINDOWS #include // CreateFileA(), CloseHandle(), etc... #include #include #include #include #include #include "hz/win32_tools.h" #include "hz/string_sprintf.h" #include "hz/fs_path.h" #include "rconfig/rconfig_mini.h" #include "app_pcrecpp.h" #include "storage_detector_win32.h" #include "storage_detector_helpers.h" /** \file
3ware Windows detection:
For 3ware 9xxx only.
Call as: smartctl -i sd[a-z],N
	N is port, a-z is logical drive (unit) provided by controller.
	N is limited to [0, 31] in the code.
	The sd[a-z] device actually exists as \\\\.\\PhysicalDrive[0-N].
		No idea how to check if it's 3ware.
Call as: smarctl -i tw_cli/cx/py
	This runs tw_cli tool and parses the output; controller x, port y.
	tw_cli may be needed for older controllers / drivers.
	In tw_cli mode only limited information-gathering is supported.
tw_cli (part of 3DM2) is automatically added to system PATH,
	no need to look for it.
3DM2 install can be detected by checking:
	HKEY_USERS\\.DEFAULT\\Software\\3ware\\3DM2, InstallPath
Another option for detection (whether it's 3ware) would be getting
	\\\\.\\PhysicalDrive0 properties, like smartctl does.
Newer (added after 5.39.1) smartctl supports --scan-open, which will give us:
	/dev/sda,0 -d ata (opened)
	/dev/sda,1 -d ata (opened)
-d 3ware is not needed under Windows. We should treat sda as pd0
	and remove pd0 from PhysicalDrive-detected list.
Running smartctl on sda gives almost the same result as on sda,0.

smartctl --scan-open output for win32 with 3ware RAID:
------------------------------------------------------------------
/dev/sda,0 -d ata (opened)
/dev/sda,1 -d ata (opened)
------------------------------------------------------------------


Intel Matrix RAID (since smartmontools SVN version on 2011-02-04):
Call as: "/dev/csmi[0-9],N" where N is the port behind the logical
	scsi controller "\\\\.\\Scsi[0-9]:".
	The prefix "/dev/" is optional.
	This is detected (with /dev/ prefix) by --scan-open.
The drives may be duplicated as pdX (with X and N being unrelated).
	This usually happens when Intel RAID drivers are installed, even
	if no RAID configuration is present. For example, on a laptop with
	Intel chipset and Intel drivers installed, we have /dev/csmi0,0 and
	pd0 for the first HDD, and /dev/csmi0,1 for DVD (no pdX entry there).
	This is what --scan-open looks like:
------------------------------------------------------------------
/dev/sda -d ata # /dev/sda, ATA device
/dev/csmi0,0 -d ata # /dev/csmi0,0, ATA device
/dev/csmi0,1 -d ata # /dev/csmi0,1, ATA device
------------------------------------------------------------------
	We filter out pdX devices using serial numbers (unless "-q noserial"
	is given to smartctl), and prefer csmi to pd (since csmi provides more features).

*/ struct DriveLetterInfo { std::set physical_drives; ///< N in pdN std::string volume_name; ///< Volume name }; namespace { /// Check which physical drives each drive letter (C, D, ...) spans across. std::map win32_get_drive_letter_map() { std::map drive_letter_map; std::bitset<32> drives(GetLogicalDrives()); // Check which drives are fixed std::vector good_drives; for (char c = 'A'; c <= 'Z'; ++c) { if (drives[c - 'A']) { // drive is present debug_out_dump("app", "Windows drive found: " << c << ".\n"); if (GetDriveType((c + std::string(":\\")).c_str()) == DRIVE_FIXED) { debug_out_dump("app", "Windows drive " << c << " is fixed.\n"); good_drives.push_back(c); } } } // Try to open each drive, check its disk extents for (std::size_t i = 0; i < good_drives.size(); ++i) { char drive = good_drives[i]; std::string drive_str = std::string("\\\\.\\") + drive + ":"; HANDLE h = CreateFileA( drive_str.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_RANDOM_ACCESS, NULL); if (h == INVALID_HANDLE_VALUE) { debug_out_warn("app", "Windows drive " << drive << " cannot be opened.\n"); continue; } DWORD bytesReturned = 0; VOLUME_DISK_EXTENTS vde; if (!DeviceIoControl( h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, &vde, sizeof(vde), &bytesReturned, NULL)) { debug_out_warn("app", "Windows drive " << drive << " is not mapped to any physical drives.\n"); continue; } std::set physical_drives; for (int i = 0; i < vde.NumberOfDiskExtents; ++i) { physical_drives.insert(vde.Extents[i].DiskNumber); debug_out_dump("app", "Windows drive " << drive << " corresponds to physical drive " << vde.Extents[i].DiskNumber << ".\n"); } std::string volume_name; wchar_t volume_name_w[MAX_PATH+1] = {0}; DWORD dummy = 0; hz::scoped_array drive_name(hz::win32_utf8_to_utf16((drive + std::string(":\\")).c_str())); if (drive_name && GetVolumeInformationW(drive_name.get(), volume_name_w, MAX_PATH+1, NULL, &dummy, &dummy, NULL, 0)) { volume_name = hz::win32_utf16_to_utf8_string(volume_name_w); } DriveLetterInfo dli; dli.physical_drives = physical_drives; dli.volume_name = volume_name; drive_letter_map[drive] = dli; } return drive_letter_map; } /// Run "smartctl --scan-open" and pick the devices which have /// a port parameter. We don't pick the others because the may /// conflict with pd* devices, and we like pd* better than sd*. std::string get_scan_open_multiport_devices(std::vector& drives, ExecutorFactoryRefPtr ex_factory, const std::map& drive_letter_map, std::set& equivalent_pds) { debug_out_info("app", "Getting multi-port devices through smartctl --scan-open...\n"); hz::intrusive_ptr smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl); std::string smartctl_binary = get_smartctl_binary(); if (smartctl_binary.empty()) { debug_out_error("app", DBG_FUNC_MSG << "Smartctl binary is not set in config.\n"); return "Smartctl binary is not specified in configuration."; } std::string smartctl_def_options; rconfig::get_data("system/smartctl_options", smartctl_def_options); if (!smartctl_def_options.empty()) smartctl_def_options += " "; smartctl_ex->set_command(Glib::shell_quote(smartctl_binary), smartctl_def_options + "--scan-open"); if (!smartctl_ex->execute() || !smartctl_ex->get_error_msg().empty()) { debug_out_warn("app", DBG_FUNC_MSG << "Smartctl binary did not execute cleanly.\n"); return smartctl_ex->get_error_msg(); } // any_to_unix is needed for windows std::string output = hz::string_trim_copy(hz::string_any_to_unix_copy(smartctl_ex->get_stdout_str())); if (output.empty()) { debug_out_error("app", DBG_FUNC_MSG << "Smartctl returned an empty output.\n"); return "Smartctl returned an empty output."; } // if we've reached smartctl port limit (older versions may have smaller limits), abort. if (app_pcre_match("/UNRECOGNIZED OPTION/mi", output)) { return "Smartctl doesn't support --scan-open switch."; } std::vector lines; hz::string_split(output, '\n', lines, true); // /dev/sda,0 -d ata (opened) // /dev/sda,1 -d ata (opened) // /dev/sda -d sat # /dev/sda [SAT], ATA device // /dev/sde,2 -d ata [ATA] (opened) // we only pick the ones with ports const pcrecpp::RE port_re = app_pcre_re("/^(/dev/[a-z0-9]+),([0-9]+)[ \\t]+-d[ \\t]+([^ \\t\\n]+)/i"); const pcrecpp::RE dev_re = app_pcre_re("/^/dev/sd([a-z])$/"); for (std::size_t i = 0; i < lines.size(); ++i) { std::string dev, port_str, type; if (port_re.PartialMatch(hz::string_trim_copy(lines.at(i)), &dev, &port_str, &type)) { std::string sd_letter; int drive_num = -1; if (dev_re.PartialMatch(dev, &sd_letter)) { // don't use pd* devices equivalent to these sd* devices. drive_num = sd_letter.at(0) - 'a'; equivalent_pds.insert(drive_num); } std::string full_dev = dev + "," + port_str; StorageDeviceRefPtr drive(new StorageDevice(full_dev, type)); std::map letters_volnames; for (std::map::const_iterator iter = drive_letter_map.begin(); iter != drive_letter_map.end(); ++iter) { if (iter->second.physical_drives.count(drive_num) > 0) { letters_volnames[iter->first] = iter->second.volume_name; } } drive->set_drive_letters(letters_volnames); drives.push_back(drive); } } return std::string(); } /// Find and execute areca cli with specified options, return its output through \c output. /// \return error message inline std::string execute_areca_cli(ExecutorFactoryRefPtr ex_factory, const std::string& cli_binary, const std::string& command_options, std::string& output) { hz::intrusive_ptr executor = ex_factory->create_executor(ExecutorFactory::ExecutorArecaCli); executor->set_command(Glib::shell_quote(cli_binary), command_options); if (!executor->execute() || !executor->get_error_msg().empty()) { debug_out_warn("app", DBG_FUNC_MSG << "Error while executing Areca cli binary.\n"); } // any_to_unix is needed for windows output = hz::string_trim_copy(hz::string_any_to_unix_copy(executor->get_stdout_str())); if (output.empty()) { debug_out_error("app", DBG_FUNC_MSG << "Areca cli returned an empty output.\n"); return "Areca CLI returned an empty output."; } return std::string(); } /**
Get the drives on Areca controller using Areca cli tool.
Note that the drives are inserted in the order they are detected.

There are 3 formats of "disk info" output:

1. No expanders:
------------------------------------------------------------
  # Ch# ModelName                       Capacity  Usage
===============================================================================
  1  1  INTEL SSDSA2M160G2GC             160.0GB  System
  2  2  INTEL SSDSA2M160G2GC             160.0GB  System
  3  3  INTEL SSDSA2M160G2GC             160.0GB  System
  4  4  INTEL SSDSA2M160G2GC             160.0GB  System
  5  5  Hitachi HDS724040ALE640         4000.8GB  Storage
  6  6  Hitachi HDS724040ALE640         4000.8GB  Storage
  7  7  Hitachi HDS724040ALE640         4000.8GB  Storage
  8  8  Hitachi HDS724040ALE640         4000.8GB  Storage
  9  9  Hitachi HDS724040ALE640         4000.8GB  Storage
 10 10  Hitachi HDS724040ALE640         4000.8GB  Storage
 11 11  Hitachi HDS724040ALE640         4000.8GB  Backup
 12 12  Hitachi HDS724040ALE640         4000.8GB  Backup
===============================================================================
GuiErrMsg<0x00>: Success.
------------------------------------------------------------

2. No expanders (this output comes from CLI documentation, possibly an old format):
------------------------------------------------------------
 #   ModelName        Serial#          FirmRev     Capacity  State
===============================================================================
 1   ST3250620NS      5QE1CP8S         3.AEE        250.1GB  RaidSet Member(1)
 2   ST3250620NS      5QE1CP8S         3.AEE        250.1GB  RaidSet Member(1)
....(snip)....
12   ST3250620NS      5QE1CP8S         3.AEE        250.1GB  RaidSet Member(1)
===============================================================================
GuiErrMsg<0x00>: Success.
------------------------------------------------------------

3. Expanders:
------------------------------------------------------------
  # Enc# Slot#   ModelName                        Capacity  Usage
===============================================================================
  1  01  Slot#1  N.A.                                0.0GB  N.A.
  2  01  Slot#2  N.A.                                0.0GB  N.A.
  3  01  Slot#3  N.A.                                0.0GB  N.A.
  4  01  Slot#4  N.A.                                0.0GB  N.A.
  5  01  Slot#5  N.A.                                0.0GB  N.A.
  6  01  Slot#6  N.A.                                0.0GB  N.A.
  7  01  Slot#7  N.A.                                0.0GB  N.A.
  8  01  Slot#8  N.A.                                0.0GB  N.A.
  9  02  SLOT 01 N.A.                                0.0GB  N.A.
 10  02  SLOT 02 N.A.                                0.0GB  N.A.
 11  02  SLOT 03 N.A.                                0.0GB  N.A.
 12  02  SLOT 04 N.A.                                0.0GB  N.A.
 13  02  SLOT 05 N.A.                                0.0GB  N.A.
 14  02  SLOT 06 N.A.                                0.0GB  N.A.
 15  02  SLOT 07 N.A.                                0.0GB  N.A.
 16  02  SLOT 08 N.A.                                0.0GB  N.A.
 17  02  SLOT 09 N.A.                                0.0GB  N.A.
 18  02  SLOT 10 N.A.                                0.0GB  N.A.
 19  02  SLOT 11 N.A.                                0.0GB  N.A.
 20  02  SLOT 12 N.A.                                0.0GB  N.A.
 21  02  SLOT 13 N.A.                                0.0GB  N.A.
 22  02  SLOT 14 ST910021AS                        100.0GB  Free
 23  02  SLOT 15 WDC WD3200BEVT-75A23T0            320.1GB  HotSpare[Global]
 24  02  SLOT 16 N.A.                                0.0GB  N.A.
 25  02  SLOT 17 Hitachi HDS724040ALE640          4000.8GB  Raid Set # 000
 26  02  SLOT 18 ST31500341AS                     1500.3GB  Raid Set # 000
 27  02  SLOT 19 ST3320620AS                       320.1GB  Raid Set # 000
 28  02  SLOT 20 ST31500341AS                     1500.3GB  Raid Set # 000
 29  02  SLOT 21 ST3500320AS                       500.1GB  Raid Set # 000
 30  02  SLOT 22 Hitachi HDS724040ALE640          4000.8GB  Raid Set # 000
 31  02  SLOT 23 Hitachi HDS724040ALE640          4000.8GB  Raid Set # 000
 32  02  SLOT 24 Hitachi HDS724040ALE640          4000.8GB  Raid Set # 000
 33  02  EXTP 01 N.A.                                0.0GB  N.A.
 34  02  EXTP 02 N.A.                                0.0GB  N.A.
 35  02  EXTP 03 N.A.                                0.0GB  N.A.
 36  02  EXTP 04 N.A.                                0.0GB  N.A.
===============================================================================
GuiErrMsg<0x00>: Success.
------------------------------------------------------------
*/ inline std::string areca_cli_get_drives(const std::string& cli_binary, const std::string& dev, int controller, std::vector& drives, ExecutorFactoryRefPtr ex_factory) { debug_out_info("app", "Getting available Areca drives (ports) for controller " << controller << " through Areca CLI...\n"); // TODO Support controller number. // So far it seems the only way to pass the controller number to CLI is to use // the interactive mode. std::string output; std::string error = execute_areca_cli(ex_factory, cli_binary, "disk info", output); if (!error.empty()) { return error; } // split to lines std::vector lines; hz::string_split(output, '\n', lines, true); enum FormatType { FormatUnknown, FormatNoEnc1, FormatNoEnc2, FormatEnc }; pcrecpp::RE noenc1_header_re = app_pcre_re("/^\\s*#\\s+Ch#/mi"); pcrecpp::RE noenc2_header_re = app_pcre_re("/^\\s*#\\s+ModelName/mi"); pcrecpp::RE exp_header_re = app_pcre_re("/^\\s*#\\s+Enc#/mi"); FormatType format_type = FormatUnknown; for (std::size_t i = 0; i < lines.size(); ++i) { if (noenc1_header_re.PartialMatch(lines.at(i))) { format_type = FormatNoEnc1; break; } else if (noenc2_header_re.PartialMatch(lines.at(i))) { format_type = FormatNoEnc2; break; } else if (exp_header_re.PartialMatch(lines.at(i))) { format_type = FormatEnc; break; } } if (format_type == FormatUnknown) { debug_out_warn("app", "Could not read Areca CLI output: No valid header found.\n"); return "Could not read Areca CLI output: No valid header found."; } // Note: These may not match the full model, but just the first part is sufficient for comparison with "N.A.". pcrecpp::RE noexp1_port_re = app_pcre_re("/^\\s*[0-9]+\\s+([0-9]+)\\s+([^\\s]+)/mi"); // matches port, model. pcrecpp::RE noexp2_port_re = app_pcre_re("/^\\s*([0-9]+)\\s+([^\\s]+)/mi"); // matches port, model. pcrecpp::RE exp_port_re = app_pcre_re("/^\\s*[0-9]+\\s+([0-9]+)\\s+(?:Slot#|SLOT\\s+)([0-9]+)\\s+([^\\s]+)/mi"); // matches enclosure, port, model. bool has_enclosure = (format_type == FormatEnc); if (has_enclosure) { debug_out_dump("app", "Areca controller seems to have enclosures.\n"); } else { debug_out_dump("app", "Areca controller doesn't have any enclosures.\n"); } for (std::size_t i = 0; i < lines.size(); ++i) { std::string port_str, model_str; if (has_enclosure) { std::string enclosure_str; if (exp_port_re.PartialMatch(hz::string_trim_copy(lines.at(i)), &enclosure_str, &port_str, &model_str)) { if (model_str != "N.A.") { int port = -1, enclosure = -1; hz::string_is_numeric(port_str, port); hz::string_is_numeric(enclosure_str, enclosure); drives.push_back(StorageDeviceRefPtr(new StorageDevice(dev, "areca," + hz::number_to_string(port) + "/" + hz::number_to_string(enclosure)))); debug_out_info("app", "Added Areca drive " << drives.back()->get_device_with_type() << ".\n"); } } } else { // no enclosures pcrecpp::RE port_re = (format_type == FormatNoEnc1 ? noexp1_port_re : noexp2_port_re); if (port_re.PartialMatch(hz::string_trim_copy(lines.at(i)), &port_str, &model_str)) { if (model_str != "N.A.") { int port = -1; hz::string_is_numeric(port_str, port); drives.push_back(StorageDeviceRefPtr(new StorageDevice(dev, "areca," + hz::number_to_string(port)))); debug_out_info("app", "Added Areca drive " << drives.back()->get_device_with_type() << ".\n"); } } } } return std::string(); } /**
Areca Windows:
Call as:
	- smartctl -i -d areca,N /dev/arcmsr0  (N is [1,24]).
	- smartctl -i -d areca,N/E /dev/arcmsr0  (N is [1,128], E is [1,8]).
The models that have "ix" (case-insensitive) in their model names
have the enclosures and require the N,E syntax.
Note: Using N/E syntax with non-enclosure cards seems to work
regardless of the value of E.

Detection:
	Check the registry keys for Areca management software and
	the CLI utility. One of them has to be installed.
Check if CLI is installed. If it is, run it and parse the output. This should
	give us the populated port list (non-enclosure), and the populated port
	and expander list (enclosure models).
	There are two problems with CLI:
	If no controller is present, it crashes.
		We try the following first:
			smartctl -i -d areca,1 /dev/arcmsr0
		If it contains "arcmsr0: No Areca controller found" (Windows), or
			"Smartctl open device: /dev/arcmsr0 [areca_disk#01_enc#01] failed: No such device" (Linux),
			then there's no controller there.
	I don't see how it reports different controllers (there's only one list).
		We ignore this for now.
If CLI is not installed, do the brute-force way:
	For arcmsr0, 1, ..., 8 (until we see "No Areca controller found"):
		Scan -d areca,[1-24] /dev/arcmsrN
		If at least one drive was found, it's a no-enclosure controller.
		If no drives were found, it's probably an enclosure controller, try
		-d areca,[1-128]/[1-8] /dev/arcmsrN
			It's 2-3 drives a second on an empty port, so some limits are set in config.
*/ inline std::string detect_drives_win32_areca(std::vector& drives, ExecutorFactoryRefPtr ex_factory) { debug_out_info("app", DBG_FUNC_MSG << "Detecting drives behind Areca controller(s)...\n"); int scan_controllers = rconfig::get_data("system/win32_areca_scan_controllers"); if (scan_controllers == 0) { // disabled debug_out_info("app", "Areca controller scanning is disabled through config.\n"); return std::string(); } // Check if Areca tools are installed std::string cli_inst_path; hz::win32_get_registry_value_string(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\CLI", "InstallPath", cli_inst_path); if (cli_inst_path.empty()) { hz::win32_get_registry_value_string(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\CLI", "InstallPath", cli_inst_path); } if (scan_controllers == 2) { // auto std::string http_inst_path; hz::win32_get_registry_value_string(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\archttp", "InstallPath", http_inst_path); if (http_inst_path.empty()) { hz::win32_get_registry_value_string(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\archttp", "InstallPath", http_inst_path); } if (cli_inst_path.empty() && http_inst_path.empty()) { debug_out_info("app", "No Areca software found. Install Areca CLI or set \"system/win32_areca_scan_controllers\" config key to 1 to force scanning.\n"); return std::string(); } if (http_inst_path.empty()) { debug_out_dump("app", "Areca HTTP installation not found.\n"); } else { debug_out_dump("app", "Areca HTTP installation found at: \"" << http_inst_path << "\".\n"); } } if (cli_inst_path.empty()) { debug_out_dump("app", "Areca CLI installation not found.\n"); } else { debug_out_dump("app", "Areca CLI installation found at: \"" << cli_inst_path << "\".\n"); } bool cli_found = !cli_inst_path.empty(); int use_cli = rconfig::get_data("system/win32_areca_use_cli"); bool scan_detect = (use_cli != 1); // Whether to detect using sequential port scanning. Only do that if CLI is not forced. if (use_cli == 2) { // auto use_cli = cli_found; } std::string cli_binary; if (use_cli == 1) { cli_binary = rconfig::get_data("system/areca_cli_binary"); if (!hz::FsPath(cli_binary).is_absolute() && !cli_inst_path.empty()) { cli_binary = hz::FsPath(cli_inst_path).append(cli_binary).str(); } if (hz::FsPath(cli_binary).is_absolute() && !hz::FsPath(cli_binary).exists()) { use_cli = 0; debug_out_dump("app", "Areca CLI binary \"" << cli_binary << "\" not found.\n"); } } hz::intrusive_ptr smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl); // --- CLI mode // Since CLI may segfault if there are no drives, test the controller presence first. // It doesn't matter if we use areca,N or areca,N/E - we will still get a different // error if there's no controller. if (use_cli) { debug_out_dump("app", "Testing Areca controller presence using smartctl...\n"); StorageDeviceRefPtr drive(new StorageDevice("/dev/arcmsr0", "areca,1")); std::string error_msg = drive->fetch_basic_data_and_parse(smartctl_ex); std::string output = drive->get_info_output(); if (app_pcre_match("/No Areca controller found/mi", output) || app_pcre_match("/Smartctl open device: .* failed: No such device/mi", output) ) { use_cli = 0; scan_detect = false; debug_out_dump("app", "Areca controller not found.\n"); } } if (use_cli) { debug_out_info("app", "Scanning Areca drives using CLI...\n"); int cli_max_controllers = 1; // TODO controller # with CLI. for (int controller_no = 0; controller_no < cli_max_controllers; ++controller_no) { std::string error_msg = areca_cli_get_drives(cli_binary, "/dev/arcmsr" + hz::number_to_string(controller_no), controller_no, drives, ex_factory); // If we get an error on controller 0, fall back to no-cli detection. if (!error_msg.empty() && controller_no == 0) { use_cli = 0; debug_out_warn("app", "Areca scan using CLI failed.\n"); if (scan_detect) { debug_out_dump("app", "Trying manual Areca scan because of a CLI error.\n"); } } } } // If CLI scanning failed (and it was not forced), or if there was no CLI, scan manually. if (use_cli == 0 && scan_detect) { debug_out_info("app", "Manually scanning Areca controllers and ports...\n"); int max_controllers = rconfig::get_data("system/win32_areca_max_controllers"); int max_noenc_ports = rconfig::get_data("system/win32_areca_neonc_max_scan_port"); max_noenc_ports = std::max(1, std::min(24, max_noenc_ports)); // 1-24 sanity check int max_enc_ports = rconfig::get_data("system/win32_areca_enc_max_scan_port"); max_enc_ports = std::max(1, std::min(128, max_enc_ports)); // 1-128 sanity check int max_enclosures = rconfig::get_data("system/win32_areca_enc_max_enclosure"); max_enclosures = std::max(1, std::min(8, max_enclosures)); // 1-8 sanity check for (int controller_no = 0; controller_no < max_controllers; ++controller_no) { std::string dev = std::string("/dev/arcmsr") + hz::number_to_string(controller_no); // First, scan using the areca,N format. debug_out_dump("app", "Starting brute-force port scan (no-enclosure) on 1-" << max_noenc_ports << " ports, device \"" << dev << "\". Change the maximum by setting \"system/win32_areca_neonc_max_scan_port\" config key.\n"); std::size_t old_drive_count = drives.size(); std::string last_output; std::string error_msg = smartctl_scan_drives_sequentially(dev, "areca,%d", 1, max_noenc_ports, drives, ex_factory, last_output); // If the scan stopped because of no controller, stop it all. if (!error_msg.empty() && (app_pcre_match("/No Areca controller found/mi", last_output) || app_pcre_match("/Smartctl open device: .* failed: No such device/mi", last_output)) ) { debug_out_dump("app", "Areca controller " << controller_no << " not present, stopping sequential scan.\n"); break; } // If no drives were added (but the controller is present), it may be due to // expander syntax requirement. Try that. if (drives.size() == old_drive_count) { for (int enclosure_no = 1; enclosure_no < max_enclosures; ++enclosure_no) { debug_out_dump("app", "Starting brute-force port scan (enclosure #" << enclosure_no << ") on 1-" << max_enc_ports << " ports, device \"" << dev << "\". Change the maximums by setting \"system/win32_areca_onc_max_scan_port\" and \"system/win32_areca_enc_max_enclosure\" config keys.\n"); error_msg = smartctl_scan_drives_sequentially(dev, "areca,%d/" + hz::number_to_string(enclosure_no), 1, max_enc_ports, drives, ex_factory, last_output); } } debug_out_dump("app", "Brute-force port scan finished.\n"); } } debug_out_info("app", DBG_FUNC_MSG << "Finished scanning Areca controllers.\n"); return std::string(); } } // smartctl accepts various variants, the most straight being pdN, // (or /dev/pdN, /dev/ being optional) where N comes from // "\\.\PhysicalDriveN" (winnt only). // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx std::string detect_drives_win32(std::vector& drives, ExecutorFactoryRefPtr ex_factory) { std::vector error_msgs; std::string error_msg; // Construct drive letter map debug_out_info("app", "Checking which drive corresponds to which \\\\.\\PhysicalDriveN device...\n"); std::map drive_letter_map = win32_get_drive_letter_map(); hz::intrusive_ptr smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl); // Fetch multiport devices using --scan-open. // Note that this may return duplicates (e.g. /dev/sda and /dev/csmi0,0) std::set used_pds; error_msg = get_scan_open_multiport_devices(drives, ex_factory, drive_letter_map, used_pds); if (!error_msg.empty()) { error_msgs.push_back(error_msg); } bool multiport_found = !drives.empty(); bool areca_open_found = false; // whether areca devices were found at --scan-open time. // Find out their serial numbers and whether there are Arecas there. std::map serials; for (std::size_t i = 0; i < drives.size(); ++i) { std::string local_error = drives.at(i)->fetch_basic_data_and_parse(smartctl_ex); if (!local_error.empty()) { debug_out_info("app", "Smartctl returned with an error: " << local_error << "\n"); // Don't exit, just report it. } if (!drives.at(i)->get_serial_number().empty()) { // add model as well, who knows, there may be duplicates across vendors std::string drive_serial_id = drives.at(i)->get_model_name() + "_" + drives.at(i)->get_serial_number(); serials[drive_serial_id] = drives.at(i); } // See if there are any areca devices. This is not implemented yet by smartctl (as of 6.0), // but if it ever is, we can skip our own detection below. std::string type_arg = drives.at(i)->get_type_argument(); if (type_arg.find("areca") != std::string::npos) { areca_open_found = true; } } // Scan PhysicalDriveN entries debug_out_info("app", "Starting sequential scan of \\\\.\\PhysicalDriveN devices...\n"); int num_failed = 0; for (int drive_num = 0; ; ++drive_num) { // If the drive was already encountered in --scan-open (with a port number), skip it. if (used_pds.count(drive_num) > 0) { debug_out_dump("app", "pd" << drive_num << " already encountered in --scan-open output (as sd*), skipping.\n"); continue; } std::string phys_name = hz::string_sprintf("\\\\.\\PhysicalDrive%d", drive_num); // If the drive is openable, then it's there. Yes, CreateFile() is open, not create. // NOTE: Administrative privileges are required to open it. // We don't use any long/unopenable files here, so use the ANSI version. HANDLE h = CreateFileA(phys_name.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); // The numbers are usually consecutive, but sometimes there are holes when // removable devices are removed. Try 3 extra drives just in case. if (h == INVALID_HANDLE_VALUE) { ++num_failed; debug_out_dump("app", "Could not open \"" << phys_name << "\".\n"); if (num_failed >= 3) { debug_out_dump("app", "Stopping sequential scan.\n"); break; } else { continue; } } CloseHandle(h); debug_out_dump("app", "Successfully opened \"" << phys_name << "\".\n"); StorageDeviceRefPtr drive(new StorageDevice(hz::string_sprintf("pd%d", drive_num))); std::map letters_volnames; for (std::map::const_iterator iter = drive_letter_map.begin(); iter != drive_letter_map.end(); ++iter) { if (iter->second.physical_drives.count(drive_num) > 0) { letters_volnames[iter->first] = iter->second.volume_name; } } drive->set_drive_letters(letters_volnames); debug_out_dump("app", "Drive letters for: " << drive->get_device() << ": " << drive->format_drive_letters(true) << ".\n"); // Sometimes, a single physical drive may be accessible from both "/.//PhysicalDriveN" // and "/.//Scsi2" (e.g. pd0 and csmi2,1). Prefer the port-having ones (which is from --scan-open), // they contain more information. // The only way to detect these duplicates is to compare them using serial numbers. if (!serials.empty()) { std::string local_error = drive->fetch_basic_data_and_parse(smartctl_ex); if (!local_error.empty()) { debug_out_info("app", "Smartctl returned with an error: " << local_error << "\n"); // Don't exit, just report it. } std::string drive_serial_id = drive->get_model_name() + "_" + drive->get_serial_number(); // A serial may be empty if "-q noserial" was given to smartctl. if (!drive->get_serial_number().empty() && serials.count(drive_serial_id) > 0) { debug_out_info("app", "Skipping drive due to duplicate S/N: model: \"" << drive->get_model_name() << "\", S/N: \"" << drive->get_serial_number() << "\".\n"); // Copy the drive letters over to previously detected one (since we can't detect drive letters there). serials[drive_serial_id]->set_drive_letters(drive->get_drive_letters()); continue; } } debug_out_info("app", "Added drive " << drive->get_device_with_type() << ".\n"); drives.push_back(drive); } // If smartctl --scan-open returns no "sd*,port"-style devices, // check if 3dm2 is installed and execute "tw_cli show" to get // the controllers, then use the "tw_cli/cN/pN"-style drives with smartctl. This may // happen with older smartctl which doesn't support --scan-open, or with // drivers that don't allow proper SMART commands. if (!multiport_found) { debug_out_info("app", "Checking for additional 3ware devices...\n"); std::string inst_path; hz::win32_get_registry_value_string(HKEY_USERS, ".DEFAULT\\Software\\3ware\\3DM2", "InstallPath", inst_path); if (!inst_path.empty()) { debug_out_dump("app", "3ware 3DM2 found at\"" << inst_path << "\".\n"); std::vector controllers; error_msg = tw_cli_get_controllers(ex_factory, controllers); // ignore the error message above, it's of no use. for (std::size_t i = 0; i < controllers.size(); ++i) { // don't specify device, it's ignored in tw_cli mode tw_cli_get_drives("", controllers.at(i), drives, ex_factory, true); } } else { debug_out_info("app", "3ware 3DM2 not installed.\n"); } } if (!areca_open_found) { detect_drives_win32_areca(drives, ex_factory); if (!error_msg.empty()) { error_msgs.push_back(error_msg); } } return hz::string_join(error_msgs, "\n"); } #endif // CONFIG_KERNEL_FAMILY_WINDOWS /// @} gsmartcontrol-1.1.4/src/applib/storage_detector_win32.h000066400000000000000000000014061417717230600232120ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef STORAGE_DETECTOR_WIN32_H #define STORAGE_DETECTOR_WIN32_H #include "hz/hz_config.h" // CONFIG_* #if defined CONFIG_KERNEL_FAMILY_WINDOWS #include #include #include "executor_factory.h" #include "storage_device.h" /// Detect drives in Windows std::string detect_drives_win32(std::vector& drives, ExecutorFactoryRefPtr ex_factory); #endif #endif /// @} gsmartcontrol-1.1.4/src/applib/storage_device.cpp000066400000000000000000000636041417717230600221610ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #include "rconfig/rconfig_mini.h" #include "hz/string_algo.h" // string_trim_copy, string_any_to_unix_copy #include "hz/fs_path.h" // FsPath #include "hz/fs_path_utils.h" // hz::filename_make_safe #include "hz/format_unit.h" // hz::format_date #include "app_pcrecpp.h" #include "storage_device.h" #include "smartctl_parser.h" #include "storage_settings.h" #include "smartctl_executor.h" std::string StorageDevice::get_type_readable_name(StorageDevice::detected_type_t type) { switch (type) { case detected_type_unknown: return "unknown"; case detected_type_invalid: return "invalid"; case detected_type_cddvd: return "cd/dvd"; case detected_type_raid: return "raid"; } return "[internal_error]"; } std::string StorageDevice::get_status_name(StorageDevice::status_t status, bool use_yesno) { switch (status) { case status_enabled: return (use_yesno ? "Yes" : "Enabled"); case status_disabled: return (use_yesno ? "No" : "Disabled"); case status_unsupported: return "Unsupported"; case status_unknown: return "Unknown"; }; return "[internal_error]"; } StorageDevice::StorageDevice(const std::string& dev_or_vfile, bool is_virtual) { detected_type_ = detected_type_unknown; // force_type_ = false; is_virtual_ = is_virtual; is_manually_added_ = false; parse_status_ = parse_status_none; test_is_active_ = false; if (is_virtual) { virtual_file_ = dev_or_vfile; } else { device_ = dev_or_vfile; } } StorageDevice::StorageDevice(const std::string& dev, const std::string& type_arg) { detected_type_ = detected_type_unknown; // force_type_ = false; is_virtual_ = false; is_manually_added_ = false; parse_status_ = parse_status_none; test_is_active_ = false; device_ = dev; type_arg_ = type_arg; } StorageDevice::StorageDevice(const StorageDevice& other) { *this = other; } StorageDevice& StorageDevice::operator=(const StorageDevice& other) { info_output_ = other.info_output_; full_output_ = other.full_output_; device_ = other.device_; type_arg_ = other.type_arg_; extra_args_ = other.extra_args_; // force_type_ = other.force_type_; is_virtual_ = other.is_virtual_; virtual_file_ = other.virtual_file_; is_manually_added_ = other.is_manually_added_; parse_status_ = other.parse_status_; test_is_active_ = other.test_is_active_; detected_type_ = other.detected_type_; smart_supported_ = other.smart_supported_; smart_enabled_ = other.smart_enabled_; aodc_status_ = other.aodc_status_; model_name_ = other.model_name_; family_name_ = other.family_name_; size_ = other.size_; health_property_ = other.health_property_; properties_ = other.properties_; return *this; } void StorageDevice::clear_fetched(bool including_outputs) { if (including_outputs) { info_output_.clear(); full_output_.clear(); } parse_status_ = parse_status_none; test_is_active_ = false; // not sure smart_supported_.reset(); smart_enabled_.reset(); model_name_.reset(); aodc_status_.reset(); family_name_.reset(); size_.reset(); health_property_.reset(); properties_.clear(); } std::string StorageDevice::fetch_basic_data_and_parse(hz::intrusive_ptr smartctl_ex) { if (this->test_is_active_) return "A test is currently being performed on this drive."; this->clear_fetched(); // clear everything fetched before, including outputs // We don't use "--all" - it may cause really screwed up the output (tests, etc...). // This looks just like "--info" only on non-smart devices. std::string error_msg = execute_device_smartctl("--info --health --capabilities", smartctl_ex, this->info_output_, true); // set type to invalid if needed // Smartctl 5.39 cvs/svn version defaults to usb type on at least linux and windows. // This means that the old SCSI identify command isn't executed by default, // and there is no information about the device manufacturer/etc... in the output. // We detect this and set the device type to scsi to at least have _some_ info. if (get_detected_type() == detected_type_invalid && get_type_argument().empty()) { debug_out_info("app", "The device seems to be of different type than auto-detected, trying again with scsi.\n"); this->set_type_argument("scsi"); return this->fetch_basic_data_and_parse(smartctl_ex); // try again with scsi } // Since the type error leads to "command line didn't parse" error here, // we do this after the scsi stuff. if (!error_msg.empty()) { // Still try to parse something. For some reason, running smartctl on usb flash drive // under winxp returns "command line didn't parse", while actually printing its name. this->parse_basic_data(false, true); return error_msg; } // Set some properties too - they are needed for e.g. AODC status, etc... return this->parse_basic_data(true); } std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_signal) { this->clear_fetched(false); // clear everything fetched before, except outputs if (this->info_output_.empty()) { debug_out_error("app", DBG_FUNC_MSG << "String to parse is empty.\n"); return "Cannot read information from an empty string."; } std::string version, version_full; if (!SmartctlParser::parse_version(this->info_output_, version, version_full)) // is this smartctl data at all? return "Cannot get smartctl version information."; // Detect type. note: we can't distinguish between sata and scsi (on linux, for -d ata switch). // Sample output line 1 (encountered on a CDRW drive): // SMART support is: Unavailable - Packet Interface Devices [this device: CD/DVD] don't support ATA SMART // Sample output line 2 (encountered on a BDRW drive): // Device type: CD/DVD if (app_pcre_match("/this device: CD\\/DVD/mi", info_output_) || app_pcre_match("/^Device type:\\s+CD\\/DVD/mi", info_output_)) { debug_out_dump("app", "Drive " << get_device_with_type() << " seems to be a CD/DVD device.\n"); this->set_detected_type(detected_type_cddvd); // This was encountered on a csmi soft-raid under windows with pd0. // The device reported that it had smart supported and enabled. // Product: Raid 5 Volume } else if (app_pcre_match("/Product:[ \\t]*Raid/mi", info_output_)) { debug_out_dump("app", "Drive " << get_device_with_type() << " seems to be a RAID volume/controller.\n"); this->set_detected_type(detected_type_raid); } // RAID volume may report that it has SMART, but it obviously doesn't. if (get_detected_type() == detected_type_raid) { smart_supported_ = false; smart_enabled_ = false; } else { // Note: We don't use SmartctlParser here, because this information // may be in some other format. If this information is valid, only then it's // passed to SmartctlParser. // Compared to SmartctlParser, this one is much looser. // Don't put complete messages here - they change across smartctl versions. if (app_pcre_match("/^SMART support is:[ \\t]*Unavailable/mi", info_output_) // cdroms output this || app_pcre_match("/Device does not support SMART/mi", info_output_) // usb flash drives, non-smart hds || app_pcre_match("/Device Read Identity Failed/mi", info_output_)) { // solaris scsi, unsupported by smartctl (maybe others?) smart_supported_ = false; smart_enabled_ = false; } else if (app_pcre_match("/^SMART support is:[ \\t]*Available/mi", info_output_) || app_pcre_match("/^SMART support is:[ \\t]*Ambiguous/mi", info_output_)) { smart_supported_ = true; if (app_pcre_match("/^SMART support is:[ \\t]*Enabled/mi", info_output_)) { smart_enabled_ = true; } else if (app_pcre_match("/^SMART support is:[ \\t]*Disabled/mi", info_output_)) { smart_enabled_ = false; } } } std::string model; if (app_pcre_match("/^Device Model:[ \\t]*(.*)$/mi", info_output_, &model)) { // HD's and cdroms model_name_ = hz::string_remove_adjacent_duplicates_copy(hz::string_trim_copy(model), ' '); } else if (app_pcre_match("/^(?:Device|Product):[ \\t]*(.*)$/mi", info_output_, &model)) { // usb flash drives model_name_ = hz::string_remove_adjacent_duplicates_copy(hz::string_trim_copy(model), ' '); } std::string family; // this is from smartctl's database if (app_pcre_match("/^Model Family:[ \\t]*(.*)$/mi", info_output_, &family)) { family_name_ = hz::string_remove_adjacent_duplicates_copy(hz::string_trim_copy(family), ' '); } std::string serial; if (app_pcre_match("/^Serial Number:[ \\t]*(.*)$/mi", info_output_, &serial)) { serial_number_ = hz::string_remove_adjacent_duplicates_copy(hz::string_trim_copy(serial), ' '); } std::string rpm_str; if (app_pcre_match("/^Rotation Rate:[ \\t]*(.*)$/mi", info_output_, &rpm_str)) { int rpm = 0; hz::string_is_numeric(rpm_str, rpm, false); hdd_ = rpm > 0; } // Note: this property is present since 5.33. std::string size; if (app_pcre_match("/^User Capacity:[ \\t]*(.*)$/mi", info_output_, &size)) { uint64_t bytes = 0; size_ = SmartctlParser::parse_byte_size(size, bytes, false); } // Try to parse the properties. ignore its errors - we already got what we came for. // Note that this may try to parse data the second time (it may already have // been parsed by parse_data() which failed at it). if (do_set_properties) { StorageAttribute::DiskType disk_type = StorageAttribute::DiskAny; if (hdd_.defined()) { disk_type = hdd_.value() ? StorageAttribute::DiskHDD : StorageAttribute::DiskSSD; } SmartctlParser ps; if (ps.parse_full(this->info_output_, disk_type)) { // try to parse it this->set_properties(ps.get_properties()); // copy to our drive, overwriting old data } } // A model field (and its aliases) is a good indication whether there was any data or not set_parse_status(model_name_.defined() ? parse_status_info : parse_status_none); if (emit_signal) signal_changed.emit(this); // notify listeners return std::string(); } std::string StorageDevice::fetch_data_and_parse(hz::intrusive_ptr smartctl_ex) { if (this->test_is_active_) return "A test is currently being performed on this drive."; this->clear_fetched(); // clear everything fetched before, including outputs std::string output; std::string error_msg; // instead of -x, we use all the individual options -x encompasses, so that // an addition to default -x output won't affect us. if (this->get_type_argument() == "scsi") { // not sure about correctness... FIXME probably fails with RAID/scsi // This doesn't do much yet, but just in case... // SCSI equivalent of -x: error_msg = execute_device_smartctl("--health --info --attributes --log=error --log=selftest --log=background --log=sasphy", smartctl_ex, output); } else { // ATA equivalent of -x: error_msg = execute_device_smartctl("--health --info --get=all --capabilities --attributes --format=brief --log=xerror,50,error --log=xselftest,50,selftest --log=selective --log=directory --log=scttemp --log=scterc --log=devstat --log=sataphy", smartctl_ex, output, true); // set type to invalid if needed } // See notes above (in fetch_basic_data_and_parse()). if (get_detected_type() == detected_type_invalid && get_type_argument().empty()) { debug_out_info("app", "The device seems to be of different type than auto-detected, trying again with scsi.\n"); this->set_type_argument("scsi"); return this->fetch_data_and_parse(smartctl_ex); // try again with scsi } // Since the type error leads to "command line didn't parse" error here, // we do this after the scsi stuff. if (!error_msg.empty()) return error_msg; this->full_output_ = output; return this->parse_data(); } std::string StorageDevice::parse_data() { this->clear_fetched(false); // clear everything fetched before, except outputs StorageAttribute::DiskType disk_type = StorageAttribute::DiskAny; if (hdd_.defined()) { disk_type = hdd_.value() ? StorageAttribute::DiskHDD : StorageAttribute::DiskSSD; } SmartctlParser ps; if (ps.parse_full(this->full_output_, disk_type)) { // try to parse it (parse only, set the properties after basic parsing). // refresh basic info too this->info_output_ = ps.get_data_full(); // put data including version information // note: this will clear the non-basic properties! // this will parse some info that is already parsed by SmartctlParser::parse_full(), // but this one sets the StorageDevice class members, not properties. this->parse_basic_data(false, false); // don't emit signal, we're not complete yet. // Call this after parse_basic_data(), since it sets parse status to "info". this->set_parse_status(StorageDevice::parse_status_full); // set the full properties this->set_properties(ps.get_properties()); // copy to our drive, overwriting old data signal_changed.emit(this); // notify listeners return std::string(); } // Don't show any GUI warnings on parse failure - it may just be an unsupported // drive (e.g. usb flash disk). Plus, it may flood the string. The data will be // parsed again in Info window, and we show the warnings there. debug_out_warn("app", DBG_FUNC_MSG << "Cannot parse smartctl output.\n"); // proper parsing failed. try to at least extract info section this->info_output_ = this->full_output_; // complete output here. sometimes it's only the info section if (!this->parse_basic_data(true).empty()) { // will add some properties too. this will emit signal_changed. return ps.get_error_msg(); // return full parser's error messages - they are more detailed. } return std::string(); // return ok if at least the info was ok. } StorageDevice::parse_status_t StorageDevice::get_parse_status() const { return parse_status_; } std::string StorageDevice::set_smart_enabled(bool b, hz::intrusive_ptr smartctl_ex) { if (this->test_is_active_) return "A test is currently being performed on this drive."; // execute smartctl --smart=on|off /dev/... // --saveauto=on is also executed when enabling smart. // Output: /* === START OF ENABLE/DISABLE COMMANDS SECTION === SMART Enabled. SMART Attribute Autosave Enabled. --------------------------- OR --------------------------- === START OF ENABLE/DISABLE COMMANDS SECTION === SMART Disabled. Use option -s with argument 'on' to enable it. --------------------------- OR --------------------------- A mandatory SMART command failed: exiting. To continue, add one or more '-T permissive' options. */ std::string output; std::string error_msg = execute_device_smartctl((b ? "--smart=on --saveauto=on" : "--smart=off"), smartctl_ex, output); if (!error_msg.empty()) return error_msg; // search at line start, because they are sometimes present in other sentences too. if (app_pcre_match("/^SMART Enabled/mi", output) || app_pcre_match("/^SMART Disabled/mi", output)) { return std::string(); // success } else if (app_pcre_match("/^A mandatory SMART command failed/mi", output)) { return "Mandatory SMART command failed."; } return "Unknown error occurred."; } std::string StorageDevice::set_aodc_enabled(bool b, hz::intrusive_ptr smartctl_ex) { if (this->test_is_active_) return "A test is currently being performed on this drive."; // execute smartctl --offlineauto=on|off /dev/... // Output: /* === START OF ENABLE/DISABLE COMMANDS SECTION === SMART Automatic Offline Testing Enabled every four hours. --------------------------- OR --------------------------- === START OF ENABLE/DISABLE COMMANDS SECTION === SMART Automatic Offline Testing Disabled. --------------------------- OR --------------------------- A mandatory SMART command failed: exiting. To continue, add one or more '-T permissive' options. */ std::string output; std::string error_msg = execute_device_smartctl((b ? "--offlineauto=on" : "--offlineauto=off"), smartctl_ex, output); if (!error_msg.empty()) return error_msg; if (app_pcre_match("/Testing Enabled/mi", output) || app_pcre_match("/Testing Disabled/mi", output)) { return std::string(); // success } else if (app_pcre_match("/^A mandatory SMART command failed/mi", output)) { return "Mandatory SMART command failed."; } return "Unknown error occurred."; } StorageDevice::status_t StorageDevice::get_smart_status() const { status_t status = status_unsupported; if (smart_enabled_.defined()) { if (smart_enabled_.value()) { // enabled, supported status = status_enabled; } else { // if it's disabled, maybe it's unsupported, check that: if (smart_supported_.defined()) { if (smart_supported_.value()) { // disabled, supported status = status_disabled; } else { // disabled, unsupported status = status_unsupported; } } else { // disabled, support unknown status = status_disabled; } } } else { // status unknown if (smart_supported_.defined()) { if (smart_supported_.value()) { // status unknown, supported status = status_disabled; // at least give the user a chance to try enabling it } else { // status unknown, unsupported status = status_unsupported; // most likely } } else { // status unknown, support unknown status = status_unsupported; } } return status; } StorageDevice::status_t StorageDevice::get_aodc_status() const { // smart-disabled drives are known to print some garbage, so // let's protect us from it. if (get_smart_status() != status_enabled) return status_unsupported; if (aodc_status_.defined()) // cached return value return aodc_status_.value(); status_t status = status_unknown; // for now bool aodc_supported = false; int found = 0; for (SmartctlParser::prop_list_t::const_iterator iter = properties_.begin(); iter != properties_.end(); ++iter) { if (iter->section == StorageProperty::section_internal) { if (iter->generic_name == "aodc_enabled") { // if this is not present at all, we set the unknown status. status = (iter->value_bool ? status_enabled : status_disabled); //++found; continue; } if (iter->generic_name == "aodc_support") { aodc_supported = iter->value_bool; ++found; continue; } if (found >= 2) break; } } if (!aodc_supported) status = status_unsupported; // if it's supported, then status may be enabled, disabled or unknown. aodc_status_ = status; // store to cache debug_out_info("app", DBG_FUNC_MSG << "AODC status: " << get_status_name(status) << "\n"); return status; } std::string StorageDevice::get_device_size_str() const { return (size_.defined() ? size_.value() : ""); } StorageProperty StorageDevice::get_health_property() const { if (health_property_.defined()) // cached return value return health_property_.value(); StorageProperty p = this->lookup_property("overall_health", StorageProperty::section_data, StorageProperty::subsection_health); if (!p.empty()) health_property_ = p; // store to cache return p; } std::string StorageDevice::get_device() const { return device_; } std::string StorageDevice::get_device_base() const { if (is_virtual_) return ""; std::string::size_type pos = device_.rfind('/'); // find basename if (pos == std::string::npos) return device_; // fall back return device_.substr(pos+1, std::string::npos); } std::string StorageDevice::get_device_with_type() const { if (this->get_is_virtual()) { std::string ret = "Virtual"; std::string vf = this->get_virtual_filename(); ret += (" (" + (vf.empty() ? "[empty]" : vf) + ")"); return ret; } std::string device = get_device(); if (!get_type_argument().empty()) { device += " (" + get_type_argument() + ")"; } return device; } void StorageDevice::set_detected_type(StorageDevice::detected_type_t t) { detected_type_ = t; } StorageDevice::detected_type_t StorageDevice::get_detected_type() const { return detected_type_; } void StorageDevice::set_type_argument(const std::string& arg) { type_arg_ = arg; } std::string StorageDevice::get_type_argument() const { return type_arg_; } void StorageDevice::set_extra_arguments(const std::string& args) { extra_args_ = args; } std::string StorageDevice::get_extra_arguments() const { return extra_args_; } void StorageDevice::set_drive_letters(const std::map& letters) { drive_letters_ = letters; } const std::map& StorageDevice::get_drive_letters() const { return drive_letters_; } std::string StorageDevice::format_drive_letters(bool with_volnames) const { std::vector drive_letters_decorated; for (std::map::const_iterator iter = drive_letters_.begin(); iter != drive_letters_.end(); ++iter) { drive_letters_decorated.push_back(std::string() + (char)std::toupper(iter->first) + ":"); if (with_volnames && !iter->second.empty()) { drive_letters_decorated.back() += std::string(" (") + iter->second + ")"; } } return hz::string_join(drive_letters_decorated, ", "); } bool StorageDevice::get_is_virtual() const { return is_virtual_; } std::string StorageDevice::get_virtual_file() const { return (is_virtual_ ? virtual_file_ : std::string()); } std::string StorageDevice::get_virtual_filename() const { return (is_virtual_ ? hz::FsPath(virtual_file_).get_basename() : std::string()); } const SmartctlParser::prop_list_t& StorageDevice::get_properties() const { return properties_; } StorageProperty StorageDevice::lookup_property(const std::string& generic_name, StorageProperty::section_t section, StorageProperty::subsection_t subsection) const { for (SmartctlParser::prop_list_t::const_iterator iter = properties_.begin(); iter != properties_.end(); ++iter) { if (section != StorageProperty::section_unknown && iter->section != section) continue; if (subsection != StorageProperty::subsection_unknown && iter->subsection != subsection) continue; if (iter->generic_name == generic_name) return *iter; } return StorageProperty(); // check with .empty() } std::string StorageDevice::get_model_name() const { return (model_name_.defined() ? model_name_.value() : ""); } std::string StorageDevice::get_family_name() const { return (family_name_.defined() ? family_name_.value() : ""); } std::string StorageDevice::get_serial_number() const { return (serial_number_.defined() ? serial_number_.value() : ""); } bool StorageDevice::get_is_hdd() const { return hdd_.defined() ? hdd_.value() : false; } void StorageDevice::set_info_output(const std::string& s) { info_output_ = s; } std::string StorageDevice::get_info_output() const { return info_output_; } void StorageDevice::set_full_output(const std::string& s) { full_output_ = s; } std::string StorageDevice::get_full_output() const { return full_output_; } void StorageDevice::set_is_manually_added(bool b) { is_manually_added_ = b; } bool StorageDevice::get_is_manually_added() const { return is_manually_added_; } void StorageDevice::set_test_is_active(bool b) { bool changed = (test_is_active_ != b); test_is_active_ = b; if (changed) { signal_changed.emit(this); // so that everybody stops any test-aborting operations. } } bool StorageDevice::get_test_is_active() const { return test_is_active_; } std::string StorageDevice::get_save_filename() const { std::string model = this->get_model_name(); // may be empty std::string serial = this->get_serial_number(); std::string date = hz::format_date("%Y-%m-%d_%H%M", true); std::string filename_format; rconfig::get_data("gui/smartctl_output_filename_format", filename_format); hz::string_replace(filename_format, "{serial}", serial); hz::string_replace(filename_format, "{model}", model); hz::string_replace(filename_format, "{date}", date); return hz::filename_make_safe(filename_format); } std::string StorageDevice::get_device_options() const { if (is_virtual_) { debug_out_warn("app", DBG_FUNC_MSG << "Cannot get device options of a virtual device.\n"); return std::string(); } // If we have some special type or option, specify it on the command line (like "-d scsi"). // Note that the latter "-d" option overrides the former. // lowest priority - the detected type std::vector args; if (!get_type_argument().empty()) { args.push_back("-d " + get_type_argument()); } // extra args, as specified manually in CLI or when adding the drive if (!get_extra_arguments().empty()) { args.push_back(get_extra_arguments()); } // config options, as specified in preferences. std::string config_options = app_get_device_option(get_device(), get_type_argument()); if (!config_options.empty()) { args.push_back(config_options); } return hz::string_join(args, " "); } std::string StorageDevice::execute_device_smartctl(const std::string& command_options, hz::intrusive_ptr smartctl_ex, std::string& smartctl_output, bool check_type) { // don't forbid running on currently tested drive - we need to call this from the test code. if (is_virtual_) { debug_out_warn("app", DBG_FUNC_MSG << "Cannot execute smartctl on a virtual device.\n"); return "Cannot execute smartctl on a virtual device."; } std::string device = get_device(); std::string error_msg = execute_smartctl(device, this->get_device_options(), command_options, smartctl_ex, smartctl_output); if (!error_msg.empty()) { debug_out_warn("app", DBG_FUNC_MSG << "Smartctl binary did not execute cleanly.\n"); // Smartctl 5.39 cvs/svn version defaults to usb type on at least linux and windows. // This means that the old SCSI identify command isn't executed by default, // and there is no information about the device manufacturer/etc... in the output. // We detect this and set the device type to scsi to at least have _some_ info. if (check_type && this->get_detected_type() == detected_type_unknown && app_pcre_match("/specify device type with the -d option/mi", smartctl_output)) { this->set_detected_type(detected_type_invalid); } return error_msg; } return std::string(); } void StorageDevice::set_parse_status(parse_status_t value) { parse_status_ = value; } void StorageDevice::set_properties(const SmartctlParser::prop_list_t& props) { properties_ = props; } /// @} gsmartcontrol-1.1.4/src/applib/storage_device.h000066400000000000000000000245661417717230600216320ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef STORAGE_DEVICE_H #define STORAGE_DEVICE_H #include #include #include #include "hz/optional_value.h" #include "hz/intrusive_ptr.h" #include "storage_property.h" #include "smartctl_parser.h" // prop_list_t #include "smartctl_executor.h" class StorageDevice; /// A reference-counting pointer to StorageDevice typedef hz::intrusive_ptr StorageDeviceRefPtr; /// This class represents a single drive class StorageDevice : public hz::intrusive_ptr_referenced { public: /// These may be used to force smartctl to a special type, as well as /// to display the correct icon enum detected_type_t { detected_type_unknown, // Unknown. Will be autodetected by smartctl. detected_type_invalid, // This is set by smartctl executor if it detects invalid type (but not if it's scsi). detected_type_cddvd, // CD/DVD/Blu-Ray. Unsupported by smartctl, only basic info is given. detected_type_raid, // RAID controller or volume. Unsupported by smartctl, only basic info is given. }; /// This gives a string which can be displayed in outputs static std::string get_type_readable_name(detected_type_t type); /// Statuses of various states enum status_t { status_enabled, ///< SMART, AODC status_disabled, ///< SMART, AODC status_unsupported, ///< SMART, AODC status_unknown ///< AODC - supported but unknown if it's enabled or not. }; /// Get displayable name for status_t. static std::string get_status_name(status_t status, bool use_yesno = false); /// Comparison function for sorting the devices static bool order_less_than(const StorageDeviceRefPtr& a, const StorageDeviceRefPtr& b); /// Statuses of various parse states enum parse_status_t { parse_status_full, ///< Fully parsed parse_status_info, ///< Only info section available parse_status_none, ///< No data }; /// Constructor StorageDevice(const std::string& dev_or_vfile, bool is_virtual = false); /// Constructor StorageDevice(const std::string& dev, const std::string& type_arg); /// Copy constructor StorageDevice(const StorageDevice& other); /// Assignment operator StorageDevice& operator= (const StorageDevice& other); // clear everything fetched before. void clear_fetched(bool including_outputs = true); /// Calls "smartctl -i -H -c" (info section, health, capabilities), then parse_basic_data(). /// Called during drive detection. /// Note: this will clear the non-basic properties! std::string fetch_basic_data_and_parse(hz::intrusive_ptr smartctl_ex = 0); /// Detects type, smart support, smart status (on / off). /// Note: this will clear the non-basic properties! std::string parse_basic_data(bool do_set_properties = true, bool emit_signal = true); /// Execute smartctl --all (all sections), get output, parse it (basic data too), fill properties. std::string fetch_data_and_parse(hz::intrusive_ptr smartctl_ex = 0); // returns error message on error. // Parses full info. If failed, try to parse it as basic info. /// \return error message on error. std::string parse_data(); /// Get the "fully parsed" flag parse_status_t get_parse_status() const; /// Try to enable SMART. /// \return error message on error, empty string on success std::string set_smart_enabled(bool b, hz::intrusive_ptr smartctl_ex = 0); /// Try to enable Automatic Offline Data Collection. /// \return error message on error, empty string on success std::string set_aodc_enabled(bool b, hz::intrusive_ptr smartctl_ex = 0); /// Get SMART status status_t get_smart_status() const; /// Get AODC status status_t get_aodc_status() const; /// Get format size string, or an empty string on error. std::string get_device_size_str() const; /// Get the overall health property StorageProperty get_health_property() const; /// Get device name (e.g. /dev/sda) std::string get_device() const; /// Get device name without path. For example, "sda". std::string get_device_base() const; /// Get device name for display purposes (with a type argument in parentheses) std::string get_device_with_type() const; /// Set detected type void set_detected_type(detected_type_t t); /// Get detected type detected_type_t get_detected_type() const; /// Set argument for "-d" smartctl parameter void set_type_argument(const std::string& arg); /// Get argument for "-d" smartctl parameter std::string get_type_argument() const; /// Set extra arguments smartctl void set_extra_arguments(const std::string& args); /// Get extra arguments smartctl std::string get_extra_arguments() const; /// Set windows drive letters for this drive void set_drive_letters(const std::map& letters_volnames); /// Get windows drive letters for this drive const std::map& get_drive_letters() const; /// Get comma-separated win32 drive letters (if present) std::string format_drive_letters(bool with_volnames) const; /// Get "virtual" status bool get_is_virtual() const; /// If the device is virtual, return its file std::string get_virtual_file() const; /// Get only the filename portion of a virtual file std::string get_virtual_filename() const; /// Get all detected properties const SmartctlParser::prop_list_t& get_properties() const; /// Find a property StorageProperty lookup_property(const std::string& generic_name, StorageProperty::section_t section = StorageProperty::section_unknown, // if unknown, search in all. StorageProperty::subsection_t subsection = StorageProperty::subsection_unknown) const; /// Get model name. /// \return empty string if not found std::string get_model_name() const; /// Get family name. /// \return empty string if not found std::string get_family_name() const; /// Get serial number. /// \return empty string if not found std::string get_serial_number() const; /// Check whether this drive is a rotational HDD. bool get_is_hdd() const; /// Set "info" output to parse void set_info_output(const std::string& s); /// Get "info" output to parse std::string get_info_output() const; /// Set "full" output to parse void set_full_output(const std::string& s); /// Get "full" output to parse std::string get_full_output() const; /// Set "manually added" flag void set_is_manually_added(bool b); /// Get "manually added" flag bool get_is_manually_added() const; /// Set "test is active" flag, emit the "changed" signal if needed. void set_test_is_active(bool b); /// Get "test is active" flag bool get_test_is_active() const; /// Get the recommended filename to save output to. Includes model and date. std::string get_save_filename() const; /// Get final smartctl options for this device from config and type info. std::string get_device_options() const; /// Execute smartctl on this device. Nothing is modified in this class. /// \return error message on error, empty string on success std::string execute_device_smartctl(const std::string& command_options, hz::intrusive_ptr smartctl_ex, std::string& output, bool check_type = false); /// Emitted whenever new information is available sigc::signal signal_changed; protected: /// Set the "fully parsed" flag void set_parse_status(parse_status_t value); /// Set parsed properties void set_properties(const SmartctlParser::prop_list_t& props); private: std::string info_output_; ///< "smartctl --info" output std::string full_output_; ///< "smartctl --all" output std::string device_; ///< e.g. /dev/sda or pd0. empty if virtual. std::string type_arg_; ///< Device type (for -d smartctl parameter), as specified when adding the device. std::string extra_args_; ///< Extra parameters for smartctl, as specified when adding the device. std::map drive_letters_; ///< Windows drive letters (if detected), with volume names bool is_virtual_; ///< If true, then this is not a real device - merely a loaded description of it. std::string virtual_file_; ///< A file (smartctl data) the virtual device was loaded from bool is_manually_added_; ///< StorageDevice doesn't use it, but it's useful for its users. parse_status_t parse_status_; ///< "Fully parsed" flag /// Sort of a "lock". If true, the device is not allowed to perform any commands /// except "-l selftest" and maybe "--capabilities" and "--info" (not sure). bool test_is_active_; // Note: These are detected through info output detected_type_t detected_type_; ///< e.g. type_unknown hz::OptionalValue smart_supported_; ///< SMART support status hz::OptionalValue smart_enabled_; ///< SMART enabled status mutable hz::OptionalValue aodc_status_; ///< Cached aodc status. hz::OptionalValue model_name_; ///< Model name hz::OptionalValue family_name_; ///< Family name hz::OptionalValue serial_number_; ///< Serial number hz::OptionalValue size_; ///< Formatted size hz::OptionalValue hdd_; ///< Whether it's a rotational drive (HDD) or something else (SSD, flash, etc...) mutable hz::OptionalValue health_property_; ///< Cached health property. SmartctlParser::prop_list_t properties_; ///< Smart properties. Detected through full output. }; /// Operator for sorting, hard drives first, then device name base inline bool operator< (const StorageDeviceRefPtr& a, const StorageDeviceRefPtr& b) { // if (a->get_detected_type() != a->get_detected_type()) { // return (a->get_detected_type() == StorageDevice::detected_type_unknown); // hard drives first // } if (a->get_is_virtual() != b->get_is_virtual()) { return int(a->get_is_virtual()) < int(b->get_is_virtual()); } if (a->get_is_virtual()) { return a->get_virtual_file() < b->get_virtual_file(); } if (a->get_device_base() != b->get_device_base()) { return a->get_device_base() < b->get_device_base(); } return a->get_type_argument() < b->get_type_argument(); } #endif /// @} gsmartcontrol-1.1.4/src/applib/storage_property.cpp000066400000000000000000000174441417717230600226070ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #include #include // not iosfwd - it doesn't work #include #include #include #include "hz/string_num.h" // number_to_string #include "hz/stream_cast.h" // stream_cast<> #include "hz/format_unit.h" // format_time_length #include "hz/string_algo.h" // string_join #include "hz/string_num.h" // number_to_string #include "storage_property.h" std::ostream& operator<< (std::ostream& os, const StorageCapability& p) { os // << p.name << ": " << p.flag_value; for (StorageCapability::strvalue_list_t::const_iterator iter = p.strvalues.begin(); iter != p.strvalues.end(); ++iter) { os << "\n\t" << *iter; } return os; } std::string StorageAttribute::format_raw_value() const { // If it's fully a number, format it with commas if (hz::number_to_string(raw_value_int) == raw_value) { std::stringstream ss; try { ss.imbue(std::locale("")); } catch (const std::runtime_error& e) { // something is wrong with system locale, can't do anything here. } ss << std::fixed << raw_value_int; return ss.str(); } return raw_value; } std::ostream& operator<< (std::ostream& os, const StorageAttribute& p) { // os << p.name << ": " if (p.value.defined()) { os << static_cast(p.value.value()); } else { os << "-"; } os << " (" << p.format_raw_value() << ")"; return os; } std::string StorageStatistic::format_value() const { // If it's fully a number, format it with commas if (hz::number_to_string(value_int) == value) { std::stringstream ss; try { ss.imbue(std::locale("")); } catch (const std::runtime_error& e) { // something is wrong with system locale, can't do anything here. } ss << std::fixed << value_int; return ss.str(); } return value; } std::ostream& operator<<(std::ostream& os, const StorageStatistic& p) { os << p.value; return os; } std::string StorageErrorBlock::get_readable_error_types(const std::vector& types) { static std::map m; if (m.empty()) { m["ABRT"] = "Command aborted"; m["AMNF"] = "Address mark not found"; m["CCTO"] = "Command completion timed out"; m["EOM"] = "End of media"; m["ICRC"] = "Interface CRC error"; m["IDNF"] = "Identity not found"; m["ILI"] = "(Packet command-set specific)"; m["MC"] = "Media changed"; m["MCR"] = "Media change request"; m["NM"] = "No media"; m["obs"] = "Obsolete"; m["TK0NF"] = "Track 0 not found"; m["UNC"] = "Uncorrectable error in data"; m["WP"] = "Media is write protected"; } std::vector sv; for (std::vector::const_iterator iter = types.begin(); iter != types.end(); ++iter) { if (m.find(*iter) != m.end()) { sv.push_back(m[*iter]); } else { sv.push_back("[unknown type" + (iter->empty() ? "" : (": " + (*iter))) + "]"); } } return hz::string_join(sv, ", "); } int StorageErrorBlock::get_warning_level_for_error_type(std::string& type) { static std::map m; if (m.empty()) { m["ABRT"] = StorageProperty::warning_none; m["AMNF"] = StorageProperty::warning_alert; m["CCTO"] = StorageProperty::warning_warn; m["EOM"] = StorageProperty::warning_warn; m["ICRC"] = StorageProperty::warning_warn; m["IDNF"] = StorageProperty::warning_alert; m["ILI"] = StorageProperty::warning_notice; m["MC"] = StorageProperty::warning_none; m["MCR"] = StorageProperty::warning_none; m["NM"] = StorageProperty::warning_none; m["obs"] = StorageProperty::warning_none; m["TK0NF"] = StorageProperty::warning_alert; m["UNC"] = StorageProperty::warning_alert; m["WP"] = StorageProperty::warning_none; } if (m.find(type) != m.end()) { return int(m[type]); } return StorageProperty::warning_none; // unknown error } std::string StorageErrorBlock::format_lifetime_hours() const { std::stringstream ss; try { ss.imbue(std::locale("")); } catch (const std::runtime_error& e) { // something is wrong with system locale, can't do anything here. } ss << std::fixed << lifetime_hours; return ss.str(); } std::ostream& operator<< (std::ostream& os, const StorageErrorBlock& b) { os << "Error number " << b.error_num << ": " << hz::string_join(b.reported_types, ", ") << " [" << StorageErrorBlock::get_readable_error_types(b.reported_types) << "]"; return os; } std::string StorageSelftestEntry::format_lifetime_hours() const { std::stringstream ss; try { ss.imbue(std::locale("")); } catch (const std::runtime_error& e) { // something is wrong with system locale, can't do anything here. } ss << std::fixed << lifetime_hours; return ss.str(); } std::ostream& operator<< (std::ostream& os, const StorageSelftestEntry& b) { os << "Test entry " << b.test_num << ": " << b.type << ", status: " << b.get_status_str() << ", remaining: " << int(b.remaining_percent); return os; } void StorageProperty::dump(std::ostream& os, int internal_offset) const { std::string offset(internal_offset, ' '); os << offset << "[" << get_section_name(section) << (section == section_data ? (", " + get_subsection_name(subsection)) : "") << "]" << " " << generic_name // << (generic_name == reported_name ? "" : (" (" + reported_name + ")")) << ": [" << get_value_type_name(value_type) << "] "; // if (!readable_value.empty()) // os << readable_value; switch(value_type) { case StorageProperty::value_type_unknown: os << "[empty]"; break; case StorageProperty::value_type_string: os << "\"" << value_string << "\""; break; case StorageProperty::value_type_integer: os << value_integer << " [" << reported_value << "]"; break; case StorageProperty::value_type_bool: os << value_bool << " [" << reported_value << "]"; break; case StorageProperty::value_type_time_length: os << value_time_length << " [" << reported_value << "]"; break; case StorageProperty::value_type_capability: os << value_capability; break; case StorageProperty::value_type_attribute: os << value_attribute; break; case StorageProperty::value_type_statistic: os << value_statistic; break; case StorageProperty::value_type_error_block: os << value_error_block; break; case StorageProperty::value_type_selftest_entry: os << value_selftest_entry; break; } } std::string StorageProperty::format_value(bool add_reported_too) const { if (!readable_value.empty()) return readable_value; switch(value_type) { case StorageProperty::value_type_unknown: return "[unknown]"; case StorageProperty::value_type_string: return value_string; case StorageProperty::value_type_integer: return hz::number_to_string(value_integer) + (add_reported_too ? (" [" + reported_value + "]") : ""); case StorageProperty::value_type_bool: return std::string(value_bool ? "Yes" : "No") + (add_reported_too ? (" [" + reported_value + "]") : ""); case StorageProperty::value_type_time_length: return hz::format_time_length(value_time_length) + (add_reported_too ? (" [" + reported_value + "]") : ""); case StorageProperty::value_type_capability: return hz::stream_cast(value_capability); case StorageProperty::value_type_attribute: return hz::stream_cast(value_attribute); case StorageProperty::value_type_statistic: return hz::stream_cast(value_statistic); case StorageProperty::value_type_error_block: return hz::stream_cast(value_error_block); case StorageProperty::value_type_selftest_entry: return hz::stream_cast(value_selftest_entry); } return "[error]"; } /// @} gsmartcontrol-1.1.4/src/applib/storage_property.h000066400000000000000000000426271417717230600222550ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef STORAGE_PROPERTY_H #define STORAGE_PROPERTY_H #include #include #include #include "hz/optional_value.h" #include "hz/cstdint.h" /// Holds one block of "capabilities" subsection /// (only for non-time-interval blocks). class StorageCapability { public: /// Constructor StorageCapability() : flag_value(0x0) { } /// String list typedef std::vector strvalue_list_t; std::string reported_flag_value; ///< original flag value as a string uint16_t flag_value; ///< Flag value. This is one or sometimes two bytes (maybe more?) std::string reported_strvalue; ///< Original flag descriptions strvalue_list_t strvalues; ///< A list of capabilities in the block. }; /// Output operator for debug purposes std::ostream& operator<< (std::ostream& os, const StorageCapability& p); /// Holds one line of "attributes" subsection class StorageAttribute { public: /// Disk type the attribute may match enum DiskType { DiskAny, ///< Any disk type DiskHDD, ///< HDD (rotational) only DiskSSD ///< SSD only }; /// Attribute pre-failure / old-age type enum attr_t { attr_type_unknown, ///< Unknown attr_type_prefail, ///< Pre-failure (reported: Pre-fail) attr_type_oldage ///< Old age (reported: Old_age) }; /// Get readable attribute type name static std::string get_attr_type_name(attr_t a) { switch (a) { case attr_type_unknown: return "[unknown]"; case attr_type_prefail: return "pre-failure"; case attr_type_oldage: return "old age"; }; return "[error]"; } /// Attribute when-updated type enum update_t { update_type_unknown, ///< Unknown update_type_always, ///< Continuously (reported: Always) update_type_offline ///< Only during offline data collection (reported: Offline) }; /// Get readable when-updated type name static std::string get_update_type_name(update_t a) { switch (a) { case update_type_unknown: return "[unknown]"; case update_type_always: return "continuously"; case update_type_offline: return "on offline data collect."; }; return "[error]"; } /// Attribute when-failed type enum fail_time_t { fail_time_unknown, ///< Unknown fail_time_none, ///< Never (reported: -) fail_time_past, ///< In the past (reported: In_the_past) fail_time_now ///< Now (reported: FAILING_NOW) }; /// Get a readable when-failed type name static std::string get_fail_time_name(fail_time_t a) { switch (a) { case fail_time_unknown: return "[unknown]"; case fail_time_none: return "never"; case fail_time_past: return "in the past"; case fail_time_now: return "now"; }; return "[error]"; } /// Constructor StorageAttribute() : id(-1), attr_type(attr_type_unknown), update_type(update_type_unknown), when_failed(fail_time_unknown), raw_value_int(0) { } /// Format raw value with commas (if it's a number) std::string format_raw_value() const; int32_t id; ///< Attribute ID (most vendors agree on this) std::string flag; ///< "Old" format is "0xXXXX", "brief" format is "PO--C-". hz::OptionalValue value; ///< Normalized value. May be unset ("---"). hz::OptionalValue worst; ///< Worst ever value. May be unset ("---"). hz::OptionalValue threshold; ///< Threshold for normalized value. May be unset ("---"). attr_t attr_type; ///< Attribute pre-fail / old-age type update_t update_type; ///< When-updated type fail_time_t when_failed; ///< When-failed type std::string raw_value; ///< Raw value as a string, as presented by smartctl (formatted). int64_t raw_value_int; ///< Same as raw_value, but parsed as int64. original value is 6 bytes I think. }; /// Output operator for debug purposes std::ostream& operator<< (std::ostream& os, const StorageAttribute& p); /// Holds one line of "devstat" subsection class StorageStatistic { public: /// Constructor StorageStatistic() : is_header(false), value_int(0), page(0), offset(0) { } /// Whether the normalization flag is present bool is_normalized() const { return flags.find('N') != flags.npos; } /// Format value with commas (if it's a number) std::string format_value() const; bool is_header; ///< If the line is a header std::string flags; ///< Flags in "NDC" / "---" format std::string value; ///< Value as a string, as presented by smartctl (formatted). int64_t value_int; ///< Same as value, but parsed as int64. int64_t page; ///< Page int64_t offset; ///< Offset in page }; /// Output operator for debug purposes std::ostream& operator<< (std::ostream& os, const StorageStatistic& p); /// Holds one error block of "error log" subsection class StorageErrorBlock { public: /// Constructor StorageErrorBlock() : error_num(0), lifetime_hours(0) { } /// Get readable error types from reported types static std::string get_readable_error_types(const std::vector& types); /// Get warning level (warning_t) for an error type static int get_warning_level_for_error_type(std::string& type); /// Format lifetime hours with comma std::string format_lifetime_hours() const; uint32_t error_num; ///< Error number uint32_t lifetime_hours; ///< When the error occurred (in lifetime hours) std::string device_state; ///< Device state during the error - "active or idle", standby, etc... std::vector reported_types; ///< Array of reported types (strings), e.g. "UNC". std::string type_more_info; ///< More info on error type (e.g. "at LBA = 0x0253eac0 = 39054016") }; /// Output operator for debug purposes std::ostream& operator<< (std::ostream& os, const StorageErrorBlock& b); /// Holds one entry of selftest_log subsection. /// Also, holds "Self-test execution status" capability's "internal" section version. class StorageSelftestEntry { public: /// Self-test log entry status enum status_t { status_unknown, ///< Initial state status_completed_no_error, ///< Completed with no error, or no test was run status_aborted_by_host, ///< Aborted by host status_interrupted, ///< Interrupted by user status_fatal_or_unknown, ///< Fatal or unknown error. Treated as test failure. status_compl_unknown_failure, ///< Completed with unknown error. Treated as test failure. status_compl_electrical_failure, ///< Completed with electrical error. Treated as test failure. status_compl_servo_failure, ///< Completed with servo error. Treated as test failure. status_compl_read_failure, ///< Completed with read error. Treated as test failure. status_compl_handling_damage, ///< Completed with handling damage error. Treated as test failure. status_in_progress, ///< Test in progress status_reserved ///< Reserved }; /// Self-test error severity enum status_severity_t { severity_none, severity_warn, severity_error }; /// Get log entry status displayable name static std::string get_status_name(status_t s) { switch (s) { case status_unknown: return "[unknown]"; case status_completed_no_error: return "Completed without error"; case status_aborted_by_host: return "Manually aborted"; case status_interrupted: return "Interrupted (host reset)"; case status_fatal_or_unknown: return "Fatal or unknown error"; case status_compl_unknown_failure: return "Completed with unknown failure"; case status_compl_electrical_failure: return "Completed with electrical failure"; case status_compl_servo_failure: return "Completed with servo/seek failure"; case status_compl_read_failure: return "Completed with read failure"; case status_compl_handling_damage: return "Completed: handling damage"; case status_in_progress: return "In progress"; case status_reserved: return "Unknown / reserved state"; }; return "[error]"; } /// Get severity of error status static status_severity_t get_status_severity(status_t s) { switch (s) { case status_unknown: return severity_none; case status_completed_no_error: return severity_none; case status_aborted_by_host: return severity_warn; case status_interrupted: return severity_warn; case status_fatal_or_unknown: return severity_error; case status_compl_unknown_failure: return severity_error; case status_compl_electrical_failure: return severity_error; case status_compl_servo_failure: return severity_error; case status_compl_read_failure: return severity_error; case status_compl_handling_damage: return severity_error; case status_in_progress: return severity_none; case status_reserved: return severity_none; }; return severity_none; } /// Constructor StorageSelftestEntry() : test_num(0), status(status_unknown), remaining_percent(-1), lifetime_hours() { } /// Get error status as a string std::string get_status_str() const { return (status == status_unknown ? status_str : get_status_name(status)); } /// Format lifetime hours with comma std::string format_lifetime_hours() const; uint32_t test_num; ///< Test number. always starts from 1. larger means older or newer, depending on model. 0 for capability. std::string type; ///< Extended offline, Short offline, Conveyance offline, etc... . capability: unused. std::string status_str; ///< Self-test routine in progress, Completed without error, etc... (as reported by log or capability) status_t status; ///< same as status_str, but from enum int8_t remaining_percent; ///< Remaining %. 0% for completed, 90% for started. -1 if n/a. uint32_t lifetime_hours; ///< When the test happened (in lifetime hours). capability: unused. std::string lba_of_first_error; ///< LBA of the first error. "-" or value (format? usually hex). capability: unused. }; /// Output operator for debug purposes std::ostream& operator<< (std::ostream& os, const StorageSelftestEntry& b); /// A single parser-extracted property class StorageProperty { public: /// Value types enum value_type_t { value_type_unknown, ///< Empty value value_type_string, ///< String value value_type_integer, ///< int64 value_type_bool, ///< Enabled / disabled; available / not available; passed / not passed. value_type_time_length, ///< Time length in seconds. value_type_capability, ///< For "capabilities" subsection (non-time-interval blocks only) value_type_attribute, ///< For "attributes" subsection value_type_statistic, ///< For "devstat" subsection value_type_error_block, // For "error_log" subsection value_type_selftest_entry // For "selftest_log" subsection }; /// Get displayable value type name static std::string get_value_type_name(value_type_t type) { switch(type) { case value_type_unknown: return "empty"; case value_type_string: return "string"; case value_type_integer: return "integer"; case value_type_bool: return "bool"; case value_type_time_length: return "time_length"; case value_type_capability: return "capability"; case value_type_attribute: return "attribute"; case value_type_statistic: return "statistic"; case value_type_error_block: return "error_block"; case value_type_selftest_entry: return "selftest_entry"; } return "[error]"; } /// Sections in output enum section_t { section_unknown, ///< Used when searching in all sections section_info, ///< Short info (--info) section_data, ///< SMART DATA section_internal ///< Internal application-specific data }; /// Get displayable section type name static std::string get_section_name(section_t s) { switch(s) { case section_unknown: return "unknown"; case section_info: return "info"; case section_data: return "data"; case section_internal: return "internal"; } return "[error]"; } /// Subsections in smart data section enum subsection_t { subsection_unknown, ///< Used when searching in all subsections subsection_health, ///< Overall-health (-H, --health) subsection_capabilities, ///< General SMART Values, aka Capabilities (-c, --capabilities) subsection_attributes, ///< Attributes (-A, --attributes). These need decoding. subsection_devstat, ///< Device statistics (--log=devstat). These need decoding. subsection_error_log, ///< Error Log (--log=error) subsection_selftest_log, ///< Self-test log (--log=selftest) subsection_selective_selftest_log, ///< Selective self-test log (--log=selective) subsection_temperature_log, ///< SCT temperature (current and history) (--log=scttemp) subsection_erc_log, ///< SCT Error Recovery Control settings (--log=scterc) subsection_phy_log, ///< Phy log (--log=sataphy) subsection_directory_log, ///< Directory log (--log=directory) }; /// Get displayable subsection type name static std::string get_subsection_name(subsection_t s) { switch(s) { case subsection_unknown: return "unknown"; case subsection_health: return "health"; case subsection_capabilities: return "capabilities"; case subsection_attributes: return "attributes"; case subsection_devstat: return "devstat"; case subsection_error_log: return "error_log"; case subsection_selftest_log: return "selftest_log"; case subsection_selective_selftest_log: return "selective_selftest_log"; case subsection_temperature_log: return "temperature_log"; case subsection_erc_log: return "erc_log"; case subsection_phy_log: return "phy_log"; case subsection_directory_log: return "directory_log"; } return "[error]"; } /// Warning type enum warning_t { warning_none, ///< No warning warning_notice, ///< A known attribute is somewhat disturbing, but no smart error warning_warn, ///< SMART warning is raised by old-age attribute warning_alert ///< SMART warning is raised by pre-fail attribute, and similar errors }; /// Constructor StorageProperty() : section(section_unknown), subsection(subsection_unknown), value_type(value_type_unknown), warning(warning_none), show_in_ui(true) { // value_from_db = false; value_integer = 0; // this should nullify all union members } /// Check if this is an empty object with no value set. bool empty() const { return (value_type == value_type_unknown); } /// Dump the property to a stream for debugging purposes void dump(std::ostream& os, int internal_offset = 0) const; /// Format this property for debugging purposes std::string format_value(bool add_reported_too = false) const; /// Get property description (used in tooltips) std::string get_description(bool clean = false) const { if (clean) return this->description; return (this->description.empty() ? "No description available" : this->description); } /// Set property description (used in tooltips) void set_description(const std::string& descr) { this->description = descr; } /// Set smartctl-reported name, generic (internal) name, readable name void set_name(const std::string& rep_name, const std::string& gen_name = "", const std::string& read_name = "") { this->reported_name = rep_name; this->generic_name = (gen_name.empty() ? this->reported_name : gen_name); this->readable_name = (read_name.empty() ? this->reported_name : read_name); } std::string reported_name; ///< Property name as reported by smartctl. std::string generic_name; ///< Generic (internal) name. May be same as reported_name, or something more program-identifiable. std::string readable_name; ///< Readable property name. May be same as reported_name, or something more user-readable. Possibly translatable. std::string description; ///< Property description (for tooltips, etc...) section_t section; ///< Section this property belongs to subsection_t subsection; ///< Subsection this property belongs to // bool empty_value; // the property has an empty value // bool value_from_db; // value retrieved from the database, not the drive std::string reported_value; ///< String representation of the value as reported std::string readable_value; ///< User-friendly readable representation of value. if empty, use the other members. value_type_t value_type; ///< Property value type std::string value_string; ///< Value (if it's a string) std::string value_version; ///< Value (if it's a version) union { int64_t value_integer; ///< Value (if it's an integer) bool value_bool; ///< Value (if it's bool) uint64_t value_time_length; ///< Value in seconds (if it's time interval) }; StorageCapability value_capability; ///< Value (if it's a capability) StorageAttribute value_attribute; ///< Value (if it's an attribute) StorageStatistic value_statistic; ///< Value (if it's a statistic from devstat) StorageErrorBlock value_error_block; ///< Value (if it's a error block) StorageSelftestEntry value_selftest_entry; ///< Value (if it's a self-test entry) warning_t warning; ///< Warning severity for this property std::string warning_reason; // Warning reason (displayable) bool show_in_ui; ///< Whether to show this property in UI or not }; /// Output operator for debug purposes inline std::ostream& operator<< (std::ostream& os, const StorageProperty& p) { p.dump(os); return os; } #endif /// @} gsmartcontrol-1.1.4/src/applib/storage_property_colors.h000066400000000000000000000051161417717230600236260ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef STORAGE_PROPERTY_COLORS_H #define STORAGE_PROPERTY_COLORS_H #include "storage_property.h" /// Get colors for tree rows according to warning severity. /// \return true if the colors were changed. inline bool app_property_get_row_highlight_colors(StorageProperty::warning_t warning, std::string& fg, std::string& bg) { // Note: we're setting both fg and bg, to avoid theme conflicts. if (warning == StorageProperty::warning_notice) { fg = "#000000"; // black bg = "#FFD5EE"; // pinkish } else if (warning == StorageProperty::warning_warn) { fg = "#000000"; // black bg = "#FFA0A0"; // even more pinkish } else if (warning == StorageProperty::warning_alert) { fg = "#000000"; // black bg = "#FF0000"; // red } return !(fg.empty()); } /// Get color for labels according to warning severity. /// \return true if the color was changed. inline bool app_property_get_label_highlight_color(StorageProperty::warning_t warning, std::string& fg) { if (warning == StorageProperty::warning_notice) { fg = "#770000"; // very dark red } else if (warning == StorageProperty::warning_warn) { fg = "#C00000"; // dark red } else if (warning == StorageProperty::warning_alert) { fg = "#FF0000"; // red } return !(fg.empty()); } /// Format warning text, but without description inline std::string storage_property_get_warning_reason(const StorageProperty& p) { std::string fg, start, stop; if (app_property_get_label_highlight_color(p.warning, fg)) { start = ""; stop = ""; } if (p.warning == StorageProperty::warning_notice) { return "" + start + "Notice:" + stop + " " + p.warning_reason; } else if (p.warning == StorageProperty::warning_warn) { return "" + start + "Warning:" + stop + " " + p.warning_reason; } else if (p.warning == StorageProperty::warning_alert) { return "" + start + "ALERT:" + stop + " " + p.warning_reason; } return ""; } /// Append warning text to description and set it on the property inline void storage_property_autoset_warning_descr(StorageProperty& p) { std::string reason = storage_property_get_warning_reason(p); p.set_description(p.get_description() + (reason.empty() ? "" : "\n\n" + reason)); } #endif /// @} gsmartcontrol-1.1.4/src/applib/storage_property_descr.cpp000066400000000000000000003226201417717230600237620ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #include #include #include "hz/string_algo.h" // string_replace_copy #include "applib/app_pcrecpp.h" #include "storage_property_descr.h" namespace { static std::string s_unc_text = "When a drive encounters a surface error, it marks that sector as "unstable" (also known as "pending reallocation"). " "If the sector is successfully read from or written to at some later point, it is unmarked. If the sector continues to be inaccessible, " "the drive reallocates (remaps) it to a specially reserved area as soon as it has a chance (usually during write request or successful read), " "transferring the data so that no changes are reported to the operating system. This is why you generally don't see "bad blocks" " "on modern drives - if you do, it means that either they have not been remapped yet, or the drive is out of reserved area." "\n\nNote: SSDs reallocate blocks as part of their normal operation, so low reallocation counts are not critical for them."; /// Attribute description for attribute database struct AttributeDescription { /// Constructor AttributeDescription() : id(-1), disk_type(StorageAttribute::DiskAny) { } /// Constructor AttributeDescription(int32_t id_, StorageAttribute::DiskType type, const std::string& smartctl_name_, const std::string& readable_name_, const std::string& generic_name_, const std::string& description_) : id(id_), disk_type(type), smartctl_name(smartctl_name_), readable_name(readable_name_), generic_name(generic_name_), description(description_) { } int32_t id; ///< e.g. 190 StorageAttribute::DiskType disk_type; ///< HDD-only, SSD-only or universal attribute std::string smartctl_name; ///< e.g. Airflow_Temperature_Cel std::string readable_name; ///< e.g. Airflow Temperature (C) std::string generic_name; ///< Generic name to be set on the property. std::string description; ///< Attribute description, can be "". }; /// Attribute description database class AttributeDatabase { public: /// Constructor AttributeDatabase() { // Note: The first one with the same ID is the one displayed in case smartctl // doesn't return a name. See atacmds.cpp (get_default_attr_name()) in smartmontools. // The rest are from drivedb.h, which contains overrides. // Based on: smartmontools r4430, 2017-05-03. // "smartctl" means it's in smartmontools' drivedb.h. // "custom" means it's somewhere else. // Descriptions are based on: // http://en.wikipedia.org/wiki/S.M.A.R.T. // http://kb.acronis.com/taxonomy/term/1644 // http://www.ariolic.com/activesmart/smart-attributes/ // http://www.ocztechnologyforum.com/staff/ryderocz/misc/Sandforce.jpg // Intel Solid-State Drive Toolbox User Guide // as well as various other sources. // Raw read error rate (smartctl) add(1, "Raw_Read_Error_Rate", "Raw Read Error Rate", "", "Indicates the rate of read errors that occurred while reading the data. A non-zero Raw value may indicate a problem with either the disk surface or read/write heads. " "Note: Some drives (e.g. Seagate) are known to report very high Raw values for this attribute; this is not an indication of a problem."); // Throughput Performance (smartctl) add(2, "Throughput_Performance", "Throughput Performance", "", "Average efficiency of a drive. Reduction of this attribute value can signal various internal problems."); // Spin Up Time (smartctl) (some say it can also happen due to bad PSU or power connection (?)) add(3, "Spin_Up_Time", "Spin-Up Time", "", "Average time of spindle spin-up time (from stopped to fully operational). Raw value may show this in milliseconds or seconds. Changes in spin-up time can reflect problems with the spindle motor or power."); // Start/Stop Count (smartctl) add(4, "Start_Stop_Count", "Start / Stop Count", "", "Number of start/stop cycles of a spindle (Raw value). That is, number of drive spin-ups."); // Reallocated Sector Count (smartctl) add(5, StorageAttribute::DiskHDD, "Reallocated_Sector_Ct", "Reallocated Sector Count", "attr_reallocated_sector_count", "Number of reallocated sectors (Raw value). Non-zero Raw value indicates a disk surface failure." "\n\n" + s_unc_text); // SSD: Reallocated Sector Count (smartctl) add(5, StorageAttribute::DiskSSD, "Reallocated_Sector_Ct", "Reallocated Sector Count", "attr_reallocated_sector_count", "Number of reallocated sectors (Raw value). High Raw value indicates an old age for an SSD."); // SandForce SSD: Retired_Block_Count (smartctl) add(5, StorageAttribute::DiskSSD, "Retired_Block_Count", "Retired Block Rate", "attr_ssd_life_left", "Indicates estimated remaining life of the drive. Normalized value is (100-100*RBC/MRB) where RBC is the number of retired blocks and MRB is the minimum required blocks."); // Crucial/Micron SSD: Reallocate_NAND_Blk_Cnt (smartctl) add(5, StorageAttribute::DiskSSD, "Reallocate_NAND_Blk_Cnt", "Reallocated NAND Block Count", "", "Number of reallocated blocks (Raw value). High Raw value indicates an old age for an SSD."); // Micron SSD: Reallocate_NAND_Blk_Cnt (smartctl) add(5, StorageAttribute::DiskSSD, "Reallocated_Block_Count", "Reallocated Block Count", "", "Number of reallocated blocks (Raw value). High Raw value indicates an old age for an SSD."); // OCZ SSD (smartctl) add(5, StorageAttribute::DiskSSD, "Runtime_Bad_Block", "Runtime Bad Block Count", "", ""); // Innodisk SSD (smartctl) add(5, StorageAttribute::DiskSSD, "Later_Bad_Block", "Later Bad Block", "", ""); // Read Channel Margin (smartctl) add(6, StorageAttribute::DiskHDD, "Read_Channel_Margin", "Read Channel Margin", "", "Margin of a channel while reading data. The function of this attribute is not specified."); // Seek Error Rate (smartctl) add(7, StorageAttribute::DiskHDD, "Seek_Error_Rate", "Seek Error Rate", "", "Frequency of errors appearance while positioning. When a drive reads data, it positions heads in the needed place. If there is a failure in the mechanical positioning system, a seek error arises. More seek errors indicate worse condition of a disk surface and disk mechanical subsystem. The exact meaning of the Raw value is manufacturer-dependent."); // Seek Time Performance (smartctl) add(8, StorageAttribute::DiskHDD, "Seek_Time_Performance", "Seek Time Performance", "", "Average efficiency of seek operations of the magnetic heads. If this value is decreasing, it is a sign of problems in the hard disk drive mechanical subsystem."); // Power-On Hours (smartctl) (Maxtor may use minutes, Fujitsu may use seconds, some even temperature?) add(9, "Power_On_Hours", "Power-On Time", "", "Number of hours in power-on state. Raw value shows total count of hours (or minutes, or half-minutes, or seconds, depending on manufacturer) in power-on state."); // SandForce, Intel SSD: Power_On_Hours_and_Msec (smartctl) (description?) add(9, StorageAttribute::DiskSSD, "Power_On_Hours_and_Msec"); // Smart Storage Systems SSD (smartctl) add(9, StorageAttribute::DiskSSD, "Proprietary_9", "Internal Attribute", "", "This attribute has been reserved by vendor as internal."); // Spin-up Retry Count (smartctl) add(10, StorageAttribute::DiskHDD, "Spin_Retry_Count", "Spin-Up Retry Count", "attr_spin_up_retry_count", "Number of retries of spin start attempts (Raw value). An increase of this attribute value is a sign of problems in the hard disk mechanical subsystem."); // Calibration Retry Count (smartctl) add(11, StorageAttribute::DiskHDD, "Calibration_Retry_Count", "Calibration Retry Count", "", "Number of times recalibration was requested, under the condition that the first attempt was unsuccessful (Raw value). A decrease is a sign of problems in the hard disk mechanical subsystem."); // Power Cycle Count (smartctl) add(12, "Power_Cycle_Count", "Power Cycle Count", "", "Number of complete power start / stop cycles of a drive."); // Soft Read Error Rate (smartctl) (same as 201 ?) (description sounds lame, fix?) add(13, "Read_Soft_Error_Rate", "Soft Read Error Rate", "attr_soft_read_error_rate", "Uncorrected read errors reported to the operating system (Raw value). If the value is non-zero, you should back up your data."); // Sandforce SSD: Soft_Read_Error_Rate (smartctl) add(13, StorageAttribute::DiskSSD, "Soft_Read_Error_Rate"); // Maxtor: Average FHC (custom) (description?) add(99, StorageAttribute::DiskHDD, "", "Average FHC (Flying Height Control)", "", ""); // Sandforce SSD: Gigabytes_Erased (smartctl) (description?) add(100, StorageAttribute::DiskSSD, "Gigabytes_Erased", "GiB Erased", "", "Number of GiB erased."); // OCZ SSD (smartctl) add(100, StorageAttribute::DiskSSD, "Total_Blocks_Erased", "Total Blocks Erased", "", "Number of total blocks erased."); // STEC CF: (custom) add(100, StorageAttribute::DiskSSD, "", "Erase / Program Cycles", "", // unused "Number of Erase / Program cycles of the entire drive."); // Maxtor: Maximum FHC (custom) (description?) add(101, StorageAttribute::DiskHDD, "", "Maximum FHC (Flying Height Control)", "", ""); // Unknown (source says Maxtor, but it's an SSD thing and Maxtor doesn't have them at this point). // add(101, "", "Translation Table Rebuild", "", // "Indicates power backup fault or internal error resulting in loss of system unit tables."); // STEC CF: Translation Table Rebuild (custom) add(103, StorageAttribute::DiskSSD, "", "Translation Table Rebuild", "", "Indicates power backup fault or internal error resulting in loss of system unit tables."); // Smart Storage Systems SSD (smartctl) (description?) add(130, StorageAttribute::DiskSSD, "Minimum_Spares_All_Zs", "Minimum Spares All Zs", "", ""); // SiliconMotion SSDs (description?) (smartctl) add(148, StorageAttribute::DiskSSD, "Total_SLC_Erase_Ct", "Total SLC Erase Count", "", ""); // SiliconMotion SSDs (description?) (smartctl) add(149, StorageAttribute::DiskSSD, "Max_SLC_Erase_Ct", "Maximum SLC Erase Count", "", ""); // SiliconMotion SSDs (description?) (smartctl) add(150, StorageAttribute::DiskSSD, "Min_SLC_Erase_Ct", "Minimum SLC Erase Count", "", ""); // SiliconMotion SSDs (description?) (smartctl) add(151, StorageAttribute::DiskSSD, "Average_SLC_Erase_Ct", "Average SLC Erase Count", "", ""); // Apacer Flash (description?) (smartctl) add(160, StorageAttribute::DiskSSD, "Initial_Bad_Block_Count", "Initial Bad Block Count", "", ""); // Samsung SSD, Intel SSD: Reported Uncorrectable (smartctl) add(160, StorageAttribute::DiskSSD, "Uncorrectable_Error_Cnt", "Uncorrectable Error Count", "", ""); // Apacer Flash (description?) (smartctl) add(161, StorageAttribute::DiskSSD, "Bad_Block_Count", "Bad Block Count", "", "Number of bad blocks. SSDs reallocate blocks as part of their normal operation, so low bad block counts are not critical for them."); // Innodisk (description?) (smartctl) add(161, StorageAttribute::DiskSSD, "Number_of_Pure_Spare", "Number of Pure Spare", "", ""); // Innodisk CF (description?) (smartctl) add(161, StorageAttribute::DiskSSD, "Valid_Spare_Block_Cnt", "Valid Spare Block Count", "", "Number of available spare blocks. Spare blocks are used when bad blocks develop."); // Apacer Flash (description?) (smartctl) add(162, StorageAttribute::DiskSSD, "Spare_Block_Count", "Spare Block Count", "", "Number of spare blocks which are used when bad blocks develop."); // Innodisk CF (smartctl) add(162, StorageAttribute::DiskSSD, "Child_Pair_Count", "Child Pair Count", "", ""); // Apacer Flash (description?) (smartctl) add(163, StorageAttribute::DiskSSD, "Max_Erase_Count", "Maximum Erase Count", "", "The maximum of individual erase counts of all the blocks."); // Innodisk SSD: (smartctl) add(163, StorageAttribute::DiskSSD, "Initial_Bad_Block_Count", "Initial Bad Block Count", "", "Factory-determined number of initial bad blocks."); // Innodisk SSD: (smartctl) add(163, StorageAttribute::DiskSSD, "Total_Bad_Block_Count", "Total Bad Block Count", "", "Number of bad blocks. SSDs reallocate blocks as part of their normal operation, so low bad block counts are not critical for them."); // Apacer Flash (description?) (smartctl) add(164, StorageAttribute::DiskSSD, "Average_Erase_Count", "Average Erase Count", "", ""); // Innodisk SSD (description?) (smartctl) add(164, StorageAttribute::DiskSSD, "Total_Erase_Count", "Total Erase Count", "", ""); // Apacer Flash (description?) (smartctl) add(165, StorageAttribute::DiskSSD, "Average_Erase_Count", "Average Erase Count", "", ""); // Innodisk SSD (description?) (smartctl) add(165, StorageAttribute::DiskSSD, "Max_Erase_Count", "Maximum Erase Count", "", ""); // Sandisk SSD (description?) (smartctl) add(165, StorageAttribute::DiskSSD, "Total_Write/Erase_Count", "Total Write / Erase Count", "", ""); // Apacer Flash (description?) (smartctl) add(166, StorageAttribute::DiskSSD, "Later_Bad_Block_Count", "Later Bad Block Count", "", ""); // Innodisk SSD (description?) (smartctl) add(166, StorageAttribute::DiskSSD, "Min_Erase_Count", "Minimum Erase Count", "", ""); // Sandisk SSD (description?) (smartctl) add(166, StorageAttribute::DiskSSD, "Min_W/E_Cycle", "Minimum Write / Erase Cycles", "", ""); // Apacer Flash, OCZ (description?) (smartctl) add(167, StorageAttribute::DiskSSD, "SSD_Protect_Mode", "SSD Protect Mode", "", ""); // Innodisk SSD (description?) (smartctl) add(167, StorageAttribute::DiskSSD, "Average_Erase_Count", "Average Erase Count", "", ""); // Sandisk SSD (description?) (smartctl) add(167, StorageAttribute::DiskSSD, "Min_Bad_Block/Die", "Minimum Bad Block / Die", "", ""); // Apacer Flash (description?) (smartctl) add(168, StorageAttribute::DiskSSD, "SATA_PHY_Err_Ct", "SATA Physical Error Count", "", ""); // Various SSDs: (smartctl) (description?) add(168, StorageAttribute::DiskSSD, "SATA_Phy_Error_Count", "SATA Physical Error Count", "", ""); // Innodisk SSDs: (smartctl) (description?) add(168, StorageAttribute::DiskSSD, "Max_Erase_Count_of_Spec", "Maximum Erase Count per Specification", "", ""); // Sandisk SSD (description?) (smartctl) add(168, StorageAttribute::DiskSSD, "Maximum_Erase_Cycle", "Maximum Erase Cycles", "", ""); // Toshiba SSDs: (smartctl) (description?) add(169, StorageAttribute::DiskSSD, "Bad_Block_Count", "Bad Block Count", "", "Number of bad blocks. SSDs reallocate blocks as part of their normal operation, so low bad block counts are not critical for them."); // Sandisk SSD (description?) (smartctl) add(169, StorageAttribute::DiskSSD, "Total_Bad_Blocks", "Total Bad Blocks", "", "Number of bad blocks. SSDs reallocate blocks as part of their normal operation, so low bad block counts are not critical for them."); // Innodisk SSDs: (smartctl) (description?) add(169, StorageAttribute::DiskSSD, "Remaining_Lifetime_Perc", "Remaining Lifetime %", "attr_ssd_life_left", "Remaining drive life in % (usually by erase count)."); // Intel SSD, STEC CF: Reserved Block Count (smartctl) add(170, StorageAttribute::DiskSSD, "Reserve_Block_Count", "Reserved Block Count", "", "Number of reserved (spare) blocks for bad block handling."); // Micron SSD: Reserved Block Count (smartctl) add(170, StorageAttribute::DiskSSD, "Reserved_Block_Count", "Reserved Block Count", "", "Number of reserved (spare) blocks for bad block handling."); // Crucial / Marvell SSD: Grown Failing Block Count (smartctl) (description?) add(170, StorageAttribute::DiskSSD, "Grown_Failing_Block_Ct", "Grown Failing Block Count", "", ""); // Intel SSD: (smartctl) (description?) add(170, StorageAttribute::DiskSSD, "Available_Reservd_Space", "Available Reserved Space", "", ""); // Various SSDs: (smartctl) (description?) add(170, StorageAttribute::DiskSSD, "Bad_Block_Count", "Bad Block Count", "", "Number of bad blocks. SSDs reallocate blocks as part of their normal operation, so low bad block counts are not critical for them."); // Kingston SSDs: (smartctl) (description?) add(170, StorageAttribute::DiskSSD, "Bad_Blk_Ct_Erl/Lat", "Bad Block Early / Later", "", ""); // Samsung SSDs: (smartctl) (description?) add(170, StorageAttribute::DiskSSD, "Unused_Rsvd_Blk_Ct_Chip", "Unused Reserved Block Count (Chip)", "", ""); // Innodisk Flash (description?) (smartctl) add(170, StorageAttribute::DiskSSD, "Spare_Block_Count", "Spare Block Count", "", "Number of spare blocks which are used in case bad blocks develop."); // Intel SSD, Sandforce SSD, STEC CF, Crucial / Marvell SSD: Program Fail Count (smartctl) add(171, StorageAttribute::DiskSSD, "Program_Fail_Count", "Program Fail Count", "", "Number of flash program (write) failures. High values may indicate old drive age or other problems."); // Samsung SSDs: (smartctl) (description?) add(171, StorageAttribute::DiskSSD, "Program_Fail_Count_Chip", "Program Fail Count (Chip)", "", ""); // OCZ SSD (smartctl) add(171, StorageAttribute::DiskSSD, "Avail_OP_Block_Count", "Available OP Block Count", "", ""); // Intel SSD, Sandforce SSD, STEC CF, Crucial / Marvell SSD: Erase Fail Count (smartctl) add(172, StorageAttribute::DiskSSD, "Erase_Fail_Count", "Erase Fail Count", "", "Number of flash erase command failures. High values may indicate old drive age or other problems."); // Various SSDs (smartctl) (description?) add(173, StorageAttribute::DiskSSD, "Erase_Count", "Erase Count", "", ""); // Samsung SSDs (smartctl) (description?) add(173, StorageAttribute::DiskSSD, "Erase_Fail_Count_Chip", "Erase Fail Count (Chip)", "", ""); // Kingston SSDs (smartctl) (description?) add(173, StorageAttribute::DiskSSD, "MaxAvgErase_Ct", "Maximum / Average Erase Count", "", ""); // Crucial/Micron SSDs (smartctl) (description?) add(173, StorageAttribute::DiskSSD, "Ave_Block-Erase_Count", "Average Block-Erase Count", "", ""); // STEC CF, Crucial / Marvell SSD: Wear Leveling Count (smartctl) (description?) add(173, StorageAttribute::DiskSSD, "Wear_Leveling_Count", "Wear Leveling Count", "", "Indicates the difference between the most worn block and the least worn block."); // Same as above, old smartctl add(173, StorageAttribute::DiskSSD, "Wear_Levelling_Count", "Wear Leveling Count", "", "Indicates the difference between the most worn block and the least worn block."); // Sandisk SSDs (smartctl) (description?) add(173, StorageAttribute::DiskSSD, "Avg_Write/Erase_Count", "Average Write / Erase Count", "", ""); // Intel SSD, Sandforce SSD, Crucial / Marvell SSD: Unexpected Power Loss (smartctl) add(174, StorageAttribute::DiskSSD, "Unexpect_Power_Loss_Ct", "Unexpected Power Loss Count", "", "Number of unexpected power loss events."); // OCZ SSD (smartctl) add(174, StorageAttribute::DiskSSD, "Pwr_Cycle_Ct_Unplanned", "Unexpected Power Loss Count", "", "Number of unexpected power loss events."); // Apple SSD (smartctl) add(174, StorageAttribute::DiskSSD, "Host_Reads_MiB", "Host Read (MiB)", "", "Total number of sectors read by the host system. The Raw value is increased by 1 for every MiB read by the host."); // Program_Fail_Count_Chip (smartctl) add(175, StorageAttribute::DiskSSD, "Program_Fail_Count_Chip", "Program Fail Count (Chip)", "", "Number of flash program (write) failures. High values may indicate old drive age or other problems."); // Various SSDs: Bad_Cluster_Table_Count (smartctl) (description?) add(175, StorageAttribute::DiskSSD, "Bad_Cluster_Table_Count", "Bad Cluster Table Count", "", ""); // Intel SSD (smartctl) (description?) add(175, StorageAttribute::DiskSSD, "Power_Loss_Cap_Test", "Power Loss Capacitor Test", "", ""); // Intel SSD (smartctl) (description?) add(175, StorageAttribute::DiskSSD, "Host_Writes_MiB", "Host Written (MiB)", "", "Total number of sectors written by the host system. The Raw value is increased by 1 for every MiB written by the host."); // Erase_Fail_Count_Chip (smartctl) add(176, StorageAttribute::DiskSSD, "Erase_Fail_Count_Chip", "Erase Fail Count (Chip)", "", "Number of flash erase command failures. High values may indicate old drive age or other problems."); // Innodisk SSD (smartctl) (description?) add(176, StorageAttribute::DiskSSD, "Uncorr_RECORD_Count", "Uncorrected RECORD Count", "", ""); // Innodisk SSD (smartctl) (description?) add(176, StorageAttribute::DiskSSD, "RANGE_RECORD_Count", "RANGE RECORD Count", "", ""); // Wear_Leveling_Count (smartctl) (same as Wear_Range_Delta?) add(177, StorageAttribute::DiskSSD, "Wear_Leveling_Count", "Wear Leveling Count", "", "Indicates the difference (in percent) between the most worn block and the least worn block."); // Sandforce SSD: Wear_Range_Delta (smartctl) add(177, StorageAttribute::DiskSSD, "Wear_Range_Delta", "Wear Range Delta", "", "Indicates the difference (in percent) between the most worn block and the least worn block."); // Used_Rsvd_Blk_Cnt_Chip (smartctl) add(178, StorageAttribute::DiskSSD, "Used_Rsvd_Blk_Cnt_Chip", "Used Reserved Block Count (Chip)", "", "Number of a chip's used reserved blocks. High values may indicate old drive age or other problems."); // Innodisk SSD (smartctl) add(178, StorageAttribute::DiskSSD, "Runtime_Invalid_Blk_Cnt", "Runtime Invalid Block Count", "", ""); // Used_Rsvd_Blk_Cnt_Tot (smartctl) (description?) add(179, StorageAttribute::DiskSSD, "Used_Rsvd_Blk_Cnt_Tot", "Used Reserved Block Count (Total)", "", "Number of used reserved blocks. High values may indicate old drive age or other problems."); // Unused_Rsvd_Blk_Cnt_Tot (smartctl) add(180, StorageAttribute::DiskSSD, "Unused_Rsvd_Blk_Cnt_Tot", "Unused Reserved Block Count (Total)", "", "Number of unused reserved blocks. High values may indicate old drive age or other problems."); // Crucial / Micron SSDs (smartctl) (description?) add(180, StorageAttribute::DiskSSD, "Unused_Reserve_NAND_Blk", "Unused Reserved NAND Blocks", "", ""); // Program_Fail_Cnt_Total (smartctl) add(181, "Program_Fail_Cnt_Total", "Program Fail Count", "", "Number of flash program (write) failures. High values may indicate old drive age or other problems."); // Sandforce SSD: Program_Fail_Count (smartctl) (Sandforce says it's identical to 171) add(181, StorageAttribute::DiskSSD, "Program_Fail_Count"); // Crucial / Marvell SSD (smartctl) (description?) add(181, StorageAttribute::DiskSSD, "Non4k_Aligned_Access", "Non-4k Aligned Access", "", ""); // Erase_Fail_Count_Total (smartctl) (description?) add(182, StorageAttribute::DiskSSD, "Erase_Fail_Count_Total", "Erase Fail Count", "", "Number of flash erase command failures. High values may indicate old drive age or other problems."); // Sandforce SSD: Erase_Fail_Count (smartctl) (Sandforce says it's identical to 172) add(182, StorageAttribute::DiskSSD, "Erase_Fail_Count"); // Runtime_Bad_Block (smartctl) (description?) add(183, "Runtime_Bad_Block", "Runtime Bad Blocks", "", ""); // Samsung, WD, Crucial / Marvell SSD: SATA Downshift Error Count (smartctl) (description?) add(183, StorageAttribute::DiskAny, "SATA_Iface_Downshift", "SATA Downshift Error Count", "", ""); // Crucial / Marvell SSD: SATA Downshift Error Count (smartctl) (description?) add(183, StorageAttribute::DiskAny, "SATA_Interfac_Downshift", "SATA Downshift Error Count", "", ""); // Intel SSD, Ubtek SSD (smartctl) (description?) add(183, StorageAttribute::DiskSSD, "SATA_Downshift_Count", "SATA Downshift Error Count", "", ""); // End to End Error (smartctl) (description?) add(184, "End-to-End_Error", "End to End Error", "", "Indicates discrepancy of data between the host and the drive cache."); // Sandforce SSD: IO_Error_Detect_Code_Ct (smartctl) add(184, StorageAttribute::DiskSSD, "IO_Error_Detect_Code_Ct", "Input/Output ECC Error Count", "", ""); // OCZ SSD (smartctl) add(184, StorageAttribute::DiskSSD, "Factory_Bad_Block_Count", "Factory Bad Block Count", "", ""); // Indilinx Barefoot SSD: IO_Error_Detect_Code_Ct (smartctl) add(184, StorageAttribute::DiskSSD, "Initial_Bad_Block_Count", "Initial Bad Block Count", "", "Factory-determined number of initial bad blocks."); // Crucial / Micron SSD (smartctl) add(184, StorageAttribute::DiskSSD, "Error_Correction_Count", "Error Correction Count", "", ""); // WD: Head Stability (custom) add(185, StorageAttribute::DiskHDD, "", "Head Stability", "", ""); // WD: Induced Op-Vibration Detection (custom) add(185, StorageAttribute::DiskHDD, "", "Induced Op-Vibration Detection", "", // unused ""); // Reported Uncorrectable (smartctl) add(187, "Reported_Uncorrect", "Reported Uncorrectable", "", "Number of errors that could not be recovered using hardware ECC (Error-Correcting Code)."); // Innodisk SSD: Reported Uncorrectable (smartctl) add(187, StorageAttribute::DiskSSD, "Uncorrectable_Error_Cnt"); // OCZ SSD (smartctl) add(187, StorageAttribute::DiskSSD, "Total_Unc_NAND_Reads", "Total Uncorrectable NAND Reads", "", ""); // Command Timeout (smartctl) add(188, "Command_Timeout", "Command Timeout", "", "Number of aborted operations due to drive timeout. High values may indicate problems with cabling or power supply."); // Micron SSD (smartctl) add(188, StorageAttribute::DiskSSD, "Command_Timeouts", "Command Timeout", "", "Number of aborted operations due to drive timeout. High values may indicate problems with cabling or power supply."); // High Fly Writes (smartctl) add(189, StorageAttribute::DiskHDD, "High_Fly_Writes", "High Fly Writes", "", "Some drives can detect when a recording head is flying outside its normal operating range. " "If an unsafe fly height condition is encountered, the write process is stopped, and the information " "is rewritten or reallocated to a safe region of the drive. This attribute indicates the count of " "these errors detected over the lifetime of the drive."); // Crucial / Marvell SSD (smartctl) add(189, StorageAttribute::DiskSSD, "Factory_Bad_Block_Ct", "Factory Bad Block Count", "", "Factory-determined number of initial bad blocks."); // Various SSD (smartctl) add(189, "Airflow_Temperature_Cel", "Airflow Temperature", "", "Indicates temperature (in Celsius), 100 - temperature, or something completely different (highly depends on manufacturer and model)."); // Airflow Temperature (smartctl) (WD Caviar (may be 50 less), Samsung). Temperature or (100 - temp.) on Seagate/Maxtor. add(190, "Airflow_Temperature_Cel", "Airflow Temperature", "", "Indicates temperature (in Celsius), 100 - temperature, or something completely different (highly depends on manufacturer and model)."); // Samsung SSD (smartctl) (description?) add(190, "Temperature_Exceed_Cnt", "Temperature Exceed Count", "", ""); // OCZ SSD (smartctl) add(190, "Temperature_Celsius", "Temperature (Celsius)", "attr_temperature_celsius", "Drive temperature. The Raw value shows built-in heat sensor registrations (in Celsius)."); // Intel SSD add(190, "Temperature_Case", "Case Temperature (Celsius)", "", "Drive case temperature. The Raw value shows built-in heat sensor registrations (in Celsius)."); // G-sense error rate (smartctl) (same as 221?) add(191, StorageAttribute::DiskHDD, "G-Sense_Error_Rate", "G-Sense Error Rate", "", "Number of errors caused by externally-induced shock and vibration (Raw value). May indicate incorrect installation."); // Power-Off Retract Cycle (smartctl) add(192, StorageAttribute::DiskHDD, "Power-Off_Retract_Count", "Head Retract Cycle Count", "", "Number of times the heads were loaded off the media (during power-offs or emergency conditions)."); // Intel SSD: Unsafe_Shutdown_Count (smartctl) add(192, StorageAttribute::DiskSSD, "Unsafe_Shutdown_Count", "Unsafe Shutdown Count", "", "Raw value indicates the number of unsafe (unclean) shutdown events over the drive lifetime. " "An unsafe shutdown occurs whenever the device is powered off without " "STANDBY IMMEDIATE being the last command."); // Various SSDs (smartctl) add(192, StorageAttribute::DiskSSD, "Unexpect_Power_Loss_Ct", "Unexpected Power Loss Count", "", "Number of unexpected power loss events."); // Fujitsu: Emergency Retract Cycle Count (smartctl) add(192, StorageAttribute::DiskHDD, "Emerg_Retract_Cycle_Ct", "Emergency Retract Cycle Count", "", "Number of times the heads were loaded off the media during emergency conditions."); // Load/Unload Cycle (smartctl) add(193, StorageAttribute::DiskHDD, "Load_Cycle_Count", "Load / Unload Cycle", "", "Number of load / unload cycles into Landing Zone position."); // Temperature Celsius (smartctl) (same as 231). This is the most common one. Some Samsungs: 10xTemp. add(194, "Temperature_Celsius", "Temperature (Celsius)", "attr_temperature_celsius", "Drive temperature. The Raw value shows built-in heat sensor registrations (in Celsius). Increases in average drive temperature often signal spindle motor problems (unless the increases are caused by environmental factors)."); // Samsung SSD: Temperature Celsius (smartctl) (not sure about the value) add(194, StorageAttribute::DiskSSD, "Airflow_Temperature", "Airflow Temperature (Celsius)", "attr_temperature_celsius", "Drive temperature (Celsius)"); // Temperature Celsius x 10 (smartctl) add(194, "Temperature_Celsius_x10", "Temperature (Celsius) x 10", "attr_temperature_celsius_x10", "Drive temperature. The Raw value shows built-in heat sensor registrations (in Celsius * 10). Increases in average drive temperature often signal spindle motor problems (unless the increases are caused by environmental factors)."); // Smart Storage Systems SSD (smartctl) add(194, StorageAttribute::DiskSSD, "Proprietary_194", "Internal Attribute", "", "This attribute has been reserved by vendor as internal."); // Intel SSD (smartctl) add(194, "Temperature_Internal", "Internal Temperature (Celsius)", "attr_temperature_celsius", "Drive case temperature. The Raw value shows built-in heat sensor registrations (in Celsius)."); // Hardware ECC Recovered (smartctl) add(195, "Hardware_ECC_Recovered", "Hardware ECC Recovered", "", "Number of ECC on the fly errors (Raw value). Users are advised to ignore this attribute."); // Fujitsu: ECC_On_The_Fly_Count (smartctl) add(195, StorageAttribute::DiskHDD, "ECC_On_The_Fly_Count"); // Sandforce SSD: ECC_Uncorr_Error_Count (smartctl) (description?) add(195, StorageAttribute::DiskSSD, "ECC_Uncorr_Error_Count", "Uncorrected ECC Error Count", "", "Number of uncorrectable errors (UECC)."); // Samsung SSD (smartctl) (description?) add(195, StorageAttribute::DiskSSD, "ECC_Rate", "Uncorrected ECC Error Rate", "", ""); // OCZ SSD (smartctl) add(195, StorageAttribute::DiskSSD, "Total_Prog_Failures", "Total Program Failures", "", ""); // Indilinx Barefoot SSD: Program_Failure_Blk_Ct (smartctl) (description?) add(195, StorageAttribute::DiskSSD, "Program_Failure_Blk_Ct", "Program Failure Block Count", "", "Number of flash program (write) failures."); // Micron SSD (smartctl) add(195, StorageAttribute::DiskSSD, "Cumulativ_Corrected_ECC", "Cumulative Corrected ECC Error Count", "", ""); // Reallocation Event Count (smartctl) add(196, StorageAttribute::DiskAny, "Reallocated_Event_Count", "Reallocation Event Count", "attr_reallocation_event_count", "Number of reallocation (remap) operations. Raw value should show the total number of attempts (both successful and unsuccessful) to reallocate sectors. An increase in Raw value indicates a disk surface failure." "\n\n" + s_unc_text); // Indilinx Barefoot SSD: Erase_Failure_Blk_Ct (smartctl) (description?) add(196, StorageAttribute::DiskSSD, "Erase_Failure_Blk_Ct", "Erase Failure Block Count", "", "Number of flash erase failures."); // OCZ SSD (smartctl) add(196, StorageAttribute::DiskSSD, "Total_Erase_Failures", "Total Erase Failures", "", ""); // Current Pending Sector Count (smartctl) add(197, "Current_Pending_Sector", "Current Pending Sector Count", "attr_current_pending_sector_count", "Number of "unstable" (waiting to be remapped) sectors (Raw value). If the unstable sector is subsequently read from or written to successfully, this value is decreased and the sector is not remapped. An increase in Raw value indicates a disk surface failure." "\n\n" + s_unc_text); // Indilinx Barefoot SSD: Read_Failure_Blk_Ct (smartctl) (description?) add(197, StorageAttribute::DiskSSD, "Read_Failure_Blk_Ct", "Read Failure Block Count", "", "Number of blocks that failed to be read."); // Samsung: Total_Pending_Sectors (smartctl). From smartctl man page: // unlike Current_Pending_Sector, this won't decrease on reallocation. add(197, "Total_Pending_Sectors", "Total Pending Sectors", "attr_total_pending_sectors", "Number of "unstable" (waiting to be remapped) sectors and already remapped sectors (Raw value). An increase in Raw value indicates a disk surface failure." "\n\n" + s_unc_text); // OCZ SSD (smartctl) add(197, StorageAttribute::DiskSSD, "Total_Unc_Read_Failures", "Total Uncorrectable Read Failures", "", ""); // Offline Uncorrectable (smartctl) add(198, "Offline_Uncorrectable", "Offline Uncorrectable", "attr_offline_uncorrectable", "Number of sectors which couldn't be corrected during Offline Data Collection (Raw value). An increase in Raw value indicates a disk surface failure. " "The value may be decreased automatically when the errors are corrected (e.g., when an unreadable sector is reallocated and the next Offline test is run to see the change)." "\n\n" + s_unc_text); // Samsung: Offline Uncorrectable (smartctl). From smartctl man page: // unlike Current_Pending_Sector, this won't decrease on reallocation. add(198, "Total_Offl_Uncorrectabl", "Total Offline Uncorrectable", "attr_total_attr_offline_uncorrectable", "Number of sectors which couldn't be corrected during Offline Data Collection (Raw value), currently and in the past. An increase in Raw value indicates a disk surface failure." "\n\n" + s_unc_text); // Sandforce SSD: Uncorrectable_Sector_Ct (smartctl) (same description?) add(198, StorageAttribute::DiskSSD, "Uncorrectable_Sector_Ct"); // Indilinx Barefoot SSD: Read_Sectors_Tot_Ct (smartctl) (description?) add(198, StorageAttribute::DiskSSD, "Read_Sectors_Tot_Ct", "Total Read Sectors", "", "Total count of read sectors."); // OCZ SSD add(198, StorageAttribute::DiskSSD, "Host_Reads_GiB", "Host Read (GiB)", "", "Total number of sectors read by the host system. The Raw value is increased by 1 for every GiB read by the host."); // Fujitsu: Offline_Scan_UNC_SectCt (smartctl) add(198, StorageAttribute::DiskHDD, "Offline_Scan_UNC_SectCt"); // Fujitsu version of Offline Uncorrectable (smartctl) (old, not in current smartctl) add(198, StorageAttribute::DiskHDD, "Off-line_Scan_UNC_Sector_Ct"); // UDMA CRC Error Count (smartctl) add(199, "UDMA_CRC_Error_Count", "UDMA CRC Error Count", "", "Number of errors in data transfer via the interface cable in UDMA mode, as determined by ICRC (Interface Cyclic Redundancy Check) (Raw value)."); // Sandforce SSD: SATA_CRC_Error_Count (smartctl) (description?) add(199, "SATA_CRC_Error_Count", "SATA CRC Error Count", "", "Number of errors in data transfer via the SATA interface cable (Raw value)."); // Sandisk SSD: SATA_CRC_Error_Count (smartctl) (description?) add(199, "SATA_CRC_Error", "SATA CRC Error Count", "", "Number of errors in data transfer via the SATA interface cable (Raw value)."); // Intel SSD, Samsung SSD (smartctl) (description?) add(199, "CRC_Error_Count", "CRC Error Count", "", "Number of errors in data transfer via the interface cable (Raw value)."); // Indilinx Barefoot SSD: Write_Sectors_Tot_Ct (smartctl) (description?) add(199, StorageAttribute::DiskSSD, "Write_Sectors_Tot_Ct", "Total Written Sectors", "", "Total count of written sectors."); // OCZ SSD add(198, StorageAttribute::DiskSSD, "Host_Writes_GiB", "Host Written (GiB)", "", "Total number of sectors written by the host system. The Raw value is increased by 1 for every GiB written by the host."); // WD: Multi-Zone Error Rate (smartctl). (maybe head flying height too (?)) add(200, StorageAttribute::DiskHDD, "Multi_Zone_Error_Rate", "Multi Zone Error Rate", "", "Number of errors found when writing to sectors (Raw value). The higher the value, the worse the disk surface condition and/or mechanical subsystem is."); // Fujitsu: Write Error Rate (smartctl) add(200, StorageAttribute::DiskHDD, "Write_Error_Count", "Write Error Count", "", "Number of errors found when writing to sectors (Raw value). The higher the value, the worse the disk surface condition and/or mechanical subsystem is."); // Indilinx Barefoot SSD: Read_Commands_Tot_Ct (smartctl) (description?) add(200, StorageAttribute::DiskSSD, "Read_Commands_Tot_Ct", "Total Read Commands Issued", "", "Total count of read commands issued."); // Soft Read Error Rate (smartctl) (description?) add(201, StorageAttribute::DiskHDD, "Soft_Read_Error_Rate", "Soft Read Error Rate", "attr_soft_read_error_rate", "Uncorrected read errors reported to the operating system (Raw value). If the value is non-zero, you should back up your data."); // Sandforce SSD: Unc_Soft_Read_Err_Rate (smartctl) add(201, StorageAttribute::DiskSSD, "Unc_Soft_Read_Err_Rate"); // Samsung SSD: (smartctl) (description?) add(201, StorageAttribute::DiskSSD, "Supercap_Status", "Supercapacitor Health", "", ""); // Maxtor: Off Track Errors (custom) // add(201, StorageAttribute::DiskHDD, "", "Off Track Errors", "", // unused // ""); // Fujitsu: Detected TA Count (smartctl) (description?) add(201, StorageAttribute::DiskHDD, "Detected_TA_Count", "Torque Amplification Count", "", "Number of attempts to compensate for platter speed variations."); // Indilinx Barefoot SSD: Write_Commands_Tot_Ct (smartctl) (description?) add(201, StorageAttribute::DiskSSD, "Write_Commands_Tot_Ct", "Total Write Commands Issued", "", "Total count of write commands issued."); // WD: Data Address Mark Errors (smartctl) add(202, StorageAttribute::DiskHDD, "Data_Address_Mark_Errs", "Data Address Mark Errors", "", "Frequency of the Data Address Mark errors."); // Fujitsu: TA Increase Count (same as 227?) add(202, StorageAttribute::DiskHDD, "TA_Increase_Count", "TA Increase Count", "", "Number of attempts to compensate for platter speed variations."); // Indilinx Barefoot SSD: Error_Bits_Flash_Tot_Ct (smartctl) (description?) add(202, StorageAttribute::DiskSSD, "Error_Bits_Flash_Tot_Ct", "Total Count of Error Bits", "", ""); // Crucial / Marvell SSD: Percent_Lifetime_Used (smartctl) (description?) add(202, StorageAttribute::DiskSSD, "Percent_Lifetime_Used", "Rated Life Used (%)", "attr_ssd_life_used", "Used drive life in %."); // Samsung SSD: (smartctl) (description?) add(202, StorageAttribute::DiskSSD, "Exception_Mode_Status", "Exception Mode Status", "", ""); // OCZ SSD (smartctl) (description?) add(202, StorageAttribute::DiskSSD, "Total_Read_Bits_Corr_Ct", "Total Read Bits Corrected", "", ""); // Micron SSD (smartctl) (description?) add(202, StorageAttribute::DiskSSD, "Percent_Lifetime_Remain", "Remaining Lifetime (%)", "attr_ssd_life_left", "Remaining drive life in %."); // Run Out Cancel (smartctl). (description?) add(203, "Run_Out_Cancel", "Run Out Cancel", "", "Number of ECC errors."); // Maxtor: ECC Errors (smartctl) (description?) add(203, StorageAttribute::DiskHDD, "Corr_Read_Errors_Tot_Ct", "ECC Errors", "", "Number of ECC errors."); // Indilinx Barefoot SSD: Corr_Read_Errors_Tot_Ct (smartctl) (description?) add(203, StorageAttribute::DiskSSD, "Corr_Read_Errors_Tot_Ct", "Total Corrected Read Errors", "", "Total cound of read sectors with correctable errors."); // Maxtor: Soft ECC Correction (smartctl) add(204, StorageAttribute::DiskHDD, "Soft_ECC_Correction", "Soft ECC Correction", "", "Number of errors corrected by software ECC (Error-Correcting Code)."); // Fujitsu: Shock_Count_Write_Opern (smartctl) (description?) add(204, StorageAttribute::DiskHDD, "Shock_Count_Write_Opern", "Shock Count During Write Operation", "", ""); // Sandforce SSD: Soft_ECC_Correct_Rate (smartctl) (description?) add(204, StorageAttribute::DiskSSD, "Soft_ECC_Correct_Rate", "Soft ECC Correction Rate", "", ""); // Indilinx Barefoot SSD: Bad_Block_Full_Flag (smartctl) (description?) add(204, StorageAttribute::DiskSSD, "Bad_Block_Full_Flag", "Bad Block Area Is Full", "", "Indicates whether the bad block (reserved) area is full or not."); // Thermal Asperity Rate (TAR) (smartctl) add(205, "Thermal_Asperity_Rate", "Thermal Asperity Rate", "", "Number of problems caused by high temperature."); // Fujitsu: Shock_Rate_Write_Opern (smartctl) (description?) add(205, StorageAttribute::DiskHDD, "Shock_Rate_Write_Opern", "Shock Rate During Write Operation", "", ""); // Indilinx Barefoot SSD: Max_PE_Count_Spec (smartctl) (description?) add(205, StorageAttribute::DiskSSD, "Max_PE_Count_Spec", "Maximum Program-Erase Count Specification", "", "Maximum Program / Erase cycle count as per specification."); // OCZ SSD (smartctl) add(205, StorageAttribute::DiskSSD, "Max_Rated_PE_Count", "Maximum Rated Program-Erase Count", "", "Maximum Program / Erase cycle count as per specification."); // Flying Height (smartctl) add(206, StorageAttribute::DiskHDD, "Flying_Height", "Head Flying Height", "", "The height of the disk heads above the disk surface. A downward trend will often predict a head crash, " "while high values may cause read / write errors."); // Indilinx Barefoot SSD, OCZ SSD: Min_Erase_Count (smartctl) (description?) add(206, StorageAttribute::DiskSSD, "Min_Erase_Count", "Minimum Erase Count", "", "The minimum of individual erase counts of all the blocks."); // Crucial / Marvell SSD: Write_Error_Rate (smartctl) (description?) add(206, StorageAttribute::DiskSSD, "Write_Error_Rate", "Write Error Rate", "", ""); // Spin High Current (smartctl) add(207, StorageAttribute::DiskHDD, "Spin_High_Current", "Spin High Current", "", "Amount of high current needed or used to spin up the drive."); // Indilinx Barefoot SSD, OCZ SSD: Max_Erase_Count (smartctl) (description?) add(207, StorageAttribute::DiskSSD, "Max_Erase_Count", "Maximum Erase Count", "", ""); // Spin Buzz (smartctl) add(208, StorageAttribute::DiskHDD, "Spin_Buzz", "Spin Buzz", "", "Number of buzz routines (retries because of low current) to spin up the drive."); // Indilinx Barefoot SSD, OCZ SSD: Average_Erase_Count (smartctl) (description?) add(208, StorageAttribute::DiskSSD, "Average_Erase_Count", "Average Erase Count", "", "The average of individual erase counts of all the blocks."); // Offline Seek Performance (smartctl) (description?) add(209, StorageAttribute::DiskHDD, "Offline_Seek_Performnce", "Offline Seek Performance", "", "Seek performance during Offline Data Collection operations."); // Indilinx Barefoot SSD, OCZ SSD: Remaining_Lifetime_Perc (smartctl) (description?) add(209, StorageAttribute::DiskSSD, "Remaining_Lifetime_Perc", "Remaining Lifetime (%)", "attr_ssd_life_left", "Remaining drive life in % (usually by erase count)."); // Vibration During Write (custom). wikipedia says 211, but it's wrong. (description?) add(210, StorageAttribute::DiskHDD, "", "Vibration During Write", "", "Vibration encountered during write operations."); // OCZ SSD (smartctl) add(210, StorageAttribute::DiskSSD, "SATA_CRC_Error_Count", "SATA CRC Error Count", "", ""); // Indilinx Barefoot SSD: Indilinx_Internal (smartctl) (description?) add(210, StorageAttribute::DiskSSD, "Indilinx_Internal", "Internal Attribute", "", "This attribute has been reserved by vendor as internal."); // Crucial / Micron SSD (smartctl) add(210, StorageAttribute::DiskSSD, "Success_RAIN_Recov_Cnt", "Success RAIN Recovered Count", "", ""); // Vibration During Read (description?) add(211, StorageAttribute::DiskHDD, "", "Vibration During Read", "", "Vibration encountered during read operations."); // Indilinx Barefoot SSD (smartctl) (description?) add(211, StorageAttribute::DiskSSD, "SATA_Error_Ct_CRC", "SATA CRC Error Count", "", "Number of errors in data transfer via the SATA interface cable"); // OCZ SSD (smartctl) (description?) add(211, StorageAttribute::DiskSSD, "SATA_UNC_Count", "SATA Uncorrectable Error Count", "", "Number of errors in data transfer via the SATA interface cable"); // Shock During Write (custom) (description?) add(212, StorageAttribute::DiskHDD, "", "Shock During Write", "", "Shock encountered during write operations"); // Indilinx Barefoot SSD: SATA_Error_Ct_Handshake (smartctl) (description?) add(212, StorageAttribute::DiskSSD, "SATA_Error_Ct_Handshake", "SATA Handshake Error Count", "", "Number of errors occurring during SATA handshake."); // OCZ SSD (smartctl) (description?) add(212, StorageAttribute::DiskSSD, "Pages_Requiring_Rd_Rtry", "Pages Requiring Read Retry", "", ""); // OCZ SSD (smartctl) (description?) add(212, StorageAttribute::DiskSSD, "NAND_Reads_with_Retry", "Number of NAND Reads with Retry", "", ""); // Sandisk SSDs: (smartctl) (description?) add(212, StorageAttribute::DiskSSD, "SATA_PHY_Error", "SATA Physical Error Count", "", ""); // Indilinx Barefoot SSD: Indilinx_Internal (smartctl) (description?) add(213, StorageAttribute::DiskSSD, "Indilinx_Internal", "Internal Attribute", "", "This attribute has been reserved by vendor as internal."); // OCZ SSD (smartctl) (description?) add(213, StorageAttribute::DiskSSD, "Simple_Rd_Rtry_Attempts", "Simple Read Retry Attempts", "", ""); // OCZ SSD (smartctl) (description?) add(213, StorageAttribute::DiskSSD, "Snmple_Retry_Attempts", "Simple Retry Attempts", "", ""); // OCZ SSD (smartctl) (description?) add(213, StorageAttribute::DiskSSD, "Simple_Retry_Attempts", "Simple Retry Attempts", "", ""); // OCZ SSD (smartctl) (description?) add(213, StorageAttribute::DiskSSD, "Adaptv_Rd_Rtry_Attempts", "Adaptive Read Retry Attempts", "", ""); // OCZ SSD (smartctl) (description?) add(214, StorageAttribute::DiskSSD, "Adaptive_Retry_Attempts", "Adaptive Retry Attempts", "", ""); // Kingston SSD (smartctl) add(218, StorageAttribute::DiskSSD, "CRC_Error_Count", "CRC Error Count", "", ""); // Disk Shift (smartctl) // Note: There's also smartctl shortcut option "-v 220,temp" (possibly for Temperature Celsius), // but it's not used anywhere, so we ignore it. add(220, StorageAttribute::DiskHDD, "Disk_Shift", "Disk Shift", "", "Shift of disks towards spindle. Shift of disks is possible as a result of a strong shock or a fall, high temperature, or some other reasons."); // G-sense error rate (smartctl) add(221, StorageAttribute::DiskHDD, "G-Sense_Error_Rate", "G-Sense Error Rate", "", "Number of errors resulting from externally-induced shock and vibration (Raw value). May indicate incorrect installation."); // OCZ SSD (smartctl) (description?) add(213, StorageAttribute::DiskSSD, "Int_Data_Path_Prot_Unc", "Internal Data Path Protection Uncorrectable", "", ""); // Loaded Hours (smartctl) add(222, StorageAttribute::DiskHDD, "Loaded_Hours", "Loaded Hours", "", "Number of hours spent operating under load (movement of magnetic head armature) (Raw value)"); // OCZ SSD (smartctl) (description?) add(222, StorageAttribute::DiskSSD, "RAID_Recovery_Count", "RAID Recovery Count", "", ""); // Load/Unload Retry Count (smartctl) (description?) add(223, StorageAttribute::DiskHDD, "Load_Retry_Count", "Load / Unload Retry Count", "", "Number of times the head armature entered / left the data zone."); // Load Friction (smartctl) add(224, StorageAttribute::DiskHDD, "Load_Friction", "Load Friction", "", "Resistance caused by friction in mechanical parts while operating. An increase of Raw value may mean that there is a problem with the mechanical subsystem of the drive."); // OCZ SSD (smartctl) (description?) add(224, StorageAttribute::DiskSSD, "In_Warranty", "In Warranty", "", ""); // Load/Unload Cycle Count (smartctl) (description?) add(225, StorageAttribute::DiskHDD, "Load_Cycle_Count", "Load / Unload Cycle Count", "", "Total number of load cycles."); // Intel SSD: Host_Writes_32MiB (smartctl) (description?) add(225, StorageAttribute::DiskSSD, "Host_Writes_32MiB", "Host Written (32 MiB)", "", "Total number of sectors written by the host system. The Raw value is increased by 1 for every 32 MiB written by the host."); // OCZ SSD (smartctl) (description?) add(225, StorageAttribute::DiskSSD, "DAS_Polarity", "DAS Polarity", "", ""); // Innodisk SSDs: (smartctl) (description?) add(225, StorageAttribute::DiskSSD, "Data_Log_Write_Count", "Data Log Write Count", "", ""); // Load-in Time (smartctl) add(226, StorageAttribute::DiskHDD, "Load-in_Time", "Load-in Time", "", "Total time of loading on the magnetic heads actuator. Indicates total time in which the drive was under load (on the assumption that the magnetic heads were in operating mode and out of the parking area)."); // Intel SSD: Intel_Internal (smartctl) add(226, StorageAttribute::DiskSSD, "Intel_Internal", "Internal Attribute", "", "This attribute has been reserved by vendor as internal."); // Intel SSD: Workld_Media_Wear_Indic (smartctl) add(226, StorageAttribute::DiskSSD, "Workld_Media_Wear_Indic", "Timed Workload Media Wear", "", "Timed workload media wear indicator (percent*1024)"); // OCZ SSD (smartctl) (description?) add(226, StorageAttribute::DiskSSD, "Partial_Pfail", "Partial Program Fail", "", ""); // Torque Amplification Count (aka TA) (smartctl) add(227, StorageAttribute::DiskHDD, "Torq-amp_Count", "Torque Amplification Count", "", "Number of attempts to compensate for platter speed variations."); // Intel SSD: Intel_Internal (smartctl) add(227, StorageAttribute::DiskSSD, "Intel_Internal", "Internal Attribute", "", "This attribute has been reserved by vendor as internal."); // Intel SSD: Workld_Host_Reads_Perc (smartctl) add(227, StorageAttribute::DiskSSD, "Workld_Host_Reads_Perc", "Timed Workload Host Reads %", "", ""); // Power-Off Retract Count (smartctl) add(228, "Power-off_Retract_Count", "Power-Off Retract Count", "", "Number of times the magnetic armature was retracted automatically as a result of power loss."); // Intel SSD: Intel_Internal (smartctl) add(228, StorageAttribute::DiskSSD, "Intel_Internal", "Internal Attribute", "", "This attribute has been reserved by vendor as internal."); // Intel SSD: Workload_Minutes (smartctl) add(228, StorageAttribute::DiskSSD, "Workload_Minutes", "Workload (Minutes)", "", ""); // Transcend SSD: Halt_System_ID (smartctl) (description?) add(229, StorageAttribute::DiskSSD, "Halt_System_ID", "Halt System ID", "", "Halt system ID and flash ID"); // InnoDisk SSD (smartctl) add(229, StorageAttribute::DiskSSD, "Flash_ID", "Flash ID", "", "Flash ID"); // IBM: GMR Head Amplitude (smartctl) add(230, StorageAttribute::DiskHDD, "Head_Amplitude", "GMR Head Amplitude", "", "Amplitude of heads trembling (GMR-head) in running mode."); // Sandforce SSD: Life_Curve_Status (smartctl) (description?) add(230, StorageAttribute::DiskSSD, "Life_Curve_Status", "Life Curve Status", "", "Current state of drive operation based upon the Life Curve."); // OCZ SSD (smartctl) (description?) add(230, StorageAttribute::DiskSSD, "SuperCap_Charge_Status", "Super-Capacitor Charge Status", "", "0 means not charged, 1 - fully charged, 2 - unknown."); // OCZ SSD (smartctl) (description?) add(230, StorageAttribute::DiskSSD, "Write_Throttling", "Write Throttling", "", ""); // Sandisk SSD (smartctl) (description?) add(230, StorageAttribute::DiskSSD, "Perc_Write/Erase_Count", "Write / Erase Count (%)", "", ""); // Temperature (Some drives) (smartctl) add(231, "Temperature_Celsius", "Temperature", "attr_temperature_celsius", "Drive temperature. The Raw value shows built-in heat sensor registrations (in Celsius). Increases in average drive temperature often signal spindle motor problems (unless the increases are caused by environmental factors)."); // Sandforce SSD: SSD_Life_Left add(231, StorageAttribute::DiskSSD, "SSD_Life_Left", "SSD Life Left", "attr_ssd_life_left", "A measure of drive's estimated life left. A Normalized value of 100 indicates a new drive. " "10 means there are reserved blocks left but Program / Erase cycles have been used. " "0 means insufficient reserved blocks, drive may be in read-only mode to allow recovery of the data."); // Intel SSD: Available_Reservd_Space (smartctl) (description?) add(232, StorageAttribute::DiskSSD, "Available_Reservd_Space", "Available reserved space", "", "Number of reserved blocks remaining. The Normalized value indicates percentage, with 100 meaning new and 10 meaning the drive being close to its end of life."); // Transcend SSD: Firmware_Version_information (smartctl) (description?) add(232, StorageAttribute::DiskSSD, "Firmware_Version_Info", "Firmware Version Information", "", "Firmware version information (year, month, day, channels, banks)."); // Same as Firmware_Version_Info, but in older smartctl versions. add(232, StorageAttribute::DiskSSD, "Firmware_Version_information", "Firmware Version Information", "", "Firmware version information (year, month, day, channels, banks)."); // OCZ SSD (description?) (smartctl) add(232, StorageAttribute::DiskSSD, "Lifetime_Writes", "Lifetime_Writes", "", ""); // Kingston SSD (description?) (smartctl) add(232, StorageAttribute::DiskSSD, "Flash_Writes_GiB", "Flash Written (GiB)", "", ""); // Innodisk SSD (description?) (smartctl) add(232, StorageAttribute::DiskSSD, "Spares_Remaining_Perc", "Spare Blocks Remaining (%)", "attr_ssd_life_left", "Percentage of spare blocks remaining. Spare blocks are used when bad blocks develop."); // Innodisk SSD (description?) (smartctl) add(232, StorageAttribute::DiskSSD, "Perc_Avail_Resrvd_Space", "Available Reserved Space (%)", "attr_ssd_life_left", "Percentage of spare blocks remaining. Spare blocks are used when bad blocks develop."); // Intel SSD: Media_Wearout_Indicator (smartctl) (description?) add(233, StorageAttribute::DiskSSD, "Media_Wearout_Indicator", "Media Wear Out Indicator", "attr_ssd_life_left", "Number of cycles the NAND media has experienced. The Normalized value decreases linearly from 100 to 1 as the average erase cycle " "count increases from 0 to the maximum rated cycles."); // OCZ SSD add(233, StorageAttribute::DiskSSD, "Remaining_Lifetime_Perc", "Remaining Lifetime %", "attr_ssd_life_left", "Remaining drive life in % (usually by erase count)."); // Sandforce SSD: SandForce_Internal (smartctl) (description?) add(233, StorageAttribute::DiskSSD, "SandForce_Internal", "Internal Attribute", "", "This attribute has been reserved by vendor as internal."); // Transcend SSD: ECC_Fail_Record (smartctl) (description?) add(233, StorageAttribute::DiskSSD, "ECC_Fail_Record", "ECC Failure Record", "", "Indicates rate of ECC (error-correcting code) failures."); // Innodisk SSD (smartctl) (description?) add(233, StorageAttribute::DiskSSD, "Flash_Writes_32MiB", "Flash Written (32MiB)", "", ""); // Innodisk SSD (smartctl) (description?) add(233, StorageAttribute::DiskSSD, "Total_NAND_Writes_GiB", "Total NAND Written (GiB)", "", ""); // Sandforce SSD: SandForce_Internal (smartctl) (description?) add(234, StorageAttribute::DiskSSD, "SandForce_Internal", "Internal Attribute", "", "This attribute has been reserved by vendor as internal."); // Intel SSD (smartctl) add(234, StorageAttribute::DiskSSD, "Thermal_Throttle", "Thermal Throttle", "", ""); // Transcend SSD: Erase_Count_Avg (smartctl) (description?) add(234, StorageAttribute::DiskSSD, "Erase_Count_Avg/Max", "Erase Count Average / Maximum", "", ""); // Innodisk SSD (smartctl) (description?) add(234, StorageAttribute::DiskSSD, "Flash_Reads_32MiB", "Flash Read (32MiB)", "", ""); // Sandisk SSD (smartctl) (description / name?) add(234, StorageAttribute::DiskSSD, "Perc_Write/Erase_Ct_BC", "Write / Erase Count BC (%)", "", ""); // Sandforce SSD: SuperCap_Health (smartctl) (description?) add(235, StorageAttribute::DiskSSD, "SuperCap_Health", "Supercapacitor Health", "", ""); // Transcend SSD: Block_Count_Good/System (smartctl) (description?) add(235, StorageAttribute::DiskSSD, "Block_Count_Good/System", "Good / System Free Block Count", "", "Good block count and system free block count."); // InnoDisk SSD (smartctl). (description / name?) add(235, StorageAttribute::DiskSSD, "Later_Bad_Block", "Later Bad Block", "", ""); // InnoDisk SSD (smartctl). (description / name?) add(235, StorageAttribute::DiskSSD, "Later_Bad_Blk_Inf_R/W/E", "Later Bad Block Read / Write / Erase", "", ""); // Samsung SSD (smartctl). (description / name?) add(235, StorageAttribute::DiskSSD, "POR_Recovery_Count", "POR Recovery Count", "", ""); // InnoDisk SSD (smartctl). (description / name?) add(236, StorageAttribute::DiskSSD, "Unstable_Power_Count", "Unstable Power Count", "", ""); // Head Flying Hours (smartctl) add(240, StorageAttribute::DiskHDD, "Head_Flying_Hours", "Head Flying Hours", "", "Time spent on head is positioning."); // Fujitsu: Transfer_Error_Rate (smartctl) (description?) add(240, StorageAttribute::DiskHDD, "Transfer_Error_Rate", "Transfer Error Rate", "", ""); // InnoDisk SSD (smartctl). (description / name?) add(240, StorageAttribute::DiskSSD, "Write_Head", "Write Head", "", ""); // Total_LBAs_Written (smartctl) (description?) add(241, "Total_LBAs_Written", "Total LBAs Written", "", "Logical blocks written during lifetime."); // Sandforce SSD: Lifetime_Writes_GiB (smartctl) (maybe in 64GiB increments?) add(241, StorageAttribute::DiskSSD, "Lifetime_Writes_GiB", "Total GiB Written", "", "Total GiB written during lifetime."); // Intel SSD: Host_Writes_32MiB (smartctl) (description?) add(241, StorageAttribute::DiskSSD, "Host_Writes_32MiB", "Host Written (32 MiB)", "", "Total number of sectors written by the host system. The Raw value is increased by 1 for every 32 MiB written by the host."); // OCZ SSD (smartctl) add(241, StorageAttribute::DiskSSD, "Host_Writes_GiB", "Host Written (GiB)", "", "Total number of sectors written by the host system. The Raw value is increased by 1 for every GiB written by the host."); // Sandisk SSD (smartctl) add(241, StorageAttribute::DiskSSD, "Total_Writes_GiB", "Total Written (GiB)", "", "Total GiB written."); // Toshiba SSD (smartctl) add(241, StorageAttribute::DiskSSD, "Host_Writes", "Host Written", "", "Total number of sectors written by the host system."); // Total_LBAs_Read (smartctl) (description?) add(242, "Total_LBAs_Read", "Total LBAs Read", "", "Logical blocks read during lifetime."); // Sandforce SSD: Lifetime_Writes_GiB (smartctl) (maybe in 64GiB increments?) add(242, StorageAttribute::DiskSSD, "Lifetime_Reads_GiB", "Total GiB Read", "", "Total GiB read during lifetime."); // Intel SSD: Host_Reads_32MiB (smartctl) (description?) add(242, StorageAttribute::DiskSSD, "Host_Reads_32MiB", "Host Read (32 MiB)", "", "Total number of sectors read by the host system. The Raw value is increased by 1 for every 32 MiB read by the host."); // OCZ SSD (smartctl) add(242, StorageAttribute::DiskSSD, "Host_Reads_GiB", "Host Read (GiB)", "", "Total number of sectors read by the host system. The Raw value is increased by 1 for every GiB read by the host."); // Marvell SSD (smartctl) add(242, StorageAttribute::DiskSSD, "Host_Reads", "Host Read", "", ""); // Sandisk SSD (smartctl) add(241, StorageAttribute::DiskSSD, "Total_Reads_GiB", "Total Read (GiB)", "", "Total GiB read."); // Intel SSD: (smartctl) (description?) add(243, StorageAttribute::DiskSSD, "NAND_Writes_32MiB", "NAND Written (32MiB)", "", ""); // Samsung SSD (smartctl). (description / name?) add(243, StorageAttribute::DiskSSD, "SATA_Downshift_Ct", "SATA Downshift Count", "", ""); // Kingston SSDs (description?) (smartctl) add(244, StorageAttribute::DiskSSD, "Average_Erase_Count", "Average Erase Count", "", "The average of individual erase counts of all the blocks"); // Samsung SSDs (description?) (smartctl) add(244, StorageAttribute::DiskSSD, "Thermal_Throttle_St", "Thermal Throttle Status", "", ""); // Sandisk SSDs (description?) (smartctl) add(244, StorageAttribute::DiskSSD, "Thermal_Throttle", "Thermal Throttle Status", "", ""); // Kingston SSDs (smartctl) add(245, StorageAttribute::DiskSSD, "Max_Erase_Count", "Maximum Erase Count", "", "The maximum of individual erase counts of all the blocks."); // Innodisk SSD (smartctl) (description?) add(245, StorageAttribute::DiskSSD, "Flash_Writes_32MiB", "Flash Written (32MiB)", "", ""); // Samsung SSD (smartctl) (description?) add(245, StorageAttribute::DiskSSD, "Timed_Workld_Media_Wear", "Timed Workload Media Wear", "", ""); // SiliconMotion SSD (smartctl) (description?) add(245, StorageAttribute::DiskSSD, "TLC_Writes_32MiB", "TLC Written (32MiB)", "", "Total number of sectors written to TLC. The Raw value is increased by 1 for every 32 MiB written by the host."); // Crucial / Micron SSD (smartctl) add(246, StorageAttribute::DiskSSD, "Total_Host_Sector_Write", "Total Host Sectors Written", "", "Total number of sectors written by the host system."); // Kingston SSDs (description?) (smartctl) add(246, StorageAttribute::DiskSSD, "Total_Erase_Count", "Total Erase Count", "", ""); // Samsung SSD (smartctl) (description?) add(246, StorageAttribute::DiskSSD, "Timed_Workld_RdWr_Ratio", "Timed Workload Read/Write Ratio", "", ""); // SiliconMotion SSD (smartctl) (description?) add(246, StorageAttribute::DiskSSD, "SLC_Writes_32MiB", "SLC Written (32MiB)", "", "Total number of sectors written to SLC. The Raw value is increased by 1 for every 32 MiB written by the host."); // Crucial / Micron SSD (smartctl) add(247, StorageAttribute::DiskSSD, "Host_Program_Page_Count", "Host Program Page Count", "", ""); // Samsung SSD (smartctl) add(247, StorageAttribute::DiskSSD, "Timed_Workld_Timer", "Timed Workload Timer", "", ""); // SiliconMotion SSD (smartctl) (description?) add(247, StorageAttribute::DiskSSD, "Raid_Recoverty_Ct", "RAID Recovery Count", "", ""); add(248, StorageAttribute::DiskSSD, "Bckgnd_Program_Page_Cnt", "Background Program Page Count", "", ""); // Intel SSD: NAND_Writes_1GiB (smartctl) (description?) add(249, StorageAttribute::DiskSSD, "NAND_Writes_1GiB", "NAND Written (1GiB)", "", ""); // OCZ SSD: Total_NAND_Prog_Ct_GiB (smartctl) (description?) add(249, StorageAttribute::DiskSSD, "Total_NAND_Prog_Ct_GiB", "Total NAND Written (1GiB)", "", ""); // Read Error Retry Rate (smartctl) (description?) add(250, "Read_Error_Retry_Rate", "Read Error Retry Rate", "", "Number of errors found while reading."); // Samsung SSD: (smartctl) (description?) add(183, StorageAttribute::DiskAny, "SATA_Iface_Downshift", "SATA Downshift Error Count", "", ""); // OCZ SSD (smartctl) (description?) add(251, StorageAttribute::DiskSSD, "Total_NAND_Read_Ct_GiB", "Total NAND Read (1GiB)", "", ""); // Samsung SSD: (smartctl) (description?) add(251, StorageAttribute::DiskAny, "NAND_Writes", "NAND Write Count", "", ""); // Free Fall Protection (smartctl) (seagate laptop drives) add(254, StorageAttribute::DiskHDD, "Free_Fall_Sensor", "Free Fall Protection", "", "Number of free fall events detected by accelerometer sensor."); } /// Add an attribute description to the attribute database void add(int32_t id, const std::string& smartctl_name, const std::string& readable_name, const std::string& generic_name, const std::string& description) { add(AttributeDescription(id, StorageAttribute::DiskAny, smartctl_name, readable_name, generic_name, description)); } /// Add a previously added description to the attribute database under a /// different smartctl name (fill the other members from the previous attribute). // void add(int32_t id, const std::string& smartctl_name) // { // std::map >::iterator iter = id_db.find(id); // DBG_ASSERT(iter != id_db.end() && !iter->second.empty()); // if (iter != id_db.end() || iter->second.empty()) { // AttributeDescription attr = iter->second.front(); // add(AttributeDescription(id, StorageAttribute::DiskAny, smartctl_name, attr.readable_name, attr.generic_name, attr.description)); // } // } /// Add an attribute description to the attribute database void add(int32_t id, StorageAttribute::DiskType type, const std::string& smartctl_name, const std::string& readable_name, const std::string& generic_name, const std::string& description) { add(AttributeDescription(id, type, smartctl_name, readable_name, generic_name, description)); } /// Add a previously added description to the attribute database under a /// different smartctl name (fill the other members from the previous attribute). void add(int32_t id, StorageAttribute::DiskType type, const std::string& smartctl_name) { std::map >::iterator iter = id_db.find(id); DBG_ASSERT(iter != id_db.end() && !iter->second.empty()); if (iter != id_db.end() || iter->second.empty()) { AttributeDescription attr = iter->second.front(); add(AttributeDescription(id, type, smartctl_name, attr.readable_name, attr.generic_name, attr.description)); } } /// Add an attribute description to the attribute database void add(const AttributeDescription& descr) { id_db[descr.id].push_back(descr); } /// Find the description by smartctl name or id, merging them if they're partial. AttributeDescription find(const std::string& smartctl_name, int32_t id, StorageAttribute::DiskType type) const { // search by ID first std::map< int32_t, std::vector >::const_iterator id_iter = id_db.find(id); if (id_iter == id_db.end()) { return AttributeDescription(); // not found } DBG_ASSERT(!id_iter->second.empty()); if (id_iter->second.empty()) { return AttributeDescription(); // invalid DB? } std::vector type_matched; for (std::vector::const_iterator attr_iter = id_iter->second.begin(); attr_iter != id_iter->second.end(); ++attr_iter) { if (attr_iter->disk_type == type || attr_iter->disk_type == StorageAttribute::DiskAny || type == StorageAttribute::DiskAny) { type_matched.push_back(*attr_iter); } } if (type_matched.empty()) { return AttributeDescription(); // not found } // search by smartctl name in ID-supplied vector for (std::vector::const_iterator attr_iter = type_matched.begin(); attr_iter != type_matched.end(); ++attr_iter) { // compare them case-insensitively, just in case if ( hz::string_to_lower_copy(attr_iter->smartctl_name) == hz::string_to_lower_copy(smartctl_name)) { return *attr_iter; // found it } } // nothing was found by name, return the first one by that ID. return type_matched.front(); } private: std::map< int32_t, std::vector > id_db; ///< id => attribute descriptions }; /// Program-wide attribute description database static const AttributeDatabase s_attribute_db; /// Attribute description for attribute database struct StatisticDescription { /// Constructor StatisticDescription() { } /// Constructor StatisticDescription(const std::string& smartctl_name_, const std::string& readable_name_, const std::string& generic_name_, const std::string& description_) : smartctl_name(smartctl_name_), readable_name(readable_name_), generic_name(generic_name_), description(description_) { } std::string smartctl_name; ///< e.g. Highest Temperature std::string readable_name; ///< e.g. Highest Temperature (C) std::string generic_name; ///< Generic name to be set on the property. std::string description; ///< Attribute description, can be "". }; /// Devstat entry description database class StatisticsDatabase { public: /// Constructor StatisticsDatabase() { // See http://www.t13.org/documents/UploadedDocuments/docs2016/di529r14-ATAATAPI_Command_Set_-_4.pdf // General Statistics add("Lifetime Power-On Resets", "", "", "The number of times the device has processed a power-on reset."); add("Power-on Hours", "", "", "The amount of time that the device has been operational since it was manufactured."); add("Logical Sectors Written", "", "", "The number of logical sectors received from the host. " "This statistic is incremented by one for each logical sector that was received from the host without an error."); add("Number of Write Commands", "", "", "The number of write commands that returned command completion without an error. " "This statistic is incremented by one for each write command that returns command completion without an error."); add("Logical Sectors Read", "", "", "The number of logical sectors sent to the host. " "This statistic is incremented by one for each logical sector that was sent to the host without an error."); add("Number of Read Commands", "", "", "The number of read commands that returned command completion without an error. " "This statistic is incremented by one for each read command that returns command completion without an error."); add("Date and Time TimeStamp", "", "", "a) the TimeStamp set by the most recent SET DATE & TIME EXT command plus the number of " "milliseconds that have elapsed since that SET DATE & TIME EXT command was processed;\n" "or\n" "b) a copy of the Power-on Hours statistic (see A.5.4.4) with the hours unit of measure changed to milliseconds as described"); add("Pending Error Count", "", "", "The number of logical sectors listed in the Pending Errors log."); add("Workload Utilization", "", "", "An estimate of device utilization as a percentage of the manufacturer's designs for various wear factors " "(e.g., wear of the medium, head load events), if any. The reported value can be greater than 100%."); add("Utilization Usage Rate", "", "", "An estimate of the rate at which device wear factors (e.g., damage to the recording medium) " "are being used during a specified interval of time. This statistic is expressed as a percentage of the manufacturer's designs."); // Free-Fall Statistics add("Number of Free-Fall Events Detected", "", "", "The number of free-fall events detected by the device."); add("Overlimit Shock Events", "", "", "The number of shock events detected by the device " "with the magnitude higher than the maximum rating of the device."); // Rotating Media Statistics add("Spindle Motor Power-on Hours", "", "", "The amount of time that the spindle motor has been powered on since the device was manufactured. "); add("Head Flying Hours", "", "", "The number of hours that the device heads have been flying over the surface of the media since the device was manufactured. "); add("Head Load Events", "", "", "The number of head load events. A head load event is defined as:\n" "a) when the heads are loaded from the ramp to the media for a ramp load device;\n" "or\n" "b) when the heads take off from the landing zone for a contact start stop device."); add("Number of Reallocated Logical Sectors", "", "", "The number of logical sectors that have been reallocated after device manufacture.\n\n" "If the value is normalized, this is the whole number percentage of the available logical sector reallocation " "resources that have been used (i.e., 0-100)." "\n\n" + s_unc_text); add("Read Recovery Attempts", "", "", "The number of logical sectors that require three or more attempts to read the data from the media for each read command. " "This statistic is incremented by one for each logical sector that encounters a read recovery attempt. " "These events may be caused by external environmental conditions (e.g., operating in a moving vehicle)."); add("Number of Mechanical Start Failures", "", "", "The number of mechanical start failures after device manufacture. " "A mechanical start failure is a failure that prevents the device from achieving a normal operating condition"); add("Number of Realloc. Candidate Logical Sectors", "Number of Reallocation Candidate Logical Sectors", "", "The number of logical sectors that are candidates for reallocation. " "A reallocation candidate sector is a logical sector that the device has determined may need to be reallocated." "\n\n" + s_unc_text); add("Number of High Priority Unload Events", "", "", "The number of emergency head unload events."); // General Errors Statistics add("Number of Reported Uncorrectable Errors", "", "", "The number of errors that are reported as an Uncorrectable Error. " "Uncorrectable errors that occur during background activity shall not be counted. " "Uncorrectable errors reported by reads to flagged uncorrectable logical blocks should not be counted" "\n\n" + s_unc_text); add("Resets Between Cmd Acceptance and Completion", "", "", "The number of software reset or hardware reset events that occur while one or more commands have " "been accepted by the device but have not reached command completion."); // Temperature Statistics add("Current Temperature", "Current Temperature (C)", "", "Drive temperature (Celsius)"); add("Average Short Term Temperature", "Average Short Term Temperature (C)", "", "A value based on the most recent 144 temperature samples in a 24 hour period."); add("Average Long Term Temperature", "Average Long Term Temperature (C)", "", "A value based on the most recent 42 Average Short Term Temperature values (1,008 recorded hours)."); add("Highest Temperature", "Highest Temperature (C)", "", "The highest temperature measured after the device is manufactured."); add("Lowest Temperature", "Lowest Temperature (C)", "", "The lowest temperature measured after the device is manufactured."); add("Highest Average Short Term Temperature", "Highest Average Short Term Temperature (C)", "", "The highest device Average Short Term Temperature after the device is manufactured."); add("Lowest Average Short Term Temperature", "Lowest Average Short Term Temperature (C)", "", "The lowest device Average Short Term Temperature after the device is manufactured."); add("Highest Average Long Term Temperature", "Highest Average Long Term Temperature (C)", "", "The highest device Average Long Term Temperature after the device is manufactured."); add("Lowest Average Long Term Temperature", "Lowest Average Long Term Temperature (C)", "", "The lowest device Average Long Term Temperature after the device is manufactured."); add("Time in Over-Temperature", "Time in Over-Temperature (Minutes)", "", "The number of minutes that the device has been operational while the device temperature specification has been exceeded."); add("Specified Maximum Operating Temperature", "Specified Maximum Operating Temperature (C)", "", "The maximum operating temperature device is designed to operate."); add("Time in Under-Temperature", "Time in Under-Temperature (C)", "", "The number of minutes that the device has been operational while the temperature is lower than the device minimum temperature specification."); add("Specified Minimum Operating Temperature", "Specified Minimum Operating Temperature (C)", "", "The minimum operating temperature device is designed to operate."); // Transport Statistics add("Number of Hardware Resets", "", "", "The number of hardware resets received by the device."); add("Number of ASR Events", "", "", "The number of ASR (Asynchronous Signal Recovery) events."); add("Number of Interface CRC Errors", "", "", "the number of Interface CRC (checksum) errors reported in the ERROR field since the device was manufactured."); // Solid State Device Statistics add("Percentage Used Endurance Indicator", "", "", "A vendor specific estimate of the percentage of device life used based on the actual device usage " "and the manufacturer's prediction of device life. A value of 100 indicates that the estimated endurance " "of the device has been consumed, but may not indicate a device failure (e.g., minimum " "power-off data retention capability reached for devices using NAND flash technology)."); } /// Add an attribute description to the attribute database void add(const std::string& smartctl_name, const std::string& readable_name, const std::string& generic_name, const std::string& description) { add(StatisticDescription(smartctl_name, readable_name, generic_name, description)); } /// Add an devstat entry description to the devstat database void add(const StatisticDescription& descr) { devstat_db[descr.smartctl_name] = descr; } /// Find the description by smartctl name or id, merging them if they're partial. StatisticDescription find(std::string smartctl_name) const { // search by ID first std::map::const_iterator iter = devstat_db.find(smartctl_name); if (iter == devstat_db.end()) { return StatisticDescription(); // not found } return iter->second; } private: std::map devstat_db; ///< smartctl_name => devstat entry description }; /// Program-wide devstat description database static const StatisticsDatabase s_devstat_db; /// Check if a property matches a name (generic or reported) inline bool name_match(StorageProperty& p, const std::string& name) { if (p.generic_name.empty()) { return hz::string_to_lower_copy(p.reported_name) == hz::string_to_lower_copy(name); } return hz::string_to_lower_copy(p.generic_name) == hz::string_to_lower_copy(name); } /// Check if a property matches a name (generic or reported) and if it does, /// set a description on it. inline bool auto_set(StorageProperty& p, const std::string& name, const char* descr) { if (name_match(p, name)) { p.set_description(descr); return true; } return false; } /// Check if a property is an attribute and matches a generic name inline bool attr_match(StorageProperty& p, const std::string& generic_name) { return (p.value_type == StorageProperty::value_type_attribute && (p.generic_name == generic_name)); } /// Find a property's attribute in the attribute database and fill the property /// with all the readable information we can gather. inline void auto_set_attr(StorageProperty& p, StorageAttribute::DiskType disk_type) { AttributeDescription attr = s_attribute_db.find(p.reported_name, p.value_attribute.id, disk_type); std::string humanized_smartctl_name; std::string ssd_hdd_str; bool known_by_smartctl = !app_pcre_match("/Unknown_(HDD|SSD)_?Attr.*/i", p.reported_name, &ssd_hdd_str); if (known_by_smartctl) { humanized_smartctl_name = " " + p.reported_name + " "; // spaces are for easy replacements std::vector searches, replacements; searches.push_back("_"); replacements.push_back(" "); searches.push_back("/"); replacements.push_back(" / "); searches.push_back(" Ct "); replacements.push_back(" Count "); searches.push_back(" Tot "); replacements.push_back(" Total "); searches.push_back(" Blk "); replacements.push_back(" Block "); searches.push_back(" Cel "); replacements.push_back(" Celsius "); searches.push_back(" Uncorrect "); replacements.push_back(" Uncorrectable "); searches.push_back(" Cnt "); replacements.push_back(" Count "); searches.push_back(" Offl "); replacements.push_back(" Offline "); searches.push_back(" UNC "); replacements.push_back(" Uncorrectable "); searches.push_back(" Err "); replacements.push_back(" Error "); searches.push_back(" Errs "); replacements.push_back(" Errors "); searches.push_back(" Perc "); replacements.push_back(" Percent "); searches.push_back(" Ct "); replacements.push_back(" Count "); searches.push_back(" Avg "); replacements.push_back(" Average "); searches.push_back(" Max "); replacements.push_back(" Maximum "); searches.push_back(" Min "); replacements.push_back(" Minimum "); hz::string_replace_array(humanized_smartctl_name, searches, replacements); hz::string_trim(humanized_smartctl_name); hz::string_remove_adjacent_duplicates(humanized_smartctl_name, ' '); // may happen with slashes } if (attr.readable_name.empty()) { // try to display something sensible (use humanized form of smartctl name) if (!humanized_smartctl_name.empty()) { attr.readable_name = humanized_smartctl_name; } else { // unknown to smartctl if (hz::string_to_upper_copy(ssd_hdd_str) == "SSD") { attr.readable_name = "Unknown SSD Attribute"; } else if (hz::string_to_upper_copy(ssd_hdd_str) == "HDD") { attr.readable_name = "Unknown HDD Attribute"; } else { attr.readable_name = "Unknown Attribute"; } } } if (attr.description.empty()) { attr.description = "No description is available for this attribute."; } else { bool same_names = true; if (known_by_smartctl) { // See if humanized smartctl-reported name looks like our found name. // If not, show it in description. std::string match = " " + humanized_smartctl_name + " "; std::string against = " " + attr.readable_name + " "; std::vector searches, replacements; searches.push_back(" Percent "); replacements.push_back(" % "); searches.push_back("-"); replacements.push_back(""); searches.push_back("("); replacements.push_back(""); searches.push_back(")"); replacements.push_back(""); searches.push_back(" "); replacements.push_back(""); hz::string_replace_array(match, searches, replacements); hz::string_replace_array(against, searches, replacements); same_names = app_pcre_match("/^" + app_pcre_escape(match) + "$/i", against); } std::string descr = std::string("") + attr.readable_name + ""; if (!same_names) { std::string smartctl_name_for_descr = hz::string_replace_copy(p.reported_name, '_', ' '); descr += "\nReported by smartctl as \"" + smartctl_name_for_descr + "\"\n"; } descr += "\n"; descr += attr.description; attr.description = descr; } p.readable_name = attr.readable_name; p.set_description(attr.description); p.generic_name = attr.generic_name; } /// Find a property's statistic in the statistics database and fill the property /// with all the readable information we can gather. inline bool auto_set_statistic(StorageProperty& p) { StatisticDescription sd = s_devstat_db.find(p.reported_name); std::string readable_name = (sd.readable_name.empty() ? sd.smartctl_name : sd.readable_name); bool found = !sd.description.empty(); if (!found) { sd.description = "No description is available for this attribute."; } else { std::string descr = std::string("") + readable_name + "\n"; descr += sd.description; if (p.value_statistic.is_normalized()) { descr += "\n\nNote: The value is normalized."; } sd.description = descr; } if (!readable_name.empty()) { p.readable_name = readable_name; } p.set_description(sd.description); p.generic_name = sd.generic_name; return found; } } bool storage_property_autoset_description(StorageProperty& p, StorageAttribute::DiskType disk_type) { bool found = false; // checksum errors first if (p.generic_name.find("_checksum_error") != std::string::npos) { p.set_description("Checksum errors indicate that SMART data is invalid. This shouldn't happen in normal circumstances."); found = true; // Section Info } else if (p.section == StorageProperty::section_info) { found = auto_set(p, "model_family", "Model family (from smartctl database)") || auto_set(p, "device_model", "Device model") || auto_set(p, "serial_number", "Serial number, unique to each physical drive") || auto_set(p, "capacity", "User-serviceable drive capacity as reported to an operating system") || auto_set(p, "in_smartctl_db", "Whether the device is in smartctl database or not. If it is, additional information may be provided; otherwise, Raw values of some attributes may be incorrectly formatted.") || auto_set(p, "smart_supported", "Whether the device supports SMART. If not, then only very limited information will be available.") || auto_set(p, "smart_enabled", "Whether the device has SMART enabled. If not, most of the reported values will be incorrect.") || auto_set(p, "aam_feature", "Automatic Acoustic Management (AAM) feature") || auto_set(p, "aam_feature", "Automatic Acoustic Management (AAM) level") || auto_set(p, "apm_feature", "Automatic Power Management (APM) feature") || auto_set(p, "apm_level", "Advanced Power Management (APM) level") || auto_set(p, "dsn_feature", "Device Statistics Notification (DSN) feature") || auto_set(p, "power_mode", "Power mode at the time of query"); // set just its name as a tooltip if (!found) { p.set_description(p.readable_name); found = true; } } else if (p.section == StorageProperty::section_data) { switch (p.subsection) { case StorageProperty::subsection_health: found = auto_set(p, "overall_health", "Overall health self-assessment test result. Note: If the drive passes this test, it doesn't mean it's OK. " "However, if the drive doesn't pass it, then it's either already dead, or it's predicting its own failure within the next 24 hours. In this case do a backup immediately!"); break; case StorageProperty::subsection_capabilities: found = auto_set(p, "offline_status_group", "Offline Data Collection (a.k.a. Offline test) is usually automatically performed when the device is idle or every fixed amount of time. " "This should show if Automatic Offline Data Collection is enabled.") || auto_set(p, "iodc_total_time_length", "Offline Data Collection (a.k.a. Offline test) is usually automatically performed when the device is idle or every fixed amount of time. " "This value shows the estimated time required to perform this operation in idle conditions. A value of 0 means unsupported.") || auto_set(p, "short_total_time_length", "This value shows the estimated time required to perform a short self-test in idle conditions. A value of 0 means unsupported.") || auto_set(p, "long_total_time_length", "This value shows the estimated time required to perform a long self-test in idle conditions. A value of 0 means unsupported.") || auto_set(p, "conveyance_total_time_length", "This value shows the estimated time required to perform a conveyance self-test in idle conditions. A value of 0 means unsupported.") || auto_set(p, "last_selftest_cap_group", "Status of the last self-test run.") || auto_set(p, "offline_cap_group", "Drive properties related to Offline Data Collection and self-tests.") || auto_set(p, "smart_cap_group", "Drive properties related to SMART handling.") || auto_set(p, "error_log_cap_group", "Drive properties related to error logging.") || auto_set(p, "sct_cap_group", "Drive properties related to temperature information."); break; case StorageProperty::subsection_attributes: found = auto_set(p, "data_structure_version", p.readable_name.c_str()); if (!found) { auto_set_attr(p, disk_type); found = true; // true, because auto_set_attr() may set "Unknown attribute", which is still "found". } break; case StorageProperty::subsection_devstat: found = auto_set_statistic(p); break; case StorageProperty::subsection_error_log: found = auto_set(p, "error_log_version", p.readable_name.c_str()); found = auto_set(p, "error_log_error_count", "Number of errors in error log. Note: Some manufacturers may list completely harmless errors in this log " "(e.g., command invalid, not implemented, etc...)."); // || auto_set(p, "error_log_unsupported", "This device does not support error logging."); // the property text already says that if (p.value_type == StorageProperty::value_type_error_block) { for (size_t i = 0; i < p.value_error_block.reported_types.size(); ++i) { p.set_description(StorageErrorBlock::get_readable_error_types(p.value_error_block.reported_types)); found = true; } } break; case StorageProperty::subsection_selftest_log: found = auto_set(p, "selftest_log_version", p.readable_name.c_str()); found = auto_set(p, "selftest_num_entries", "Number of tests in selftest log. Note: The number of entries may be limited to the newest manual tests."); // || auto_set(p, "selftest_log_unsupported", "This device does not support self-test logging."); // the property text already says that break; case StorageProperty::subsection_selective_selftest_log: // nothing here break; case StorageProperty::subsection_temperature_log: found = auto_set(p, "sct_unsupported", "SCT support is needed for SCT temperature logging."); break; case StorageProperty::subsection_erc_log: // nothing here break; case StorageProperty::subsection_phy_log: // nothing here break; case StorageProperty::subsection_directory_log: // nothing here break; } } return found; } StorageProperty::warning_t storage_property_autoset_warning(StorageProperty& p) { StorageProperty::warning_t w = StorageProperty::warning_none; std::string reason; // checksum errors first if (p.generic_name.find("_checksum_error") != std::string::npos) { w = StorageProperty::warning_warn; reason = "The drive may have a broken implementation of SMART, or it's failing."; // Section Info } else if (p.section == StorageProperty::section_info) { if (name_match(p, "smart_supported") && !p.value_bool) { w = StorageProperty::warning_notice; reason = "SMART is not supported. You won't be able to read any SMART information from this drive."; } else if (name_match(p, "smart_enabled") && !p.value_bool) { w = StorageProperty::warning_notice; reason = "SMART is disabled. You should enable it to read any SMART information from this drive. " "Additionally, some drives do not log useful data with SMART disabled, so it's advisable to keep it always enabled."; } else if (name_match(p, "info_warning")) { w = StorageProperty::warning_notice; reason = "Your drive may be affected by the warning, please see the details."; } } else if (p.section == StorageProperty::section_data) { switch(p.subsection) { case StorageProperty::subsection_health: if (name_match(p, "overall_health") && p.value_string != "PASSED") { w = StorageProperty::warning_alert; reason = "The drive is reporting that it will FAIL very soon. Please back up as soon as possible!"; } break; case StorageProperty::subsection_capabilities: // nothing break; case StorageProperty::subsection_attributes: // Set notices for known pre-fail attributes. These are notices only, since the warnings // and alerts are shown only in case of attribute failure. // Reallocated Sector Count if (attr_match(p, "attr_reallocated_sector_count") && p.value_attribute.raw_value_int > 0) { w = StorageProperty::warning_notice; reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. This could be an indication of future failures and/or potential data loss in bad sectors."; // Spin-up Retry Count } else if (attr_match(p, "attr_spin_up_retry_count") && p.value_attribute.raw_value_int > 0) { w = StorageProperty::warning_notice; reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. Your drive may have problems spinning up, which could lead to a complete mechanical failure. Please back up."; // Soft Read Error Rate } else if (attr_match(p, "attr_soft_read_error_rate") && p.value_attribute.raw_value_int > 0) { w = StorageProperty::warning_notice; reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. This could be an indication of future failures and/or potential data loss in bad sectors."; // Temperature (for some it may be 10xTemp, so limit the upper bound.) } else if (attr_match(p, "attr_temperature_celsius") && p.value_attribute.raw_value_int > 50 && p.value_attribute.raw_value_int <= 120) { // 50C w = StorageProperty::warning_notice; reason = "The temperature of the drive is higher than 50 degrees Celsius. This may shorten its lifespan and cause damage under severe load. Please install a cooling solution."; // Temperature (for some it may be 10xTemp, so limit the upper bound.) } else if (attr_match(p, "attr_temperature_celsius_x10") && p.value_attribute.raw_value_int > 500) { // 50C w = StorageProperty::warning_notice; reason = "The temperature of the drive is higher than 50 degrees Celsius. This may shorten its lifespan and cause damage under severe load. Please install a cooling solution."; // Reallocation Event Count } else if (attr_match(p, "attr_reallocation_event_count") && p.value_attribute.raw_value_int > 0) { w = StorageProperty::warning_notice; reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. This could be an indication of future failures and/or potential data loss in bad sectors."; // Current Pending Sector Count } else if ((attr_match(p, "attr_current_pending_sector_count") || attr_match(p, "attr_total_pending_sectors")) && p.value_attribute.raw_value_int > 0) { w = StorageProperty::warning_notice; reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. This could be an indication of future failures and/or potential data loss in bad sectors."; // Uncorrectable Sector Count } else if ((attr_match(p, "attr_offline_uncorrectable") || attr_match(p, "attr_total_attr_offline_uncorrectable")) && p.value_attribute.raw_value_int > 0) { w = StorageProperty::warning_notice; reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. This could be an indication of future failures and/or potential data loss in bad sectors."; // SSD Life Left (%) } else if ((attr_match(p, "attr_ssd_life_left")) && p.value_attribute.value.value() < 50) { w = StorageProperty::warning_notice; reason = "The drive has less than half of its estimated life left."; // SSD Life Used (%) } else if ((attr_match(p, "attr_ssd_life_used")) && p.value_attribute.raw_value_int >= 50) { w = StorageProperty::warning_notice; reason = "The drive has less than half of its estimated life left."; } // Now override this with reported SMART attribute failure warnings / errors if (p.value_type == StorageProperty::value_type_attribute) { if (p.value_attribute.when_failed == StorageAttribute::fail_time_now) { // NOW if (p.value_attribute.attr_type == StorageAttribute::attr_type_oldage) { // old-age w = StorageProperty::warning_warn; reason = "The drive has a failing old-age attribute. Usually this indicates a wear-out. You should consider replacing the drive."; } else { // pre-fail w = StorageProperty::warning_alert; reason = "The drive has a failing pre-fail attribute. Usually this indicates a that the drive will FAIL soon. Please back up immediately!"; } } else if (p.value_attribute.when_failed == StorageAttribute::fail_time_past) { // PAST if (p.value_attribute.attr_type == StorageAttribute::attr_type_oldage) { // old-age // nothing. we don't warn about e.g. temperature increase in the past } else { // pre-fail w = StorageProperty::warning_warn; // there was a problem, it got corrected (hopefully) reason = "The drive had a failing pre-fail attribute, but it has been restored to a normal value. This may be a serious problem, you should consider replacing the drive."; } } } break; case StorageProperty::subsection_devstat: if (name_match(p, "Pending Error Count") && p.value_statistic.value_int > 0) { w = StorageProperty::warning_notice; reason = "The drive is reporting surface errors. This could be an indication of future failures and/or potential data loss in bad sectors."; // "Workload Utilization" is either normalized, or encodes several values, so we can't use it. /* } else if (name_match(p, "Workload Utilization") && p.value_statistic.value_int >= 50) { w = StorageProperty::warning_notice; reason = "The drive has less than half of its estimated life left."; } else if (name_match(p, "Workload Utilization") && p.value_statistic.value_int >= 100) { w = StorageProperty::warning_warn; reason = "The drive is past its estimated lifespan."; */ } else if (name_match(p, "Utilization Usage Rate") && p.value_statistic.value_int >= 50) { w = StorageProperty::warning_notice; reason = "The drive has less than half of its estimated life left."; } else if (name_match(p, "Utilization Usage Rate") && p.value_statistic.value_int >= 100) { w = StorageProperty::warning_warn; reason = "The drive is past its estimated lifespan."; } else if (name_match(p, "Number of Reallocated Logical Sectors") && !p.value_statistic.is_normalized() && p.value_statistic.value_int > 0) { w = StorageProperty::warning_notice; reason = "The drive is reporting surface errors. This could be an indication of future failures and/or potential data loss in bad sectors."; } else if (name_match(p, "Number of Reallocated Logical Sectors") && p.value_statistic.is_normalized() && p.value_statistic.value_int <= 0) { w = StorageProperty::warning_warn; reason = "The drive is reporting surface errors. This could be an indication of future failures and/or potential data loss in bad sectors."; } else if (name_match(p, "Number of Mechanical Start Failures") && p.value_statistic.value_int > 0) { w = StorageProperty::warning_notice; reason = "The drive is reporting mechanical errors."; } else if (name_match(p, "Number of Realloc. Candidate Logical Sectors") && p.value_statistic.value_int > 0) { w = StorageProperty::warning_notice; reason = "The drive is reporting surface errors. This could be an indication of future failures and/or potential data loss in bad sectors."; } else if (name_match(p, "Number of Reported Uncorrectable Errors") && p.value_statistic.value_int > 0) { w = StorageProperty::warning_notice; reason = "The drive is reporting surface errors. This could be an indication of future failures and/or potential data loss in bad sectors."; } else if (name_match(p, "Current Temperature") && p.value_statistic.value_int > 50) { w = StorageProperty::warning_notice; reason = "The temperature of the drive is higher than 50 degrees Celsius. " "This may shorten its lifespan and cause damage under severe load. Please install a cooling solution."; } else if (name_match(p, "Time in Over-Temperature") && p.value_statistic.value_int > 0) { w = StorageProperty::warning_notice; reason = "The temperature of the drive is or was over the manufacturer-specified maximum. " "This may have shortened its lifespan and caused damage. Please install a cooling solution."; } else if (name_match(p, "Time in Under-Temperature") && p.value_statistic.value_int > 0) { w = StorageProperty::warning_notice; reason = "The temperature of the drive is or was under the manufacturer-specified minimum. " "This may have shortened its lifespan and caused damage. Please operate the drive within manufacturer-specified temperature range."; } else if (name_match(p, "Percentage Used Endurance Indicator") && p.value_statistic.value_int >= 50) { w = StorageProperty::warning_notice; reason = "The drive has less than half of its estimated life left."; } else if (name_match(p, "Percentage Used Endurance Indicator") && p.value_statistic.value_int >= 100) { w = StorageProperty::warning_warn; reason = "The drive is past its estimated lifespan."; } break; case StorageProperty::subsection_error_log: // Note: The error list table doesn't display any descriptions, so if any // error-entry related descriptions are added here, don't forget to enable // the tooltips. if (name_match(p, "error_log_error_count") && p.value_integer > 0) { w = StorageProperty::warning_notice; reason = "The drive is reporting internal errors. Usually this means uncorrectable data loss and similar severe errors. " "Check the actual errors for details."; } else if (name_match(p, "error_log_unsupported")) { w = StorageProperty::warning_notice; reason = "The drive does not support error logging. This means that SMART error history is unavailable."; } // Rate individual error log entries. if (!p.value_error_block.reported_types.empty()) { StorageProperty::warning_t error_block_warning = StorageProperty::warning_none; for (size_t i = 0; i < p.value_error_block.reported_types.size(); ++i) { int individual_warning = StorageErrorBlock::get_warning_level_for_error_type(p.value_error_block.reported_types[i]); if (individual_warning > int(error_block_warning)) { error_block_warning = StorageProperty::warning_t(individual_warning); } } if (int(error_block_warning) > int(StorageProperty::warning_none)) { w = error_block_warning; reason = "The drive is reporting internal errors. Your data may be at risk depending on error severity."; } } break; case StorageProperty::subsection_selftest_log: // Note: The error list table doesn't display any descriptions, so if any // error-entry related descriptions are added here, don't forget to enable // the tooltips. // Don't include selftest warnings - they may be old or something. // Self-tests are carried manually anyway, so the user is expected to check their status anyway. if (name_match(p, "selftest_log_unsupported")) { w = StorageProperty::warning_notice; reason = "The drive does not support self-test logging. This means that SMART test results won't be logged."; } break; case StorageProperty::subsection_selective_selftest_log: // nothing here break; case StorageProperty::subsection_temperature_log: // Don't highlight SCT Unsupported as warning, it's harmless. // if (name_match(p, "sct_unsupported") && p.value_bool) { // w = StorageProperty::warning_notice; // reason = "The drive does not support SCT Temperature logging."; // } // Current temperature if (name_match(p, "sct_temperature_celsius") && p.value_integer > 50) { // 50C w = StorageProperty::warning_notice; reason = "The temperature of the drive is higher than 50 degrees Celsius. This may shorten its lifespan and cause damage under severe load. Please install a cooling solution."; } break; case StorageProperty::subsection_erc_log: // nothing here break; case StorageProperty::subsection_phy_log: // nothing here break; case StorageProperty::subsection_directory_log: // nothing here break; } } p.warning = w; p.warning_reason = reason; return w; } /// @} gsmartcontrol-1.1.4/src/applib/storage_property_descr.h000066400000000000000000000014611417717230600234240ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef STORAGE_PROPERTY_DESCR_H #define STORAGE_PROPERTY_DESCR_H #include "storage_property.h" /// Fill the property with all the information we can gather (description, etc...). bool storage_property_autoset_description(StorageProperty& p, StorageAttribute::DiskType disk_type); /// Do some basic checks on the property and set warnings if needed. StorageProperty::warning_t storage_property_autoset_warning(StorageProperty& p); #endif /// @} gsmartcontrol-1.1.4/src/applib/storage_settings.h000066400000000000000000000062471417717230600222270ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef STORAGE_SETTINGS_H #define STORAGE_SETTINGS_H #include #include #include #include "rconfig/rconfig_mini.h" #include "hz/string_algo.h" #include "hz/bin2ascii_encoder.h" /// A map of Device =\> Options typedef std::map device_option_map_t; /// Unserialize device option map from a string inline device_option_map_t app_unserialize_device_option_map(const std::string& str) { hz::Bin2AsciiEncoder enc; std::vector pairs; hz::string_split(str, ";", pairs, true); device_option_map_t option_map; for (std::vector::const_iterator iter = pairs.begin(); iter != pairs.end(); ++iter) { std::vector dev_entry; hz::string_split(*iter, ":", dev_entry, true, 2); std::string dev, opt; if (dev_entry.size() == 2) { dev = dev_entry.at(0); // includes type (separated by encoded "::") opt = dev_entry.at(1); } if (dev != "") { dev = hz::string_trim_copy(enc.decode(dev)); opt = hz::string_trim_copy(enc.decode(opt)); // ignore potentially harmful chars if (!dev.empty() && !opt.empty() // this discards the entries with empty options && dev.find_first_of(";><|&") == std::string::npos && opt.find_first_of(";><|&") == std::string::npos) { option_map[dev] = opt; } } } return option_map; } /// Serialize device option map from a string (to store it in config file, for example) inline std::string app_serialize_device_option_map(const device_option_map_t& option_map) { hz::Bin2AsciiEncoder enc; std::vector pairs; for (device_option_map_t::const_iterator iter = option_map.begin(); iter != option_map.end(); ++iter) { if (!iter->first.empty() && !iter->second.empty()) // discard the ones with empty device name or options pairs.push_back(enc.encode(iter->first) + ":" + enc.encode(iter->second)); } return hz::string_join(pairs, ";"); } /// Read device option map from config and get the options for (dev, type_arg) pair. inline std::string app_get_device_option(const std::string& dev, const std::string& type_arg) { if (dev.empty()) return std::string(); std::string devmap_str; if (!rconfig::get_data("system/smartctl_device_options", devmap_str)) return std::string(); device_option_map_t devmap = app_unserialize_device_option_map(devmap_str); // try the concrete type first if (!type_arg.empty()) { device_option_map_t::const_iterator iter = devmap.find(dev + "::" + type_arg); if (iter != devmap.end()) { return iter->second; } } // in case there's a trailing delimiter device_option_map_t::const_iterator iter = devmap.find(dev + "::" + type_arg); if (iter != devmap.end()) { return iter->second; } // just the device name iter = devmap.find(dev); if (iter != devmap.end()) { return iter->second; } return std::string(); } #endif /// @} gsmartcontrol-1.1.4/src/global_macros.h000066400000000000000000000125541417717230600201760ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ #ifndef GLOBAL_MACROS_H #define GLOBAL_MACROS_H /** \file This file serves as a compile-time configuration for various library components. This file is included from hz_config.h. Additionally, it may be included through compiler's "-include" option (if supported) for pch support. */ // Include autoconf's config.h. #ifdef HAVE_CONFIG_H #include #endif /// So that others may check whether this file was included or not. #define APP_GLOBAL_MACROS_INCLUDED 1 // #ifdef _WIN32 // needed for hz::send_process_signal(), winxp or later. // #define WINVER 0x0501 // #endif // this is either defined to 1 (by autoconf), or undefined #ifdef HAVE_LONG_LONG_INT #define DISABLE_LL_INT 0 #else // there is no long long int // explicitly disable it in our code. by default, it assumes that long long int exists. #define DISABLE_LL_INT 1 #endif // this is either defined to 1 (by autoconf), or undefined #ifdef HAVE_UNSIGNED_LONG_LONG_INT #define DISABLE_ULL_INT 0 #else // there is no long long int // explicitly disable it in our code. by default, it assumes that long long int exists. #define DISABLE_ULL_INT 1 #endif // HAVE_func means that func doesn't throw undefined symbol. // HAVE_DECL_func means that it's declared in a header. // HAVE_DECL_func is always defined as 0 or 1. // check if it's not in stdlib.h. // HAVE_DECL_* is always either 1 or 0. #if !defined DISABLE_STRTOF && defined HAVE_DECL_STRTOF && !HAVE_DECL_STRTOF #define DISABLE_STRTOF 1 #else #define DISABLE_STRTOF 0 #endif // HAVE_DECL_* is always either 1 or 0. #if !defined DISABLE_STRTOLD && defined HAVE_DECL_STRTOLD && !HAVE_DECL_STRTOLD #define DISABLE_STRTOLD 1 #else #define DISABLE_STRTOLD 0 #endif // -- Make most file operations work with large files (replaces // off_t -> off64_t, stat -> stat64, fopen -> fopen64, etc...). // Works on glibc. Automatically defined by autoconf. // #define _FILE_OFFSET_BITS 64 // -- These have effect in rmn, hz error library, hz string_tools. // These will be defined automatically by autoconf if found. // #define ENABLE_GLIB 1 // #define ENABLE_GLIBMM 1 // -- RMN settings - see rmn/rmn.h for details // #define RMN_TYPE_TRACKING // Note: We use DISABLE_* because all the libraries should assume // complete C++ support, unless indicated otherwise. This also // makes the headers work in full when there is no config.h. // -- Define this if using -fno-rtti or similar (automatic from autoconf). // HAVE_RTTI is always defined as 1 or 0. #if !defined DISABLE_RTTI && defined HAVE_RTTI && !HAVE_RTTI #define DISABLE_RTTI 1 #else #define DISABLE_RTTI 0 #endif // -- Define this if using -fno-exceptions (automatic from autoconf). // HAVE_EXCEPTIONS is always defined as 1 or 0. #if !defined DISABLE_EXCEPTIONS && defined HAVE_EXCEPTIONS && !HAVE_EXCEPTIONS #define DISABLE_EXCEPTIONS 1 #else #define DISABLE_EXCEPTIONS 0 #endif // -- Default policy for synchronization primitives (sync.h); // (define only one of these): /* // check if any of them are forced #if (!defined HZ_SYNC_DEFAULT_POLICY_BOOST) \ && (!defined HZ_SYNC_DEFAULT_POLICY_GLIB) \ && (!defined HZ_SYNC_DEFAULT_POLICY_GLIBMM) \ && (!defined HZ_SYNC_DEFAULT_POLICY_POCO) \ && (!defined HZ_SYNC_DEFAULT_POLICY_PTHREAD) \ && (!defined HZ_SYNC_DEFAULT_POLICY_WIN32) #if defined ENABLE_GLIB && ENABLE_GLIB #define HZ_SYNC_DEFAULT_POLICY_GLIB #elif defined _WIN32 #define HZ_SYNC_DEFAULT_POLICY_WIN32 #else #define HZ_SYNC_DEFAULT_POLICY_PTHREAD #endif // #define HZ_SYNC_DEFAULT_POLICY_NONE // #define HZ_SYNC_DEFAULT_POLICY_GLIBMM // #define HZ_SYNC_DEFAULT_POLICY_BOOST // #define HZ_SYNC_DEFAULT_POLICY_POCO #endif */ // -- Default policy for thread-local storage pointer (tls.h); // (define only one of these): /* // check if any of them are forced #if (!defined HZ_TLS_DEFAULT_POLICY_BOOST) \ && (!defined HZ_TLS_DEFAULT_POLICY_GLIB) \ && (!defined HZ_TLS_DEFAULT_POLICY_PTHREAD) \ && (!defined HZ_TLS_DEFAULT_POLICY_WIN32) #if defined ENABLE_GLIB && ENABLE_GLIB #define HZ_TLS_DEFAULT_POLICY_GLIB #elif defined _WIN32 // use pthread-win32 or boost on win32. // our win32 policy lacks good cleanup function support. #define HZ_TLS_DEFAULT_POLICY_PTHREAD #else #define HZ_TLS_DEFAULT_POLICY_PTHREAD #endif // #define HZ_TLS_DEFAULT_POLICY_BOOST // #define HZ_TLS_DEFAULT_POLICY_WIN32 #endif */ // -- hz/debug.h settings. // #ifndef HZ_USE_LIBDEBUG #define HZ_USE_LIBDEBUG 1 #endif // or // #ifndef HZ_EMULATE_LIBDEBUG // #define HZ_EMULATE_LIBDEBUG 1 // #endif // or none of the above to disable debug output completely. // -- hz/i18n.h settings. // #ifndef ENABLE_NLS #define ENABLE_NLS 0 #endif // -- hz/res_data.h settings. // #ifndef HZ_ENABLE_COMPILED_RES_DATA #define HZ_ENABLE_COMPILED_RES_DATA 1 #endif // -- increased verbosity levels, etc...; better define this through compiler option. // #define DEBUG_BUILD // This enables reference count tracing (very verbose) // #define INTRUSIVE_PTR_REF_TRACING // This enables runtime checks for errors (with exception throwing) #if defined DEBUG_BUILD && !defined INTRUSIVE_PTR_RUNTIME_CHECKS #define INTRUSIVE_PTR_RUNTIME_CHECKS 1 #endif #endif gsmartcontrol-1.1.4/src/gsc_about_dialog.cpp000066400000000000000000000055531417717230600212130ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup gsc /// \weakgroup gsc /// @{ #include "hz/hz_config.h" // VERSION #include #include "hz/debug.h" #include "hz/string_algo.h" // hz::string_* #include "hz/launch_url.h" #include "applib/app_gtkmm_features.h" #include "applib/gui_utils.h" // gui_show_error_dialog #include "gsc_about_dialog.h" // GtkBuilder needs this constructor GscAboutDialog::GscAboutDialog(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui) : AppUIResWidget(gtkcobj, ref_ui) { // Connect callbacks // Note: The dialogs have ESC accelerator attached by default. // APP_GTKMM_CONNECT_VIRTUAL(delete_event); // make sure the event handler is called APP_GTKMM_CONNECT_VIRTUAL(response); APP_GTKMM_CONNECT_VIRTUAL(activate_link); set_version(VERSION); // set these properties here (after setting hooks) to make the links work. set_website("https://gsmartcontrol.shaduri.dev"); set_license(LicenseTextResData().get_string()); // This overrides set_license(), so don't do it. // #if APP_GTKMM_CHECK_VERSION(3, 12, 0) // set_license_type(Gtk::LICENSE_GPL_3_0_ONLY); // #endif // spammers go away set_copyright("Copyright (C) 2008 - 2022 Alexander Shaduri " ""); std::string authors_str = AuthorsTextResData().get_string(); hz::string_any_to_unix(authors_str); std::vector authors; hz::string_split(authors_str, '\n', authors, true); for (std::vector::iterator iter = authors.begin(); iter != authors.end(); ++iter) { std::string s = *iter; hz::string_replace(s, " '@' ", "@"); // despammer hz::string_replace(s, " 'at' ", "@"); // despammer *iter = s; } set_authors(authors); set_documenters(authors); // run(); // don't use run - it's difficult to exit it manually. // show(); // shown by the caller to enable setting the parent window. } void GscAboutDialog::on_response_before(int response_id) { debug_out_info("app", DBG_FUNC_MSG << "Response ID: " << response_id << "\n"); if (response_id == Gtk::RESPONSE_NONE || response_id == Gtk::RESPONSE_DELETE_EVENT || response_id == Gtk::RESPONSE_CANCEL || response_id == Gtk::RESPONSE_CLOSE) { debug_out_info("app", DBG_FUNC_MSG << "Closing the dialog.\n"); destroy(this); // close the window and delete the object } } bool GscAboutDialog::on_activate_link_before(const std::string& uri) { // The default handler - gtk_show_uri_on_window() doesn't work with mailto: URIs in Windows. // Our handler does. return hz::launch_url(GTK_WINDOW(this->gobj()), uri).empty(); } /// @} gsmartcontrol-1.1.4/src/gsc_about_dialog.h000066400000000000000000000032731417717230600206550ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup gsc /// \weakgroup gsc /// @{ #ifndef GSC_ABOUT_DIALOG_H #define GSC_ABOUT_DIALOG_H #include #include "applib/app_ui_res_utils.h" /// The About dialog. /// Use create() / destroy() with this class instead of new / delete! class GscAboutDialog : public AppUIResWidget { public: // name of ui file without a .ui extension and quotes APP_UI_RES_DATA_INIT(gsc_about_dialog); // we need the license file to show it. HZ_RES_DATA_INIT_NAMED(LICENSE_gsmartcontrol_txt, "LICENSE_gsmartcontrol.txt", LicenseTextResData); // show the authors HZ_RES_DATA_INIT_NAMED(AUTHORS_txt, "AUTHORS.txt", AuthorsTextResData); /// Constructor, GtkBuilder needs this. GscAboutDialog(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui); /// Virtual destructor virtual ~GscAboutDialog() { } protected: // -------------------- Callbacks /// Callback - dialog response void on_response_before(int response_id); bool on_activate_link_before(const std::string& uri); // ---------- override virtual methods // we use .run(), so we don't need this /* // by default, delete_event calls hide(). bool on_delete_event_before(GdkEventAny* e) { destroy(this); // deletes this object and nullifies instance return true; // event handled, don't call default virtual handler } */ }; #endif /// @} gsmartcontrol-1.1.4/src/gsc_add_device_window.cpp000066400000000000000000000153151417717230600222150ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2011 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup gsc /// \weakgroup gsc /// @{ #include #include // GDK_KEY_Escape #include #include "hz/fs_path.h" #include "hz/string_sprintf.h" #include "hz/scoped_ptr.h" #include "applib/app_gtkmm_utils.h" #include "gsc_add_device_window.h" #include "gsc_main_window.h" GscAddDeviceWindow::GscAddDeviceWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui) : AppUIResWidget(gtkcobj, ref_ui), main_window_(0) { // Connect callbacks APP_GTKMM_CONNECT_VIRTUAL(delete_event); // make sure the event handler is called Gtk::Button* window_cancel_button = 0; APP_UI_RES_AUTO_CONNECT(window_cancel_button, clicked); Gtk::Button* window_ok_button = 0; APP_UI_RES_AUTO_CONNECT(window_ok_button, clicked); Gtk::Button* device_name_browse_button = 0; APP_UI_RES_AUTO_CONNECT(device_name_browse_button, clicked); Glib::ustring device_name_tooltip = "Device name"; #if defined CONFIG_KERNEL_FAMILY_WINDOWS device_name_tooltip = "Device name (for example, use \"pd0\" for the first physical drive)"; #elif defined CONFIG_KERNEL_LINUX device_name_tooltip = "Device name (for example, /dev/sda or /dev/twa0)"; #endif if (Gtk::Label* device_name_label = lookup_widget("device_name_label")) { app_gtkmm_set_widget_tooltip(*device_name_label, device_name_tooltip); } Gtk::Entry* device_name_entry = 0; APP_UI_RES_AUTO_CONNECT(device_name_entry, changed); if (device_name_entry) { app_gtkmm_set_widget_tooltip(*device_name_entry, device_name_tooltip); } Glib::ustring device_type_tooltip = "Smartctl -d option parameter"; #if defined CONFIG_KERNEL_LINUX || defined CONFIG_KERNEL_FAMILY_WINDOWS device_type_tooltip = "Smartctl -d option parameter. For example, use areca,1 for the first drive behind Areca RAID controller."; #endif if (Gtk::Label* device_type_label = lookup_widget("device_type_label")) { app_gtkmm_set_widget_tooltip(*device_type_label, device_type_tooltip); } if (Gtk::ComboBoxText* type_combo = lookup_widget("device_type_combo")) { app_gtkmm_set_widget_tooltip(*type_combo, device_type_tooltip); } // Accelerators Glib::RefPtr accel_group = this->get_accel_group(); if (window_cancel_button) { window_cancel_button->add_accelerator("clicked", accel_group, GDK_KEY_Escape, Gdk::ModifierType(0), Gtk::AccelFlags(0)); } #ifdef _WIN32 // "Browse" doesn't make sense in win32, hide it. if (device_name_browse_button) { device_name_browse_button->hide(); } #endif // Populate type combo with common types Gtk::ComboBoxText* type_combo = lookup_widget("device_type_combo"); if (type_combo) { type_combo->append("sat,12"); type_combo->append("sat,16"); type_combo->append("usbcypress"); type_combo->append("usbjmicron"); type_combo->append("usbsunplus"); type_combo->append("ata"); type_combo->append("scsi"); #if defined CONFIG_KERNEL_LINUX type_combo->append("marvell"); type_combo->append("megaraid,N"); type_combo->append("areca,N"); type_combo->append("areca,N/E"); #endif #if defined CONFIG_KERNEL_LINUX || defined CONFIG_KERNEL_FREEBSD || defined CONFIG_KERNEL_DRAGONFLY type_combo->append("3ware,N"); // this option is not needed in windows type_combo->append("cciss,N"); type_combo->append("hpt,L/M"); type_combo->append("hpt,L/M/N"); #endif } // This sets the initial state of OK button on_device_name_entry_changed(); // show(); } void GscAddDeviceWindow::set_main_window(GscMainWindow* main_window) { main_window_ = main_window; } bool GscAddDeviceWindow::on_delete_event_before(GdkEventAny* e) { destroy(this); // deletes this object and nullifies instance return true; // event handled, don't call default virtual handler } void GscAddDeviceWindow::on_window_cancel_button_clicked() { destroy(this); } void GscAddDeviceWindow::on_window_ok_button_clicked() { std::string dev, type, params; if (Gtk::Entry* entry = lookup_widget("device_name_entry")) { dev = entry->get_text(); } if (Gtk::ComboBoxText* type_combo = lookup_widget("device_type_combo")) { type = type_combo->get_entry_text(); } if (Gtk::Entry* entry = lookup_widget("smartctl_params_entry")) { params = entry->get_text(); } if (main_window_ && !dev.empty()) { main_window_->add_device(dev, type, params); } destroy(this); } void GscAddDeviceWindow::on_device_name_browse_button_clicked() { std::string default_file; Gtk::Entry* entry = this->lookup_widget("device_name_entry"); if (!entry) return; hz::FsPath path(entry->get_text()); int result = 0; #if GTK_CHECK_VERSION(3, 20, 0) hz::scoped_ptr dialog(gtk_file_chooser_native_new( "Choose Device...", this->gobj(), GTK_FILE_CHOOSER_ACTION_OPEN, NULL, NULL), g_object_unref); if (path.is_absolute()) gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog.get()), path.c_str()); result = gtk_native_dialog_run(GTK_NATIVE_DIALOG(dialog.get())); #else Gtk::FileChooserDialog dialog(*this, "Choose Device...", Gtk::FILE_CHOOSER_ACTION_OPEN); // Add response buttons the the dialog dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT); // Note: This works on absolute paths only (otherwise it's gtk warning). if (path.is_absolute()) dialog.set_filename(path.str()); // change to its dir and select it if exists. // Show the dialog and wait for a user response result = dialog.run(); // the main cycle blocks here #endif // Handle the response switch (result) { case Gtk::RESPONSE_ACCEPT: { Glib::ustring file; #if GTK_CHECK_VERSION(3, 20, 0) file = app_ustring_from_gchar(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog.get()))); #else file = dialog.get_filename(); // in fs encoding #endif entry->set_text(file); break; } case Gtk::RESPONSE_CANCEL: case Gtk::RESPONSE_DELETE_EVENT: // nothing, the dialog is closed already break; default: debug_out_error("app", DBG_FUNC_MSG << "Unknown dialog response code: " << result << ".\n"); break; } } void GscAddDeviceWindow::on_device_name_entry_changed() { // Allow OK only if name is not empty Gtk::Entry* entry = lookup_widget("device_name_entry"); Gtk::Button* ok_button = lookup_widget("window_ok_button"); if (entry && ok_button) { ok_button->set_sensitive(!entry->get_text().empty()); } } /// @} gsmartcontrol-1.1.4/src/gsc_add_device_window.h000066400000000000000000000033541417717230600216620ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2011 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup gsc /// \weakgroup gsc /// @{ #ifndef GSC_ADD_DEVICE_WINDOW_H #define GSC_ADD_DEVICE_WINDOW_H #include #include "applib/app_ui_res_utils.h" class GscMainWindow; /// The "Add Device" window. /// Use create() / destroy() with this class instead of new / delete! class GscAddDeviceWindow : public AppUIResWidget { public: // name of ui file without a .ui extension and quotes APP_UI_RES_DATA_INIT(gsc_add_device_window); /// Constructor, GtkBuilder needs this. GscAddDeviceWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui); /// Virtual destructor virtual ~GscAddDeviceWindow() { } /// Set the main window. /// On OK button click main_window->add_device() will be called. void set_main_window(GscMainWindow* main_window); protected: // ---------- overriden virtual methods /// Destroy this object on delete event (by default it calls hide()). /// Reimplemented from Gtk::Window. bool on_delete_event_before(GdkEventAny* e); // ---------- other callbacks /// Button click callback void on_window_cancel_button_clicked(); /// Button click callback void on_window_ok_button_clicked(); /// Button click callback void on_device_name_browse_button_clicked(); /// Entry text change callback void on_device_name_entry_changed(); private: GscMainWindow* main_window_; ///< The main window that created us }; #endif /// @} gsmartcontrol-1.1.4/src/gsc_executor_error_dialog.cpp000066400000000000000000000056421417717230600231470ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup gsc /// \weakgroup gsc /// @{ #include #include "gsc_executor_log_window.h" #include "gsc_executor_error_dialog.h" #include "gsc_text_window.h" namespace { /// Helper function inline int show_executor_dialog(Gtk::MessageType type, const std::string& message, const std::string& sec_message, Gtk::Window* parent, bool sec_msg_markup, bool show_output_button) { // no markup, modal Gtk::MessageDialog dialog("\n" + message + (sec_message.empty() ? "\n" : ""), false, type, Gtk::BUTTONS_NONE, true); if (!sec_message.empty()) dialog.set_secondary_text(sec_message, sec_msg_markup); if (parent) { dialog.set_transient_for(*parent); dialog.set_position(Gtk::WIN_POS_CENTER_ON_PARENT); } else { dialog.set_position(Gtk::WIN_POS_MOUSE); } Gtk::Button ok_button(Gtk::Stock::OK); ok_button.show_all(); ok_button.set_can_default(true); dialog.add_action_widget(ok_button, Gtk::RESPONSE_OK); Gtk::Button output_button("_Show Output", true); // don't put this inside if, it needs to live beyond it. if (show_output_button) { output_button.show_all(); dialog.add_action_widget(output_button, Gtk::RESPONSE_HELP); } dialog.set_default_response(Gtk::RESPONSE_OK); int response = dialog.run(); // blocks until the dialog is closed return response; } } void gsc_executor_error_dialog_show(const std::string& message, const std::string& sec_message, Gtk::Window* parent, bool sec_msg_markup, bool show_output_button) { int response = show_executor_dialog(Gtk::MESSAGE_ERROR, message, sec_message, parent, sec_msg_markup, show_output_button); if (response == Gtk::RESPONSE_HELP) { // this one will only hide on close. GscExecutorLogWindow* win = GscExecutorLogWindow::create(); // probably already created // win->set_transient_for(*this); // don't do this - it will make it always-on-top of this. win->show_last(); // show the window and select last entry } } void gsc_no_info_dialog_show(const std::string& message, const std::string& sec_message, Gtk::Window* parent, bool sec_msg_markup, const std::string& output, const std::string& output_window_title, const std::string& default_save_filename) { int response = show_executor_dialog(Gtk::MESSAGE_WARNING, message, sec_message, parent, sec_msg_markup, !output.empty()); if (response == Gtk::RESPONSE_HELP) { GscTextWindow* win = GscTextWindow::create(); win->set_text(output_window_title, output, true, true); if (!default_save_filename.empty()) win->set_save_filename(default_save_filename); win->show(); } } /// @} gsmartcontrol-1.1.4/src/gsc_executor_error_dialog.h000066400000000000000000000023141417717230600226050ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup gsc /// \weakgroup gsc /// @{ #ifndef GSC_EXECUTOR_ERROR_DIALOG_H #define GSC_EXECUTOR_ERROR_DIALOG_H #include #include /// Show a dialog when an execution error occurs. A dialog /// will have a "Show Output" button, which shows the last executed /// command details. void gsc_executor_error_dialog_show(const std::string& message, const std::string& sec_message, Gtk::Window* parent, bool sec_msg_markup = false, bool show_output_button = true); /// Show a dialog when no additional information is available. /// If \c output is not empty, a "Show Output" button will be displayed /// which shows this output. void gsc_no_info_dialog_show(const std::string& message, const std::string& sec_message, Gtk::Window* parent, bool sec_msg_markup, const std::string& output, const std::string& output_window_title, const std::string& default_save_filename); #endif /// @} gsmartcontrol-1.1.4/src/gsc_executor_log_window.cpp000066400000000000000000000321641417717230600226460ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup gsc /// \weakgroup gsc /// @{ #include #include // std::size_t #include #include // GDK_KEY_Escape #include "applib/app_gtkmm_utils.h" // app_gtkmm_create_tree_view_column #include "applib/app_gtkmm_features.h" #include "hz/scoped_ptr.h" #include "rconfig/rconfig_mini.h" #include "gsc_executor_log_window.h" #include "gsc_init.h" // app_get_debug_buffer_str() GscExecutorLogWindow::GscExecutorLogWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui) : AppUIResWidget(gtkcobj, ref_ui) { // Connect callbacks APP_GTKMM_CONNECT_VIRTUAL(delete_event); // make sure the event handler is called Gtk::Button* window_close_button = 0; APP_UI_RES_AUTO_CONNECT(window_close_button, clicked); Gtk::Button* window_save_current_button = 0; APP_UI_RES_AUTO_CONNECT(window_save_current_button, clicked); Gtk::Button* window_save_all_button = 0; APP_UI_RES_AUTO_CONNECT(window_save_all_button, clicked); Gtk::Button* clear_command_list_button = 0; APP_UI_RES_AUTO_CONNECT(clear_command_list_button, clicked); // Accelerators Glib::RefPtr accel_group = this->get_accel_group(); if (window_close_button) { window_close_button->add_accelerator("clicked", accel_group, GDK_KEY_Escape, Gdk::ModifierType(0), Gtk::AccelFlags(0)); } // --------------- Make a treeview Gtk::TreeView* treeview = this->lookup_widget("command_list_treeview"); if (treeview) { Gtk::TreeModelColumnRecord model_columns; // #, Command + parameters, [EntryPtr] model_columns.add(col_num); app_gtkmm_create_tree_view_column(col_num, *treeview, "#", "# of executed command", true); // sortable model_columns.add(col_command); app_gtkmm_create_tree_view_column(col_command, *treeview, "Command", "Command with parameters", true); // sortable model_columns.add(col_entry); // create a TreeModel (ListStore) list_store = Gtk::ListStore::create(model_columns); // list_store->set_sort_column(col_num, Gtk::SORT_DESCENDING); // default sort treeview->set_model(list_store); selection = treeview->get_selection(); selection->signal_changed().connect(sigc::mem_fun(*this, &self_type::on_tree_selection_changed) ); } // Hide command text entry in win32. // Setting text on this entry segfaults under win32, (utf8 conversion doesn't // help, not that it should). Seems to be connected to non-english locale. // Surprisingly, the treeview column text still works. // The problem seems to have disappeared (new compiler/runtime?) // #ifdef _WIN32 // Gtk::Box* command_hbox = this->lookup_widget("command_hbox"); // if (command_hbox) // command_hbox->hide(); // #endif // --------------- // Connect to CmdexSync signal cmdex_sync_signal_execute_finish()->connect(sigc::mem_fun(*this, &self_type::on_command_output_received)); // show(); } void GscExecutorLogWindow::show_last() { Gtk::TreeView* treeview = this->lookup_widget("command_list_treeview"); if (treeview && !list_store->children().empty()) { // Gtk::TreeRow row = *(list_store->children().rbegin()); // this causes invalid read error in valgrind Gtk::TreeRow row = *(--(list_store->children().end())); selection->select(row); // you would think that scroll_to_row would accept a TreeRow for a change (shock!) treeview->scroll_to_row(list_store->get_path(row)); } show(); } void GscExecutorLogWindow::clear_view_widgets() { Gtk::Button* window_save_current_button = this->lookup_widget("window_save_current_button"); if (window_save_current_button) window_save_current_button->set_sensitive(false); Gtk::TextView* output_textview = this->lookup_widget("output_textview"); if (output_textview) { Glib::RefPtr buffer = output_textview->get_buffer(); buffer->set_text(""); } Gtk::Entry* command_entry = this->lookup_widget("command_entry"); if (command_entry) command_entry->set_text(""); } void GscExecutorLogWindow::on_command_output_received(const CmdexSyncCommandInfo& info) { CmdexSyncCommandInfoRefPtr entry = info.copy(); entries.push_back(entry); // update tree model Gtk::TreeRow row = *(list_store->append()); row[col_num] = entries.size(); row[col_command] = entry->command + " " + entry->parameters; row[col_entry] = entry; // if visible, set the selection to it Gtk::TreeView* treeview = this->lookup_widget("command_list_treeview"); if (treeview) { selection->select(row); treeview->scroll_to_row(list_store->get_path(row)); } } bool GscExecutorLogWindow::on_delete_event_before(GdkEventAny* e) { this->hide(); return true; // event handled, don't call default virtual handler } void GscExecutorLogWindow::on_window_close_button_clicked() { this->hide(); // hide only, don't destroy } void GscExecutorLogWindow::on_window_save_current_button_clicked() { if (!selection->count_selected_rows()) return; Gtk::TreeIter iter = selection->get_selected(); CmdexSyncCommandInfoRefPtr entry = (*iter)[col_entry]; static std::string last_dir; if (last_dir.empty()) { rconfig::get_data("gui/drive_data_open_save_dir", last_dir); } int result = 0; Glib::RefPtr specific_filter = Gtk::FileFilter::create(); specific_filter->set_name("Text Files"); specific_filter->add_pattern("*.txt"); Glib::RefPtr all_filter = Gtk::FileFilter::create(); all_filter->set_name("All Files"); all_filter->add_pattern("*"); #if GTK_CHECK_VERSION(3, 20, 0) hz::scoped_ptr dialog(gtk_file_chooser_native_new( "Save Data As...", this->gobj(), GTK_FILE_CHOOSER_ACTION_SAVE, NULL, NULL), g_object_unref); gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog.get()), true); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog.get()), specific_filter->gobj()); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog.get()), all_filter->gobj()); if (!last_dir.empty()) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog.get()), last_dir.c_str()); gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog.get()), ".txt"); result = gtk_native_dialog_run(GTK_NATIVE_DIALOG(dialog.get())); #else Gtk::FileChooserDialog dialog(*this, "Save Data As...", Gtk::FILE_CHOOSER_ACTION_SAVE); // Add response buttons the the dialog dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT); dialog.set_do_overwrite_confirmation(true); dialog.add_filter(specific_filter); dialog.add_filter(all_filter); if (!last_dir.empty()) dialog.set_current_folder(last_dir); dialog.set_current_name(".txt"); // Show the dialog and wait for a user response result = dialog.run(); // the main cycle blocks here #endif // Handle the response switch (result) { case Gtk::RESPONSE_ACCEPT: { std::string file; #if GTK_CHECK_VERSION(3, 20, 0) file = app_ustring_from_gchar(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog.get()))); last_dir = hz::path_get_dirname(file); #else file = dialog.get_filename(); // in fs encoding last_dir = dialog.get_current_folder(); // save for the future #endif rconfig::set_data("gui/drive_data_open_save_dir", last_dir); if (file.rfind(".txt") != (file.size() - std::strlen(".txt"))) { file += ".txt"; } hz::File f(file); if (!f.put_contents(entry->std_output)) { gui_show_error_dialog("Cannot save data to file", f.get_error_utf8(), this); } break; } case Gtk::RESPONSE_CANCEL: case Gtk::RESPONSE_DELETE_EVENT: // nothing, the dialog is closed already break; default: debug_out_error("app", DBG_FUNC_MSG << "Unknown dialog response code: " << result << ".\n"); break; } } void GscExecutorLogWindow::on_window_save_all_button_clicked() { // complete libdebug output + execution logs std::ostringstream exss; exss << "\n------------------------- LIBDEBUG LOG -------------------------\n\n\n"; exss << app_get_debug_buffer_str() << "\n\n\n"; exss << "\n\n\n------------------------- EXECUTION LOG -------------------------\n\n\n"; for (std::size_t i = 0; i < entries.size(); ++i) { exss << "\n\n\n------------------------- EXECUTED COMMAND " << (i+1) << " -------------------------\n\n"; exss << "\n---------------" << "Command" << "---------------\n"; exss << entries[i]->command << "\n"; exss << "\n---------------" << "Parameters" << "---------------\n"; exss << entries[i]->parameters << "\n"; exss << "\n---------------" << "STDOUT" << "---------------\n"; exss << entries[i]->std_output << "\n\n"; exss << "\n---------------" << "STDERR" << "---------------\n"; exss << entries[i]->std_error << "\n\n"; exss << "\n---------------" << "Error Message" << "---------------\n"; exss << entries[i]->error_msg << "\n\n"; } static std::string last_dir; if (last_dir.empty()) { rconfig::get_data("gui/drive_data_open_save_dir", last_dir); } int result = 0; Glib::RefPtr specific_filter = Gtk::FileFilter::create(); specific_filter->set_name("Text Files"); specific_filter->add_pattern("*.txt"); Glib::RefPtr all_filter = Gtk::FileFilter::create(); all_filter->set_name("All Files"); all_filter->add_pattern("*"); #if GTK_CHECK_VERSION(3, 20, 0) hz::scoped_ptr dialog(gtk_file_chooser_native_new( "Save Data As...", this->gobj(), GTK_FILE_CHOOSER_ACTION_SAVE, NULL, NULL), g_object_unref); gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog.get()), true); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog.get()), specific_filter->gobj()); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog.get()), all_filter->gobj()); if (!last_dir.empty()) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog.get()), last_dir.c_str()); gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog.get()), ".txt"); result = gtk_native_dialog_run(GTK_NATIVE_DIALOG(dialog.get())); #else Gtk::FileChooserDialog dialog(*this, "Save Data As...", Gtk::FILE_CHOOSER_ACTION_SAVE); // Add response buttons the the dialog dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT); dialog.set_do_overwrite_confirmation(true); dialog.add_filter(specific_filter); dialog.add_filter(all_filter); if (!last_dir.empty()) dialog.set_current_folder(last_dir); dialog.set_current_name(".txt"); // Show the dialog and wait for a user response result = dialog.run(); // the main cycle blocks here #endif // Handle the response switch (result) { case Gtk::RESPONSE_ACCEPT: { std::string file; #if GTK_CHECK_VERSION(3, 20, 0) file = app_ustring_from_gchar(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog.get()))); last_dir = hz::path_get_dirname(file); #else file = dialog.get_filename(); // in fs encoding last_dir = dialog.get_current_folder(); // save for the future #endif rconfig::set_data("gui/drive_data_open_save_dir", last_dir); if (file.rfind(".txt") != (file.size() - std::strlen(".txt"))) { file += ".txt"; } hz::File f(file); if (!f.put_contents(exss.str())) { gui_show_error_dialog("Cannot save data to file", f.get_error_utf8(), this); } break; } case Gtk::RESPONSE_CANCEL: case Gtk::RESPONSE_DELETE_EVENT: // nothing, the dialog is closed already break; default: debug_out_error("app", DBG_FUNC_MSG << "Unknown dialog response code: " << result << ".\n"); break; } } void GscExecutorLogWindow::on_clear_command_list_button_clicked() { entries.clear(); list_store->clear(); // this will unselect & clear widgets too. } void GscExecutorLogWindow::on_tree_selection_changed() { this->clear_view_widgets(); if (selection->count_selected_rows()) { Gtk::TreeIter iter = selection->get_selected(); Gtk::TreeRow row = *iter; CmdexSyncCommandInfoRefPtr entry = row[col_entry]; Gtk::TextView* output_textview = this->lookup_widget("output_textview"); if (output_textview) { Glib::RefPtr buffer = output_textview->get_buffer(); if (buffer) { buffer->set_text(app_output_make_valid(entry->std_output)); Glib::RefPtr tag; Glib::RefPtr table = buffer->get_tag_table(); if (table) tag = table->lookup("font"); if (!tag) tag = buffer->create_tag("font"); tag->property_family() = "Monospace"; buffer->apply_tag(tag, buffer->begin(), buffer->end()); } } Gtk::Entry* command_entry = this->lookup_widget("command_entry"); if (command_entry) { std::string cmd_text = entry->command + " " + entry->parameters; command_entry->set_text(app_output_make_valid(cmd_text)); } Gtk::Button* window_save_current_button = this->lookup_widget("window_save_current_button"); if (window_save_current_button) window_save_current_button->set_sensitive(true); } } /// @} gsmartcontrol-1.1.4/src/gsc_executor_log_window.h000066400000000000000000000045451417717230600223150ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup gsc /// \weakgroup gsc /// @{ #ifndef GSC_EXECUTOR_LOG_WINDOW_H #define GSC_EXECUTOR_LOG_WINDOW_H #include #include // std::size_t #include #include "applib/app_ui_res_utils.h" #include "applib/cmdex_sync.h" /// The "Execution Log" window. /// Use create() / destroy() with this class instead of new / delete! class GscExecutorLogWindow : public AppUIResWidget { public: // name of ui file without a .ui extension and quotes APP_UI_RES_DATA_INIT(gsc_executor_log_window); /// Constructor, GtkBuilder needs this. GscExecutorLogWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui); /// Virtual destructor virtual ~GscExecutorLogWindow() { } /// Show this window and select the last entry void show_last(); protected: /// Clear entries and textviews void clear_view_widgets(); // -------------------- callbacks /// Callback attached to external source, adds entries in real time. void on_command_output_received(const CmdexSyncCommandInfo& info); // ---------- overriden virtual methods /// Hide the window, don't destroy. /// Reimplemented from Gtk::Window. bool on_delete_event_before(GdkEventAny* e); // ---------- other callbacks /// Button click callback void on_window_close_button_clicked(); /// Button click callback void on_window_save_current_button_clicked(); /// Button click callback void on_window_save_all_button_clicked(); /// Button click callback void on_clear_command_list_button_clicked(); /// Callback void on_tree_selection_changed(); private: std::vector entries; ///< Command information entries Glib::RefPtr list_store; ///< List store Glib::RefPtr selection; ///< Tree selection Gtk::TreeModelColumn col_num; ///< Tree column Gtk::TreeModelColumn col_command; ///< Tree column Gtk::TreeModelColumn col_entry; ///< Tree column }; #endif /// @} gsmartcontrol-1.1.4/src/gsc_info_window.cpp000066400000000000000000002411101417717230600210730ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup gsc /// \weakgroup gsc /// @{ #include #include // GDK_KEY_Escape #include // better use vector, it's needed by others too #include // std::min, std::max #include "hz/scoped_ptr.h" #include "hz/down_cast.h" #include "hz/string_num.h" // number_to_string #include "hz/string_sprintf.h" // string_sprintf #include "hz/string_algo.h" // string_join #include "hz/fs_file.h" // hz::File #include "hz/format_unit.h" // format_time_length #include "rconfig/rconfig_mini.h" // rconfig::* #include "applib/app_gtkmm_utils.h" // app_gtkmm_* #include "applib/storage_property_colors.h" #include "applib/gui_utils.h" // gui_show_error_dialog #include "applib/smartctl_executor_gui.h" #include "gsc_text_window.h" #include "gsc_info_window.h" #include "gsc_executor_error_dialog.h" /// A label for StorageProperty struct PropertyLabel { /// Constructor PropertyLabel(const std::string& label_, const StorageProperty* prop, bool markup_ = false) : label(label_), property(prop), markup(markup_) { } std::string label; ///< Label text const StorageProperty* property; ///< Storage property bool markup; ///< Whether the label text uses markup }; /// A vector of PropertyLabel objects typedef std::vector label_list_t; namespace { /// Set "top" labels - the generic text at the top of each tab page. inline void app_set_top_labels(Gtk::Box* vbox, const label_list_t& label_strings) { if (!vbox) return; // remove all first std::vector wv = vbox->get_children(); for (unsigned int i = 0; i < wv.size(); ++i) { vbox->remove(*(wv[i])); delete wv[i]; // since it's without parent anymore, it won't be auto-deleted. } vbox->set_visible(!label_strings.empty()); if (label_strings.empty()) { // add one label only // Gtk::Label* label = Gtk::manage(new Gtk::Label("No data available", Gtk::ALIGN_START)); // label->set_padding(6, 0); // vbox->pack_start(*label, false, false); } else { // add one label per element for (label_list_t::const_iterator iter = label_strings.begin(); iter != label_strings.end(); ++iter) { std::string label_text = (iter->markup ? Glib::ustring(iter->label) : Glib::Markup::escape_text(iter->label)); Gtk::Label* label = Gtk::manage(new Gtk::Label()); label->set_markup(label_text); label->set_padding(6, 0); label->set_alignment(Gtk::ALIGN_START); // label->set_ellipsize(Pango::ELLIPSIZE_END); label->set_selectable(true); label->set_can_focus(false); std::string fg; if (app_property_get_label_highlight_color(iter->property->warning, fg)) label->set_markup("" + label_text + ""); vbox->pack_start(*label, false, false); // set it after packing, else the old tooltips api won't have anything to attach them to. app_gtkmm_set_widget_tooltip(*label, // label_text + "\n\n" + // add label text too, in case it's ellipsized iter->property->get_description(), true); // already markupped label->show(); } } vbox->show_all_children(true); } /// Cell renderer functions for list cells inline void app_list_cell_renderer_func(Gtk::CellRenderer* cr, const Gtk::TreeModel::iterator& iter, Gtk::TreeModelColumn storage_column) { const StorageProperty* p = (*iter)[storage_column]; Gtk::CellRendererText* crt = hz::down_cast(cr); if (crt) { std::string fg, bg; if (app_property_get_row_highlight_colors(p->warning, fg, bg)) { // Note: property_cell_background makes horizontal tree lines disappear around it, // but property_background doesn't play nice with sorted column color. crt->property_cell_background() = bg; crt->property_foreground() = fg; } else { // this is needed because cellrenderer is shared in column, so the previous call // may set the color for all subsequent cells. crt->property_cell_background().reset_value(); crt->property_foreground().reset_value(); } if (p->value_type == StorageProperty::value_type_statistic) { if (p->value_statistic.is_header) { crt->property_weight() = Pango::WEIGHT_BOLD; } else { crt->property_weight().reset_value(); } } } } /// Highlight a tab label according to \c warning inline void app_highlight_tab_label(Gtk::Widget* label_widget, StorageProperty::warning_t warning, const Glib::ustring& original_label) { Gtk::Label* label = hz::down_cast(label_widget); if (!label) return; if (warning == StorageProperty::warning_none) { label->set_markup_with_mnemonic(original_label); return; } std::string fg; if (app_property_get_label_highlight_color(warning, fg)) label->set_markup_with_mnemonic("" + original_label + ""); } /// Scroll to appropriate error in text when row is selected in tree. inline void on_error_log_treeview_row_selected(GscInfoWindow* window, Gtk::TreeModelColumn mark_name_column) { Gtk::TreeView* treeview = window->lookup_widget("error_log_treeview"); Gtk::TextView* textview = window->lookup_widget("error_log_textview"); Glib::RefPtr buffer; if (treeview && textview && (buffer = textview->get_buffer())) { Gtk::TreeModel::iterator iter = treeview->get_selection()->get_selected(); if (iter) { Glib::RefPtr mark = buffer->get_mark((*iter)[mark_name_column]); if (mark) textview->scroll_to(mark, 0., 0., 0.); } } } } GscInfoWindow::GscInfoWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui) : AppUIResWidget(gtkcobj, ref_ui), device_name_label(0), test_force_bar_update(true) { // Size { int def_size_w = 0, def_size_h = 0; rconfig::get_data("gui/info_window/default_size_w", def_size_w); rconfig::get_data("gui/info_window/default_size_h", def_size_h); if (def_size_w > 0 && def_size_h > 0) { set_default_size(def_size_w, def_size_h); } } // Create missing widgets Gtk::Box* device_name_hbox = lookup_widget("device_name_label_hbox"); if (device_name_hbox) { device_name_label = Gtk::manage(new Gtk::Label("No data available", Gtk::ALIGN_START)); device_name_label->set_selectable(true); device_name_label->show(); device_name_hbox->pack_start(*device_name_label, true, true); } // Connect callbacks APP_GTKMM_CONNECT_VIRTUAL(delete_event); // make sure the event handler is called Gtk::Button* refresh_info_button = 0; APP_UI_RES_AUTO_CONNECT(refresh_info_button, clicked); Gtk::Button* view_output_button = 0; APP_UI_RES_AUTO_CONNECT(view_output_button, clicked); Gtk::Button* save_info_button = 0; APP_UI_RES_AUTO_CONNECT(save_info_button, clicked); Gtk::Button* close_window_button = 0; APP_UI_RES_AUTO_CONNECT(close_window_button, clicked); Gtk::ComboBox* test_type_combo = 0; APP_UI_RES_AUTO_CONNECT(test_type_combo, changed); Gtk::Button* test_execute_button = 0; APP_UI_RES_AUTO_CONNECT(test_execute_button, clicked); Gtk::Button* test_stop_button = 0; APP_UI_RES_AUTO_CONNECT(test_stop_button, clicked); // Accelerators if (close_window_button) { close_window_button->add_accelerator("clicked", this->get_accel_group(), GDK_KEY_Escape, Gdk::ModifierType(0), Gtk::AccelFlags(0)); } // Context menu in treeviews { std::vector treeview_names; treeview_names.push_back("attributes_treeview"); treeview_names.push_back("statistics_treeview"); treeview_names.push_back("selftest_log_treeview"); for (std::size_t i = 0; i < treeview_names.size(); ++i) { std::string treeview_name = treeview_names[i]; Gtk::TreeView* treeview = lookup_widget(treeview_name); treeview_menus[treeview_name] = new Gtk::Menu(); // deleted in window destructor treeview->signal_button_press_event().connect( sigc::bind(sigc::bind(sigc::mem_fun(*this, &GscInfoWindow::on_treeview_button_press_event), treeview), treeview_menus[treeview_name]), false); // before Gtk::MenuItem* item = Gtk::manage(new Gtk::MenuItem("Copy Selected Data", true)); item->signal_activate().connect( sigc::bind(sigc::mem_fun(*this, &GscInfoWindow::on_treeview_menu_copy_clicked), treeview) ); treeview_menus[treeview_name]->append(*item); treeview_menus[treeview_name]->show_all(); // Show all menu items when the menu pops up } } // --------------- // Set default texts on TextView-s, because glade's "text" property doesn't work // on them in gtkbuilder. { Gtk::TextView* textview = lookup_widget("error_log_textview"); if (textview) { Glib::RefPtr buffer = textview->get_buffer(); buffer->set_text("\nNo data available"); } } { Gtk::TextView* textview = lookup_widget("selective_selftest_log_textview"); if (textview) { Glib::RefPtr buffer = textview->get_buffer(); buffer->set_text("\nNo data available"); } } { Gtk::TextView* textview = lookup_widget("temperature_log_textview"); if (textview) { Glib::RefPtr buffer = textview->get_buffer(); buffer->set_text("\nNo data available"); } } { Gtk::TextView* textview = lookup_widget("erc_log_textview"); if (textview) { Glib::RefPtr buffer = textview->get_buffer(); buffer->set_text("\nNo data available"); } } { Gtk::TextView* textview = lookup_widget("phy_log_textview"); if (textview) { Glib::RefPtr buffer = textview->get_buffer(); buffer->set_text("\nNo data available"); } } { Gtk::TextView* textview = lookup_widget("directory_log_textview"); if (textview) { Glib::RefPtr buffer = textview->get_buffer(); buffer->set_text("\nNo data available"); } } // Save their original texts so that we can apply markup to them. Gtk::Label* tab_label = 0; tab_label = lookup_widget("general_tab_label"); tab_identity_name = (tab_label ? tab_label->get_label() : ""); tab_label = lookup_widget("attributes_tab_label"); tab_attributes_name = (tab_label ? tab_label->get_label() : ""); tab_label = lookup_widget("statistics_tab_label"); tab_statistics_name = (tab_label ? tab_label->get_label() : ""); tab_label = lookup_widget("test_tab_label"); tab_test_name = (tab_label ? tab_label->get_label() : ""); tab_label = lookup_widget("error_log_tab_label"); tab_error_log_name = (tab_label ? tab_label->get_label() : ""); tab_label = lookup_widget("temperature_log_tab_label"); tab_temperature_name = (tab_label ? tab_label->get_label() : ""); tab_label = lookup_widget("advanced_tab_label"); tab_advanced_name = (tab_label ? tab_label->get_label() : ""); tab_label = lookup_widget("capabilities_tab_label"); tab_capabilities_name = (tab_label ? tab_label->get_label() : ""); tab_label = lookup_widget("erc_tab_label"); tab_erc_name = (tab_label ? tab_label->get_label() : ""); tab_label = lookup_widget("selective_selftest_tab_label"); tab_selective_selftest_name = (tab_label ? tab_label->get_label() : ""); tab_label = lookup_widget("phy_tab_label"); tab_phy_name = (tab_label ? tab_label->get_label() : ""); tab_label = lookup_widget("directory_tab_label"); tab_directory_name = (tab_label ? tab_label->get_label() : ""); // show(); // don't show here, removing tabs later is ugly. } GscInfoWindow::~GscInfoWindow() { for (std::map::iterator iter = treeview_menus.begin(); iter != treeview_menus.end(); ++iter) { delete iter->second; } } void GscInfoWindow::obj_destroy() { // Main window size. We don't store position to avoid overlaps { int window_w = 0, window_h = 0; get_size(window_w, window_h); rconfig::set_data("gui/info_window/default_size_w", window_w); rconfig::set_data("gui/info_window/default_size_h", window_h); } } void GscInfoWindow::set_drive(StorageDeviceRefPtr d) { if (drive) // if an old drive is present, disconnect our callback from it. drive_changed_connection.disconnect(); drive = d; drive_changed_connection = drive->signal_changed.connect(sigc::mem_fun(this, &GscInfoWindow::on_drive_changed)); } void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests) { debug_out_info("app", DBG_FUNC_MSG << "Scan " << (scan ? "" : "not ") << "requested.\n"); if (clear_ui) clear_ui_info(clear_tests); if (!drive->get_is_virtual()) { // fetch all smartctl info, even if it already has it (to refresh it). if (scan) { SmartctlExecutorGuiRefPtr ex(new SmartctlExecutorGui()); ex->create_running_dialog(this, "Running %s on " + drive->get_device_with_type() + "..."); std::string error_msg = drive->fetch_data_and_parse(ex); // run it with GUI support if (!error_msg.empty()) { gsc_executor_error_dialog_show("Cannot retrieve SMART data", error_msg, this); return; } // it's already parsed // } else { // std::string error_msg = drive->parse_data(); // if (!error_msg.empty()) { // gui_show_error_dialog("Cannot interpret SMART data", error_msg, this); // return; // } } } // disable refresh button if virtual if (drive->get_is_virtual()) { Gtk::Button* b = lookup_widget("refresh_info_button"); if (b) { b->set_sensitive(false); app_gtkmm_set_widget_tooltip(*b, "Cannot re-read information from virtual drive"); } } // hide all tabs except the first if smart is disabled, because they may contain // completely random data (smartctl does that sometimes). bool hide_tabs = true; rconfig::get_data("/runtime/gui/hide_tabs_on_smart_disabled", hide_tabs); if (hide_tabs) { bool smart_enabled = (drive->get_smart_status() == StorageDevice::status_enabled); Gtk::Widget* note_page_box = 0; if ((note_page_box = lookup_widget("attributes_tab_vbox")) != 0) { if (smart_enabled) { note_page_box->show(); } else { note_page_box->hide(); } } if ((note_page_box = lookup_widget("statistics_tab_vbox")) != 0) { if (smart_enabled) { note_page_box->show(); } else { note_page_box->hide(); } } if ((note_page_box = lookup_widget("test_tab_vbox")) != 0) { if (smart_enabled) { note_page_box->show(); } else { note_page_box->hide(); } } if ((note_page_box = lookup_widget("error_log_tab_vbox")) != 0) { if (smart_enabled) { note_page_box->show(); } else { note_page_box->hide(); } } if ((note_page_box = lookup_widget("temperature_log_tab_vbox")) != 0) { if (smart_enabled) { note_page_box->show(); } else { note_page_box->hide(); } } if ((note_page_box = lookup_widget("advanced_tab_vbox")) != 0) { if (smart_enabled) { note_page_box->show(); } else { note_page_box->hide(); } } if (Gtk::Notebook* notebook = lookup_widget("main_notebook")) { notebook->set_show_tabs(smart_enabled); } } { std::string device = Glib::Markup::escape_text(drive->get_device_with_type()); std::string model = Glib::Markup::escape_text(drive->get_model_name().empty() ? "Unknown model" : drive->get_model_name()); std::string drive_letters = Glib::Markup::escape_text(drive->format_drive_letters(false)); this->set_title("Device Information - " + device + ": " + model + " - GSmartControl"); // Gtk::Label* device_name_label = lookup_widget("device_name_label"); if (device_name_label) { device_name_label->set_markup( "Device: " + device + (drive_letters.empty() ? "" : (" (" + drive_letters + ")")) + " Model: " + model); } } // Fill the tabs with info // we need reference here - we take addresses of the elements const SmartctlParser::prop_list_t& props = drive->get_properties(); // it's a vector typedef SmartctlParser::prop_list_t::const_iterator prop_iterator; // ------------------------------------------- Identity, version, overall health do { // filter out some properties SmartctlParser::prop_list_t id_props, version_props, health_props; for (prop_iterator iter = props.begin(); iter != props.end(); ++iter) { if (iter->section == StorageProperty::section_info) { if (iter->generic_name == "smartctl_version_full") { version_props.push_back(*iter); } else if (iter->generic_name == "smartctl_version") { continue; // we use the full version string instead. } else { id_props.push_back(*iter); } } else if (iter->section == StorageProperty::section_data && iter->subsection == StorageProperty::subsection_health) { health_props.push_back(*iter); } } // put version after all the info for (prop_iterator iter = version_props.begin(); iter != version_props.end(); ++iter) id_props.push_back(*iter); // health is the last one for (prop_iterator iter = health_props.begin(); iter != health_props.end(); ++iter) id_props.push_back(*iter); Gtk::Grid* identity_table = lookup_widget("identity_table"); if (!identity_table) break; identity_table->hide(); StorageProperty::warning_t max_tab_warning = StorageProperty::warning_none; int row = 0; for (prop_iterator iter = id_props.begin(); iter != id_props.end(); ++iter) { if (!iter->show_in_ui) { continue; // hide debug messages from smartctl } if (iter->generic_name == "overall_health") { // a little distance for this one Gtk::Label* empty_label = Gtk::manage(new Gtk::Label()); empty_label->set_can_focus(false); identity_table->attach(*empty_label, 0, row, 2, 1); ++row; } Gtk::Label* name = Gtk::manage(new Gtk::Label()); // name->set_ellipsize(Pango::ELLIPSIZE_END); name->set_alignment(Gtk::ALIGN_END); // right-align name->set_selectable(true); name->set_can_focus(false); name->set_markup("" + Glib::Markup::escape_text(iter->readable_name) + ""); // If the above is Label, then this has to be Label too, else it will shrink // and "name" will take most of the horizontal space. If "name" is set to shrink, // then it stops being right-aligned. Gtk::Label* value = Gtk::manage(new Gtk::Label()); // value->set_ellipsize(Pango::ELLIPSIZE_END); value->set_alignment(Gtk::ALIGN_START); // left-align value->set_selectable(true); value->set_can_focus(false); value->set_markup(Glib::Markup::escape_text(iter->format_value())); std::string fg; if (app_property_get_label_highlight_color(iter->warning, fg)) { name->set_markup(""+ name->get_label() + ""); value->set_markup(""+ value->get_label() + ""); } identity_table->attach(*name, 0, row, 1, 1); identity_table->attach(*value, 1, row, 1, 1); app_gtkmm_set_widget_tooltip(*name, iter->get_description(), true); app_gtkmm_set_widget_tooltip(*value, // value->get_label() + "\n\n" + iter->get_description(), true); if (int(iter->warning) > int(max_tab_warning)) max_tab_warning = iter->warning; ++row; } identity_table->show_all(); // tab label app_highlight_tab_label(lookup_widget("general_tab_label"), max_tab_warning, tab_identity_name); } while (false); // ------------------------------------------- Attributes do { Gtk::TreeView* treeview = lookup_widget("attributes_treeview"); if (!treeview) break; Gtk::TreeModelColumnRecord model_columns; unsigned int num_tree_cols = 0; // ID (int), Name, Flag (hex), Normalized Value (uint8), Worst (uint8), Thresh (uint8), Raw (int64), Type (string), // Updated (string), When Failed (string) Gtk::TreeModelColumn col_id; model_columns.add(col_id); // we can use the column variable by value after this. num_tree_cols = app_gtkmm_create_tree_view_column(col_id, *treeview, "ID", "Attribute ID", true); Gtk::TreeModelColumn col_name; model_columns.add(col_name); num_tree_cols = app_gtkmm_create_tree_view_column(col_name, *treeview, "Name", "Attribute name (this is deduced from ID by smartctl and may be incorrect, as it's highly vendor-specific)", true); treeview->set_search_column(col_name.index()); Gtk::CellRendererText* cr_name = hz::down_cast(treeview->get_column_cell_renderer(num_tree_cols - 1)); if (cr_name) cr_name->property_weight() = Pango::WEIGHT_BOLD; Gtk::TreeModelColumn col_failed; model_columns.add(col_failed); num_tree_cols = app_gtkmm_create_tree_view_column(col_failed, *treeview, "Failed", "When failed (that is, the normalized value became equal to or less than threshold)", true, true); Gtk::TreeModelColumn col_value; model_columns.add(col_value); num_tree_cols = app_gtkmm_create_tree_view_column(col_value, *treeview, "Norm-ed value", "Normalized value (highly vendor-specific; converted from Raw value by the drive's firmware)", false); Gtk::TreeModelColumn col_worst; model_columns.add(col_worst); num_tree_cols = app_gtkmm_create_tree_view_column(col_worst, *treeview, "Worst", "The worst normalized value recorded for this attribute during the drive's lifetime (with SMART enabled)", false); Gtk::TreeModelColumn col_threshold; model_columns.add(col_threshold); num_tree_cols = app_gtkmm_create_tree_view_column(col_threshold, *treeview, "Threshold", "Threshold for normalized value. Normalized value should be greater than threshold (unless vendor thinks otherwise).", false); Gtk::TreeModelColumn col_raw; model_columns.add(col_raw); num_tree_cols = app_gtkmm_create_tree_view_column(col_raw, *treeview, "Raw value", "Raw value as reported by drive. May or may not be sensible.", false); Gtk::TreeModelColumn col_type; model_columns.add(col_type); num_tree_cols = app_gtkmm_create_tree_view_column(col_type, *treeview, "Type", "Alarm condition is reached when if normalized value becomes less than or equal to threshold. Type indicates whether it's a signal of drive's pre-failure time or just an old age.", false, true); // Doesn't carry that much info. Advanced users can look at the flags. // Gtk::TreeModelColumn col_updated; // model_columns.add(col_updated); // num_tree_cols = app_gtkmm_create_tree_view_column(col_updated, *treeview, // "Updated", "The attribute is usually updated continuously, or during Offline Data Collection only. This column indicates that.", true); Gtk::TreeModelColumn col_flag_value; model_columns.add(col_flag_value); num_tree_cols = app_gtkmm_create_tree_view_column(col_flag_value, *treeview, "Flags", "Flags\n\n" "If given in POSRCK+ format, the presence of each letter indicates that the flag is on.\n" "P: pre-failure attribute (if the attribute failed, the drive is failing)\n" "O: updated continuously (as opposed to updated on offline data collection)\n" "S: speed / performance attribute\n" "R: error rate\n" "C: event count\n" "K: auto-keep\n" "+: undocumented bits present", false); Gtk::TreeModelColumn col_tooltip; model_columns.add(col_tooltip); treeview->set_tooltip_column(col_tooltip.index()); Gtk::TreeModelColumn col_storage; model_columns.add(col_storage); // create a TreeModel (ListStore) Glib::RefPtr list_store = Gtk::ListStore::create(model_columns); list_store->set_sort_column(col_id, Gtk::SORT_ASCENDING); // default sort treeview->set_model(list_store); for (unsigned int i = 0; i < num_tree_cols; ++i) { Gtk::TreeViewColumn* tcol = treeview->get_column(i); tcol->set_cell_data_func(*(tcol->get_first_cell()), sigc::bind(sigc::ptr_fun(app_list_cell_renderer_func), col_storage)); } StorageProperty::warning_t max_tab_warning = StorageProperty::warning_none; label_list_t label_strings; // outside-of-tree properties for (prop_iterator iter = props.begin(); iter != props.end(); ++iter) { if (iter->section != StorageProperty::section_data || iter->subsection != StorageProperty::subsection_attributes) continue; // add non-attribute-type properties to label above if (iter->value_type != StorageProperty::value_type_attribute) { label_strings.push_back(PropertyLabel(iter->readable_name + ": " + iter->format_value(), &(*iter))); if (int(iter->warning) > int(max_tab_warning)) max_tab_warning = iter->warning; continue; } std::string attr_type = StorageAttribute::get_attr_type_name(iter->value_attribute.attr_type); if (iter->value_attribute.attr_type == StorageAttribute::attr_type_prefail) attr_type = "" + attr_type + ""; std::string fail_time = StorageAttribute::get_fail_time_name(iter->value_attribute.when_failed); if (iter->value_attribute.when_failed != StorageAttribute::fail_time_none) fail_time = "" + fail_time + ""; Gtk::TreeRow row = *(list_store->append()); row[col_id] = iter->value_attribute.id; row[col_name] = iter->readable_name; row[col_flag_value] = iter->value_attribute.flag; // it's a string, not int. row[col_value] = (iter->value_attribute.value.defined() ? hz::number_to_string(iter->value_attribute.value.value()) : "-"); row[col_worst] = (iter->value_attribute.worst.defined() ? hz::number_to_string(iter->value_attribute.worst.value()) : "-"); row[col_threshold] = (iter->value_attribute.threshold.defined() ? hz::number_to_string(iter->value_attribute.threshold.value()) : "-"); row[col_raw] = iter->value_attribute.format_raw_value(); row[col_type] = attr_type; // row[col_updated] = StorageAttribute::get_update_type_name(iter->value_attribute.update_type); row[col_failed] = fail_time; row[col_tooltip] = iter->get_description(); row[col_storage] = &(*iter); if (int(iter->warning) > int(max_tab_warning)) max_tab_warning = iter->warning; } Gtk::Box* label_vbox = lookup_widget("attributes_label_vbox"); app_set_top_labels(label_vbox, label_strings); // tab label app_highlight_tab_label(lookup_widget("attributes_tab_label"), max_tab_warning, tab_attributes_name); } while (false); // ------------------------------------------- Statistics do { Gtk::TreeView* treeview = lookup_widget("statistics_treeview"); if (!treeview) break; Gtk::TreeModelColumnRecord model_columns; unsigned int num_tree_cols = 0; Gtk::TreeModelColumn col_description; model_columns.add(col_description); num_tree_cols = app_gtkmm_create_tree_view_column(col_description, *treeview, "Description", "Entry description", true); treeview->set_search_column(col_description.index()); // Gtk::CellRendererText* cr_name = hz::down_cast(treeview->get_column_cell_renderer(num_tree_cols - 1)); // if (cr_name) // cr_name->property_weight() = Pango::WEIGHT_BOLD ; Gtk::TreeModelColumn col_value; model_columns.add(col_value); num_tree_cols = app_gtkmm_create_tree_view_column(col_value, *treeview, "Value", "Value (can be normalized if 'N' flag is present)", false); Gtk::TreeModelColumn col_flags; model_columns.add(col_flags); num_tree_cols = app_gtkmm_create_tree_view_column(col_flags, *treeview, "Flags", "Flags\n\n" "N: value is normalized\n" "D: supports Device Statistics Notification (DSN)\n" "C: monitored condition met\n" // Related to DSN? From the specification, it looks like something user-controllable. "+: undocumented bits present", false); Gtk::TreeModelColumn col_page_offset; model_columns.add(col_page_offset); num_tree_cols = app_gtkmm_create_tree_view_column(col_page_offset, *treeview, "Page, Offset", "Page and offset of the entry", false); Gtk::TreeModelColumn col_tooltip; model_columns.add(col_tooltip); treeview->set_tooltip_column(col_tooltip.index()); Gtk::TreeModelColumn col_storage; model_columns.add(col_storage); // create a TreeModel (ListStore) Glib::RefPtr list_store = Gtk::ListStore::create(model_columns); treeview->set_model(list_store); // No sorting (we don't want to screw up the headers). for (unsigned int i = 0; i < num_tree_cols; ++i) { Gtk::TreeViewColumn* tcol = treeview->get_column(i); tcol->set_cell_data_func(*(tcol->get_first_cell()), sigc::bind(sigc::ptr_fun(app_list_cell_renderer_func), col_storage)); } StorageProperty::warning_t max_tab_warning = StorageProperty::warning_none; label_list_t label_strings; // outside-of-tree properties for (prop_iterator iter = props.begin(); iter != props.end(); ++iter) { if (iter->section != StorageProperty::section_data || iter->subsection != StorageProperty::subsection_devstat) continue; // add non-entry-type properties to label above if (iter->value_type != StorageProperty::value_type_statistic) { label_strings.push_back(PropertyLabel(iter->readable_name + ": " + iter->format_value(), &(*iter))); if (int(iter->warning) > int(max_tab_warning)) max_tab_warning = iter->warning; continue; } Gtk::TreeRow row = *(list_store->append()); row[col_description] = (iter->value_statistic.is_header ? iter->readable_name : (" " + iter->readable_name)); row[col_value] = iter->value_statistic.format_value(); row[col_flags] = iter->value_statistic.flags; // it's a string, not int. row[col_page_offset] = (iter->value_statistic.is_header ? std::string() : hz::string_sprintf("0x%02x, 0x%03x", int(iter->value_statistic.page), int(iter->value_statistic.offset))); row[col_tooltip] = iter->get_description(); row[col_storage] = &(*iter); if (int(iter->warning) > int(max_tab_warning)) max_tab_warning = iter->warning; } Gtk::Box* label_vbox = lookup_widget("statistics_label_vbox"); app_set_top_labels(label_vbox, label_strings); // tab label app_highlight_tab_label(lookup_widget("statistics_tab_label"), max_tab_warning, tab_statistics_name); } while (false); // ------------------------------------------- Tests if (clear_tests) { do { // Modify only after clearing tests! Gtk::ComboBox* test_type_combo = lookup_widget("test_type_combo"); if (!test_type_combo) // huh? break; // don't check with get_model(), it comes pre-modeled from glade. if (!test_combo_model) { Gtk::TreeModelColumnRecord model_columns; // Test name, [description], [selftest_obj] model_columns.add(test_combo_col_name); // we can use the column variable by value after this. model_columns.add(test_combo_col_description); model_columns.add(test_combo_col_self_test); test_combo_model = Gtk::ListStore::create(model_columns); test_type_combo->set_model(test_combo_model); // visible columns test_type_combo->clear(); // clear old (glade) cellrenderers test_type_combo->pack_start(test_combo_col_name); } // add possible tests Gtk::TreeModel::Row row; SelfTestPtr test_ioffline(new SelfTest(drive, SelfTest::type_ioffline)); if (test_ioffline->is_supported()) { row = *(test_combo_model->append()); row[test_combo_col_name] = SelfTest::get_test_name(SelfTest::type_ioffline); row[test_combo_col_description] = "Immediate Offline Test (also known as Immediate Offline Data Collection)" " is the manual version of Automatic Offline Data Collection, which, if enabled, is automatically run" " every four hours. If an error occurs during this test, it will be reported in Error Log. Besides that," " its effects are visible only in that it updates the \"Offline\" Attribute values."; row[test_combo_col_self_test] = test_ioffline; } SelfTestPtr test_short(new SelfTest(drive, SelfTest::type_short)); if (test_short->is_supported()) { row = *(test_combo_model->append()); row[test_combo_col_name] = SelfTest::get_test_name(SelfTest::type_short); row[test_combo_col_description] = "Short self-test consists of a collection of test routines that have the highest chance" " of detecting drive problems. Its result is reported in the Self-Test Log." " Note that this test is in no way comprehensive. Its main purpose is to detect totally damaged" " drives without running the full surface scan." "\nNote: On some drives this actually runs several consequent tests, which may" " cause the program to display the test progress incorrectly."; // seagate multi-pass test on 7200.11. row[test_combo_col_self_test] = test_short; } SelfTestPtr test_long(new SelfTest(drive, SelfTest::type_long)); if (test_long->is_supported()) { row = *(test_combo_model->append()); row[test_combo_col_name] = SelfTest::get_test_name(SelfTest::type_long); row[test_combo_col_description] = "Extended self-test examines complete disk surface and performs various test routines" " built into the drive. Its result is reported in the Self-Test Log."; row[test_combo_col_self_test] = test_long; } SelfTestPtr test_conveyance(new SelfTest(drive, SelfTest::type_conveyance)); if (test_conveyance->is_supported()) { row = *(test_combo_model->append()); row[test_combo_col_name] = SelfTest::get_test_name(SelfTest::type_conveyance); row[test_combo_col_description] = "Conveyance self-test is intended to identify damage incurred during transporting of the drive."; row[test_combo_col_self_test] = test_conveyance; } if (!test_combo_model->children().empty()) { test_type_combo->set_sensitive(true); test_type_combo->set_active(test_combo_model->children().begin()); // select first entry // At least one test is possible, so enable test button. // Note: we disable only Execute button on virtual drives. The combo is left // sensitive so that the user can see which tests are supported by the drive. Gtk::Button* test_execute_button = lookup_widget("test_execute_button"); if (test_execute_button) test_execute_button->set_sensitive(!drive->get_is_virtual()); } } while (false); } // end if (clear_tests) // ------------------------------------------- Selftest Log (Tests tab) do { Gtk::TreeView* treeview = lookup_widget("selftest_log_treeview"); if (!treeview) break; Gtk::TreeModelColumnRecord model_columns; unsigned int num_tree_cols = 0; // Test num., Type, Status, % Completed, Lifetime hours, LBA of the first error Gtk::TreeModelColumn col_num; model_columns.add(col_num); // we can use the column variable by value after this. num_tree_cols = app_gtkmm_create_tree_view_column(col_num, *treeview, "Test #", "Test # (greater may mean newer or older depending on drive model)", true); Gtk::CellRendererText* cr_test_num = hz::down_cast(treeview->get_column_cell_renderer(num_tree_cols - 1)); if (cr_test_num) cr_test_num->property_weight() = Pango::WEIGHT_BOLD ; Gtk::TreeModelColumn col_type; model_columns.add(col_type); num_tree_cols = app_gtkmm_create_tree_view_column(col_type, *treeview, "Type", "Type of the test performed", true); treeview->set_search_column(col_type.index()); Gtk::TreeModelColumn col_status; model_columns.add(col_status); num_tree_cols = app_gtkmm_create_tree_view_column(col_status, *treeview, "Status", "Test completion status", true); Gtk::TreeModelColumn col_percent; model_columns.add(col_percent); num_tree_cols = app_gtkmm_create_tree_view_column(col_percent, *treeview, "% Completed", "Percentage of the test completed. Instantly-aborted tests have 10%, while unsupported ones _may_ have 100%.", true); Gtk::TreeModelColumn col_hours; model_columns.add(col_hours); num_tree_cols = app_gtkmm_create_tree_view_column(col_hours, *treeview, "Lifetime hours", "During which hour of the drive's (powered on) lifetime did the test complete (or abort)", true); Gtk::TreeModelColumn col_lba; model_columns.add(col_lba); num_tree_cols = app_gtkmm_create_tree_view_column(col_lba, *treeview, "LBA of the first error", "LBA of the first error (if an LBA-related error happened)", true); Gtk::TreeModelColumn col_tooltip; model_columns.add(col_tooltip); treeview->set_tooltip_column(col_tooltip.index()); Gtk::TreeModelColumn col_storage; model_columns.add(col_storage); // create a TreeModel (ListStore) Glib::RefPtr list_store = Gtk::ListStore::create(model_columns); list_store->set_sort_column(col_num, Gtk::SORT_ASCENDING); // default sort treeview->set_model(list_store); for (unsigned int i = 0; i < num_tree_cols; ++i) { Gtk::TreeViewColumn* tcol = treeview->get_column(i); tcol->set_cell_data_func(*(tcol->get_first_cell()), sigc::bind(sigc::ptr_fun(app_list_cell_renderer_func), col_storage)); } StorageProperty::warning_t max_tab_warning = StorageProperty::warning_none; label_list_t label_strings; // outside-of-tree properties for (prop_iterator iter = props.begin(); iter != props.end(); ++iter) { if (iter->section != StorageProperty::section_data || iter->subsection != StorageProperty::subsection_selftest_log) continue; if (iter->generic_name == "selftest_log") // the whole section, we don't need it continue; // add non-entry properties to label above if (iter->value_type != StorageProperty::value_type_selftest_entry) { label_strings.push_back(PropertyLabel(iter->readable_name + ": " + iter->format_value(), &(*iter))); if (int(iter->warning) > int(max_tab_warning)) max_tab_warning = iter->warning; continue; } Gtk::TreeRow row = *(list_store->append()); row[col_num] = iter->value_selftest_entry.test_num; row[col_type] = iter->value_selftest_entry.type; row[col_status] = iter->value_selftest_entry.get_status_str(); row[col_percent] = hz::number_to_string(100 - iter->value_selftest_entry.remaining_percent) + "%"; row[col_hours] = iter->value_selftest_entry.format_lifetime_hours(); row[col_lba] = iter->value_selftest_entry.lba_of_first_error; // There are no descriptions in self-test log entries, so don't display // "No description available" for all of them. // row[col_tooltip] = iter->get_description(); row[col_storage] = &(*iter); if (int(iter->warning) > int(max_tab_warning)) max_tab_warning = iter->warning; } Gtk::Box* label_vbox = lookup_widget("selftest_log_label_vbox"); app_set_top_labels(label_vbox, label_strings); // tab label app_highlight_tab_label(lookup_widget("test_tab_label"), max_tab_warning, tab_test_name); } while (false); // ------------------------------------------- Error Log do { Gtk::TreeView* treeview = lookup_widget("error_log_treeview"); if (!treeview) break; Gtk::TreeModelColumnRecord model_columns; unsigned int num_tree_cols = 0; // Error Number, Lifetime Hours, State, Type, Details, [tooltips] Gtk::TreeModelColumn col_num; model_columns.add(col_num); // we can use the column variable by value after this. num_tree_cols = app_gtkmm_create_tree_view_column(col_num, *treeview, "Error #", "Error # in the error log (greater means newer)", true); Gtk::CellRendererText* cr_name = hz::down_cast(treeview->get_column_cell_renderer(num_tree_cols - 1)); if (cr_name) cr_name->property_weight() = Pango::WEIGHT_BOLD ; Gtk::TreeModelColumn col_hours; model_columns.add(col_hours); num_tree_cols = app_gtkmm_create_tree_view_column(col_hours, *treeview, "Lifetime hours", "During which hour of the drive's (powered on) lifetime did the error happen.", true); Gtk::TreeModelColumn col_state; model_columns.add(col_state); num_tree_cols = app_gtkmm_create_tree_view_column(col_state, *treeview, "State", "Power state of the drive when the error occurred", false); Gtk::TreeModelColumn col_type; model_columns.add(col_type); num_tree_cols = app_gtkmm_create_tree_view_column(col_type, *treeview, "Type", "Type of error", true); Gtk::TreeModelColumn col_details; model_columns.add(col_details); num_tree_cols = app_gtkmm_create_tree_view_column(col_details, *treeview, "Details", "Additional details (e.g. LBA where the error occurred, etc...)", true); Gtk::TreeModelColumn col_tooltip; model_columns.add(col_tooltip); treeview->set_tooltip_column(col_tooltip.index()); Gtk::TreeModelColumn col_storage; model_columns.add(col_storage); Gtk::TreeModelColumn col_mark_name; model_columns.add(col_mark_name); // create a TreeModel (ListStore) Glib::RefPtr list_store = Gtk::ListStore::create(model_columns); list_store->set_sort_column(col_num, Gtk::SORT_DESCENDING); // default sort treeview->set_model(list_store); for (unsigned int i = 0; i < num_tree_cols; ++i) { Gtk::TreeViewColumn* tcol = treeview->get_column(i); tcol->set_cell_data_func(*(tcol->get_first_cell()), sigc::bind(sigc::ptr_fun(app_list_cell_renderer_func), col_storage)); } StorageProperty::warning_t max_tab_warning = StorageProperty::warning_none; label_list_t label_strings; // outside-of-tree properties for (prop_iterator iter = props.begin(); iter != props.end(); ++iter) { if (iter->section != StorageProperty::section_data || iter->subsection != StorageProperty::subsection_error_log) continue; // Note: Don't use property description as a tooltip here. It won't be available if there's no property. if (iter->generic_name == "error_log") { Gtk::TextView* textview = lookup_widget("error_log_textview"); if (textview) { // Add complete error log to textview window. Glib::RefPtr buffer = textview->get_buffer(); if (buffer) { buffer->set_text("\nComplete error log:\n\n" + iter->value_string); // Make the text monospace (the 3.16+ glade property does not work anymore for some reason). Glib::RefPtr tag = buffer->create_tag(); tag->property_family() = "Monospace"; buffer->apply_tag(tag, buffer->begin(), buffer->end()); // Set marks so we can scroll to them if (!error_log_row_selected_conn.connected()) { // avoid double-connect error_log_row_selected_conn = treeview->get_selection()->signal_changed().connect( sigc::bind(sigc::bind(sigc::ptr_fun(on_error_log_treeview_row_selected), col_mark_name), this)); } Gtk::TextIter titer = buffer->begin(); Gtk::TextIter match_start, match_end; while (titer.forward_search("\nError ", Gtk::TEXT_SEARCH_TEXT_ONLY, match_start, match_end)) { match_start.forward_char(); // place after newline match_end.forward_word_end(); // include error number titer = match_end; // continue searching from here Glib::ustring mark_name = match_start.get_slice(match_end); // e.g. "Error 3" buffer->create_mark(mark_name, titer); } } } // add non-tree properties to label above } else if (iter->value_type != StorageProperty::value_type_error_block) { label_strings.push_back(PropertyLabel(iter->readable_name + ": " + iter->format_value(), &(*iter))); if (iter->generic_name == "error_log_error_count") label_strings.back().label += " (Note: The number of entries may be limited to the newest ones)"; } else { std::string type_details = iter->value_error_block.type_more_info; Gtk::TreeRow row = *(list_store->append()); row[col_num] = iter->value_error_block.error_num; row[col_hours] = iter->value_error_block.format_lifetime_hours(); row[col_state] = iter->value_error_block.device_state; row[col_type] = StorageErrorBlock::get_readable_error_types(iter->value_error_block.reported_types); row[col_details] = (type_details.empty() ? "-" : type_details); // e.g. OBS has no details // There are no descriptions in self-test log entries, so don't display // "No description available" for all of them. row[col_tooltip] = iter->get_description(); row[col_storage] = &(*iter); row[col_mark_name] = "Error " + hz::number_to_string(iter->value_error_block.error_num); } if (int(iter->warning) > int(max_tab_warning)) max_tab_warning = iter->warning; } Gtk::Box* label_vbox = lookup_widget("error_log_label_vbox"); app_set_top_labels(label_vbox, label_strings); // inner tab label app_highlight_tab_label(lookup_widget("error_log_tab_label"), max_tab_warning, tab_error_log_name); } while (false); // ------------------------------------------- Temperature log do { Gtk::TextView* textview = lookup_widget("temperature_log_textview"); if (!textview) break; StorageProperty::warning_t max_tab_warning = StorageProperty::warning_none; label_list_t label_strings; // outside-of-tree properties std::string temperature; StorageProperty temp_property; enum { temp_attr2 = 1, temp_attr1, temp_stat, temp_sct }; // less important to more important int temp_prop_source = 0; for (prop_iterator iter = props.begin(); iter != props.end(); ++iter) { // Find temperature if (temp_prop_source < temp_sct && iter->generic_name == "sct_temperature_celsius") { temperature = hz::number_to_string(iter->value_integer); temp_property = *iter; temp_prop_source = temp_sct; } if (temp_prop_source < temp_stat && iter->generic_name == "stat_temperature_celsius") { temperature = hz::number_to_string(iter->value_statistic.value_int); temp_property = *iter; temp_prop_source = temp_stat; } if (temp_prop_source < temp_attr1 && iter->generic_name == "attr_temperature_celsius") { temperature = hz::number_to_string(iter->value_attribute.raw_value_int); temp_property = *iter; temp_prop_source = temp_attr1; } if (temp_prop_source < temp_attr2 && iter->generic_name == "attr_temperature_celsius_x10") { temperature = hz::number_to_string(iter->value_attribute.raw_value_int / 10); temp_property = *iter; temp_prop_source = temp_attr2; } if (iter->section != StorageProperty::section_data || iter->subsection != StorageProperty::subsection_temperature_log) continue; if (iter->generic_name == "sct_unsupported" && iter->value_bool) { // only show if unsupported label_strings.push_back(PropertyLabel("SCT temperature commands not supported.", &(*iter))); if (int(iter->warning) > int(max_tab_warning)) max_tab_warning = iter->warning; continue; } // Note: Don't use property description as a tooltip here. It won't be available if there's no property. if (iter->generic_name == "scttemp_log") { Glib::RefPtr buffer = textview->get_buffer(); buffer->set_text("\nComplete SCT temperature log:\n\n" + iter->value_string); // Make the text monospace (the 3.16+ glade property does not work anymore for some reason). Glib::RefPtr tag = buffer->create_tag(); tag->property_family() = "Monospace"; buffer->apply_tag(tag, buffer->begin(), buffer->end()); } } if (temperature.empty()) { temperature = "Unknown"; } else { temperature += " C"; } temp_property.set_description("Current drive temperature in Celsius."); // overrides attribute description label_strings.push_back(PropertyLabel("Current temperature: " + temperature + "", &temp_property, true)); if (int(temp_property.warning) > int(max_tab_warning)) max_tab_warning = temp_property.warning; Gtk::Box* label_vbox = lookup_widget("temperature_log_label_vbox"); app_set_top_labels(label_vbox, label_strings); // tab label app_highlight_tab_label(lookup_widget("temperature_log_tab_label"), max_tab_warning, tab_temperature_name); } while (false); // ------------------------------------------- Advanced tab - Capabilities StorageProperty::warning_t max_advanced_tab_warning = StorageProperty::warning_none; do { Gtk::TreeView* treeview = lookup_widget("capabilities_treeview"); if (!treeview) break; Gtk::TreeModelColumnRecord model_columns; unsigned int num_tree_cols = 0; // N, Name, Flag, Capabilities, [tooltips] Gtk::TreeModelColumn col_index; model_columns.add(col_index); // we can use the column variable by value after this. num_tree_cols = app_gtkmm_create_tree_view_column(col_index, *treeview, "#", "Entry #", true); Gtk::TreeModelColumn col_name; model_columns.add(col_name); num_tree_cols = app_gtkmm_create_tree_view_column(col_name, *treeview, "Name", "Name", true); treeview->set_search_column(col_name.index()); Gtk::CellRendererText* cr_name = hz::down_cast(treeview->get_column_cell_renderer(num_tree_cols - 1)); if (cr_name) cr_name->property_weight() = Pango::WEIGHT_BOLD ; Gtk::TreeModelColumn col_flag_value; model_columns.add(col_flag_value); num_tree_cols = app_gtkmm_create_tree_view_column(col_flag_value, *treeview, "Flags", "Flags", false); Gtk::TreeModelColumn col_str_values; model_columns.add(col_str_values); num_tree_cols = app_gtkmm_create_tree_view_column(col_str_values, *treeview, "Capabilities", "Capabilities", false); Gtk::TreeModelColumn col_tooltip; model_columns.add(col_tooltip); treeview->set_tooltip_column(col_tooltip.index()); Gtk::TreeModelColumn col_storage; model_columns.add(col_storage); // create a TreeModel (ListStore) Glib::RefPtr list_store = Gtk::ListStore::create(model_columns); list_store->set_sort_column(col_index, Gtk::SORT_ASCENDING); // default sort treeview->set_model(list_store); for (unsigned int i = 0; i < num_tree_cols; ++i) { Gtk::TreeViewColumn* tcol = treeview->get_column(i); tcol->set_cell_data_func(*(tcol->get_first_cell()), sigc::bind(sigc::ptr_fun(app_list_cell_renderer_func), col_storage)); } StorageProperty::warning_t max_tab_warning = StorageProperty::warning_none; int index = 1; for (prop_iterator iter = props.begin(); iter != props.end(); ++iter) { if (iter->section != StorageProperty::section_data || iter->subsection != StorageProperty::subsection_capabilities) continue; Glib::ustring name = iter->readable_name; std::string flag_value; Glib::ustring str_value; if (iter->value_type == StorageProperty::value_type_capability) { flag_value = hz::number_to_string(iter->value_capability.flag_value, 16); // 0xXX str_value = hz::string_join(iter->value_capability.strvalues, "\n"); } else { // no flag value here str_value = iter->format_value(); } Gtk::TreeRow row = *(list_store->append()); row[col_index] = index; row[col_name] = name; row[col_flag_value] = (flag_value.empty() ? "-" : flag_value); row[col_str_values] = str_value; row[col_tooltip] = iter->get_description(); row[col_storage] = &(*iter); if (int(iter->warning) > int(max_tab_warning)) max_tab_warning = iter->warning; ++index; } if (int(max_tab_warning) > int(max_advanced_tab_warning)) max_advanced_tab_warning = max_tab_warning; // tab label app_highlight_tab_label(lookup_widget("capabilities_tab_label"), max_tab_warning, tab_capabilities_name); } while (false); // ------------------------------------------- Advanced tab - ERC log do { Gtk::TextView* textview = lookup_widget("erc_log_textview"); if (!textview) break; StorageProperty::warning_t max_tab_warning = StorageProperty::warning_none; for (prop_iterator iter = props.begin(); iter != props.end(); ++iter) { if (iter->section != StorageProperty::section_data || iter->subsection != StorageProperty::subsection_erc_log) continue; // Note: Don't use property description as a tooltip here. It won't be available if there's no property. if (iter->generic_name == "scterc_log") { Glib::RefPtr buffer = textview->get_buffer(); buffer->set_text("\nComplete SCT Error Recovery Control settings:\n\n" + iter->value_string); // Make the text monospace (the 3.16+ glade property does not work anymore for some reason). Glib::RefPtr tag = buffer->create_tag(); tag->property_family() = "Monospace"; buffer->apply_tag(tag, buffer->begin(), buffer->end()); } } if (int(max_tab_warning) > int(max_advanced_tab_warning)) max_advanced_tab_warning = max_tab_warning; // tab label app_highlight_tab_label(lookup_widget("erc_tab_label"), max_tab_warning, tab_erc_name); } while (false); // ------------------------------------------- Advanced tab - Selective self-test log do { Gtk::TextView* textview = lookup_widget("selective_selftest_log_textview"); if (!textview) break; StorageProperty::warning_t max_tab_warning = StorageProperty::warning_none; for (prop_iterator iter = props.begin(); iter != props.end(); ++iter) { if (iter->section != StorageProperty::section_data || iter->subsection != StorageProperty::subsection_selective_selftest_log) continue; // Note: Don't use property description as a tooltip here. It won't be available if there's no property. if (iter->generic_name == "selective_selftest_log") { Glib::RefPtr buffer = textview->get_buffer(); buffer->set_text("\nComplete selective self-test log:\n\n" + iter->value_string); // Make the text monospace (the 3.16+ glade property does not work anymore for some reason). Glib::RefPtr tag = buffer->create_tag(); tag->property_family() = "Monospace"; buffer->apply_tag(tag, buffer->begin(), buffer->end()); } } if (int(max_tab_warning) > int(max_advanced_tab_warning)) max_advanced_tab_warning = max_tab_warning; // tab label app_highlight_tab_label(lookup_widget("selective_selftest_tab_label"), max_tab_warning, tab_selective_selftest_name); } while (false); // ------------------------------------------- Advanced tab - Phy log do { Gtk::TextView* textview = lookup_widget("phy_log_textview"); if (!textview) break; StorageProperty::warning_t max_tab_warning = StorageProperty::warning_none; for (prop_iterator iter = props.begin(); iter != props.end(); ++iter) { if (iter->section != StorageProperty::section_data || iter->subsection != StorageProperty::subsection_phy_log) continue; // Note: Don't use property description as a tooltip here. It won't be available if there's no property. if (iter->generic_name == "sataphy_log") { Glib::RefPtr buffer = textview->get_buffer(); buffer->set_text("\nComplete phy log:\n\n" + iter->value_string); // Make the text monospace (the 3.16+ glade property does not work anymore for some reason). Glib::RefPtr tag = buffer->create_tag(); tag->property_family() = "Monospace"; buffer->apply_tag(tag, buffer->begin(), buffer->end()); } } if (int(max_tab_warning) > int(max_advanced_tab_warning)) max_advanced_tab_warning = max_tab_warning; // tab label app_highlight_tab_label(lookup_widget("phy_tab_label"), max_tab_warning, tab_phy_name); } while (false); // ------------------------------------------- Advanced tab - Directory log do { Gtk::TextView* textview = lookup_widget("directory_log_textview"); if (!textview) break; StorageProperty::warning_t max_tab_warning = StorageProperty::warning_none; for (prop_iterator iter = props.begin(); iter != props.end(); ++iter) { if (iter->section != StorageProperty::section_data || iter->subsection != StorageProperty::subsection_directory_log) continue; // Note: Don't use property description as a tooltip here. It won't be available if there's no property. if (iter->generic_name == "directory_log") { Glib::RefPtr buffer = textview->get_buffer(); buffer->set_text("\nComplete directory log:\n\n" + iter->value_string); // Make the text monospace (the 3.16+ glade property does not work anymore for some reason). Glib::RefPtr tag = buffer->create_tag(); tag->property_family() = "Monospace"; buffer->apply_tag(tag, buffer->begin(), buffer->end()); } } if (int(max_tab_warning) > int(max_advanced_tab_warning)) max_advanced_tab_warning = max_tab_warning; // tab label app_highlight_tab_label(lookup_widget("directory_tab_label"), max_tab_warning, tab_directory_name); } while (false); // Advanced tab label app_highlight_tab_label(lookup_widget("advanced_tab_label"), max_advanced_tab_warning, tab_advanced_name); } void GscInfoWindow::clear_ui_info(bool clear_tests_too) { // Note: We do NOT show/hide the notebook tabs here. // fill_ui_with_info() will do it all by itself. { this->set_title("Device Information - GSmartControl"); // Gtk::Label* device_name_label = lookup_widget("device_name_label"); if (device_name_label) device_name_label->set_text("No data available"); } { Gtk::Grid* identity_table = lookup_widget("identity_table"); if (identity_table) { // manually remove all children. without this visual corruption occurs. std::vector children = identity_table->get_children(); for (std::vector::iterator iter = children.begin(); iter != children.end(); ++iter) { identity_table->remove(*(*iter)); } } // tab label app_highlight_tab_label(lookup_widget("general_tab_label"), StorageProperty::warning_none, tab_identity_name); } { Gtk::Box* label_vbox = lookup_widget("attributes_label_vbox"); app_set_top_labels(label_vbox, label_list_t()); Gtk::TreeView* treeview = lookup_widget("attributes_treeview"); if (treeview) { // Glib::RefPtr model = Glib::RefPtr::cast_dynamic(treeview->get_model()); // if (model) // model->clear(); treeview->remove_all_columns(); treeview->unset_model(); } // tab label app_highlight_tab_label(lookup_widget("attributes_tab_label"), StorageProperty::warning_none, tab_attributes_name); } { Gtk::Box* label_vbox = lookup_widget("statistics_label_vbox"); app_set_top_labels(label_vbox, label_list_t()); Gtk::TreeView* treeview = lookup_widget("statistics_treeview"); if (treeview) { treeview->remove_all_columns(); treeview->unset_model(); } // tab label app_highlight_tab_label(lookup_widget("statistics_tab_label"), StorageProperty::warning_none, tab_statistics_name); } { Gtk::Box* label_vbox = lookup_widget("selftest_log_label_vbox"); app_set_top_labels(label_vbox, label_list_t()); Gtk::TreeView* treeview = lookup_widget("selftest_log_treeview"); if (treeview) { // Glib::RefPtr model = Glib::RefPtr::cast_dynamic(treeview->get_model()); // if (model) // model->clear(); treeview->remove_all_columns(); treeview->unset_model(); } if (clear_tests_too) { Gtk::ComboBox* test_type_combo = lookup_widget("test_type_combo"); if (test_type_combo) { test_type_combo->set_sensitive(false); // true if testing is possible and not active. // test_type_combo->clear(); // clear cellrenderers if (test_combo_model) test_combo_model->clear(); } Gtk::Label* min_duration_label = lookup_widget("min_duration_label"); if (min_duration_label) min_duration_label->set_text("N/A"); // set on test selection Gtk::Button* test_execute_button = lookup_widget("test_execute_button"); if (test_execute_button) test_execute_button->set_sensitive(false); // true if testing is possible and not active Gtk::TextView* test_description_textview = lookup_widget("test_description_textview"); if (test_description_textview && test_description_textview->get_buffer()) test_description_textview->get_buffer()->set_text(""); // set on test selection Gtk::ProgressBar* test_completion_progressbar = lookup_widget("test_completion_progressbar"); if (test_completion_progressbar) { test_completion_progressbar->set_text(""); // set when test is run or completed test_completion_progressbar->set_sensitive(false); // set when test is run or completed test_completion_progressbar->hide(); } Gtk::Button* test_stop_button = lookup_widget("test_stop_button"); if (test_stop_button) { test_stop_button->set_sensitive(false); // true when test is active test_stop_button->hide(); } Gtk::Box* test_result_hbox = lookup_widget("test_result_hbox"); if (test_result_hbox) test_result_hbox->hide(); // hide by default. show when test is completed. } // tab label app_highlight_tab_label(lookup_widget("test_tab_label"), StorageProperty::warning_none, tab_test_name); } { Gtk::Box* label_vbox = lookup_widget("error_log_label_vbox"); app_set_top_labels(label_vbox, label_list_t()); Gtk::TreeView* treeview = lookup_widget("error_log_treeview"); if (treeview) { // Glib::RefPtr model = Glib::RefPtr::cast_dynamic(treeview->get_model()); // if (model) // model->clear(); treeview->remove_all_columns(); treeview->unset_model(); } Gtk::TextView* textview = lookup_widget("error_log_textview"); if (textview) { // we re-create the buffer to get rid of all the Marks textview->set_buffer(Gtk::TextBuffer::create()); textview->get_buffer()->set_text("\nNo data available"); } // tab label app_highlight_tab_label(lookup_widget("error_log_tab_label"), StorageProperty::warning_none, tab_error_log_name); } { Gtk::TextView* textview = lookup_widget("temperature_log_textview"); if (textview) { Glib::RefPtr buffer = textview->get_buffer(); buffer->set_text("\nNo data available"); } // tab label app_highlight_tab_label(lookup_widget("temperature_log_tab_label"), StorageProperty::warning_none, tab_temperature_name); } // tab label app_highlight_tab_label(lookup_widget("advanced_tab_label"), StorageProperty::warning_none, tab_advanced_name); { Gtk::TreeView* treeview = lookup_widget("capabilities_treeview"); if (treeview) { // It's better to clear the model rather than unset it. If we unset it, we'll have // to deattach the callbacks too. But if we clear it, we have to remember column vars. // Glib::RefPtr model = Glib::RefPtr::cast_dynamic(treeview->get_model()); // if (model) // model->clear(); treeview->remove_all_columns(); treeview->unset_model(); } // tab label app_highlight_tab_label(lookup_widget("capabilities_tab_label"), StorageProperty::warning_none, tab_capabilities_name); } { Gtk::TextView* textview = lookup_widget("erc_log_textview"); if (textview) { Glib::RefPtr buffer = textview->get_buffer(); buffer->set_text("\nNo data available"); } // tab label app_highlight_tab_label(lookup_widget("erc_tab_label"), StorageProperty::warning_none, tab_erc_name); } { Gtk::TextView* textview = lookup_widget("selective_selftest_log_textview"); if (textview) { Glib::RefPtr buffer = textview->get_buffer(); buffer->set_text("\nNo data available"); } // tab label app_highlight_tab_label(lookup_widget("selective_selftest_tab_label"), StorageProperty::warning_none, tab_selective_selftest_name); } { Gtk::TextView* textview = lookup_widget("phy_log_textview"); if (textview) { Glib::RefPtr buffer = textview->get_buffer(); buffer->set_text("\nNo data available"); } // tab label app_highlight_tab_label(lookup_widget("phy_tab_label"), StorageProperty::warning_none, tab_phy_name); } { Gtk::TextView* textview = lookup_widget("directory_log_textview"); if (textview) { Glib::RefPtr buffer = textview->get_buffer(); buffer->set_text("\nNo data available"); } // tab label app_highlight_tab_label(lookup_widget("directory_tab_label"), StorageProperty::warning_none, tab_directory_name); } } void GscInfoWindow::refresh_info(bool clear_tests_too) { this->set_sensitive(false); // make insensitive until filled. helps with pressed F5 problem. // this->clear_ui_info(); // no need, fill_ui_with_info() will call it. this->fill_ui_with_info(true, true, clear_tests_too); this->set_sensitive(true); // make sensitive again. } void GscInfoWindow::show_tests() { Gtk::Notebook* book = lookup_widget("main_notebook"); if (book) book->set_current_page(3); // the Tests tab } bool GscInfoWindow::on_delete_event_before(GdkEventAny* e) { if (drive && drive->get_test_is_active()) { // disallow close if test is active. gui_show_warn_dialog("Please wait until all tests are finished.", this); return true; // handled } destroy(this); // deletes this object and nullifies instance return true; // event handled, don't call default virtual handler } void GscInfoWindow::on_refresh_info_button_clicked() { this->refresh_info(); } void GscInfoWindow::on_view_output_button_clicked() { GscTextWindow* win = GscTextWindow::create(); // make save visible and enable monospace font std::string output = this->drive->get_full_output(); if (output.empty()) { output = this->drive->get_info_output(); } win->set_text("Smartctl Output", output, true, true); std::string filename = drive->get_save_filename(); if (!filename.empty()) win->set_save_filename(filename); win->show(); } void GscInfoWindow::on_save_info_button_clicked() { static std::string last_dir; if (last_dir.empty()) { rconfig::get_data("gui/drive_data_open_save_dir", last_dir); } int result = 0; std::string filename = drive->get_save_filename(); Glib::RefPtr specific_filter = Gtk::FileFilter::create(); specific_filter->set_name("Text Files"); specific_filter->add_pattern("*.txt"); Glib::RefPtr all_filter = Gtk::FileFilter::create(); all_filter->set_name("All Files"); all_filter->add_pattern("*"); #if GTK_CHECK_VERSION(3, 20, 0) hz::scoped_ptr dialog(gtk_file_chooser_native_new( "Save Data As...", this->gobj(), GTK_FILE_CHOOSER_ACTION_SAVE, NULL, NULL), g_object_unref); gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog.get()), true); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog.get()), specific_filter->gobj()); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog.get()), all_filter->gobj()); if (!last_dir.empty()) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog.get()), last_dir.c_str()); if (!filename.empty()) gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog.get()), filename.c_str()); result = gtk_native_dialog_run(GTK_NATIVE_DIALOG(dialog.get())); #else Gtk::FileChooserDialog dialog(*this, "Save Data As...", Gtk::FILE_CHOOSER_ACTION_SAVE); // Add response buttons the the dialog dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT); dialog.set_do_overwrite_confirmation(true); dialog.add_filter(specific_filter); dialog.add_filter(all_filter); if (!last_dir.empty()) dialog.set_current_folder(last_dir); if (!filename.empty()) dialog.set_current_name(filename); // Show the dialog and wait for a user response result = dialog.run(); // the main cycle blocks here #endif // Handle the response switch (result) { case Gtk::RESPONSE_ACCEPT: { std::string file; #if GTK_CHECK_VERSION(3, 20, 0) file = app_ustring_from_gchar(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog.get()))); last_dir = hz::path_get_dirname(file); #else file = dialog.get_filename(); // in fs encoding last_dir = dialog.get_current_folder(); // save for the future #endif rconfig::set_data("gui/drive_data_open_save_dir", last_dir); if (file.rfind(".txt") != (file.size() - std::strlen(".txt"))) { file += ".txt"; } hz::File f(file); std::string data = this->drive->get_full_output(); if (data.empty()) { data = this->drive->get_info_output(); } if (!f.put_contents(data)) { // this will send to debug_ too. gui_show_error_dialog("Cannot save SMART data to file", f.get_error_utf8(), this); } break; } case Gtk::RESPONSE_CANCEL: case Gtk::RESPONSE_DELETE_EVENT: // nothing, the dialog is closed already break; default: debug_out_error("app", DBG_FUNC_MSG << "Unknown dialog response code: " << result << ".\n"); break; } } void GscInfoWindow::on_close_window_button_clicked() { on_delete_event_before(0); } void GscInfoWindow::on_test_type_combo_changed() { Gtk::ComboBox* test_type_combo = lookup_widget("test_type_combo"); if (!test_type_combo) return; Gtk::TreeRow row = *(test_type_combo->get_active()); if (row) { SelfTestPtr test = row[test_combo_col_self_test]; //debug_out_error("app", test->get_min_duration_seconds() << "\n"); Gtk::Label* min_duration_label = lookup_widget("min_duration_label"); if (min_duration_label) { int64_t duration = test->get_min_duration_seconds(); min_duration_label->set_text(duration == -1 ? "N/A" : (duration == 0 ? "Unknown" : hz::format_time_length(duration))); } Gtk::TextView* test_description_textview = lookup_widget("test_description_textview"); if (test_description_textview && test_description_textview->get_buffer()) test_description_textview->get_buffer()->set_text(row[test_combo_col_description]); } } // Note: Another loop like this may run inside it for another drive. gboolean GscInfoWindow::test_idle_callback(void* data) { GscInfoWindow* self = static_cast(data); DBG_ASSERT(self); if (!self->current_test) // shouldn't happen return false; // stop Gtk::ProgressBar* test_completion_progressbar = self->lookup_widget("test_completion_progressbar"); bool active = true; do { // goto if (!self->current_test->is_active()) { // check status active = false; break; } int8_t rem_percent = self->current_test->get_remaining_percent(); std::string rem_percent_str = (rem_percent == -1 ? "Unknown" : hz::number_to_string(100 - rem_percent)); int64_t poll_in = self->current_test->get_poll_in_seconds(); // sec // One update() is performed by start(), so do the timeout first. // Wait until next poll (up to several minutes). Meanwhile, interpolate // the remaining time, update the progressbar, etc... if (self->test_timer_poll.elapsed() < static_cast(poll_in)) { // elapsed() is seconds in double. // Update progress bar right after poll, plus every 5 seconds. if (self->test_force_bar_update || self->test_timer_bar.elapsed() >= 5.) { int64_t rem_seconds = self->current_test->get_remaining_seconds(); if (test_completion_progressbar) { std::string rem_seconds_str = (rem_seconds == -1 ? "Unknown" : hz::format_time_length(rem_seconds)); Glib::ustring bar_str; if (self->test_error_msg.empty()) { bar_str = hz::string_sprintf("Test completion: %s%%; ETA: %s", rem_percent_str.c_str(), rem_seconds_str.c_str()); } else { bar_str = self->test_error_msg; // better than popup every few seconds } test_completion_progressbar->set_text(bar_str); test_completion_progressbar->set_fraction(std::max(0., std::min(1., 1. - (rem_percent / 100.)))); } self->test_force_bar_update = false; self->test_timer_bar.start(); // restart } if (!self->current_test->is_active()) { // check status active = false; break; } } else { // it's poll time if (!self->current_test->is_active()) { // the inner loop stopped, stop this one too active = false; break; } SmartctlExecutorGuiRefPtr ex(new SmartctlExecutorGui()); ex->create_running_dialog(self); self->test_error_msg = self->current_test->update(ex); if (!self->test_error_msg.empty()) { // gui_show_error_dialog("Cannot monitor test progress", self->test_error_msg, this); // better show in progressbar. self->current_test->force_stop(ex); // what else can we do? active = false; break; } self->test_timer_poll.start(); // restart it self->test_force_bar_update = true; // force progressbar / ETA update on the next tick } } while (false); if (active) { return true; // continue the idle callback } // Test is finished, clean up self->test_timer_poll.stop(); // just in case self->test_timer_bar.stop(); // just in case StorageSelftestEntry::status_t status = self->current_test->get_status(); bool aborted = false; StorageSelftestEntry::status_severity_t severity = StorageSelftestEntry::severity_none; std::string result_msg; if (!self->test_error_msg.empty()) { aborted = true; severity = StorageSelftestEntry::severity_error; result_msg = "Test aborted: " + self->test_error_msg; } else { severity = StorageSelftestEntry::get_status_severity(status); if (status == StorageSelftestEntry::status_aborted_by_host) { aborted = true; result_msg = "Test was manually aborted."; // it's a severity_none message } else { result_msg = "Test result: " + StorageSelftestEntry::get_status_name(status) + "."; // It may not reach 100% somehow, so do it manually. if (test_completion_progressbar) test_completion_progressbar->set_fraction(1.); // yes, we're at 100% anyway (at least logically). } } if (severity != StorageSelftestEntry::severity_none) { result_msg += "\nCheck the Self-Test Log for more information."; } Gtk::ComboBox* test_type_combo = self->lookup_widget("test_type_combo"); if (test_type_combo) test_type_combo->set_sensitive(true); Gtk::Button* test_execute_button = self->lookup_widget("test_execute_button"); if (test_execute_button) test_execute_button->set_sensitive(true); if (test_completion_progressbar) test_completion_progressbar->set_text(aborted ? "Test aborted" : "Test completed"); Gtk::Button* test_stop_button = self->lookup_widget("test_stop_button"); if (test_stop_button) test_stop_button->set_sensitive(false); Gtk::StockID stock_id = Gtk::Stock::DIALOG_ERROR; if (severity == StorageSelftestEntry::severity_none) { stock_id = Gtk::Stock::DIALOG_INFO; } else if (severity == StorageSelftestEntry::severity_warn) { stock_id = Gtk::Stock::DIALOG_WARNING; } Gtk::Image* test_result_image = self->lookup_widget("test_result_image"); // we use large icon size here because the icons we use are from dialogs. // unfortunately, there are no non-dialog icons of this sort. if (test_result_image) test_result_image->set(stock_id, Gtk::ICON_SIZE_DND); Gtk::Label* test_result_label = self->lookup_widget("test_result_label"); if (test_result_label) test_result_label->set_markup(result_msg); Gtk::Box* test_result_hbox = self->lookup_widget("test_result_hbox"); if (test_result_hbox) test_result_hbox->show(); self->refresh_info(false); // don't clear the tests tab return false; // stop idle callback } void GscInfoWindow::on_test_execute_button_clicked() { Gtk::ComboBox* test_type_combo = lookup_widget("test_type_combo"); if (!test_type_combo) return; Gtk::TreeRow row = *(test_type_combo->get_active()); if (!row) return; SelfTestPtr test = row[test_combo_col_self_test]; if (!test) return; // hide previous test results from GUI Gtk::Box* test_result_hbox = this->lookup_widget("test_result_hbox"); if (test_result_hbox) test_result_hbox->hide(); SmartctlExecutorGuiRefPtr ex(new SmartctlExecutorGui()); ex->create_running_dialog(this); std::string error_msg = test->start(ex); // this runs update() too. if (!error_msg.empty()) { gui_show_error_dialog("Cannot run " + SelfTest::get_test_name(test->get_test_type()), error_msg, this); return; } current_test = test; // Switch GUI to "running test" mode if (test_type_combo) test_type_combo->set_sensitive(false); Gtk::Button* test_execute_button = lookup_widget("test_execute_button"); if (test_execute_button) test_execute_button->set_sensitive(false); Gtk::ProgressBar* test_completion_progressbar = lookup_widget("test_completion_progressbar"); if (test_completion_progressbar) { test_completion_progressbar->set_text(""); test_completion_progressbar->set_sensitive(true); test_completion_progressbar->show(); } Gtk::Button* test_stop_button = lookup_widget("test_stop_button"); if (test_stop_button) { test_stop_button->set_sensitive(true); // true while test is active test_stop_button->show(); } // reset these test_error_msg = ""; test_timer_poll.start(); test_timer_bar.start(); test_force_bar_update = true; // We don't use idle function here, because it has the following problem: // CmdexSync::execute() (which is called on force_stop()) calls g_main_context_pending(), // which returns true EVERY time, until the idle callback returns false. // So, force_stop() exits its "execute abort" command only when the // idle callback polls the drive on the next timeout and sees that the test // has been actually aborted. // So, we use timeout callback - and hope that there are no usleeps // with 300ms so that g_main_context_pending() returns false at least once, // to escape the execute() loop. // g_idle_add(test_idle_callback, this); g_timeout_add(300, test_idle_callback, this); // every 300ms } void GscInfoWindow::on_test_stop_button_clicked() { if (!current_test) return; SmartctlExecutorGuiRefPtr ex(new SmartctlExecutorGui()); ex->create_running_dialog(this); std::string error_msg = current_test->force_stop(ex); if (!error_msg.empty()) { gui_show_error_dialog("Cannot stop " + SelfTest::get_test_name(current_test->get_test_type()), error_msg, this); return; } // nothing else to do - the cleanup is performed by the idle callback. } // Callback attached to StorageDevice. // We don't refresh automatically (that would make it impossible to do // several same-drive info window comparisons side by side). // But we need to look for testing status change, to avoid aborting it. void GscInfoWindow::on_drive_changed(StorageDevice* pdrive) { if (!drive) return; bool test_active = drive->get_test_is_active(); // disable refresh button if test is active or if it's a virtual drive Gtk::Button* refresh_info_button = lookup_widget("refresh_info_button"); if (refresh_info_button) refresh_info_button->set_sensitive(!test_active && !drive->get_is_virtual()); // disallow close. usually modal dialogs are used for this, but we can't have // per-drive modal dialogs. Gtk::Button* close_window_button = lookup_widget("close_window_button"); if (close_window_button) close_window_button->set_sensitive(!test_active); // test_active is also checked in delete_event handler, because this call may not // succeed - the window manager may refuse to do it. this->set_deletable(!test_active); } bool GscInfoWindow::on_treeview_button_press_event(GdkEventButton* button_event, Gtk::Menu* menu, Gtk::TreeView* treeview) { if (button_event->type == GDK_BUTTON_PRESS && button_event->button == 3) { bool selection_empty = treeview->get_selection()->get_selected_rows().empty(); std::vector children = menu->get_children(); for (std::size_t i = 0; i < children.size(); ++i) { children[i]->set_sensitive(!selection_empty); } menu->popup(button_event->button, button_event->time); return true; } return false; } void GscInfoWindow::on_treeview_menu_copy_clicked(Gtk::TreeView* treeview) { std::string text; guint num_cols = treeview->get_n_columns(); std::vector col_texts; for (guint i = 0; i < num_cols; ++i) { Gtk::TreeViewColumn* tcol = treeview->get_column(i); col_texts.push_back("\"" + hz::string_replace_copy(tcol->get_title(), "\"", "\"\"") + "\""); } text += hz::string_join(col_texts, ',') + "\n"; std::vector selection = treeview->get_selection()->get_selected_rows(); Glib::RefPtr list_store = Glib::RefPtr::cast_dynamic(treeview->get_model()); for (std::size_t i = 0; i < selection.size(); ++i) { std::vector cell_texts; Gtk::TreeModel::Path path = selection[i]; Gtk::TreeRow row = *(list_store->get_iter(path)); for (guint j = 0; j < num_cols; ++j) { // gather data only from tree columns, not model columns (like tooltips and helper data) GType type = list_store->get_column_type(j); if (type == G_TYPE_INT) { int32_t value = 0; row.get_value(j, value); cell_texts.push_back(hz::number_to_string(value)); } else if (type == G_TYPE_STRING) { std::string value; row.get_value(j, value); cell_texts.push_back("\"" + hz::string_replace_copy(value, "\"", "\"\"") + "\""); } } text += hz::string_join(cell_texts, ',') + "\n"; } Glib::RefPtr clipboard = Gtk::Clipboard::get(); if (clipboard) { clipboard->set_text(text); } } /// @} gsmartcontrol-1.1.4/src/gsc_info_window.h000066400000000000000000000112171417717230600205430ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup gsc /// \weakgroup gsc /// @{ #ifndef GSC_INFO_WINDOW_H #define GSC_INFO_WINDOW_H #include #include #include "applib/app_ui_res_utils.h" #include "applib/storage_device.h" #include "applib/selftest.h" /// The "Drive Information" window. /// Use create() / destroy() with this class instead of new / delete! class GscInfoWindow : public AppUIResWidget { public: // name of ui file without a .ui extension and quotes APP_UI_RES_DATA_INIT(gsc_info_window); /// Constructor, GtkBuilder needs this. GscInfoWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui); /// Virtual destructor virtual ~GscInfoWindow(); // Overridden from InstanceManager virtual void obj_destroy(); /// Set the drive to show void set_drive(StorageDeviceRefPtr d); /// Fill the dialog with info from the drive void fill_ui_with_info(bool scan = true, bool clear_ui = true, bool clear_tests = true); /// Clear all info in UI void clear_ui_info(bool clear_tests_too = true); /// Refresh the drive information in UI void refresh_info(bool clear_tests_too = true); // Show the Tests tab. Called by main window. void show_tests(); protected: // -------------------- callbacks /// An idle callback to update the status while the test is running. static gboolean test_idle_callback(void* data); // ---------- overriden virtual methods /// Destroy this object on delete event (by default it calls hide()). /// If a test is running, show a question dialog first. /// Reimplemented from Gtk::Window. bool on_delete_event_before(GdkEventAny* e); // ---------- other callbacks /// Button click callback void on_refresh_info_button_clicked(); /// Button click callback void on_view_output_button_clicked(); /// Button click callback void on_save_info_button_clicked(); /// Button click callback void on_close_window_button_clicked(); /// Callback void on_test_type_combo_changed(); /// Button click callback void on_test_execute_button_clicked(); /// Button click callback void on_test_stop_button_clicked(); /// Callback attached to StorageDevice change signal. void on_drive_changed(StorageDevice* pdrive); /// Callback bool on_treeview_button_press_event(GdkEventButton* button_event, Gtk::Menu* menu, Gtk::TreeView* treeview); /// Callback void on_treeview_menu_copy_clicked(Gtk::TreeView* treeview); private: // --------- Connections sigc::connection error_log_row_selected_conn; ///< Callback connection sigc::connection test_type_combo_changed_conn; ///< Callback connection sigc::connection drive_changed_connection; // Callback connection of drive's signal_changed callback // --------- Data members std::map treeview_menus; ///< Context menus // tab headers, to perform their coloration Glib::ustring tab_identity_name; ///< Tab header name Glib::ustring tab_attributes_name; ///< Tab header name Glib::ustring tab_statistics_name; ///< Tab header name Glib::ustring tab_test_name; ///< Tab header name Glib::ustring tab_error_log_name; ///< Tab header name Glib::ustring tab_temperature_name; ///< Tab header name Glib::ustring tab_advanced_name; ///< Tab header name Glib::ustring tab_capabilities_name; ///< Tab header name Glib::ustring tab_erc_name; ///< Tab header name Glib::ustring tab_selective_selftest_name; ///< Tab header name Glib::ustring tab_phy_name; ///< Tab header name Glib::ustring tab_directory_name; ///< Tab header name Gtk::Label* device_name_label; ///< Top label StorageDeviceRefPtr drive; ///< The drive we're showing SelfTestPtr current_test; ///< Currently running test, or 0. // test idle callback temporaries std::string test_error_msg; ///< Our errors Glib::Timer test_timer_poll; ///< Timer for testing phase Glib::Timer test_timer_bar; ///< Timer for testing phase bool test_force_bar_update; ///< Helper for testing callback // test type combobox stuff Gtk::TreeModelColumn test_combo_col_name; ///< Combobox model column Gtk::TreeModelColumn test_combo_col_description; ///< Combobox model column Gtk::TreeModelColumn test_combo_col_self_test; ///< Combobox model column Glib::RefPtr test_combo_model; ///< Combobox model }; #endif /// @} gsmartcontrol-1.1.4/src/gsc_init.cpp000066400000000000000000000476771417717230600175420ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup gsc /// \weakgroup gsc /// @{ #include // #include // _configthreadlocale (win32) #include // std::runtime_error #include // std::printf #include #include #include #include #include // g_, G* #include // set_application_name #ifdef _WIN32 #include #include #endif #include "hz/hz_config.h" // ENABLE_GLIB, VERSION, DEBUG_BUILD #include "libdebug/libdebug.h" // include full libdebug here (to add domains, etc...) #include "rconfig/rconfig.h" // include full rconfig here #include "hz/data_file.h" // data_file_add_search_directory #include "hz/fs_tools.h" // get_user_config_dir() #include "hz/fs_path.h" // FsPath #include "hz/locale_tools.h" // locale_c* #include "hz/string_algo.h" // string_join() #include "hz/win32_tools.h" // win32_get_registry_value_string() #include "hz/env_tools.h" #include "gsc_main_window.h" #include "gsc_executor_log_window.h" #include "gsc_settings.h" #include "gsc_init.h" namespace { /// Config file in user's HOME static std::string s_home_config_file; /// Libdebug channel buffer static debug_channel_base_ptr s_debug_buf_channel; /// Libdebug channel buffer stream static std::ostringstream s_debug_buf_channel_stream; /// Get libdebug buffer channel (create new one if unavailable). /// This function is not thread-safe. The channel must be locked properly. inline debug_channel_base_ptr app_get_debug_buf_channel() { if (!s_debug_buf_channel) return (s_debug_buf_channel = new DebugChannelOStream(s_debug_buf_channel_stream)); return s_debug_buf_channel; } } std::string app_get_debug_buffer_str() { debug_channel_base_ptr channel = app_get_debug_buf_channel(); DebugChannelOStream::intrusive_ptr_scoped_lock_type locker(channel->get_ref_mutex()); // lock // now the channel is locked, so we have a proper access to the stream. return s_debug_buf_channel_stream.str(); } namespace { /// Find the configuration files and load them. inline bool app_init_config() { // $XDG_CONFIG_DIRS defaults to /etc/xdg, which is really silly. // Actually, the whole specification is silly imho, since instead of // removing just one directory/file I have to remove 3 now (and tell // the users to do the same thing). And the *_DIRS stuff completely // breaks parallel installations of the same program. Implementing // only $XDG_CONFIG_HOME is harmless enough, so we do it. s_home_config_file = hz::get_user_config_dir() + hz::DIR_SEPARATOR_S + "gsmartcontrol" + hz::DIR_SEPARATOR_S + "gsmartcontrol.conf"; std::string global_config_file; std::string old_local_config; // pre-XDG and pre-CSIDL_APPDATA file for migration #ifdef _WIN32 global_config_file = "gsmartcontrol.conf"; // CWD, installation dir by default. std::string old_config_dir; hz::win32_get_registry_value_string(HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", "Personal", old_config_dir); old_local_config = old_config_dir + hz::DIR_SEPARATOR_S + "gsmartcontrol.conf"; #else global_config_file = std::string(PACKAGE_SYSCONF_DIR) + hz::DIR_SEPARATOR_S + "gsmartcontrol.conf"; old_local_config = hz::get_home_dir() + hz::DIR_SEPARATOR_S + ".gsmartcontrolrc"; #endif debug_out_dump("app", DBG_FUNC_MSG << "Global config file: \"" << global_config_file << "\"\n"); debug_out_dump("app", DBG_FUNC_MSG << "Local config file: \"" << s_home_config_file << "\"\n"); debug_out_dump("app", DBG_FUNC_MSG << "Old local config file: \"" << old_local_config << "\"\n"); hz::FsPath gp(global_config_file); // Default system-wide settings. This file is empty by default. hz::FsPath hp(s_home_config_file); // Per-user settings. hz::FsPath op(old_local_config); // Old user settings, should be migrated. if (gp.exists() && gp.is_readable()) { // load global first rconfig::load_from_file(gp.str()); } if (hp.exists() && hp.is_readable()) { // load local rconfig::load_from_file(hp.str()); } else { // create the parent directories of the config file hz::FsPath config_loc(hp.get_dirname()); if (!config_loc.exists()) { config_loc.make_dir(0700, true); // with parents. } if (op.exists() && op.is_readable()) { // load the old config if the new one doesn't exist. debug_print_info("app", "Old configuration file found at \"%s\", migrating to \"%s\".\n", op.c_str(), hp.c_str()); rconfig::load_from_file(op.str()); // force saving of the config to the new location (so that it's preserved in case of crash). if (rconfig::save_to_file(hp.str())) { // remove the old config op.remove(); } } } rconfig::dump_tree(); init_default_settings(); // initialize /default #if defined ENABLE_GLIB && ENABLE_GLIB rconfig::autosave_set_config_file(s_home_config_file); uint32_t autosave_timeout = rconfig::get_data("system/config_autosave_timeout"); if (autosave_timeout) rconfig::autosave_start(autosave_timeout); #endif return true; } /// If it's the first time the application was started by this user, show a message. inline void app_show_first_boot_message(Gtk::Window* parent) { bool first_boot = false; rconfig::get_data("system/first_boot", first_boot); if (first_boot) { // Glib::ustring msg = "First boot"; // Gtk::MessageDialog(*parent, msg, false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK, true).run(); } // rconfig::set_data("system/first_boot", false); // don't show it again } } // anon. ns extern "C" { /// Glib message -> libdebug message convertor static void glib_message_handler(const gchar* log_domain, GLogLevelFlags log_level, const gchar* message, gpointer user_data) { // log_domain is already printed as part of message. switch(log_level) { case G_LOG_FLAG_RECURSION: case G_LOG_FLAG_FATAL: case G_LOG_LEVEL_ERROR: // fatal case G_LOG_LEVEL_CRITICAL: debug_print_error("gtk", "%s\n", message); break; case G_LOG_LEVEL_WARNING: debug_print_warn("gtk", "%s\n", message); break; case G_LOG_LEVEL_MESSAGE: case G_LOG_LEVEL_INFO: debug_print_info("gtk", "%s\n", message); break; case G_LOG_LEVEL_DEBUG: debug_print_dump("gtk", "%s\n", message); break; case G_LOG_LEVEL_MASK: break; } } } namespace { /// Command-line argument values struct CmdArgs { CmdArgs() : // defaults arg_locale(TRUE), arg_version(FALSE), arg_scan(TRUE), arg_hide_tabs(TRUE), arg_add_virtual(NULL), arg_add_device(NULL), arg_gdk_scale(std::numeric_limits::quiet_NaN()), arg_gdk_dpi_scale(std::numeric_limits::quiet_NaN()) { } // Note: Use GLib types here: gboolean arg_locale; ///< if false, disable using system locale gboolean arg_version; ///< if true, show version and exit gboolean arg_scan; ///< if false, don't scan the system for drives on startup gboolean arg_hide_tabs; ///< if true, hide additional info tabs when smart is disabled. false may help debugging. gchar** arg_add_virtual; ///< load smartctl data from these files as virtual drives gchar** arg_add_device; ///< add these device files manually double arg_gdk_scale; ///< The value of GDK_SCALE environment variable double arg_gdk_dpi_scale; ///< The value of GDK_DPI_SCALE environment variable }; /// Parse command-line arguments (fills \c args) inline bool parse_cmdline_args(CmdArgs& args, int& argc, char**& argv) { static const GOptionEntry arg_entries[] = { { "no-locale", 'l', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &(args.arg_locale), "Don't use system locale", NULL }, { "version", 'V', 0, G_OPTION_ARG_NONE, &(args.arg_version), "Display version information", NULL }, { "no-scan", '\0', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &(args.arg_scan), "Don't scan devices on startup", NULL }, { "no-hide-tabs", '\0', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &(args.arg_hide_tabs), "Don't hide non-identity tabs when SMART is disabled. Useful for debugging.", NULL }, { "add-virtual", '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, &(args.arg_add_virtual), "Load smartctl data from file, creating a virtual drive. You can specify this option multiple times.", NULL }, { "add-device", '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, &(args.arg_add_device), "Add this device to device list. The format of the device is \"::::\", where type and extra_args are optional." " This option is useful with --no-scan to list certain drives only. You can specify this option multiple times." " Example: --add-device /dev/sda --add-device /dev/twa0::3ware,2 --add-device '/dev/sdb::::-T permissive'", NULL }, #ifndef _WIN32 // X11-specific { "gdk-scale", 'l', 0, G_OPTION_ARG_DOUBLE, &(args.arg_gdk_scale), "The value of GDK_SCALE environment variable (useful when executing with pkexec)", NULL }, { "gdk-dpi-scale", 'l', 0, G_OPTION_ARG_DOUBLE, &(args.arg_gdk_dpi_scale), "The value of GDK_DPI_SCALE environment variable (useful when executing with pkexec)", NULL }, #endif { NULL } }; GError* error = 0; GOptionContext* context = g_option_context_new("- A GTK+ GUI for smartmontools"); // our options g_option_context_add_main_entries(context, arg_entries, NULL); // gtk options g_option_context_add_group(context, gtk_get_option_group(false)); // libdebug options; this will also automatically apply them g_option_context_add_group(context, debug_get_option_group()); // The command-line parser stops at the first unknown option. Since this // is kind of inconsistent, we abort altogether. bool parsed = g_option_context_parse(context, &argc, &argv, &error); if (error) { std::string error_text = "\n" + std::string("Error parsing command-line options: "); error_text += (error->message ? error->message : "invalid error"); error_text += "\n\n"; g_error_free(error); #if (GLIB_CHECK_VERSION(2,14,0)) gchar* help_text = g_option_context_get_help(context, true, NULL); if (help_text) { error_text += help_text; g_free(help_text); } #else error_text += "Exiting.\n"; #endif std::fprintf(stderr, "%s", error_text.c_str()); } g_option_context_free(context); return parsed; } /// Print application version information inline void app_print_version_info() { std::string versiontext = std::string("\nGSmartControl version ") + VERSION + "\n"; std::string warningtext = std::string("\nWarning: GSmartControl"); warningtext += " comes with ABSOLUTELY NO WARRANTY.\n"; warningtext += "See LICENSE_gsmartcontrol.txt file for details.\n"; warningtext += "\nCopyright (C) 2008 - 2022 Alexander Shaduri \n\n"; std::fprintf(stdout, "%s%s", versiontext.c_str(), warningtext.c_str()); } } bool app_init_and_loop(int& argc, char**& argv) { // initialize GThread (for mutexes, etc... to work). Must be called before any other glib function. // Glib::thread_init(); #ifdef _WIN32 std::string csd_value; if (!hz::env_get_value("GTK_CSD", csd_value)) { // if not set // Disable client-side decorations (enable native windows decorations) under Windows. hz::env_set_value("GTK_CSD", "0"); } #endif // Glib needs the C locale set to system locale for command line args. // We will reset it later if needed. hz::locale_c_set(""); // set the current locale to system locale // Parse command line args. // Due to gtk_get_option_group()/g_option_context_parse() calls, this // will also initialize GTK and set the C locale to system locale (as well // as do some locale-specific gdk initialization). CmdArgs args; if (! parse_cmdline_args(args, argc, argv)) { return true; } // If locale setting is explicitly disabled, revert to the original classic C locale. // Note that changing GTK locale after it's inited isn't really supported by GTK, // but we have no other choice - glib needs system locale when parsing the // arguments, and gtk is inited while the parsing is performed. if (!args.arg_locale) { hz::locale_c_set("C"); } else { // change the C++ locale to match the C one. hz::locale_cpp_set(""); // this may fail on some systems } if (args.arg_version) { // show version information and exit app_print_version_info(); return true; } // register libdebug domains debug_register_domain("gtk"); debug_register_domain("app"); debug_register_domain("hz"); debug_register_domain("rmn"); debug_register_domain("rconfig"); // Add special debug channel to collect all libdebug output into a buffer. debug_add_channel("all", debug_level::all, app_get_debug_buf_channel()); std::vector load_virtuals; if (args.arg_add_virtual) { const gchar* entry = 0; while ( (entry = *(args.arg_add_virtual)++) != NULL ) { load_virtuals.push_back(entry); } } std::string load_virtuals_str = hz::string_join(load_virtuals, ", "); // for display purposes only std::vector load_devices; if (args.arg_add_device) { const gchar* entry = 0; while ( (entry = *(args.arg_add_device)++) != NULL ) { load_devices.push_back(entry); } } std::string load_devices_str = hz::string_join(load_devices, "; "); // for display purposes only // it's here because earlier there are no domains debug_out_dump("app", "Application options:\n" << "\tlocale: " << args.arg_locale << "\n" << "\tversion: " << args.arg_version << "\n" << "\thide_tabs: " << args.arg_hide_tabs << "\n" << "\tscan: " << args.arg_scan << "\n" << "\targ_add_virtual: " << (load_virtuals_str.empty() ? "[empty]" : load_virtuals_str) << "\n" << "\targ_add_device: " << (load_devices_str.empty() ? "[empty]" : load_devices_str) << "\n" << "\targ_gdk_scale: " << args.arg_gdk_scale << "\n" << "\targ_gdk_dpi_scale: " << args.arg_gdk_dpi_scale << "\n"); debug_out_dump("app", "LibDebug options:\n" << debug_get_cmd_args_dump()); #ifndef _WIN32 if (args.arg_gdk_scale == args.arg_gdk_scale) { // not NaN hz::env_set_value("GDK_SCALE", hz::number_to_string(args.arg_gdk_scale)); } if (args.arg_gdk_dpi_scale == args.arg_gdk_dpi_scale) { // not NaN hz::env_set_value("GDK_DPI_SCALE", hz::number_to_string(args.arg_gdk_dpi_scale)); } #endif // Load config files app_init_config(); // Redirect all GTK+/Glib and related messages to libdebug. // Do this before GTK+ init, to capture its possible warnings as well. static const char* const gtkdomains[] = { // no atk or cairo, they don't log. libgnomevfs may be loaded by gtk file chooser. "GLib", "GModule", "GLib-GObject", "GLib-GRegex", "GLib-GIO", "GThread", "Pango", "Gtk", "Gdk", "GdkPixbuf", "libgnomevfs", "glibmm", "giomm", "atkmm", "pangomm", "gdkmm", "gtkmm" }; for (unsigned int i = 0; i < G_N_ELEMENTS(gtkdomains); ++i) { g_log_set_handler(gtkdomains[i], GLogLevelFlags(G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION), glib_message_handler, NULL); } // Save the locale std::locale final_loc_cpp = hz::locale_cpp_get(); // Initialize GTK+ (it's already initialized by command-line parser, // so this doesn't do much). // Newer gtkmm will try to set the C++ locale here. // Note: passing false (as use_locale) as the third parameter here // will generate a gtk_disable_setlocale() warning (due to gtk being // already initialized), so manually save / restore the C++ locale // (C locale won't be touched). // Nothing is affected in gtkmm itself by C++ locale, so it's ok to do it. Gtk::Main m(argc, argv); // Restore the locale hz::locale_cpp_set(final_loc_cpp); debug_out_info("app", "Current C locale: " << hz::locale_c_get() << "\n"); debug_out_info("app", "Current C++ locale: " << hz::locale_cpp_get() << "\n"); #ifdef _WIN32 // Now that all program-specific locale setup has been performed, // make sure the future locale changes affect only current thread. // Not available in mingw, so disable for now. // _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); #endif // This shows up in About dialog gtk. Glib::set_application_name("GSmartControl"); // should be localized // Add data file search paths #ifdef _WIN32 // In windows the program is distributed with all the data files in the same directory. hz::data_file_add_search_directory("."); #else #ifdef DEBUG_BUILD hz::data_file_add_search_directory(std::string(TOP_SRC_DIR) + "/src/res"); // application data resources hz::data_file_add_search_directory(std::string(TOP_SRC_DIR) + "/data"); // application data resources #else hz::data_file_add_search_directory(PACKAGE_PKGDATA_DIR); // /usr/share/program_name #endif #endif #ifdef _WIN32 // Windows "Classic" theme is broken under GTK+3's "win32" theme. // Make sure we fall back to Adwaita (which works, but looks non-native) // for platforms which support "Classic" theme - Windows Server and Windows Vista / 7. // Windows 8 / 10 don't support "Classic" so native look is preferred. { Glib::RefPtr gtk_settings = Gtk::Settings::get_default(); if (gtk_settings) { Glib::ustring theme_name = gtk_settings->property_gtk_theme_name().get_value(); debug_out_dump("app", "Current GTK theme: " << theme_name << "\n"); if (IsWindowsServer() || !IsWindows8OrGreater()) { if (theme_name == "win32") { debug_out_dump("app", "Windows with Classic theme support detected, switching to Adwaita theme.\n"); gtk_settings->property_gtk_theme_name().set_value("Adwaita"); } } } } #endif // Set default icon for all windows. // Win32 version has its icon compiled-in, so no need to set it there. #ifndef _WIN32 { // we load it via icontheme to provide multi-size version. // application-installed, /usr/share/icons//apps/ if (Gtk::IconTheme::get_default()->has_icon("gsmartcontrol")) { Gtk::Window::set_default_icon_name("gsmartcontrol"); // try the gnome icon, it's higher quality / resolution } else if (Gtk::IconTheme::get_default()->has_icon("gnome-dev-harddisk")) { Gtk::Window::set_default_icon_name("gnome-dev-harddisk"); // gtk built-in, always available } else { Gtk::Window::set_default_icon_name("gtk-harddisk"); } } #endif // Export some command line arguments to rmn // obey the command line option for no-scan on startup rconfig::set_data("/runtime/gui/force_no_scan_on_startup", !bool(args.arg_scan)); // load virtual drives on startup if specified. rconfig::set_data("/runtime/gui/add_virtuals_on_startup", load_virtuals); // add devices to the list on startup if specified. rconfig::set_data("/runtime/gui/add_devices_on_startup", load_devices); // hide tabs if SMART is disabled rconfig::set_data("/runtime/gui/hide_tabs_on_smart_disabled", bool(args.arg_hide_tabs)); // Create executor log window, but don't show it. // It will track all command executor outputs. GscExecutorLogWindow::create(); // Open the main window GscMainWindow* win = GscMainWindow::create(); if (!win) { debug_out_fatal("app", "Cannot create the main window. Exiting.\n"); return false; // cannot create main window } // first-boot message app_show_first_boot_message(win); // The Main Loop (tm) debug_out_info("app", "Entering main loop.\n"); m.run(); debug_out_info("app", "Main loop exited.\n"); // close the main window and delete its object GscMainWindow::destroy(); GscExecutorLogWindow::destroy(); // std::cerr << app_get_debug_buffer_str(); // this will output everything that went through libdebug. return true; } void app_quit() { debug_out_info("app", "Saving config before exit...\n"); // save the config #if defined ENABLE_GLIB && ENABLE_GLIB rconfig::autosave_force_now(); #else rconfig::save_to_file(s_home_config_file); #endif // exit the main loop debug_out_info("app", "Trying to exit the main loop...\n"); Gtk::Main::quit(); // don't destroy main window here - we may be in one of its callbacks } /// @} gsmartcontrol-1.1.4/src/gsc_init.h000066400000000000000000000014141417717230600171620ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup gsc /// \weakgroup gsc /// @{ #ifndef GSC_INIT_H #define GSC_INIT_H #include /// Initialize the application and run the main loop bool app_init_and_loop(int& argc, char**& argv); /// Quit the application (exit the main loop) void app_quit(); /// Return everything that went through libdebug's channels. /// Useful for showing logs. /// This should be called from one thread only. std::string app_get_debug_buffer_str(); #endif /// @} gsmartcontrol-1.1.4/src/gsc_main.cpp000066400000000000000000000042201417717230600174740ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup gsc /// \weakgroup gsc /// @{ #include // cerr #include // std::exception, std::set_terminate() #include // EXIT_* #include "hz/hz_config.h" // HAVE_VERBOSE_TERMINATE_HANDLER #include "hz/win32_tools.h" // hz::win32_* #include "gsc_init.h" // app_init_and_loop() /// Application main function int main(int argc, char* argv[]) { // we still leave __GNUC__ for autoconf-less setups. #if defined HAVE_VERBOSE_TERMINATE_HANDLER && HAVE_VERBOSE_TERMINATE_HANDLER // Verbose uncaught exception handler std::set_terminate(__gnu_cxx::__verbose_terminate_handler); #else try { #endif // disable "Send to MS..." dialog box in non-debug builds #if defined _WIN32 && !(defined DEBUG_BUILD && DEBUG_BUILD) SetErrorMode(SEM_FAILCRITICALERRORS); #endif // debug builds already have a console, no need to create one. #if defined _WIN32 && !(defined DEBUG_BUILD && DEBUG_BUILD) // if the console is not open, or unsupported (win2k), use files. if (!hz::win32_redirect_stdio_to_console()) { // redirect stdout/stderr to console (if open and supported) hz::win32_redirect_stdio_to_files(); // redirect stdout/stderr to output files } #endif // initialize stuff and enter the main loop if (!app_init_and_loop(argc, argv)) return EXIT_FAILURE; // print uncaught exceptions for non-gcc-compatible #if !(defined HAVE_VERBOSE_TERMINATE_HANDLER && HAVE_VERBOSE_TERMINATE_HANDLER) } catch(std::exception& e) { // don't use anything other than cerr here, it's the most safe option. std::cerr << "main(): Unhandled exception: " << e.what() << std::endl; return EXIT_FAILURE; } catch(...) { // this guarantees proper unwinding in case of unhandled exceptions (win32 I think) std::cerr << "main(): Unhandled unknown exception." << std::endl; return EXIT_FAILURE; } #endif return EXIT_SUCCESS; } /// @} gsmartcontrol-1.1.4/src/gsc_main_window.cpp000066400000000000000000001377411417717230600211020ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup gsc /// \weakgroup gsc /// @{ #include #include #include "hz/string_algo.h" // string_split #include "hz/fs_file.h" // hz::File #include "hz/debug.h" #include "hz/scoped_ptr.h" #include "hz/launch_url.h" #include "rconfig/rconfig_mini.h" #include "applib/storage_detector.h" #include "applib/smartctl_parser.h" #include "applib/gui_utils.h" // gui_show_error_dialog #include "applib/smartctl_executor.h" // get_smartctl_binary() #include "applib/smartctl_executor_gui.h" #include "applib/app_gtkmm_utils.h" // app_gtkmm_* #include "applib/storage_property_colors.h" // app_property_get_label_highlight_color #include "applib/app_pcrecpp.h" // app_pcre_match #include "gsc_init.h" // app_quit() #include "gsc_about_dialog.h" #include "gsc_info_window.h" #include "gsc_preferences_window.h" #include "gsc_executor_log_window.h" #include "gsc_executor_error_dialog.h" // gsc_executor_error_dialog_show #include "gsc_main_window_iconview.h" #include "gsc_main_window.h" #include "gsc_add_device_window.h" #include "applib/executor_factory.h" // Compiled-in resources GscMainWindow::GscMainWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui) : AppUIResWidget(gtkcobj, ref_ui), iconview(0), action_handling_enabled_(true), name_label(0), health_label(0), family_label(0), scanning_(false) { APP_GTKMM_CONNECT_VIRTUAL(delete_event); // make sure the event handler is called // iconview, gtkuimanager stuff (menus), custom labels create_widgets(); // Size { int def_size_w = 0, def_size_h = 0; rconfig::get_data("gui/main_window/default_size_w", def_size_w); rconfig::get_data("gui/main_window/default_size_h", def_size_h); if (def_size_w > 0 && def_size_h > 0) { set_default_size(def_size_w, def_size_h); } } // show the window first, scan later show(); // Position (after the window has been shown) { int pos_x = 0, pos_y = 0; rconfig::get_data("gui/main_window/default_pos_x", pos_x); rconfig::get_data("gui/main_window/default_pos_y", pos_y); if (pos_x > 0 && pos_y > 0) { // to avoid situations where positions are not supported this->move(pos_x, pos_y); } } while (Gtk::Main::events_pending()) // allow the window to show Gtk::Main::iteration(); // Check if smartctl is executable std::string error_msg; bool show_output_button = true; do { std::string smartctl_binary = get_smartctl_binary(); // Don't use default options here - they are used when invoked // with a device option. // std::string smartctl_def_options; // rconfig::get_data("system/smartctl_options", smartctl_def_options); if (smartctl_binary.empty()) { error_msg = "Smartctl binary is not specified in configuration."; show_output_button = false; break; } // if (!smartctl_def_options.empty()) // smartctl_def_options += " "; SmartctlExecutorGui ex; ex.create_running_dialog(this); ex.set_running_msg("Checking if smartctl is executable..."); // ex.set_command(Glib::shell_quote(smartctl_binary), smartctl_def_options + "-V"); // --version ex.set_command(Glib::shell_quote(smartctl_binary), "-V"); // --version if (!ex.execute() || !ex.get_error_msg().empty()) { error_msg = ex.get_error_msg(); break; } std::string output = ex.get_stdout_str(); if (output.empty()) { error_msg = "Smartctl returned an empty output."; break; } std::string version, version_full; if (!SmartctlParser::parse_version(output, version, version_full)) { error_msg = "Smartctl returned invalid output."; break; } { // We require this version at runtime to support --get=all. const double minimum_req_version = 5.43; double version_double = 0; if (hz::string_is_numeric(version, version_double, false)) { if (version_double < minimum_req_version) { error_msg = "Smartctl version " + version + " found, " + hz::number_to_string(minimum_req_version) + " required."; break; } } } } while (false); bool smartctl_valid = error_msg.empty(); if (!smartctl_valid) { gsc_executor_error_dialog_show("There was an error while executing smartctl", error_msg + "\n\nPlease specify the correct smartctl binary in Preferences.", this, true, show_output_button); } // Scan populate_iconview(smartctl_valid); } void GscMainWindow::obj_destroy() { // This is needed because for some reason, if any icon is selected, // on_iconview_selection_changed() is called even after the window is deleted, // causing crash on exit. iconview->clear_all(); } void GscMainWindow::populate_iconview(bool smartctl_valid) { if (!smartctl_valid) { iconview->set_empty_view_message(GscMainWindowIconView::message_no_smartctl); iconview->clear_all(); // the message won't be shown without invalidating the region. while (Gtk::Main::events_pending()) // give expose event the time it needs Gtk::Main::iteration(); } else if (rconfig::get_data("gui/scan_on_startup") // config option && !rconfig::get_data("/runtime/gui/force_no_scan_on_startup")) { // command-line option rescan_devices(); // scan for devices and fill the iconview } else { iconview->set_empty_view_message(GscMainWindowIconView::message_scan_disabled); iconview->clear_all(); // the message won't be shown without invalidating the region. while (Gtk::Main::events_pending()) // give expose event the time it needs Gtk::Main::iteration(); } // Add command-line-requested devices and virtual drives. if (smartctl_valid) { std::vector load_devices; if (rconfig::get_data("/runtime/gui/add_devices_on_startup", load_devices)) { for (unsigned int i = 0; i < load_devices.size(); ++i) { if (!load_devices[i].empty()) { std::vector parts; hz::string_split(load_devices[i], "::", parts, false); std::string file = (parts.size() > 0 ? parts.at(0) : ""); std::string type_arg = (parts.size() > 1 ? parts.at(1) : ""); std::string extra_args = (parts.size() > 2 ? parts.at(2) : ""); if (!file.empty()) { add_device(file, type_arg, extra_args); } } } } } std::vector load_virtuals; if (rconfig::get_data("/runtime/gui/add_virtuals_on_startup", load_virtuals)) { for (unsigned int i = 0; i < load_virtuals.size(); ++i) { if (!load_virtuals[i].empty()) { add_virtual_drive(load_virtuals[i]); } } } // update the menus (group sensitiveness, etc...) iconview->update_menu_actions(); this->update_status_widgets(); } // pass enum elements here #define APP_ACTION_NAME(a) #a bool GscMainWindow::create_widgets() { // --------------------------------- Icon View get_ui()->get_widget_derived("drive_iconview", iconview); // fill our iconview and do the rest iconview->set_main_window(this); // --------------------------------- Action widgets static Glib::ustring ui_info = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "" "" " " " " " " " " " " " " " " " " " " "" "" " " " " " " ""; // Action groups actiongroup_main = Gtk::ActionGroup::create("main_actions"); actiongroup_device = Gtk::ActionGroup::create("device_actions"); Glib::RefPtr action; // Add actions actiongroup_main->add(Gtk::Action::create("file_menu", "_File")); action = Gtk::Action::create(APP_ACTION_NAME(action_quit), Gtk::Stock::QUIT); actiongroup_main->add((action_map[action_quit] = action), Gtk::AccelKey("Q"), sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_quit)); actiongroup_main->add(Gtk::Action::create("device_menu", "_Device")); action = Gtk::Action::create(APP_ACTION_NAME(action_view_details), Gtk::Stock::INFO, "_View details", "View detailed information"); actiongroup_device->add((action_map[action_view_details] = action), Gtk::AccelKey("V"), sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_view_details)); action = Gtk::ToggleAction::create(APP_ACTION_NAME(action_enable_smart), "Enable S_MART", "Toggle SMART status. The status will be preserved at least until reboot (unless you toggle it again)."); lookup_widget("status_smart_enabled_check")->set_related_action(action); actiongroup_device->add((action_map[action_enable_smart] = action), Gtk::AccelKey("M"), sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_enable_smart)); action = Gtk::ToggleAction::create(APP_ACTION_NAME(action_enable_aodc), "Enable Auto O_ffline Data Collection", "Toggle Automatic Offline Data Collection which will update \"offline\" SMART attributes every four hours"); lookup_widget("status_aodc_enabled_check")->set_related_action(action); actiongroup_device->add((action_map[action_enable_aodc] = action), Gtk::AccelKey("F"), sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_enable_aodc)); action = Gtk::Action::create(APP_ACTION_NAME(action_reread_device_data), Gtk::Stock::REFRESH, "R_e-read Data", "Re-read basic SMART data"); actiongroup_device->add((action_map[action_reread_device_data] = action), Gtk::AccelKey("E"), sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_reread_device_data)); action = Gtk::Action::create(APP_ACTION_NAME(action_perform_tests), "Perform _Tests...", "Perform various self-tests on the drive"); actiongroup_device->add((action_map[action_perform_tests] = action), Gtk::AccelKey("T"), sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_perform_tests)); action = Gtk::Action::create(APP_ACTION_NAME(action_remove_device), Gtk::Stock::REMOVE, "Re_move Added Device", "Remove previously added device"); actiongroup_device->add((action_map[action_remove_device] = action), Gtk::AccelKey("W"), sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_remove_device)); action = Gtk::Action::create(APP_ACTION_NAME(action_remove_virtual_device), Gtk::Stock::REMOVE, "Re_move Virtual Device", "Remove previously loaded virtual device"); actiongroup_device->add((action_map[action_remove_virtual_device] = action), Gtk::AccelKey("Delete"), sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_remove_virtual_device)); // --- action = Gtk::Action::create(APP_ACTION_NAME(action_add_device), Gtk::Stock::OPEN, "_Add Device...", "Manually add device to device list"); actiongroup_main->add((action_map[action_add_device] = action), Gtk::AccelKey("D"), sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_add_device)); action = Gtk::Action::create(APP_ACTION_NAME(action_load_virtual), Gtk::Stock::OPEN, "L_oad Smartctl Output as Virtual Device...", "Load smartctl output from a text file as a read-only virtual device"); actiongroup_main->add((action_map[action_load_virtual] = action), Gtk::AccelKey("O"), sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_load_virtual)); action = Gtk::Action::create(APP_ACTION_NAME(action_rescan_devices), Gtk::Stock::REFRESH, "_Re-scan Device List", "Re-scan device list"); actiongroup_main->add((action_map[action_rescan_devices] = action), Gtk::AccelKey("R"), sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_rescan_devices)); actiongroup_main->add(Gtk::Action::create("options_menu", "_Options")); action = Gtk::Action::create(APP_ACTION_NAME(action_executor_log), "View Execution Log"); actiongroup_main->add((action_map[action_executor_log] = action), sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_executor_log)); action = Gtk::Action::create(APP_ACTION_NAME(action_update_drivedb), "Update Drive Database"); actiongroup_main->add((action_map[action_update_drivedb] = action), sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_update_drivedb)); action = Gtk::Action::create(APP_ACTION_NAME(action_preferences), Gtk::Stock::PREFERENCES); actiongroup_main->add((action_map[action_preferences] = action), Gtk::AccelKey("P"), sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_preferences)); actiongroup_main->add(Gtk::Action::create("help_menu", "_Help")); action = Gtk::Action::create(APP_ACTION_NAME(action_online_documentation), Gtk::Stock::HELP); actiongroup_main->add((action_map[action_online_documentation] = action), Gtk::AccelKey("F1"), sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_online_documentation)); action = Gtk::Action::create(APP_ACTION_NAME(action_support), "Support"); actiongroup_main->add((action_map[action_support] = action), sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_support)); action = Gtk::Action::create(APP_ACTION_NAME(action_about), Gtk::Stock::ABOUT); actiongroup_main->add((action_map[action_about] = action), sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_about)); // create uimanager ui_manager = Gtk::UIManager::create(); ui_manager->insert_action_group(actiongroup_main); ui_manager->insert_action_group(actiongroup_device); // add accelerator group to our window so that they work add_accel_group(ui_manager->get_accel_group()); try { ui_manager->add_ui_from_string(ui_info); } catch(Glib::Error& ex) { debug_out_error("app", DBG_FUNC_MSG << "UI creation failed: " << ex.what() << "\n"); return false; } // add some more accelerators (in addition to existing ones) Gtk::Widget* rescan_item = ui_manager->get_widget("/main_menubar/device_menu/" APP_ACTION_NAME(action_rescan_devices)); if (rescan_item) rescan_item->add_accelerator("activate", get_accel_group(), GDK_KEY_F5, Gdk::ModifierType(0), Gtk::AccelFlags(0)); // look after the created widgets Gtk::Box* menubar_vbox = lookup_widget("menubar_vbox"); Gtk::Widget* menubar = ui_manager->get_widget("/main_menubar"); if (menubar_vbox && menubar) { menubar_vbox->pack_start(*menubar, Gtk::PACK_EXPAND_WIDGET); menubar->set_hexpand(true); // menubar->set_halign(Gtk::ALIGN_START); } // Set tooltips on menu items - gtk does that only on toolbar items. Glib::ustring tooltip_text; std::vector > groups = ui_manager->get_action_groups(); for (unsigned int i = 0; i < groups.size(); ++i) { std::vector > actions = groups[i]->get_actions(); for (unsigned int j = 0; j < actions.size(); ++j) { std::vector widgets = actions[j]->get_proxies(); if (!(tooltip_text = actions[j]->property_tooltip()).empty()) { for (unsigned int k = 0; k < widgets.size(); ++k) { app_gtkmm_set_widget_tooltip(*(widgets[k]), tooltip_text, true); } } } } // ----------------------------------------- Labels // create and add labels Gtk::Box* name_label_box = lookup_widget("status_name_label_hbox"); if (name_label_box) { name_label = Gtk::manage(new Gtk::Label("No drive selected", Gtk::ALIGN_START)); name_label->set_line_wrap(true); name_label->set_selectable(true); name_label->show(); name_label_box->pack_start(*name_label, true, true); } Gtk::Box* health_label_box = lookup_widget("status_health_label_hbox"); if (health_label_box) { health_label = Gtk::manage(new Gtk::Label("No drive selected", Gtk::ALIGN_START)); health_label->set_line_wrap(true); health_label->set_selectable(true); health_label->show(); health_label_box->pack_start(*health_label, true, true); } Gtk::Box* family_label_box = lookup_widget("status_family_label_hbox"); if (family_label_box) { family_label = Gtk::manage(new Gtk::Label("No drive selected", Gtk::ALIGN_START)); family_label->set_line_wrap(true); family_label->set_selectable(true); family_label->show(); family_label_box->pack_start(*family_label, true, true); } return true; } namespace { /// Return true if the user agrees to quit inline bool ask_about_quit_on_test(Gtk::Window& parent) { int status = 0; { Gtk::MessageDialog dialog(parent, "\nOne of the drives is performing a test. Do you really want to quit?\n\n" "The test will continue to run in the background, but you won't be" " able to monitor it using GSmartControl.", true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO, true); status = dialog.run(); } return (status == Gtk::RESPONSE_YES); } } // NOTE: Do NOT bind the Glib::RefPtr parameter to this function. // Doing so causes valgrind errors on window destroy (and Send Report dialogs on win32). // Probably a gtkmm bug. Use action maps or Gtk::Action* instead. void GscMainWindow::on_action_activated(GscMainWindow::action_t action_type) { if (!this->action_handling_enabled_) // check if we should do something return; if (action_map.find(action_type) == action_map.end()) { debug_out_error("app", DBG_FUNC_MSG << "Invalid action activated: " << static_cast(action_type) << ".\n"); return; } Glib::RefPtr action = action_map[action_type]; if (!action) { debug_out_error("app", DBG_FUNC_MSG << "Action is NULL for action type " << static_cast(action_type) << ".\n"); return; } std::string action_name = action->get_name(); // Do NOT output action->get_name() directly, it dies with unhandled conversion error // exception if used with operator <<. debug_out_info("app", DBG_FUNC_MSG << "Action activated: \"" + action_name << "\"\n"); switch (action_type) { case action_quit: quit_requested(); break; case action_view_details: if (iconview) this->show_device_info_window(iconview->get_selected_drive()); break; case action_enable_smart: // this may be invoked on menu manipulation on_action_enable_smart_toggled(hz::down_cast( actiongroup_device->get_action(APP_ACTION_NAME(action_enable_smart)).operator->())); break; case action_enable_aodc: // this may be invoked on menu manipulation on_action_enable_aodc_toggled(hz::down_cast( actiongroup_device->get_action(APP_ACTION_NAME(action_enable_aodc)).operator->())); break; case action_reread_device_data: on_action_reread_device_data(); break; case action_perform_tests: if (iconview) { GscInfoWindow* win = this->show_device_info_window(iconview->get_selected_drive()); if (win) // won't be created if test is already running win->show_tests(); } break; case action_remove_device: if (iconview) { StorageDeviceRefPtr drive = iconview->get_selected_drive(); if (drive && drive->get_is_manually_added() && !drive->get_test_is_active()) iconview->remove_selected_drive(); } break; case action_remove_virtual_device: if (iconview) { StorageDeviceRefPtr drive = iconview->get_selected_drive(); if (drive && drive->get_is_virtual()) iconview->remove_selected_drive(); } break; case action_add_device: this->show_add_device_chooser(); break; case action_load_virtual: this->show_load_virtual_file_chooser(); break; case action_rescan_devices: rescan_devices(); break; case action_executor_log: { // this one will only hide on close. GscExecutorLogWindow* win = GscExecutorLogWindow::create(); // probably already created // win->set_transient_for(*this); // don't do this - it will make it always-on-top of this. win->show_last(); // show the window and select last entry break; } case action_update_drivedb: { run_update_drivedb(); break; } case action_preferences: { GscPreferencesWindow* win = GscPreferencesWindow::create(); // destroyed on close win->set_transient_for(*this); // for "destroy with parent", always-on-top win->set_main_window(this); win->set_modal(true); win->show(); break; } case action_online_documentation: { hz::launch_url(gobj(), "https://gsmartcontrol.sourceforge.io/documentation.html"); break; } case action_support: { hz::launch_url(gobj(), "https://gsmartcontrol.sourceforge.io/support.html"); break; } case action_about: { GscAboutDialog* dialog = GscAboutDialog::create(); // destroyed on close dialog->set_transient_for(*this); // for "destroy with parent" dialog->show(); break; } default: debug_out_error("app", DBG_FUNC_MSG << "Unknown action: \"" << action_name << "\"\n"); break; } } void GscMainWindow::on_action_enable_smart_toggled(Gtk::ToggleAction* action) { if (!action || !iconview) return; if (!action->get_sensitive()) // it's insensitive, nothing to do (this shouldn't happen). return; StorageDeviceRefPtr drive = iconview->get_selected_drive(); // we should be protected from these by disabled actions, but still... if (!drive || drive->get_is_virtual() || drive->get_test_is_active()) return; StorageDevice::status_t status = drive->get_smart_status(); if (status == StorageDevice::status_unsupported) // this shouldn't happen return; bool toggle_active = action->get_active(); if ( (toggle_active && status == StorageDevice::status_disabled) || (!toggle_active && status == StorageDevice::status_enabled) ) { SmartctlExecutorGuiRefPtr ex(new SmartctlExecutorGui()); ex->create_running_dialog(this); std::string error_msg = drive->set_smart_enabled(toggle_active, ex); // run it with GUI support if (!error_msg.empty()) { std::string error_header = (toggle_active ? "Cannot enable SMART" : "Cannot disable SMART"); gsc_executor_error_dialog_show(error_header, error_msg, this); } on_action_reread_device_data(); // reread if changed } } void GscMainWindow::on_action_enable_aodc_toggled(Gtk::ToggleAction* action) { if (!action || !iconview) return; if (!action->get_sensitive()) // it's insensitive, nothing to do (this shouldn't happen). return; StorageDeviceRefPtr drive = iconview->get_selected_drive(); // we should be protected from these by disabled actions, but still... if (!drive || drive->get_is_virtual() || drive->get_test_is_active()) return; StorageDevice::status_t status = drive->get_aodc_status(); if (status == StorageDevice::status_unsupported) // this shouldn't happen return; if (status == StorageDevice::status_unknown) { // it's supported, but we don't know if it's enabled or not. ask the user. int response = 0; { // the dialog hides at the end of scope Gtk::MessageDialog dialog(*this, "\nAutomatic Offline Data Collection status could not be determined.\n" "\nDo you want to enable or disable it?\n", true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_NONE, true); // markup, modal Gtk::Button dismiss_button("Dis_miss", true); dismiss_button.set_image(*Gtk::manage(new Gtk::Image(Gtk::Stock::CANCEL, Gtk::ICON_SIZE_BUTTON))); dismiss_button.show_all(); dialog.add_action_widget(dismiss_button, Gtk::RESPONSE_CANCEL); Gtk::Button disable_button("_Disable", true); disable_button.set_image(*Gtk::manage(new Gtk::Image(Gtk::Stock::NO, Gtk::ICON_SIZE_BUTTON))); disable_button.show_all(); dialog.add_action_widget(disable_button, Gtk::RESPONSE_NO); Gtk::Button enable_button("_Enable", true); enable_button.set_image(*Gtk::manage(new Gtk::Image(Gtk::Stock::YES, Gtk::ICON_SIZE_BUTTON))); enable_button.set_can_default(true); enable_button.show_all(); dialog.add_action_widget(enable_button, Gtk::RESPONSE_YES); enable_button.grab_default(); // make it the default widget dialog.set_position(Gtk::WIN_POS_CENTER_ON_PARENT); response = dialog.run(); // blocks until the dialog is closed } bool enable_aodc = false; switch (response) { case Gtk::RESPONSE_YES: enable_aodc = true; break; case Gtk::RESPONSE_NO: enable_aodc = false; break; case Gtk::RESPONSE_CANCEL: case Gtk::RESPONSE_DELETE_EVENT: // nothing, the dialog is closed already return; default: debug_out_error("app", DBG_FUNC_MSG << "Unknown dialog response code: " << response << ".\n"); return; } SmartctlExecutorGuiRefPtr ex(new SmartctlExecutorGui()); ex->create_running_dialog(this); std::string error_msg = drive->set_aodc_enabled(enable_aodc, ex); // run it with GUI support if (!error_msg.empty()) { std::string error_header = (enable_aodc ? "Cannot enable Automatic Offline Data Collection" : "Cannot disable Automatic Offline Data Collection"); gsc_executor_error_dialog_show(error_header, error_msg, this); } else { // tell the user, because there's no other feedback gui_show_info_dialog((enable_aodc ? "Automatic Offline Data Collection enabled." : "Automatic Offline Data Collection disabled."), this); } return; } bool toggle_active = action->get_active(); if ( (toggle_active && status == StorageDevice::status_disabled) || (!toggle_active && status == StorageDevice::status_enabled) ) { SmartctlExecutorGuiRefPtr ex(new SmartctlExecutorGui()); ex->create_running_dialog(this); std::string error_msg = drive->set_aodc_enabled(toggle_active, ex); // run it with GUI support if (!error_msg.empty()) { std::string error_header = (toggle_active ? "Cannot enable Automatic Offline Data Collection" : "Cannot disable Automatic Offline Data Collection"); gsc_executor_error_dialog_show(error_header, error_msg, this); } on_action_reread_device_data(); // reread if changed } } void GscMainWindow::on_action_reread_device_data() { if (!iconview) return; StorageDeviceRefPtr drive = iconview->get_selected_drive(); if (!drive->get_is_virtual() && !drive->get_test_is_active()) { // disallow on virtual and testing SmartctlExecutorGuiRefPtr ex(new SmartctlExecutorGui()); ex->create_running_dialog(this); // note: this will clear the non-basic properties! std::string error_msg = drive->fetch_basic_data_and_parse(ex); // run it with GUI support // the icon will be updated through drive's signal_changed callback. if (!error_msg.empty()) { gsc_executor_error_dialog_show("Cannot retrieve SMART data", error_msg, this); } } } Gtk::Menu* GscMainWindow::get_popup_menu(StorageDeviceRefPtr drive) { if (!ui_manager) return 0; if (drive) { return hz::down_cast(ui_manager->get_widget("/device_popup")); } return hz::down_cast(ui_manager->get_widget("/empty_area_popup")); } void GscMainWindow::set_drive_menu_status(StorageDeviceRefPtr drive) { // disable any action handling until we're out of here, else we'll get some // bogus toggle actions, etc... this->action_handling_enabled_ = false; do { // for quick skipping // if no drive is selected or if a test is being run on selected drive, disallow. if (!drive || drive->get_test_is_active()) { actiongroup_device->set_sensitive(false); break; // nothing else to do here } // make everything sensitive, then disable one by one actiongroup_device->set_sensitive(true); bool is_virtual = (drive && drive->get_is_virtual()); StorageDevice::status_t smart_status = StorageDevice::status_unsupported; StorageDevice::status_t aodc_status = StorageDevice::status_unsupported; if (drive && !is_virtual) { smart_status = drive->get_smart_status(); aodc_status = drive->get_aodc_status(); } // Sensitivity and visibility manipulation. // Do this first, then do the enable / disable stuff. { Glib::RefPtr action; if ((action = actiongroup_device->get_action(APP_ACTION_NAME(action_perform_tests)))) action->set_sensitive(smart_status == StorageDevice::status_enabled); if ((action = actiongroup_device->get_action(APP_ACTION_NAME(action_reread_device_data)))) action->set_visible(drive && !is_virtual); if ((action = actiongroup_device->get_action(APP_ACTION_NAME(action_remove_device)))) { action->set_visible(drive && drive->get_is_manually_added()); // action->set_sensitive(drive && drive->get_is_manually_added()); } if ((action = actiongroup_device->get_action(APP_ACTION_NAME(action_remove_virtual_device)))) action->set_visible(drive && is_virtual); if ((action = actiongroup_device->get_action(APP_ACTION_NAME(action_enable_smart)))) { action->set_sensitive(smart_status != StorageDevice::status_unsupported); } if ((action = actiongroup_device->get_action(APP_ACTION_NAME(action_enable_aodc)))) action->set_sensitive(aodc_status != StorageDevice::status_unsupported); } // smart toggle status { Gtk::ToggleAction* action = hz::down_cast( actiongroup_device->get_action(APP_ACTION_NAME(action_enable_smart)).operator->()); if (action) { action->set_active(smart_status == StorageDevice::status_enabled); } } // aodc toggle status { Gtk::ToggleAction* action = hz::down_cast( actiongroup_device->get_action(APP_ACTION_NAME(action_enable_aodc)).operator->()); if (action) { Gtk::CheckMenuItem* dev_odc_item = hz::down_cast(ui_manager->get_widget( "/main_menubar/device_menu/" APP_ACTION_NAME(action_enable_aodc))); Gtk::CheckMenuItem* popup_odc_item = hz::down_cast(ui_manager->get_widget( "/device_popup/" APP_ACTION_NAME(action_enable_aodc))); Gtk::CheckButton* status_aodc_check = lookup_widget("status_aodc_enabled_check"); // true if supported, but unknown whether it's enabled or not. if (dev_odc_item) dev_odc_item->set_inconsistent(aodc_status == StorageDevice::status_unknown); if (popup_odc_item) popup_odc_item->set_inconsistent(aodc_status == StorageDevice::status_unknown); if (status_aodc_check) status_aodc_check->set_inconsistent(aodc_status == StorageDevice::status_unknown); // for unknown it doesn't really matter what state it's in. action->set_active(aodc_status == StorageDevice::status_enabled); } } } while (false); // re-enable action handling this->action_handling_enabled_ = true; } // update statusbar with selected drive info void GscMainWindow::update_status_widgets() { if (!iconview) return; // Gtk::Label* name_label = this->lookup_widget("status_name_label"); // Gtk::Label* health_label = this->lookup_widget("status_health_label"); // Gtk::Label* family_label = this->lookup_widget("status_family_label"); // Gtk::Statusbar* statusbar = this->lookup_widget("window_statusbar"); StorageDeviceRefPtr drive = iconview->get_selected_drive(); if (!drive) { if (name_label) name_label->set_text("No drive selected"); if (health_label) health_label->set_text("No drive selected"); if (family_label) family_label->set_text("No drive selected"); // if (statusbar) // statusbar->pop(); return; } std::string device = Glib::Markup::escape_text(drive->get_is_virtual() ? ("Virtual: " + drive->get_virtual_filename()) : drive->get_device_with_type()); std::string size = Glib::Markup::escape_text(drive->get_device_size_str()); std::string model = Glib::Markup::escape_text(drive->get_model_name().empty() ? std::string("Unknown model") : drive->get_model_name()); std::string family = Glib::Markup::escape_text(drive->get_family_name().empty() ? "Unknown" : drive->get_family_name()); std::string family_fallback = Glib::Markup::escape_text(drive->get_family_name().empty() ? model : drive->get_family_name()); std::string drive_letters_str = Glib::Markup::escape_text(drive->format_drive_letters(false)); std::string info_str = device + (drive_letters_str.empty() ? "" : (" (" + drive_letters_str + ")")) + (size.empty() ? "" : (", " + size)) + (model.empty() ? "" : (", " + model)); if (name_label) { name_label->set_markup(info_str); app_gtkmm_set_widget_tooltip(*name_label, info_str, false); // in case it doesn't fit } StorageProperty health_prop = drive->get_health_property(); if (health_label) { if (health_prop.generic_name == "overall_health") { health_label->set_text(health_prop.format_value()); std::string fg; if (app_property_get_label_highlight_color(health_prop.warning, fg)) { health_label->set_markup(""+ health_label->get_text() + ""); } // don't set description tooltip - we already have the basic one. // unless it's failing. // app_gtkmm_set_widget_tooltip(*health_label, health_prop.get_description(), true); if (health_prop.warning != StorageProperty::warning_none) { std::string tooltip_str = storage_property_get_warning_reason(health_prop) + "\n\nView details for more information."; app_gtkmm_set_widget_tooltip(*health_label, tooltip_str, true); } } else { health_label->set_text("Unknown"); } } if (family_label) { family_label->set_text(family); app_gtkmm_set_widget_tooltip(*family_label, family, false); // in case it doesn't fit } // std::string status_str = " " + device + (size.empty() ? "" : (", " + size)) + (family_fallback.empty() ? "" : (", " + family_fallback)); // if (statusbar) { // statusbar->pop(); // statusbar->push(status_str); // } } void GscMainWindow::rescan_devices() { // ignore double-scan (may happen because we use gtk loop iterations here). if (this->scanning_) return; // don't manipulate window sensitiveness here - it breaks things // (cursors, gtk errors pop out, etc...) // if at least one drive is having a test performed, disallow. if (this->testing_active()) { int status = 0; { Gtk::MessageDialog dialog(*this, "\nThis operation may abort any running tests. Do you wish to continue?", true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO, true); status = dialog.run(); } if (status != Gtk::RESPONSE_YES) { return; } } this->scanning_ = true; // std::string match_str = rconfig::get_data("system/device_match_patterns"); std::string blacklist_str = rconfig::get_data("system/device_blacklist_patterns"); // std::vector match_patterns; std::vector blacklist_patterns; // hz::string_split(match_str, ';', match_patterns, true); hz::string_split(blacklist_str, ';', blacklist_patterns, true); iconview->set_empty_view_message(GscMainWindowIconView::message_scanning); iconview->clear_all(); // clear previous icons, invalidate region to update the message. while (Gtk::Main::events_pending()) // give expose event the time it needs Gtk::Main::iteration(); this->drives.clear(); // populate the icon area with drive icons StorageDetector sd; // sd.add_match_patterns(match_patterns); sd.add_blacklist_patterns(blacklist_patterns); ExecutorFactoryRefPtr ex_factory(new ExecutorFactory(true, this)); // run it with GUI support std::string error_msg = sd.detect_and_fetch_basic_data(drives, ex_factory); bool error = false; // Catch permission errors. // executor errors and outputs, not reported through error_msg. std::vector fetch_outputs = sd.get_fetch_data_error_outputs(); for (unsigned int i = 0; i < fetch_outputs.size(); ++i) { // debug_out_error("app", DBG_FUNC_MSG << fetch_outputs[i] << "\n"); if (app_pcre_match("/Smartctl open device.+Permission denied/mi", fetch_outputs[i])) { gsc_executor_error_dialog_show("An error occurred while scanning the system", "It seems that smartctl doesn't have enough permissions to access devices.\n" "See \"Resolving Permission Problems\" in Help menu for possible solutions.", this, true, true); error = true; break; } } if (!error && !error_msg.empty()) { // generic scan error. smartctl errors are not reported during scan at all. // we don't show output button here gsc_executor_error_dialog_show("An error occurred while scanning the system", error_msg, this, false, false); error = true; // add them anyway, in case the error was only on one drive. } else { // if (!error) { // add them to iconview for (unsigned int i = 0; i < drives.size(); ++i) { if (rconfig::get_data("gui/show_smart_capable_only")) { if (drives[i]->get_smart_status() != StorageDevice::status_unsupported) iconview->add_entry(drives[i]); } else { iconview->add_entry(drives[i]); } } } // in case there are no drives in the system. if (iconview->get_num_icons() == 0) iconview->set_empty_view_message(GscMainWindowIconView::message_no_drives_found); this->scanning_ = false; } void GscMainWindow::run_update_drivedb() { std::string smartctl_binary = get_smartctl_binary(); if (smartctl_binary.empty()) { gui_show_error_dialog("Error Updating Drive Database", "Smartctl binary is not specified in configuration.", this); return; } std::string update_binary; hz::FsPath path(smartctl_binary); if (path.is_absolute()) { update_binary = path.get_dirname() + "/"; } update_binary += "update-smart-drivedb"; update_binary = Glib::shell_quote(update_binary); #ifndef _WIN32 update_binary = "xterm -hold -e " + update_binary; #endif hz::scoped_ptr spawn_error(0, g_error_free); if (!g_spawn_command_line_async(update_binary.c_str(), &spawn_error.get_ref())) { gui_show_error_dialog("Error Updating Drive Database", spawn_error->message, this); } } bool GscMainWindow::add_device(const std::string& file, const std::string& type_arg, const std::string& extra_args) { #ifndef _WIN32 // win32 doesn't have device files, so skip the check hz::File f(file); if (!f.exists()) { std::string msg = f.get_error_utf8(); // only errors are reported gui_show_error_dialog("Cannot add device", (msg.empty() ? ("Device \"" + file + "\" doesn't exist.") : msg), this); return false; } #endif StorageDeviceRefPtr d(new StorageDevice(file)); d->set_type_argument(type_arg); d->set_extra_arguments(extra_args); d->set_is_manually_added(true); ExecutorFactoryRefPtr ex_factory(new ExecutorFactory(true, this)); // pass this as dialog parent std::vector tmp_drives; tmp_drives.push_back(d); StorageDetector sd; std::string error_msg = sd.fetch_basic_data(tmp_drives, ex_factory, true); // return its first error if (!error_msg.empty()) { gsc_executor_error_dialog_show("An error occurred while adding the device", error_msg, this); } else { this->drives.push_back(d); this->iconview->add_entry(d, true); // add it, scroll and select it. } return true; } bool GscMainWindow::add_virtual_drive(const std::string& file) { hz::File f(file); std::string output; if (!f.get_contents(output)) { // this will send to debug_ too. gui_show_error_dialog("Cannot load data file", f.get_error_utf8(), this); return false; } // we have to use smart pointers here, because a pointer may be invalidated // on vector reallocation StorageDeviceRefPtr d(new StorageDevice(file, true)); d->set_full_output(output); d->set_info_output(output); // info can be parsed from full output string too. std::string error_msg = d->parse_data(); // this will set the type and add the properties if (!error_msg.empty()) { gui_show_error_dialog("Cannot interpret SMART data", error_msg, this); return false; } this->drives.push_back(d); this->iconview->add_entry(drives.back(), true); // add it, scroll and select it. return true; } bool GscMainWindow::testing_active() const { for (std::vector::const_iterator iter = drives.begin(); iter != drives.end(); ++iter) { if ((*iter) && (*iter)->get_test_is_active()) { return true; } } return false; } GscInfoWindow* GscMainWindow::show_device_info_window(StorageDeviceRefPtr drive) { if (!drive) { return 0; } // if a test is being run on it, disallow. if (drive->get_test_is_active()) { gui_show_warn_dialog("Please wait until the test is finished on this drive.", this); return 0; } // ask to enable SMART if it's supported but disabled if (!drive->get_is_virtual() && (drive->get_smart_status() == StorageDevice::status_disabled)) { int status = 0; // scope hides the dialog, without it two dialogs may be shown (this and) // the error one, and we don't want that. { Gtk::MessageDialog dialog(*this, "\nThis drive has SMART disabled. Do you want to enable it?\n\n" "SMART will stay enabled at least until you reboot your computer.\n" "See \"How to Enable SMART Permanently\" in Help menu for more information.", true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO, true); status = dialog.run(); } if (status == Gtk::RESPONSE_YES) { SmartctlExecutorGuiRefPtr ex(new SmartctlExecutorGui()); ex->create_running_dialog(this, "Running %s on " + drive->get_device_with_type() + "..."); std::string error_msg = drive->set_smart_enabled(true, ex); // run it with GUI support if (!error_msg.empty()) { gsc_executor_error_dialog_show("Cannot enable SMART", error_msg, this); } } } // Virtual drives are parsed at load time. // Parse non-virtual, smart-supporting drives here. if (!drive->get_is_virtual() && drive->get_smart_status() != StorageDevice::status_unsupported) { SmartctlExecutorGuiRefPtr ex(new SmartctlExecutorGui()); ex->create_running_dialog(this, "Running %s on " + drive->get_device_with_type() + "..."); std::string error_msg = drive->fetch_data_and_parse(ex); // run it with GUI support if (!error_msg.empty()) { gsc_executor_error_dialog_show("Cannot retrieve SMART data", error_msg, this); return 0; } } // If the drive output wasn't fully parsed (happens with e.g. scsi and // usb devices), only very basic info is available and there's no point // in showing this window. - for both virtual and non-virtual. if (drive->get_parse_status() == StorageDevice::parse_status_none) { gsc_no_info_dialog_show("No additional information is available for this drive.", "", this, false, drive->get_info_output(), "Smartctl Output", drive->get_save_filename()); return 0; } GscInfoWindow* win = GscInfoWindow::create(); // self-destroyed win->set_drive(drive); win->fill_ui_with_info(false); // already scanned. "refresh" will scan it again in the info window. // win->set_transient_for(*this); // for "destroy with parent", always-on-top win->show(); return win; } void GscMainWindow::show_prefs_updated_message() { iconview->set_empty_view_message(GscMainWindowIconView::message_please_rescan); iconview->clear_all(); // the message won't be shown without invalidating the region. while (Gtk::Main::events_pending()) // give expose event the time it needs Gtk::Main::iteration(); } void GscMainWindow::show_add_device_chooser() { GscAddDeviceWindow* window = GscAddDeviceWindow::create(); window->set_main_window(this); window->set_transient_for(*this); window->show(); } void GscMainWindow::show_load_virtual_file_chooser() { static std::string last_dir; if (last_dir.empty()) { rconfig::get_data("gui/drive_data_open_save_dir", last_dir); } int result = 0; Glib::RefPtr specific_filter = Gtk::FileFilter::create(); specific_filter->set_name("Text Files"); specific_filter->add_pattern("*.txt"); Glib::RefPtr all_filter = Gtk::FileFilter::create(); all_filter->set_name("All Files"); all_filter->add_pattern("*"); #if GTK_CHECK_VERSION(3, 20, 0) hz::scoped_ptr dialog(gtk_file_chooser_native_new( "Load Data From...", this->gobj(), GTK_FILE_CHOOSER_ACTION_OPEN, NULL, NULL), g_object_unref); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog.get()), specific_filter->gobj()); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog.get()), all_filter->gobj()); gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog.get()), true); if (!last_dir.empty()) { gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog.get()), last_dir.c_str()); } result = gtk_native_dialog_run(GTK_NATIVE_DIALOG(dialog.get())); #else Gtk::FileChooserDialog dialog(*this, "Load Data From...", Gtk::FILE_CHOOSER_ACTION_OPEN); // Add response buttons the the dialog dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT); dialog.add_filter(specific_filter); dialog.add_filter(all_filter); dialog.set_select_multiple(true); if (!last_dir.empty()) dialog.set_current_folder(last_dir); // Show the dialog and wait for a user response result = dialog.run(); // the main cycle blocks here #endif // Handle the response switch (result) { case Gtk::RESPONSE_ACCEPT: { std::vector files; #if GTK_CHECK_VERSION(3, 20, 0) hz::scoped_ptr file_slist(gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog.get())), g_slist_free); GSList* iterator = file_slist.get(); while(iterator) { files.push_back(app_ustring_from_gchar((gchar*)iterator->data)); iterator = g_slist_next(iterator); } #else files = dialog.get_filenames(); // in fs encoding #endif if (!files.empty()) { last_dir = hz::path_get_dirname(files.front()); } rconfig::set_data("gui/drive_data_open_save_dir", last_dir); for (size_t i = 0; i < files.size(); ++i) { if (hz::File(files[i]).is_file()) { // file chooser returns selected directories as well, ignore them. this->add_virtual_drive(files[i]); } } break; } case Gtk::RESPONSE_CANCEL: case Gtk::RESPONSE_DELETE_EVENT: // nothing, the dialog is closed already break; default: debug_out_error("app", DBG_FUNC_MSG << "Unknown dialog response code: " << result << ".\n"); break; } } bool GscMainWindow::quit_requested() { // if at least one drive is having a test performed, disallow. if (this->testing_active()) { if (!ask_about_quit_on_test(*this)) { return true; // handled } } // window size / pos { int window_w = 0, window_h = 0; get_size(window_w, window_h); rconfig::set_data("gui/main_window/default_size_w", window_w); rconfig::set_data("gui/main_window/default_size_h", window_h); int pos_x = 0, pos_y = 0; get_position(pos_x, pos_y); rconfig::set_data("gui/main_window/default_pos_x", pos_x); rconfig::set_data("gui/main_window/default_pos_y", pos_y); } app_quit(); // ends the main loop return true; // event handled, don't call default virtual handler } // by default, delete_event calls hide(). bool GscMainWindow::on_delete_event_before(GdkEventAny* e) { return quit_requested(); } /// @} gsmartcontrol-1.1.4/src/gsc_main_window.h000066400000000000000000000112311417717230600205300ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup gsc /// \weakgroup gsc /// @{ #ifndef GSC_MAIN_WINDOW_H #define GSC_MAIN_WINDOW_H #include #include #include "applib/app_ui_res_utils.h" #include "applib/storage_device.h" #include "applib/app_gtkmm_features.h" // APP_GTKMM_CONNECT_VIRTUAL class GscMainWindowIconView; // defined in gsc_main_window_iconview.h class GscInfoWindow; // declared in gsc_info_window.h /// The main window. /// Use create() / destroy() with this class instead of new / delete! class GscMainWindow : public AppUIResWidget { public: friend class GscMainWindowIconView; // It needs our privates // name of ui file without a .ui extension and quotes APP_UI_RES_DATA_INIT(gsc_main_window); /// Constructor, GtkBuilder needs this. GscMainWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui); /// Virtual destructor virtual ~GscMainWindow() { } // Overridden from InstanceManager virtual void obj_destroy(); /// Scan for devices and fill the iconview void rescan_devices(); /// Execute update-smart-drivedb void run_update_drivedb(); /// Manually add device file to icon list bool add_device(const std::string& file, const std::string& type_arg, const std::string& extra_args); /// Read smartctl data from file, add it as a virtual drive to icon list bool add_virtual_drive(const std::string& file); /// If at least one drive is having a test performed, return true. bool testing_active() const; /// Show the info window for the drive GscInfoWindow* show_device_info_window(StorageDeviceRefPtr drive); /// Show "Preferences updated, please rescan" message void show_prefs_updated_message(); protected: /// Action type enum action_t { action_quit, action_view_details, action_enable_smart, action_enable_aodc, action_reread_device_data, action_perform_tests, action_remove_device, action_remove_virtual_device, action_add_device, action_load_virtual, action_rescan_devices, action_executor_log, action_update_drivedb, action_preferences, action_online_documentation, action_support, action_about }; /// Enable/disable items in Drive menu, set toggles in menu items void set_drive_menu_status(StorageDeviceRefPtr drive); /// Get popup menu for a drive Gtk::Menu* get_popup_menu(StorageDeviceRefPtr drive); /// Update status widgets (status area, etc...) void update_status_widgets(); /// Create the widgets - iconview, gtkuimanager stuff (menus), custom labels bool create_widgets(); /// scan and populate iconview widget with drive icons void populate_iconview(bool smartctl_valid); /// Show "Add Device" window void show_add_device_chooser(); /// Show "Load Virtual File" dialog void show_load_virtual_file_chooser(); /// Called when quit has been requested (by delete event or Quit action) bool quit_requested(); // -------------------- callbacks // ---------- overriden virtual methods /// Quit the application on delete event (by default it calls hide()). /// If some test is running, show a question dialog first. /// Reimplemented from Gtk::Window. bool on_delete_event_before(GdkEventAny* e); // ---------- other callbacks // void on_action_activated(Glib::RefPtr action, action_t action_type); /// Action activate callback void on_action_activated(action_t action_type); /// Action callback void on_action_enable_smart_toggled(Gtk::ToggleAction* action); /// Action callback void on_action_enable_aodc_toggled(Gtk::ToggleAction* action); /// Action callback void on_action_reread_device_data(); private: GscMainWindowIconView* iconview; ///< The main icon view, as created by obj_create() std::vector drives; ///< Scanned drives Glib::RefPtr ui_manager; ///< UI manager Glib::RefPtr actiongroup_main; ///< Action group Glib::RefPtr actiongroup_device; ///< Action group bool action_handling_enabled_; ///< Whether action handling is enabled or not std::map > action_map; ///< Used by on_action_activated(). Gtk::Label* name_label; ///< A UI label Gtk::Label* health_label; ///< A UI label Gtk::Label* family_label; ///< A UI label bool scanning_; ///< If the scanning is in process or not }; #endif /// @} gsmartcontrol-1.1.4/src/gsc_main_window_iconview.h000066400000000000000000000470511417717230600224440ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup gsc /// \weakgroup gsc /// @{ #ifndef GSC_MAIN_WINDOW_ICONVIEW_H #define GSC_MAIN_WINDOW_ICONVIEW_H #include #include #include // std::floor #include #include "hz/string_algo.h" // string_join #include "hz/debug.h" #include "hz/data_file.h" // data_file_find #include "applib/app_gtkmm_utils.h" #include "applib/storage_property_colors.h" #include "gsc_main_window.h" #include "rconfig/rconfig_mini.h" /// The icon view of the main window (shows a drive list). /// Note: The IconView must have a fixed icon width set (e.g. in .ui file). /// Otherwise, it doesn't re-compute it when clearing and adding new icons. class GscMainWindowIconView : public Gtk::IconView { public: typedef GscMainWindowIconView self_type; ///< Self type, needed for CONNECT_VIRTUAL /// Message type to show enum message_t { message_none, ///< No message message_scan_disabled, ///< Scanning is disabled message_scanning, ///< Scanning drives... message_no_drives_found, ///< No drives found message_no_smartctl, ///< No smartctl installed message_please_rescan, ///< Re-scan to see the drives }; /// Constructor, GtkBuilder needs this. GscMainWindowIconView(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui) : Gtk::IconView(gtkcobj), num_icons(0), main_window(0), empty_view_message(message_none) { columns.add(col_name); // we can use the col_name variable by value after this. this->set_markup_column(col_name); columns.add(col_description); columns.add(col_pixbuf); #if GTK_CHECK_VERSION(3, 10, 0) // For high quality rendering with GDK_SCALE=2 this->pack_start(cell_renderer_pixbuf, false); this->set_cell_data_func(cell_renderer_pixbuf, sigc::mem_fun(this, &GscMainWindowIconView::on_cell_data_render)); #else this->set_pixbuf_column(col_pixbuf); #endif columns.add(col_drive_ptr); columns.add(col_populated); // create a Tree Model ref_list_model = Gtk::ListStore::create(columns); // ref_list_model->set_sort_column(col_name, Gtk::SORT_ASCENDING); this->set_model(ref_list_model); // we add it here because list model must be created already set_tooltip_column(col_description.index()); // icons Glib::RefPtr default_icon_theme; try { default_icon_theme = Gtk::IconTheme::get_default(); } catch (...) { // nothing } // Adwaita's drive-harddisk icons are really small at 48, so 64 is better. // Plus, it scales well to 128 and 256 (if using GDK_SCALE). const int icon_size = 64; // Try XDG version first #if APP_GTKMM_CHECK_VERSION(3, 10, 0) try { hd_icon = default_icon_theme->load_icon("drive-harddisk", icon_size, get_scale_factor(), Gtk::IconLookupFlags(0)); } catch (...) { } // ignore exceptions #endif if (!hd_icon) { // Before gtk 3.10 it was called gtk-harddisk. try { hd_icon = default_icon_theme->load_icon("gtk-harddisk", icon_size, Gtk::IconLookupFlags(0)); } catch (...) { } // ignore exceptions } if (!hd_icon) { // Still no luck, use bundled ones. std::string icon_file = hz::data_file_find("icon_hdd.png"); if (!icon_file.empty()) { try { hd_icon = Gdk::Pixbuf::create_from_file(icon_file); } catch (...) { } // ignore exceptions } } // Try XDG version first #if APP_GTKMM_CHECK_VERSION(3, 10, 0) try { cddvd_icon = default_icon_theme->load_icon("media-optical", icon_size, get_scale_factor(), Gtk::IconLookupFlags(0)); } catch (...) { } // ignore exceptions #endif if (!cddvd_icon) { // Before gtk 3.10 it was called gtk-cdrom. try { cddvd_icon = default_icon_theme->load_icon("gtk-cdrom", icon_size, Gtk::IconLookupFlags(0)); } catch (...) { } // ignore exceptions } if (!cddvd_icon) { // Still no luck, use bundled ones. std::string icon_file = hz::data_file_find("icon_cddvd.png"); if (!icon_file.empty()) { try { cddvd_icon = Gdk::Pixbuf::create_from_file(icon_file); } catch (...) { } // ignore exceptions } } this->signal_item_activated().connect(sigc::mem_fun(*this, &self_type::on_iconview_item_activated) ); this->signal_selection_changed().connect(sigc::mem_fun(*this, &self_type::on_iconview_selection_changed) ); this->signal_button_press_event().connect(sigc::mem_fun(*this, &self_type::on_iconview_button_press_event) ); } /// Set the parent window void set_main_window(GscMainWindow* w) { main_window = w; } /// Set the message type to display when there are no icons to show void set_empty_view_message(message_t message) { empty_view_message = message; } /// Get the number of icons currently displayed unsigned int get_num_icons() const { return num_icons; } // Overridden from Gtk::Widget bool on_draw(const Cairo::RefPtr& cr) { if (empty_view_message != message_none && this->num_icons == 0) { // no icons std::string msg; switch(empty_view_message) { case message_scan_disabled: msg = "Automatic scanning is disabled.\nPress Ctrl+R to scan manually."; break; case message_scanning: msg = "Scanning system, please wait..."; break; case message_no_drives_found: msg = "No drives found."; break; case message_no_smartctl: msg = "Please specify the correct smartctl binary in\nPreferences and press Ctrl-R to re-scan."; break; case message_please_rescan: msg = "Preferences changed.\nPress Ctrl-R to re-scan."; break; default: msg = "[error - invalid message]"; } Glib::RefPtr layout = this->create_pango_layout(""); layout->set_alignment(Pango::ALIGN_CENTER); layout->set_markup(msg); int layout_w = 0, layout_h = 0; layout->get_pixel_size(layout_w, layout_h); int pos_x = (get_allocation().get_width() - layout_w) / 2; int pos_y = (get_allocation().get_height() - layout_h) / 2; cr->move_to(pos_x, pos_y); layout->show_in_cairo_context(cr); return true; } return Gtk::IconView::on_draw(cr); } #if GTK_CHECK_VERSION(3, 10, 0) /// Cell data renderer (needed for high quality icons in GDK_SCALE=2). /// We have to use Cairo surfaces, because pixbufs are scaled by GtkIconView. void on_cell_data_render(const Gtk::TreeModel::const_iterator& iter) { Gtk::TreeRow row = *iter; if (!row[col_populated]) { // protect against rendering incomplete model entry return; } Glib::RefPtr pixbuf = row[col_pixbuf]; if (!pixbuf) { return; } // Gtkmm property_surface() doesn't work, so use plain C. // https://bugzilla.gnome.org/show_bug.cgi?id=788513 // Also, Gtkmm doesn't have gdk_cairo_surface_create_from_pixbuf() wrapper. // https://bugzilla.gnome.org/show_bug.cgi?id=788533 // Cairo::Format format = Cairo::FORMAT_ARGB32; // if (pixbuf->get_n_channels() == 3) { // format = Cairo::FORMAT_RGB24; // } // Cairo::RefPtr surface = get_window()->create_similar_image_surface( // format, pixbuf->get_width(), pixbuf->get_height(), get_scale_factor()); // cell_renderer_pixbuf.property_surface().set_value(surface); // gdk_cairo_surface_create_from_pixbuf() (and create_similar_image_surface()) from gtk 3.10. cairo_surface_t* surface = gdk_cairo_surface_create_from_pixbuf(pixbuf->gobj(), get_scale_factor(), get_window()->gobj()); g_object_set(G_OBJECT(cell_renderer_pixbuf.gobj()), "surface", surface, NULL); cairo_surface_destroy(surface); } #endif /// Add a drive entry to the icon view void add_entry(StorageDeviceRefPtr drive, bool scroll_to_it = false) { if (!drive) return; Gtk::TreeModel::Row row = *(ref_list_model->append()); row[col_drive_ptr] = drive; this->decorate_entry(row); row[col_populated] = true; // triggers rendering drive->signal_changed.connect(sigc::mem_fun(this, &GscMainWindowIconView::on_drive_changed)); if (scroll_to_it) { Gtk::TreeModel::Path tpath(row); // scroll_to_path() and set/get_cursor() are since gtkmm 2.8. this->scroll_to_path(tpath, true, 0.5, 0.5); // select it (keyboard & selection) Gtk::CellRenderer* cell = 0; if (this->get_cursor(cell) && cell) { this->set_cursor(tpath, *cell, false); } this->select_path(tpath); // highlight it } ++num_icons; } /// Decorate a drive entry (colorize it if it has errors, etc...). /// This should be called to update the icon of already refreshed drive. void decorate_entry(Gtk::TreePath model_path) { if (model_path.empty()) return; Gtk::TreeModel::Row row = *(ref_list_model->get_iter(model_path)); this->decorate_entry(row); } /// Decorate a drive entry (colorize it if it has errors, etc...). /// This should be called to update the icon of already refreshed drive. void decorate_entry(Gtk::TreeModel::Row& row) { StorageDeviceRefPtr drive = row[col_drive_ptr]; if (!drive) { return; } // it needs this space to be symmetric (why?); std::string name; // = "" + drive->get_device_with_type() + " \n"; Glib::ustring drive_letters = Glib::Markup::escape_text(drive->format_drive_letters(false)); if (drive_letters.empty()) { drive_letters = "not mounted"; } Glib::ustring drive_letters_with_volname = Glib::Markup::escape_text(drive->format_drive_letters(true)); if (drive_letters_with_volname.empty()) { drive_letters_with_volname = "not mounted"; } // note: if this wraps, it becomes left-aligned in gtk <= 2.10. name += (drive->get_model_name().empty() ? Glib::ustring("Unknown model") : Glib::Markup::escape_text(drive->get_model_name())); if (rconfig::get_data("gui/icons_show_device_name")) { if (!drive->get_is_virtual()) { name += "\n" + Glib::Markup::escape_text(drive->get_device_with_type()); #ifdef _WIN32 name += " (" + drive_letters + ")"; #endif } else if (!drive->get_virtual_filename().empty()) { name += "\n" + Glib::Markup::escape_text(drive->get_virtual_filename()); } } if (rconfig::get_data("gui/icons_show_serial_number") && !drive->get_serial_number().empty()) { name += "\n" + Glib::Markup::escape_text(drive->get_serial_number()); } StorageProperty scan_time_prop; if (drive->get_is_virtual()) { scan_time_prop = drive->lookup_property("scan_time"); if (!scan_time_prop.value_string.empty()) { name += "\n" + Glib::Markup::escape_text(scan_time_prop.value_string); } } std::vector tooltip_strs; if (drive->get_is_virtual()) { std::string vfile = drive->get_virtual_filename(); tooltip_strs.push_back("Loaded from: " + (vfile.empty() ? "[empty]" : Glib::Markup::escape_text(vfile))); if (!scan_time_prop.value_string.empty()) { tooltip_strs.push_back("Scanned on: " + Glib::Markup::escape_text(scan_time_prop.value_string)); } } else { tooltip_strs.push_back("Device: " + Glib::Markup::escape_text(drive->get_device_with_type()) + ""); } #ifdef _WIN32 tooltip_strs.push_back("Drive letters: " + drive_letters_with_volname + ""); #endif if (!drive->get_serial_number().empty()) { tooltip_strs.push_back("Serial number: " + Glib::Markup::escape_text(drive->get_serial_number()) + ""); } tooltip_strs.push_back("SMART status: " + StorageDevice::get_status_name(drive->get_smart_status(), false) + ""); tooltip_strs.push_back("Automatic Offline Data Collection status: " + StorageDevice::get_status_name(drive->get_aodc_status(), false) + ""); std::string tooltip_str = hz::string_join(tooltip_strs, '\n'); Glib::RefPtr icon; switch(drive->get_detected_type()) { case StorageDevice::detected_type_cddvd: icon = cddvd_icon; break; case StorageDevice::detected_type_unknown: // standard HD icon case StorageDevice::detected_type_invalid: case StorageDevice::detected_type_raid: // TODO a separate icon for this icon = hd_icon; break; } StorageProperty health_prop = drive->get_health_property(); if (health_prop.warning != StorageProperty::warning_none && health_prop.generic_name == "overall_health") { if (icon) { icon = icon->copy(); // work on a copy if (icon->get_colorspace() == Gdk::COLORSPACE_RGB && icon->get_bits_per_sample() == 8) { int n_channels = icon->get_n_channels(); int icon_width = icon->get_width(); int icon_height = icon->get_height(); int rowstride = icon->get_rowstride(); guint8* pixels = icon->get_pixels(); guint8* p = 0; for (int y = 0; y < icon_height; ++y) { for (int x = 0; x < icon_width; ++x) { p = pixels + y * rowstride + x * n_channels; uint8_t avg = static_cast(std::floor((p[0] * 0.30) + (p[1] * 0.59) + (p[2] * 0.11) + 0.001 + 0.5)); p[0] = avg; // R p[1] = 0; // G p[2] = 0; // B } } } } tooltip_str += "\n\n" + storage_property_get_warning_reason(health_prop) + "\n\nView details for more information."; } // we use all these if-s because changing the data (without actually changing it) // sometimes leads to screwed up icons in iconview (blame gtk). if (row.get_value(col_name) != name) row[col_name] = name; if (row.get_value(col_description) != tooltip_str) row[col_description] = tooltip_str; if (row.get_value(col_pixbuf) != icon) row[col_pixbuf] = icon; } /// Remove drive entry void remove_entry(const Gtk::TreePath& model_path) { Gtk::TreeModel::Row row = *(ref_list_model->get_iter(model_path)); ref_list_model->erase(row); } /// Remove selected drive entry void remove_selected_drive() { if (this->get_selected_items().size()) { Gtk::TreePath model_path = *(this->get_selected_items().begin()); this->remove_entry(model_path); } } /// Remove all entries void clear_all() { num_icons = 0; ref_list_model->clear(); // this is needed to update the label from "disabled" to "scanning" if (this->get_realized()) { Gdk::Rectangle rect = this->get_allocation(); Glib::RefPtr win = this->get_window(); win->invalidate_rect(rect, true); // force expose event win->process_updates(false); // update immediately } } /// Get selected drive StorageDeviceRefPtr get_selected_drive() { StorageDeviceRefPtr drive = 0; if (this->get_selected_items().size()) { Gtk::TreePath model_path = *(this->get_selected_items().begin()); Gtk::TreeModel::Row row = *(ref_list_model->get_iter(model_path)); drive = row[col_drive_ptr]; } return drive; } /// Get tree path by a drive Gtk::TreePath get_path_by_drive(StorageDevice* drive) { Gtk::TreeNodeChildren children = ref_list_model->children(); for (Gtk::TreeNodeChildren::const_iterator iter = children.begin(); iter != children.end(); ++iter) { // convert iter to row (iter is row's base, but can we cast it?) Gtk::TreeModel::Row row = *iter; if (drive == row.get_value(col_drive_ptr)) return ref_list_model->get_path(row); } return Gtk::TreePath(); // check with .empty() } /// Update menu actions in the Drives menu void update_menu_actions() { // if there's nothing selected, disable items from "Drives" menu if (!this->get_selected_items().size()) { main_window->set_drive_menu_status(NULL); } else { // enable drives menu, set proper smart toggles Gtk::TreePath model_path = *(this->get_selected_items().begin()); Gtk::TreeModel::Row row = *(ref_list_model->get_iter(model_path)); if (!row[col_populated]) { // protect against using incomplete model entry return; } StorageDeviceRefPtr drive = row[col_drive_ptr]; main_window->set_drive_menu_status(drive); } } /// Callback void on_iconview_item_activated(const Gtk::TreePath& model_path) { debug_out_info("app", DBG_FUNC << "\n"); if (!main_window) return; Gtk::TreeModel::Row row = *(ref_list_model->get_iter(model_path)); if (!row[col_populated]) { // protect against using incomplete model entry return; } StorageDeviceRefPtr drive = row[col_drive_ptr]; main_window->show_device_info_window(drive); } /// Callback void on_iconview_selection_changed() { // Must do it here - if done during menu activation, the actions won't work // properly before that. this->update_menu_actions(); main_window->update_status_widgets(); // status area, etc... } /// Callback bool on_iconview_button_press_event(GdkEventButton* event_button) { // select and show popup menu on right-click if (event_button->type == GDK_BUTTON_PRESS && event_button->button == 3) { StorageDeviceRefPtr drive = 0; // clicked drive (if any) // don't use get_item_at_pos() - it's not available in gtkmm < 2.8. Gtk::TreePath tpath = this->get_path_at_pos(static_cast(event_button->x), static_cast(event_button->y)); // if (this->get_item_at_pos(static_cast(event_button->x), static_cast(event_button->y), model_path)) { if (tpath.gobj() && !tpath.empty()) { // without gobj() check gtkmm 2.6 (but not 2.12) prints lots of errors // move keyboard focus to the icon (just as left-click does) Gtk::CellRenderer* cell = 0; if (this->get_cursor(cell) && cell) { // gtkmm's set_cursor() is undefined (but declared) in 2.8, so use gtk variant. gtk_icon_view_set_cursor(GTK_ICON_VIEW(this->gobj()), tpath.gobj(), cell->gobj(), false); } // select the icon this->select_path(tpath); Gtk::TreeModel::Row row = *(ref_list_model->get_iter(tpath)); drive = row[col_drive_ptr]; } else { this->unselect_all(); // unselect on empty area right-click } if (drive) { Gtk::Menu* menu = main_window->get_popup_menu(drive); if (menu) menu->popup(event_button->button, event_button->time); } return true; // stop handling } // left click and everything else - continue handling. // left click selects the icon by default, and allows "activated" signal on double-click. return false; } /// Callback attached to StorageDevice, updates its view. void on_drive_changed(StorageDevice* drive) { Gtk::TreePath model_path = this->get_path_by_drive(drive); this->decorate_entry(model_path); this->update_menu_actions(); main_window->update_status_widgets(); } private: Gtk::TreeModel::ColumnRecord columns; ///< Model columns Gtk::CellRendererPixbuf cell_renderer_pixbuf; ///< Cell renderer for icons. Gtk::TreeModelColumn col_name; ///< Model column Gtk::TreeModelColumn col_description; ///< Model column Gtk::TreeModelColumn > col_pixbuf; ///< Model column Gtk::TreeModelColumn col_drive_ptr; ///< Model column Gtk::TreeModelColumn col_populated; ///< Model column, indicates whether the model entry has been fully populated. Glib::RefPtr ref_list_model; ///< The icon view model unsigned int num_icons; ///< Track the number of icons, because liststore makes it difficult to count them. // available icons Glib::RefPtr hd_icon; ///< Icon pixbuf Glib::RefPtr cddvd_icon; ///< Icon pixbuf GscMainWindow* main_window; ///< The main window, our parent message_t empty_view_message; ///< Message type to display when not showing any icons }; #endif /// @} gsmartcontrol-1.1.4/src/gsc_preferences_window.cpp000066400000000000000000000514551417717230600224540ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup gsc /// \weakgroup gsc /// @{ #include #include #include // GDK_KEY_Escape #include "hz/fs_path.h" #include "hz/string_sprintf.h" #include "hz/scoped_ptr.h" #include "rconfig/rconfig_mini.h" #include "applib/storage_settings.h" #include "applib/app_gtkmm_utils.h" #include "gsc_main_window.h" #include "gsc_preferences_window.h" /// Device Options tree view of the Preferences window class GscPreferencesDeviceOptionsTreeView : public Gtk::TreeView { public: typedef GscPreferencesDeviceOptionsTreeView self_type; ///< Self type, needed for CONNECT_VIRTUAL /// Constructor, GtkBuilder needs this. GscPreferencesDeviceOptionsTreeView(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui) : Gtk::TreeView(gtkcobj), preferences_window(0) { Gtk::TreeModelColumnRecord model_columns; // Device, Type, [Parameters], [Device Real], [Type Real]. // Device may hold "", while Device Real is "". // Type may hold "", while Type Real is "". model_columns.add(col_device); this->append_column("Device", col_device); this->set_search_column(col_device.index()); model_columns.add(col_type); this->append_column("Type", col_type); model_columns.add(col_parameters); model_columns.add(col_device_real); model_columns.add(col_type_real); // create a TreeModel (ListStore) model = Gtk::ListStore::create(model_columns); model->set_sort_column(col_device, Gtk::SORT_ASCENDING); // default sort this->set_model(model); Glib::RefPtr selection = this->get_selection(); selection->signal_changed().connect(sigc::mem_fun(*this, &self_type::on_selection_changed) ); } /// Set the parent window void set_preferences_window(GscPreferencesWindow* w) { preferences_window = w; } /// Remove selected row void remove_selected_row() { if (this->get_selection()->count_selected_rows()) { Gtk::TreeIter iter = this->get_selection()->get_selected(); model->erase(iter); } } /// Add a new row (for a new device) void add_new_row(const std::string& device, const std::string& type, const std::string& params, bool select = true) { Gtk::TreeRow row = *(model->append()); row[col_device] = (device.empty() ? "" : device); row[col_type] = (type.empty() ? "" : type); row[col_parameters] = params; row[col_device_real] = device; row[col_type_real] = type; if (select) this->get_selection()->select(row); } /// Update selected row device entry void update_selected_row_device(const std::string& device) { if (this->get_selection()->count_selected_rows()) { Gtk::TreeRow row = *(this->get_selection()->get_selected()); row[col_device] = (device.empty() ? "" : device); row[col_device_real] = device; } } /// Update selected row type entry void update_selected_row_type(const std::string& type) { if (this->get_selection()->count_selected_rows()) { Gtk::TreeRow row = *(this->get_selection()->get_selected()); row[col_type] = (type.empty() ? "" : type); row[col_type_real] = type; } } /// Update selected row parameters entry void update_selected_row_params(const std::string& params) { if (this->get_selection()->count_selected_rows()) { Gtk::TreeRow row = *(this->get_selection()->get_selected()); row[col_parameters] = params; } } /// Remove all rows void clear_all() { model->clear(); } /// Check whether there is a row selected bool has_selected_row() { return this->get_selection()->count_selected_rows(); } /// Set the device map (as loaded from config) void set_device_map(const device_option_map_t& devmap) { clear_all(); for (device_option_map_t::const_iterator iter = devmap.begin(); iter != devmap.end(); ++iter) { std::vector parts; hz::string_split(iter->first, "::", parts, 2); std::string dev = (parts.size() > 0 ? parts.at(0) : ""); std::string type = (parts.size() > 1 ? parts.at(1) : ""); std::string params = iter->second; this->add_new_row(dev, type, params, false); } } /// Get the device map (to be saved to config) device_option_map_t get_device_map() { device_option_map_t devmap; Gtk::TreeNodeChildren children = model->children(); for (Gtk::TreeNodeChildren::iterator iter = children.begin(); iter != children.end(); ++iter) { Gtk::TreeModel::Row row = *iter; std::string dev = row.get_value(col_device_real); if (!dev.empty()) { std::string type = row.get_value(col_type_real); if (!type.empty()) { dev += "::" + type; } if (devmap.find(dev) == devmap.end()) { devmap[dev] = row.get_value(col_parameters); } } } return devmap; } /// Selection change callback void on_selection_changed() { std::string dev, type, par; if (this->get_selection()->count_selected_rows()) { Gtk::TreeRow row = *(this->get_selection()->get_selected()); dev = row[col_device_real]; type = row[col_type_real]; par = row[col_parameters]; preferences_window->device_widget_set_remove_possible(true); } else { preferences_window->device_widget_set_remove_possible(false); } preferences_window->update_device_widgets(dev, type, par); } private: Glib::RefPtr model; ///< The list model Gtk::TreeModelColumn col_device; ///< Model column Gtk::TreeModelColumn col_type; ///< Model column Gtk::TreeModelColumn col_parameters; ///< Model column Gtk::TreeModelColumn col_device_real; ///< Model column Gtk::TreeModelColumn col_type_real; ///< Model column GscPreferencesWindow* preferences_window; ///< The parent window }; GscPreferencesWindow::GscPreferencesWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui) : AppUIResWidget(gtkcobj, ref_ui), device_options_treeview(0) { // Connect callbacks APP_GTKMM_CONNECT_VIRTUAL(delete_event); // make sure the event handler is called Gtk::Button* window_cancel_button = 0; APP_UI_RES_AUTO_CONNECT(window_cancel_button, clicked); Gtk::Button* window_ok_button = 0; APP_UI_RES_AUTO_CONNECT(window_ok_button, clicked); Gtk::Button* window_reset_all_button = 0; APP_UI_RES_AUTO_CONNECT(window_reset_all_button, clicked); Glib::ustring smartctl_binary_tooltip = "A path to smartctl binary. If the path is not absolute, the binary will be looked for in user's PATH."; #if defined CONFIG_KERNEL_FAMILY_WINDOWS smartctl_binary_tooltip += Glib::ustring("\n") + "Note: smartctl.exe shows a console during execution, while smartctl-nc.exe (default) doesn't (nc means no-console)."; #endif if (Gtk::Label* smartctl_binary_label = lookup_widget("smartctl_binary_label")) { app_gtkmm_set_widget_tooltip(*smartctl_binary_label, smartctl_binary_tooltip); } if (Gtk::Entry* smartctl_binary_entry = lookup_widget("smartctl_binary_entry")) { app_gtkmm_set_widget_tooltip(*smartctl_binary_entry, smartctl_binary_tooltip); } Gtk::Button* smartctl_binary_browse_button = 0; APP_UI_RES_AUTO_CONNECT(smartctl_binary_browse_button, clicked); Gtk::Button* device_options_add_device_button = 0; APP_UI_RES_AUTO_CONNECT(device_options_add_device_button, clicked); Gtk::Button* device_options_remove_device_button = 0; APP_UI_RES_AUTO_CONNECT(device_options_remove_device_button, clicked); Gtk::Entry* device_options_device_entry = 0; APP_UI_RES_AUTO_CONNECT(device_options_device_entry, changed); Glib::ustring device_options_tooltip = "A device name to match"; #if defined CONFIG_KERNEL_FAMILY_WINDOWS device_options_tooltip = "A device name to match (for example, use \"pd0\" for the first physical drive)"; #elif defined CONFIG_KERNEL_LINUX device_options_tooltip = "A device name to match (for example, /dev/sda or /dev/twa0)"; #endif if (Gtk::Label* device_options_device_label = lookup_widget("device_options_device_label")) { app_gtkmm_set_widget_tooltip(*device_options_device_label, device_options_tooltip); } if (device_options_device_entry) { app_gtkmm_set_widget_tooltip(*device_options_device_entry, device_options_tooltip); } Gtk::Entry* device_options_type_entry = 0; APP_UI_RES_AUTO_CONNECT(device_options_type_entry, changed); Gtk::Entry* device_options_parameter_entry = 0; APP_UI_RES_AUTO_CONNECT(device_options_parameter_entry, changed); // Accelerators Glib::RefPtr accel_group = this->get_accel_group(); if (window_cancel_button) { window_cancel_button->add_accelerator("clicked", accel_group, GDK_KEY_Escape, Gdk::ModifierType(0), Gtk::AccelFlags(0)); } // create Device Options treeview get_ui()->get_widget_derived("device_options_treeview", device_options_treeview); device_options_treeview->set_preferences_window(this); // we can't do this in treeview's constructor, it doesn't know about this window yet. this->device_widget_set_remove_possible(false); // initial state // hide win32-only options for non-win32. #ifndef _WIN32 Gtk::CheckButton* smartctl_search_check = this->lookup_widget("search_in_smartmontools_first_check"); if (smartctl_search_check) smartctl_search_check->hide(); #endif // --------------- import_config(); // show(); } void GscPreferencesWindow::set_main_window(GscMainWindow* window) { main_window_ = window; } void GscPreferencesWindow::update_device_widgets(const std::string& device, const std::string& type, const std::string& params) { Gtk::Entry* entry = 0; if ((entry = this->lookup_widget("device_options_device_entry"))) entry->set_text(device); if ((entry = this->lookup_widget("device_options_type_entry"))) entry->set_text(type); if ((entry = this->lookup_widget("device_options_parameter_entry"))) entry->set_text(params); } void GscPreferencesWindow::device_widget_set_remove_possible(bool b) { Gtk::Button* button = 0; if ((button = this->lookup_widget("device_options_remove_device_button"))) button->set_sensitive(b); } namespace { /// Set configuration in a smart way - don't set the defaults. template inline void prefs_config_set(const std::string& path, const T& data) { T tmp = T(); // we set the data only if one of the following is true: // 1. config node with that path already exists. // 2. data differs from default data. if (rconfig::get_config_data(path, tmp)) { rconfig::set_data(path, data); } else if (rconfig::get_default_data(path, tmp)) { if (tmp != data) rconfig::set_data(path, data); } else { debug_out_error("app", DBG_FUNC_MSG << "Path \"" << path << "\" doesn't exist in config trees.\n"); } } /// Get configuration for \c path template inline bool prefs_config_get(const std::string& path, T& data) { if (rconfig::get_data(path, data)) return true; debug_out_error("app", DBG_FUNC_MSG << "Path \"" << path << "\" doesn't exist in config trees.\n"); return false; } } void GscPreferencesWindow::import_config() { // Clear and fill the entries. Gtk::CheckButton* check = 0; Gtk::Entry* entry = 0; // ------- General tab bool scan_on_startup = 0; if ( prefs_config_get("gui/scan_on_startup", scan_on_startup) && (check = this->lookup_widget("scan_on_startup_check")) ) check->set_active(scan_on_startup); bool show_smart_capable_only = 0; if ( prefs_config_get("gui/show_smart_capable_only", show_smart_capable_only) && (check = this->lookup_widget("show_smart_capable_only_check")) ) check->set_active(show_smart_capable_only); bool icons_show_device_name = 0; if ( prefs_config_get("gui/icons_show_device_name", icons_show_device_name) && (check = this->lookup_widget("show_device_name_under_icon_check")) ) check->set_active(icons_show_device_name); bool icons_show_serial_number = 0; if ( prefs_config_get("gui/icons_show_serial_number", icons_show_serial_number) && (check = this->lookup_widget("show_serial_number_under_icon_check")) ) check->set_active(icons_show_serial_number); bool win32_search_smartctl_in_smartmontools = 0; if ( prefs_config_get("system/win32_search_smartctl_in_smartmontools", win32_search_smartctl_in_smartmontools) && (check = this->lookup_widget("search_in_smartmontools_first_check")) ) check->set_active(win32_search_smartctl_in_smartmontools); std::string smartctl_binary; if ( prefs_config_get("system/smartctl_binary", smartctl_binary) && (entry = this->lookup_widget("smartctl_binary_entry")) ) entry->set_text(smartctl_binary); std::string smartctl_options; if ( prefs_config_get("system/smartctl_options", smartctl_options) && (entry = this->lookup_widget("smartctl_options_entry")) ) entry->set_text(smartctl_options); // ------- Drives tab std::string device_blacklist_patterns; if ( prefs_config_get("system/device_blacklist_patterns", device_blacklist_patterns) && (entry = this->lookup_widget("device_blacklist_patterns_entry")) ) entry->set_text(device_blacklist_patterns); std::string devmap_str; if ( prefs_config_get("system/smartctl_device_options", devmap_str) ) { device_option_map_t devmap = app_unserialize_device_option_map(devmap_str); device_options_treeview->set_device_map(devmap); } } void GscPreferencesWindow::export_config() { // we don't clear config here, it might contain non-dialog options too. Gtk::CheckButton* check = 0; Gtk::Entry* entry = 0; // ------- General tab if ((check = this->lookup_widget("scan_on_startup_check"))) prefs_config_set("gui/scan_on_startup", bool(check->get_active())); if ((check = this->lookup_widget("show_smart_capable_only_check"))) prefs_config_set("gui/show_smart_capable_only", bool(check->get_active())); if ((check = this->lookup_widget("show_device_name_under_icon_check"))) prefs_config_set("gui/icons_show_device_name", bool(check->get_active())); if ((check = this->lookup_widget("show_serial_number_under_icon_check"))) prefs_config_set("gui/icons_show_serial_number", bool(check->get_active())); if ((check = this->lookup_widget("search_in_smartmontools_first_check"))) prefs_config_set("system/win32_search_smartctl_in_smartmontools", bool(check->get_active())); if ((entry = this->lookup_widget("smartctl_binary_entry"))) prefs_config_set("system/smartctl_binary", std::string(entry->get_text())); if ((entry = this->lookup_widget("smartctl_options_entry"))) prefs_config_set("system/smartctl_options", std::string(entry->get_text())); // ------- Drives tab if ((entry = this->lookup_widget("device_blacklist_patterns_entry"))) prefs_config_set("system/device_blacklist_patterns", std::string(entry->get_text())); device_option_map_t devmap = device_options_treeview->get_device_map(); std::string devmap_str = app_serialize_device_option_map(devmap); prefs_config_set("system/smartctl_device_options", devmap_str); } bool GscPreferencesWindow::on_delete_event_before(GdkEventAny* e) { destroy(this); // deletes this object and nullifies instance return true; // event handled, don't call default virtual handler } void GscPreferencesWindow::on_window_cancel_button_clicked() { destroy(this); } void GscPreferencesWindow::on_window_ok_button_clicked() { // Check if device map contains drives with empty device names or parameters. device_option_map_t devmap = device_options_treeview->get_device_map(); bool contains_empty = false; for (device_option_map_t::const_iterator iter = devmap.begin(); iter != devmap.end(); ++iter) { if (iter->first.empty() || iter->second.empty()) { contains_empty = true; } } if (contains_empty) { Gtk::MessageDialog dialog(*this, "You have specified an empty Parameters field for one or more entries" " in Per-Drive Smartctl Parameters section. Such entries will be discarded.\n" "\nDo you want to continue?", true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO, true); if (dialog.run() != Gtk::RESPONSE_YES) { return; } } export_config(); if (main_window_) { main_window_->show_prefs_updated_message(); } destroy(this); } void GscPreferencesWindow::on_window_reset_all_button_clicked() { Gtk::MessageDialog dialog(*this, "\nAre you sure you want to reset all program settings to their defaults?\n", true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO, true); if (dialog.run() == Gtk::RESPONSE_YES) { rconfig::clear_config_all(); import_config(); // close the window, because the user might get the impression that "Cancel" will revert. destroy(this); } } void GscPreferencesWindow::on_smartctl_binary_browse_button_clicked() { std::string default_file; Gtk::Entry* entry = this->lookup_widget("smartctl_binary_entry"); if (!entry) return; hz::FsPath path(entry->get_text()); int result = 0; #ifdef _WIN32 Glib::RefPtr specific_filter = Gtk::FileFilter::create(); specific_filter->set_name("Executable Files"); specific_filter->add_pattern("*.exe"); Glib::RefPtr all_filter = Gtk::FileFilter::create(); all_filter->set_name("All Files"); all_filter->add_pattern("*"); #endif #if GTK_CHECK_VERSION(3, 20, 0) hz::scoped_ptr dialog(gtk_file_chooser_native_new( "Choose Smartctl Binary...", this->gobj(), GTK_FILE_CHOOSER_ACTION_OPEN, NULL, NULL), g_object_unref); if (path.is_absolute()) gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog.get()), path.c_str()); #ifdef _WIN32 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog.get()), specific_filter->gobj()); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog.get()), all_filter->gobj()); #endif result = gtk_native_dialog_run(GTK_NATIVE_DIALOG(dialog.get())); #else Gtk::FileChooserDialog dialog(*this, "Choose Smartctl Binary...", Gtk::FILE_CHOOSER_ACTION_OPEN); // Add response buttons the the dialog dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT); #ifdef _WIN32 dialog.add_filter(specific_filter); dialog.add_filter(all_filter); #endif // Note: This works on absolute paths only (otherwise it's gtk warning). if (path.is_absolute()) dialog.set_filename(path.str()); // change to its dir and select it if exists. // Show the dialog and wait for a user response result = dialog.run(); // the main cycle blocks here #endif // Handle the response switch (result) { case Gtk::RESPONSE_ACCEPT: { Glib::ustring file; #if GTK_CHECK_VERSION(3, 20, 0) file = app_ustring_from_gchar(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog.get()))); #else file = dialog.get_filename(); // in fs encoding #endif entry->set_text(file); break; } case Gtk::RESPONSE_CANCEL: case Gtk::RESPONSE_DELETE_EVENT: // nothing, the dialog is closed already break; default: debug_out_error("app", DBG_FUNC_MSG << "Unknown dialog response code: " << result << ".\n"); break; } } void GscPreferencesWindow::on_device_options_remove_device_button_clicked() { device_options_treeview->remove_selected_row(); } void GscPreferencesWindow::on_device_options_add_device_button_clicked() { Gtk::Entry* entry = 0; std::string dev, type, par; if ((entry = this->lookup_widget("device_options_device_entry"))) dev = entry->get_text(); if ((entry = this->lookup_widget("device_options_type_entry"))) type = entry->get_text(); if ((entry = this->lookup_widget("device_options_parameter_entry"))) par = entry->get_text(); if (device_options_treeview->has_selected_row()) { device_options_treeview->add_new_row("", "", ""); // without this it would clone the existing. } else { device_options_treeview->add_new_row(dev, type, par); } } void GscPreferencesWindow::on_device_options_device_entry_changed() { Gtk::Entry* entry = 0; if ((entry = this->lookup_widget("device_options_device_entry"))) device_options_treeview->update_selected_row_device(entry->get_text()); } void GscPreferencesWindow::on_device_options_type_entry_changed() { Gtk::Entry* entry = 0; if ((entry = this->lookup_widget("device_options_type_entry"))) device_options_treeview->update_selected_row_type(entry->get_text()); } void GscPreferencesWindow::on_device_options_parameter_entry_changed() { Gtk::Entry* entry = 0; if ((entry = this->lookup_widget("device_options_parameter_entry"))) device_options_treeview->update_selected_row_params(entry->get_text()); } /// @} gsmartcontrol-1.1.4/src/gsc_preferences_window.h000066400000000000000000000054111417717230600221100ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup gsc /// \weakgroup gsc /// @{ #ifndef GSC_PREFERENCES_WINDOW_H #define GSC_PREFERENCES_WINDOW_H #include #include "applib/app_ui_res_utils.h" class GscPreferencesDeviceOptionsTreeView; // defined in cpp file class GscMainWindow; // declared in gsc_main_window.h /// The Preferences window. /// Use create() / destroy() with this class instead of new / delete! class GscPreferencesWindow : public AppUIResWidget { public: // name of ui file without a .ui extension and quotes APP_UI_RES_DATA_INIT(gsc_preferences_window); /// Constructor, GtkBuilder needs this. GscPreferencesWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui); /// Virtual destructor virtual ~GscPreferencesWindow() { } /// Set main window so that we can manipulate it void set_main_window(GscMainWindow* window); /// Update the device widgets (per-device parameters), used by GscPreferencesDeviceOptionsTreeView. void update_device_widgets(const std::string& device, const std::string& type, const std::string& params); /// Set whether to allow removing device entries, used by GscPreferencesDeviceOptionsTreeView. void device_widget_set_remove_possible(bool b); protected: /// Import the configuration into UI void import_config(); /// Export the configuration from UI void export_config(); // -------------------- callbacks // ---------- overriden virtual methods /// Destroy this object on delete event (by default it calls hide()). /// Reimplemented from Gtk::Window. bool on_delete_event_before(GdkEventAny* e); // ---------- other callbacks /// Button click callback void on_window_cancel_button_clicked(); /// Button click callback void on_window_ok_button_clicked(); /// Button click callback void on_window_reset_all_button_clicked(); /// Button click callback void on_smartctl_binary_browse_button_clicked(); /// Button click callback void on_device_options_remove_device_button_clicked(); /// Button click callback void on_device_options_add_device_button_clicked(); /// Callback void on_device_options_device_entry_changed(); /// Callback void on_device_options_type_entry_changed(); /// Callback void on_device_options_parameter_entry_changed(); private: GscMainWindow* main_window_; ///< Main window that called us. GscPreferencesDeviceOptionsTreeView* device_options_treeview; ///< Device options tree view }; #endif /// @} gsmartcontrol-1.1.4/src/gsc_settings.h000066400000000000000000000137311417717230600200640ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup gsc /// \weakgroup gsc /// @{ #ifndef GSC_SETTINGS_H #define GSC_SETTINGS_H #include "rconfig/rconfig_mini.h" #include "hz/cstdint.h" /// Initializes ALL default settings. /// Absolute paths go to root node, relative ones go to /config and /default. /// Note: There must be no degradation if /config is removed entirely /// during runtime. /default must provide every path which /config could /// have held. /// ALL runtime (that is, non-config-file-writable) settings go to /runtime. inline void init_default_settings() { // Populate /default rconfig::set_default_data("system/config_autosave_timeout", uint32_t(3*60)); // 3 minutes. 0 to disable. rconfig::set_default_data("system/first_boot", true); // used to show the first-start warning. #ifndef _WIN32 rconfig::set_default_data("system/smartctl_binary", "smartctl"); // must be in PATH or use absolute path. rconfig::set_default_data("system/tw_cli_binary", "tw_cli"); // must be in PATH or use absolute path. #else rconfig::set_default_data("system/smartctl_binary", "smartctl-nc.exe"); // use no-console version by default. rconfig::set_default_data("system/tw_cli_binary", "tw_cli.exe"); rconfig::set_default_data("system/areca_cli_binary", "cli.exe"); // if relative, an installation path is prepended (if found). #endif // search for "smartctl-nc.exe" in smartmontools installation first. rconfig::set_default_data("system/win32_search_smartctl_in_smartmontools", true); rconfig::set_default_data("system/win32_smartmontools_regpath", "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\smartmontools"); // in HKLM rconfig::set_default_data("system/win32_smartmontools_regpath_wow", "SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\smartmontools"); // in HKLM rconfig::set_default_data("system/win32_smartmontools_regkey", "InstallLocation"); rconfig::set_default_data("system/win32_smartmontools_smartctl_binary", "bin\\smartctl-nc.exe"); // relative to smt install path rconfig::set_default_data("system/win32_areca_scan_controllers", int32_t(2)); // 0 - no, 1 - yes, 2 - auto (if areca tools are found) rconfig::set_default_data("system/win32_areca_use_cli", int32_t(2)); // 0 - no, 1 - yes, 2 - auto (if it's found) rconfig::set_default_data("system/win32_areca_max_controllers", int32_t(4)); // Maximum number of areca controllers (a safety measure). CLI supports 4. rconfig::set_default_data("system/win32_areca_enc_max_scan_port", int32_t(36)); // 1-128 (areca with enclosures). The last RAID port to scan if no other method is available rconfig::set_default_data("system/win32_areca_enc_max_enclosure", int32_t(3)); // 1-8 (areca with enclosures). The last RAID enclosure to scan if no other method is available rconfig::set_default_data("system/win32_areca_neonc_max_scan_port", int32_t(24)); // 1-24 (areca without enclosures). The last RAID port to scan if no other method is available rconfig::set_default_data("system/smartctl_options", ""); // default options on ALL commands rconfig::set_default_data("system/smartctl_device_options", ""); // dev1:val1;dev2:val2;... format, each bin2ascii-encoded. rconfig::set_default_data("system/linux_udev_byid_path", "/dev/disk/by-id"); // linux hard disk device links here rconfig::set_default_data("system/linux_proc_partitions_path", "/proc/partitions"); // file in linux /proc/partitions format rconfig::set_default_data("system/linux_proc_devices_path", "/proc/devices"); // file in linux /proc/devices format rconfig::set_default_data("system/linux_proc_scsi_scsi_path", "/proc/scsi/scsi"); // file in linux /proc/scsi/scsi format rconfig::set_default_data("system/linux_proc_scsi_sg_devices_path", "/proc/scsi/sg/devices"); // file in linux /proc/scsi/sg/devices format rconfig::set_default_data("system/linux_3ware_max_scan_port", int32_t(23)); // 0-127 (3ware). The last RAID port to scan if no other method is available rconfig::set_default_data("system/linux_areca_enc_max_scan_port", int32_t(36)); // 1-128 (areca with enclosures). The last RAID port to scan if no other method is available rconfig::set_default_data("system/linux_areca_enc_max_enclosure", int32_t(4)); // 1-8 (areca with enclosures). The last RAID enclosure to scan if no other method is available rconfig::set_default_data("system/linux_areca_neonc_max_scan_port", int32_t(24)); // 1-24 (areca without enclosures). The last RAID port to scan if no other method is available rconfig::set_default_data("system/solaris_dev_path", "/dev/rdsk"); // path to /dev/rdsk for solaris. rconfig::set_default_data("system/unix_sdev_path", "/dev"); // path to /dev. used by other unices // rconfig::set_default_data("system/device_match_patterns", ""); // semicolon-separated PCRE patterns rconfig::set_default_data("system/device_blacklist_patterns", ""); // semicolon-separated PCRE patterns rconfig::set_default_data("gui/show_smart_capable_only", false); // show smart-capable drives only rconfig::set_default_data("gui/scan_on_startup", true); // scan drives on startup rconfig::set_default_data("gui/smartctl_output_filename_format", "{model}_{serial}_{date}.txt"); // when suggesting filename rconfig::set_default_data("gui/icons_show_device_name", false); // text under icons rconfig::set_default_data("gui/icons_show_serial_number", false); // text under icons // Populate /runtime too, just in case. The values don't really matter. rconfig::set_data("/runtime/gui/hide_tabs_on_smart_disabled", true); rconfig::set_data("/runtime/gui/force_no_scan_on_startup", false); // rconfig::set_data("/runtime/gui/add_virtuals_on_startup", ""); // vector // rconfig::set_data("/runtime/gui/add_devices_on_startup", ""); // vector } #endif /// @} gsmartcontrol-1.1.4/src/gsc_text_window.h000066400000000000000000000164161417717230600206020ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup gsc /// \weakgroup gsc /// @{ #ifndef GSC_TEXT_WINDOW_H #define GSC_TEXT_WINDOW_H #include #include // GDK_KEY_Escape #include "hz/debug.h" #include "hz/fs_file.h" #include "hz/scoped_ptr.h" #include "rconfig/rconfig_mini.h" #include "applib/app_gtkmm_features.h" #include "applib/app_ui_res_utils.h" #include "applib/app_gtkmm_utils.h" /// GscTextWindow InstanceSwitch parameter for smartctl output instance. struct SmartctlOutputInstance { static const bool multi_instance = true; }; /// A generic text-displaying window. /// Use create() / destroy() with this class instead of new / delete! /// \tparam InstanceSwitch e.g. supply 3 different classes to use 3 different single-instance windows. template class GscTextWindow : public AppUIResWidget, InstanceSwitch::multi_instance> { public: // name of ui file without a .ui extension and quotes APP_UI_RES_DATA_INIT(gsc_text_window); /// Self type, needed for GtkBuilder, not inherited from parent because of templates typedef GscTextWindow self_type; /// Constructor, GtkBuilder needs this. GscTextWindow(typename Gtk::Window::BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui) : AppUIResWidget, InstanceSwitch::multi_instance>(gtkcobj, ref_ui) { // Connect callbacks APP_GTKMM_CONNECT_VIRTUAL(delete_event); // make sure the event handler is called Gtk::Button* save_as_button = 0; APP_UI_RES_AUTO_CONNECT(save_as_button, clicked); Gtk::Button* close_window_button = 0; APP_UI_RES_AUTO_CONNECT(close_window_button, clicked); // Accelerators Glib::RefPtr accel_group = this->get_accel_group(); if (close_window_button) { close_window_button->add_accelerator("clicked", accel_group, GDK_KEY_Escape, Gdk::ModifierType(0), Gtk::AccelFlags(0)); } // show(); // better show later, after set_text(). default_title_ = this->get_title(); } /// Virtual destructor virtual ~GscTextWindow() { } /// Set the text to display void set_text(const Glib::ustring& title, const Glib::ustring& contents, bool save_visible = false, bool use_monospace = false) { this->set_title(title + " - " + default_title_); // something - gsmartcontrol this->contents_ = contents; // we save it to prevent its mangling through the widget Gtk::TextView* textview = this->template lookup_widget("main_textview"); if (textview) { Glib::RefPtr buffer = textview->get_buffer(); buffer->set_text(app_output_make_valid(contents)); if (use_monospace) { Glib::RefPtr tag = buffer->create_tag(); tag->property_family() = "Monospace"; buffer->apply_tag(tag, buffer->begin(), buffer->end()); } } Gtk::Button* save_as_button = this->template lookup_widget("save_as_button"); if (save_as_button) { if (save_visible) { save_as_button->set_sensitive(true); save_as_button->show(); } else { save_as_button->hide(); save_as_button->set_sensitive(false); // disable accelerators } } } /// Set the default file name to be shown on Save As void set_save_filename(const std::string& filename) { save_filename_ = filename; } protected: // -------------------- callbacks // ---------- overriden virtual methods /// Destroy this object on delete event (by default it calls hide()). /// Reimplemented from Gtk::Window. bool on_delete_event_before(GdkEventAny* e) { this->destroy(this); // deletes this object and nullifies instance return true; // event handled, don't call default virtual handler } // ---------- other callbacks /// Button click callback void on_save_as_button_clicked() { static std::string last_dir; if (last_dir.empty()) { rconfig::get_data("gui/drive_data_open_save_dir", last_dir); } int result = 0; Glib::RefPtr specific_filter = Gtk::FileFilter::create(); specific_filter->set_name("Text Files"); specific_filter->add_pattern("*.txt"); Glib::RefPtr all_filter = Gtk::FileFilter::create(); all_filter->set_name("All Files"); all_filter->add_pattern("*"); #if GTK_CHECK_VERSION(3, 20, 0) hz::scoped_ptr dialog(gtk_file_chooser_native_new( "Save Data As...", this->gobj(), GTK_FILE_CHOOSER_ACTION_SAVE, NULL, NULL), g_object_unref); gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog.get()), true); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog.get()), specific_filter->gobj()); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog.get()), all_filter->gobj()); if (!last_dir.empty()) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog.get()), last_dir.c_str()); if (!save_filename_.empty()) gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog.get()), save_filename_.c_str()); result = gtk_native_dialog_run(GTK_NATIVE_DIALOG(dialog.get())); #else Gtk::FileChooserDialog dialog(*this, "Save Data As...", Gtk::FILE_CHOOSER_ACTION_SAVE); // Add response buttons the the dialog dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT); dialog.set_do_overwrite_confirmation(true); dialog.add_filter(specific_filter); dialog.add_filter(all_filter); if (!last_dir.empty()) dialog.set_current_folder(last_dir); if (!save_filename_.empty()) dialog.set_current_name(save_filename_); // Show the dialog and wait for a user response result = dialog.run(); // the main cycle blocks here #endif // Handle the response switch (result) { case Gtk::RESPONSE_ACCEPT: { std::string file; #if GTK_CHECK_VERSION(3, 20, 0) file = app_ustring_from_gchar(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog.get()))); last_dir = hz::path_get_dirname(file); #else file = dialog.get_filename(); // in fs encoding last_dir = dialog.get_current_folder(); // save for the future #endif rconfig::set_data("gui/drive_data_open_save_dir", last_dir); if (file.rfind(".txt") != (file.size() - std::strlen(".txt"))) { file += ".txt"; } hz::File f(file); if (!f.put_contents(this->contents_)) { // this will send to debug_ too. gui_show_error_dialog("Cannot save data to file", f.get_error_utf8(), this); } break; } case Gtk::RESPONSE_CANCEL: case Gtk::RESPONSE_DELETE_EVENT: // nothing, the dialog is closed already break; default: debug_out_error("app", DBG_FUNC_MSG << "Unknown dialog response code: " << result << ".\n"); break; } } /// Button click callback void on_close_window_button_clicked() { this->destroy(this); } private: Glib::ustring default_title_; ///< Window title Glib::ustring contents_; ///< The text to display std::string save_filename_; ///< Default filename for Save As }; #endif /// @} gsmartcontrol-1.1.4/src/gsc_winres.rc.in000066400000000000000000000015101417717230600203050ustar00rootroot00000000000000 gsmartcontrol ICON "gsmartcontrol.ico" // Embed manifest in exe 1 24 "gsmartcontrol.exe.manifest" 1 VERSIONINFO FILEVERSION 0,0,0,0 PRODUCTVERSION 0,0,0,0 // FILEOS VOS__WINDOWS32 // FILETYPE VFT_FONT BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "04090025" BEGIN VALUE "FileDescription", "GSmartControl - Hard disk drive and SSD health inspection tool" VALUE "FileVersion", "@VERSION@" VALUE "InternalName", "gsmartcontrol.exe" VALUE "LegalCopyright", "Copyright (C) 2008 - 2022 Alexander Shaduri" VALUE "OriginalFilename", "gsmartcontrol.exe" VALUE "ProductName", "GSmartControl" VALUE "ProductVersion", "@VERSION@" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 1033, 37 END END gsmartcontrol-1.1.4/src/gsmartcontrol.exe.manifest.in000066400000000000000000000022161417717230600230260ustar00rootroot00000000000000 GSmartControl - Hard disk drive and SSD health inspection tool true gsmartcontrol-1.1.4/src/hz/000077500000000000000000000000001417717230600156335ustar00rootroot00000000000000gsmartcontrol-1.1.4/src/hz/Makefile.am000066400000000000000000000042001417717230600176630ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) METASOURCES = AUTO noinst_HEADERS = any_convert.h any_type.h any_type_holder.h ascii.h \ bad_cast_exception.h bin2ascii_encoder.h common_types.h cstdint.h cstdint_impl_msvc.h \ data_file.h debug.h down_cast.h env_tools.h errno_string.h error.h error_holder.h \ exceptions.h format_unit.h fs_common.h fs_dir.h fs_dir_platform.h fs_error_holder.h \ fs_file.h fs_path.h fs_path_utils.h fs_tools.h hz_config.h i18n.h \ instance_manager.h intrusive_ptr.h intrusive_scoped_lock.h launch_url.h local_algo.h \ locale_tools.h noncopyable.h optional_value.h portable_snprintf.h process_signal.h \ ptr_container.h res_data.h scoped_array.h scoped_ptr.h static_assert.h stream_cast.h \ string_algo.h string_num.h string_sprintf.h string_sprintf_macros.h string_wcmatch.h sync.h \ sync_lock_ptr.h sync_multilock.h sync_part_get_policy.h sync_policy_boost.h \ sync_policy_glib.h sync_policy_glibmm.h sync_policy_poco.h sync_policy_pthread.h \ sync_policy_win32.h system_specific.h tls.h tls_policy_boost.h tls_policy_glib.h \ tls_policy_pthread.h tls_policy_win32.h type_categories.h type_properties.h win32_tools.h any_type_test_SOURCES = any_type_test.cpp bin2ascii_encoder_test_SOURCES = bin2ascii_encoder_test.cpp down_cast_test_SOURCES = down_cast_test.cpp errno_string_test_SOURCES = errno_string_test.cpp format_unit_test_SOURCES = format_unit_test.cpp fs_dir_test_SOURCES = fs_dir_test.cpp fs_file_test_SOURCES = fs_file_test.cpp fs_path_test_SOURCES = fs_path_test.cpp string_algo_test_SOURCES = string_algo_test.cpp string_num_test_SOURCES = string_num_test.cpp string_wcmatch_test_SOURCES = string_wcmatch_test.cpp sync_test_SOURCES = sync_test.cpp sync_multilock_test_SOURCES = sync_multilock_test.cpp tls_test_SOURCES = tls_test.cpp # we don't list them in a separate variable because otherwise kdevelop won't see them. noinst_PROGRAMS = if ENABLE_TESTS noinst_PROGRAMS += any_type_test bin2ascii_encoder_test down_cast_test errno_string_test \ format_unit_test fs_dir_test fs_file_test fs_path_test string_algo_test string_num_test \ string_wcmatch_test sync_test sync_multilock_test tls_test endif tests: $(noinst_PROGRAMS) gsmartcontrol-1.1.4/src/hz/any_convert.h000066400000000000000000000141301417717230600203320ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_ANY_CONVERT_H #define HZ_ANY_CONVERT_H #include "hz_config.h" // feature macros #include #include "string_num.h" // string_is_numeric(), number_to_string() #include "type_categories.h" // type_check_category /// \file /// Loose conversions: /// Try to convert any type to any other type as long as specialization /// for any_convert is defined. /// String conversions to numbers are _not_ strictly checked. /// Numeric types are converted by static_cast between each other. namespace hz { /// This can be used for inheritance to provide a const static \c value member /// that is either true or false. template struct AnyConvertibleValue { static const bool value = v; }; /// Convert \c from to \c to using loose conversion methods. template inline bool any_convert(From from, To& to) { return false; } /// Convert \c from to \c to using loose conversion methods. /// This is a same-type specialization. template inline bool any_convert(From from, From& to) { to = from; return true; } /// The structure that says whether type \c From is convertible to type \c To /// using loose conversion methods. /// This is the master struct, defaults to non-convertible. template struct any_convertible : public AnyConvertibleValue { }; /// The structure that says whether type \c From is convertible to type \c To /// using loose conversion methods. // Same-type specialization template struct any_convertible : public AnyConvertibleValue { }; // specific type specs - see below. /// Define \c any_convertible specialization for \c from_type and \c to_type, saying that /// they're convertible. Also print \c any_convert function header. #define DEFINE_ANY_CONVERT_SPEC(from_type, to_type) \ template<> \ struct any_convertible : public AnyConvertibleValue { }; \ \ template<> \ inline bool any_convert(from_type from, to_type& to) \ /// Define \c any_convertible and \c any_convert specializations using static_cast. #define DEFINE_ANY_CONVERT_SPEC_STATIC(from_type, to_type) \ DEFINE_ANY_CONVERT_SPEC(from_type, to_type) \ { \ to = static_cast(from); \ return true; \ } /// Define \c any_convertible and \c any_convert specializations using static_cast using bool. #define DEFINE_ANY_CONVERT_SPEC_STATIC_TOBOOL(from_type) \ DEFINE_ANY_CONVERT_SPEC(from_type, bool) \ { \ to = static_cast(static_cast(from)); /* this disables gcc warning about unsafe != */ \ return true; \ } /// Define \c any_convertible and \c any_convert specializations using hz::number_to_string(). #define DEFINE_ANY_CONVERT_SPEC_NUMTOSTRING(from_type) \ DEFINE_ANY_CONVERT_SPEC(from_type, std::string) \ { \ to = hz::number_to_string(from); \ return true; \ } /// Define \c any_convertible and \c any_convert specializations using hz::string_is_numeric(). #define DEFINE_ANY_CONVERT_SPEC_STRINGTONUM(to_type) \ DEFINE_ANY_CONVERT_SPEC(std::string, to_type) \ { \ return hz::string_is_numeric(from, to, false); \ } \ DEFINE_ANY_CONVERT_SPEC(const std::string&, to_type) \ { \ return hz::string_is_numeric(from, to, false); \ } #if !(defined DISABLE_LL_INT && DISABLE_LL_INT) #define DEFINE_ANY_CONVERT_SPEC_STATIC_LLI(from_type) \ DEFINE_ANY_CONVERT_SPEC_STATIC(from_type, long long int) #else #define DEFINE_ANY_CONVERT_SPEC_STATIC_LLI(from_type) #endif #if !(defined DISABLE_ULL_INT && DISABLE_ULL_INT) #define DEFINE_ANY_CONVERT_SPEC_STATIC_ULLI(from_type) \ DEFINE_ANY_CONVERT_SPEC_STATIC(from_type, unsigned long long int) #else #define DEFINE_ANY_CONVERT_SPEC_STATIC_ULLI(from_type) #endif /// Define \c any_convertible and \c any_convert specializations for all built-in types as \c To. #define DEFINE_ANY_CONVERT_SPEC_ALL(from_type) \ DEFINE_ANY_CONVERT_SPEC_STATIC_TOBOOL(from_type) \ DEFINE_ANY_CONVERT_SPEC_STATIC(from_type, char) \ DEFINE_ANY_CONVERT_SPEC_STATIC(from_type, signed char) \ DEFINE_ANY_CONVERT_SPEC_STATIC(from_type, unsigned char) \ DEFINE_ANY_CONVERT_SPEC_STATIC(from_type, wchar_t) \ DEFINE_ANY_CONVERT_SPEC_STATIC(from_type, short int) \ DEFINE_ANY_CONVERT_SPEC_STATIC(from_type, unsigned short int) \ DEFINE_ANY_CONVERT_SPEC_STATIC(from_type, int) \ DEFINE_ANY_CONVERT_SPEC_STATIC(from_type, unsigned int) \ DEFINE_ANY_CONVERT_SPEC_STATIC(from_type, long int) \ DEFINE_ANY_CONVERT_SPEC_STATIC(from_type, unsigned long int) \ DEFINE_ANY_CONVERT_SPEC_STATIC_LLI(from_type) \ DEFINE_ANY_CONVERT_SPEC_STATIC_ULLI(from_type) \ DEFINE_ANY_CONVERT_SPEC_STATIC(from_type, double) \ DEFINE_ANY_CONVERT_SPEC_STATIC(from_type, float) \ DEFINE_ANY_CONVERT_SPEC_STATIC(from_type, long double) \ DEFINE_ANY_CONVERT_SPEC_NUMTOSTRING(from_type) \ DEFINE_ANY_CONVERT_SPEC_STRINGTONUM(from_type) DEFINE_ANY_CONVERT_SPEC_ALL(bool) DEFINE_ANY_CONVERT_SPEC_ALL(char) DEFINE_ANY_CONVERT_SPEC_ALL(signed char) DEFINE_ANY_CONVERT_SPEC_ALL(unsigned char) DEFINE_ANY_CONVERT_SPEC_ALL(wchar_t) DEFINE_ANY_CONVERT_SPEC_ALL(short int) DEFINE_ANY_CONVERT_SPEC_ALL(unsigned short int) DEFINE_ANY_CONVERT_SPEC_ALL(int) DEFINE_ANY_CONVERT_SPEC_ALL(unsigned int) DEFINE_ANY_CONVERT_SPEC_ALL(long int) DEFINE_ANY_CONVERT_SPEC_ALL(unsigned long int) #if !(defined DISABLE_LL_INT && DISABLE_LL_INT) DEFINE_ANY_CONVERT_SPEC_ALL(long long int) #endif #if !(defined DISABLE_ULL_INT && DISABLE_ULL_INT) DEFINE_ANY_CONVERT_SPEC_ALL(unsigned long long int) #endif DEFINE_ANY_CONVERT_SPEC_ALL(double) DEFINE_ANY_CONVERT_SPEC_ALL(float) DEFINE_ANY_CONVERT_SPEC_ALL(long double) #undef DEFINE_ANY_CONVERT_SPEC #undef DEFINE_ANY_CONVERT_SPEC_STATIC #undef DEFINE_ANY_CONVERT_SPEC_NUMTOSTRING #undef DEFINE_ANY_CONVERT_SPEC_STRINGTONUM #undef DEFINE_ANY_CONVERT_SPEC_ALL } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/any_type.h000066400000000000000000000232371417717230600176430ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2000 - 2010 Kevlin Henney (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_boost_1_0.txt file ***************************************************************************/ /// \file /// \author Kevlin Henney /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_ANY_TYPE_H #define HZ_ANY_TYPE_H #include "hz_config.h" // feature macros /** \file any_type library (any type one-element container), based on boost::any. Original notes and copyright info follow: // what: variant type boost::any // who: contributed by Kevlin Henney, // with features contributed and bugs found by // Ed Brey, Mark Rodgers, Peter Dimov, and James Curran // when: July 2001 // where: tested with BCC 5.5, MSVC 6.0, and g++ 2.95 // Copyright Kevlin Henney, 2000, 2001, 2002. All rights reserved. // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // Compilation options: // - Define DISABLE_RTTI=1 to disable RTTI checks and typeinfo-getter // functions. NOT recommended. // - Define DISABLE_ANY_CONVERT=1 to disable all .convert functions // (avoids dependency on any_convert.h) */ #if !(defined DISABLE_RTTI && DISABLE_RTTI) #include // std::type_info #endif #include // std::ostream #include // to enable std::string printing. is included already from any_type_holder.h. #include "type_properties.h" #include "bad_cast_exception.h" #include "any_type_holder.h" namespace hz { /// An object of this type can contain any variable of any type in it. class any_type { public: /// Constructor any_type() : content(0) { } /// Construct from a value template any_type(const ValueType& value) : content(new internal::AnyHolder(value)) { } /// Copy constructor any_type(const any_type& other) : content(other.content ? other.content->clone() : 0) { } /// Destructor ~any_type() { delete content; } /// Swap *this with rhs inline any_type& swap(any_type& rhs); /// Check whether the wrapped variable is set bool empty() const { return !content; } /// Remove the wrapped variable void clear() { delete content; content = 0; } #if !(defined DISABLE_RTTI && DISABLE_RTTI) /// Get type info of the wrapped variable const std::type_info& type() const { return (content ? content->type() : typeid(void)); } /// Check whether the wrapped variable is of type T. template inline bool is_type() const; // e.g. is_type #endif /// Get the value into a variable of _exactly_ the same type. /// \return false if casting fails template inline bool get(T& put_it_here) const; /// Get the value using _exactly_ the same type. /// \throw bad_any_cast if casting fails template inline T get() const; // this may throw if cast fails! #if !(defined DISABLE_ANY_CONVERT && DISABLE_ANY_CONVERT) /// Convert the value into a castable type using any_convert(). template inline bool convert(T& val) const; /// Convert the value into a castable type using any_convert(). /// \throw bad_any_cast if casting fails template inline T convert() const; #endif /// Assignment operator from specific type template any_type& operator= (const ValueType& value) { delete content; content = new internal::AnyHolder(value); return *this; } /// Assignment operator from another any_type any_type& operator= (const any_type& other) { delete content; content = other.content->clone(); return *this; } /// A helper class for easy operator<<() usage. struct StreamHelper { StreamHelper(internal::AnyHolderBase* c) : content(c) { } internal::AnyHolderBase* content; }; /// A helper function to send any_type to a stream. /// Example: std::cerr << any_type_object.to_stream() StreamHelper to_stream() const { return StreamHelper(content); } // helper classes for enabling <<. /// A helper struct for operator<<() template struct is_default_printable : public type_integral_constant::value || type_is_floating_point::value || type_is_pointer::value)> { }; /// A helper struct for operator<<() template struct printable { static const bool value = b; }; /// A helper struct for operator<<(). // You may specialize this to
, with "public printable" to enable default printing template struct set_printable : public printable { }; /// A helper struct for operator<<() template struct is_printable { static const bool value = (is_default_printable::value || set_printable::value); }; internal::AnyHolderBase* content; ///< Pointer to internal::AnyHolder }; /// Operator<<() for any_type::StreamHelper. See any_type::to_stream(). inline std::ostream& operator<< (std::ostream& os, const any_type::StreamHelper& sh) { if (sh.content) sh.content->to_stream(os); return os; } /// This is thrown in case of bad reference casts DEFINE_BAD_CAST_EXCEPTION(bad_any_cast, "Data type mismatch for AnyType. Original type: \"%s\", requested type: \"%s\".", "Data type mismatch for AnyType."); // --------------------------------------------------------- // any_cast operators /// Cast a "pointer to any_type" to a "pointer to value_type" template ValueType any_cast(any_type* operand) { typedef typename type_remove_pointer::type nopointer; if (operand && operand->content #if !(defined DISABLE_RTTI && DISABLE_RTTI) && operand->type() == typeid(nopointer) #endif ) { return &static_cast*>(operand->content)->value; } return 0; } /// Cast a "pointer to any_type" to a "pointer to value_type" template const ValueType any_cast(const any_type* operand) { return any_cast(const_cast(operand)); } /// Cast an any_type object to value_type template ValueType any_cast(any_type& operand) { typedef typename type_remove_reference::type nonref; if (!operand.content) THROW_CUSTOM_BAD_CAST(bad_any_cast, operand.type(), typeid(nonref)); #if !(defined DISABLE_RTTI && DISABLE_RTTI) if (operand.type() != typeid(nonref)) THROW_CUSTOM_BAD_CAST(bad_any_cast, operand.type(), typeid(nonref)); #endif return static_cast*>(operand.content)->value; } /// Cast an any_type object to value_type template ValueType any_cast(const any_type& operand) { typedef typename type_remove_reference::type nonref; if (!operand.content) THROW_CUSTOM_BAD_CAST(bad_any_cast, operand.type(), typeid(nonref)); #if !(defined DISABLE_RTTI && DISABLE_RTTI) if (operand.type() != typeid(nonref)) THROW_CUSTOM_BAD_CAST(bad_any_cast, operand.type(), typeid(nonref)); #endif return static_cast*>(operand.content)->value; } // --------------------------------------------------------- inline any_type& any_type::swap(any_type& rhs) { // don't use std::swap(), we don't need just for one function. internal::AnyHolderBase* tmp = rhs.content; rhs.content = content; content = tmp; return *this; } #if !(defined DISABLE_RTTI && DISABLE_RTTI) template inline bool any_type::is_type() const { // without rtti, any_cast will always return true. return static_cast(any_cast(this)); } #endif template inline bool any_type::get(T& put_it_here) const { T* a = any_cast(this); if (!a) return false; put_it_here = *a; return true; } template inline T any_type::get() const { return any_cast(*this); } #if !(defined DISABLE_ANY_CONVERT && DISABLE_ANY_CONVERT) template inline bool any_type::convert(T& val) const { return (content ? content->convert(val) : false); } template inline T any_type::convert() const { typedef typename type_remove_reference::type nonref; nonref result; if (!this->convert(result)) THROW_CUSTOM_BAD_CAST(bad_any_cast, this->type(), typeid(nonref)); return result; } #endif // --------------------------------------------------------- namespace internal { /// You may specialize this for to enable custom printing. template ::value > struct AnyPrinter { static void to_stream(std::ostream& os, const ValueType& value) { os << "[non-representable]"; } }; /// Specialization template struct AnyPrinter { static void to_stream(std::ostream& os, const ValueType& value) { os << value; } }; template inline void AnyHolder::to_stream(std::ostream& os) const { AnyPrinter::to_stream(os, value); } } // Do NOT define operator<< for any_type ! // Since any C++ type is implicitly convertable to any_type, and it will // serve all unrelated types and may even end up in an endless loop. /// Use this to add supported types to any_type::to_stream(). /// Any type may be added if it supports operator <<. #define ANY_TYPE_SET_PRINTABLE(type, truefalse) \ namespace hz { \ template<> struct any_type::set_printable : public any_type::printable { }; \ } } // ns /// Make std::string printable using any_type::to_stream(). ANY_TYPE_SET_PRINTABLE(std::string, true) #endif /// @} gsmartcontrol-1.1.4/src/hz/any_type_holder.h000066400000000000000000000112271417717230600211740ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2000 - 2010 Kevlin Henney (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_boost_1_0.txt file ***************************************************************************/ /// \file /// \author Kevlin Henney /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_ANY_TYPE_HOLDER_H #define HZ_ANY_TYPE_HOLDER_H #include "hz_config.h" // feature macros /** \file Internal header, do not include manually. */ #if !(defined DISABLE_RTTI && DISABLE_RTTI) #include // std::type_info #endif #include // std::ostream #include #if !(defined DISABLE_ANY_CONVERT && DISABLE_ANY_CONVERT) #include "any_convert.h" // any_convert #endif namespace hz { namespace internal { /// Base class for content of hz::any_type struct AnyHolderBase { /// Virtual destructor virtual ~AnyHolderBase() { } #if !(defined DISABLE_RTTI && DISABLE_RTTI) /// Get std::type_info for the wrapped variable virtual const std::type_info& type() const = 0; #endif /// Clone the wrapped variable virtual AnyHolderBase* clone() const = 0; /// Send the wrapped variable to std::ostream virtual void to_stream(std::ostream& os) const = 0; #if !(defined DISABLE_ANY_CONVERT && DISABLE_ANY_CONVERT) /// Convert the wrapped variable to argument type virtual bool convert(bool& val) const = 0; virtual bool convert(char& val) const = 0; virtual bool convert(signed char& val) const = 0; virtual bool convert(unsigned char& val) const = 0; virtual bool convert(wchar_t& val) const = 0; virtual bool convert(short int& val) const = 0; virtual bool convert(unsigned short int& val) const = 0; virtual bool convert(int& val) const = 0; virtual bool convert(unsigned int& val) const = 0; virtual bool convert(long int& val) const = 0; virtual bool convert(unsigned long int& val) const = 0; #if !(defined DISABLE_LL_INT && DISABLE_LL_INT) virtual bool convert(long long int& val) const = 0; #endif #if !(defined DISABLE_ULL_INT && DISABLE_ULL_INT) virtual bool convert(unsigned long long int& val) const = 0; #endif virtual bool convert(double& val) const = 0; virtual bool convert(float& val) const = 0; virtual bool convert(long double& val) const = 0; virtual bool convert(std::string& val) const = 0; #endif // DISABLE_ANY_CONVERT }; /// Child class for content, holds the actual value template struct AnyHolder : public AnyHolderBase { /// Type of the wrapped variable typedef ValueType value_type; /// Constructor AnyHolder(const ValueType& val) : value(val) { } #if !(defined DISABLE_RTTI && DISABLE_RTTI) /// Get std::type_info object for the wrapped variable const std::type_info& type() const { return typeid(ValueType); } #endif /// Clone the wrapped variable. Note that this is not a deep clone. AnyHolderBase* clone() const { return new AnyHolder(value); } /// Send the wrapped variable to std::ostream inline void to_stream(std::ostream& os) const; // { // AnyPrinter::to_stream(os, value); // } #if !(defined DISABLE_ANY_CONVERT && DISABLE_ANY_CONVERT) // Reimplemented from AnyHolderBase bool convert(bool& val) const { return any_convert(value, val); } bool convert(char& val) const { return any_convert(value, val); } bool convert(signed char& val) const { return any_convert(value, val); } bool convert(unsigned char& val) const { return any_convert(value, val); } bool convert(wchar_t& val) const { return any_convert(value, val); } bool convert(short int& val) const { return any_convert(value, val); } bool convert(unsigned short int& val) const { return any_convert(value, val); } bool convert(int& val) const { return any_convert(value, val); } bool convert(unsigned int& val) const { return any_convert(value, val); } bool convert(long int& val) const { return any_convert(value, val); } bool convert(unsigned long int& val) const { return any_convert(value, val); } #if !(defined DISABLE_LL_INT && DISABLE_LL_INT) bool convert(long long int& val) const { return any_convert(value, val); } #endif #if !(defined DISABLE_ULL_INT && DISABLE_ULL_INT) bool convert(unsigned long long int& val) const { return any_convert(value, val); } #endif bool convert(double& val) const { return any_convert(value, val); } bool convert(float& val) const { return any_convert(value, val); } bool convert(long double& val) const { return any_convert(value, val); } bool convert(std::string& val) const { return any_convert(value, val); } #endif // DISABLE_ANY_CONVERT ValueType value; ///< The wrapped data }; } // ns } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/any_type_test.cpp000066400000000000000000000062321417717230600212310ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_unlicense.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz_tests /// \weakgroup hz_tests /// @{ // disable libdebug, we don't link to it #undef HZ_USE_LIBDEBUG #define HZ_USE_LIBDEBUG 0 // enable libdebug emulation through std::cerr #undef HZ_EMULATE_LIBDEBUG #define HZ_EMULATE_LIBDEBUG 1 // define this to disable any_convert.h dependency // #define DISABLE_ANY_CONVERT // The first header should be then one we're testing, to avoid missing // header pitfalls. #include "any_type.h" #include #include #include using namespace hz; /// TestClass test structure struct TestClass { typedef std::list any_list; void append_int(any_list& values, int value) { any_type to_append = value; values.push_back(to_append); } void append_string(any_list& values, const std::string& value) { values.push_back(value); } void append_char_ptr(any_list& values, const char* value) { values.push_back(value); } void append_any(any_list& values, const any_type& value) { values.push_back(value); } void append_nothing(any_list& values) { values.push_back(any_type()); } std::string get_string(any_list& values) { any_type a = values.back(); any_cast(a); std::string b; a.get(b); a.get(); // is_type<> is available only with RTTI #if !(defined DISABLE_RTTI && DISABLE_RTTI) if (a.is_type()) return any_cast(a); return std::string(); #else // can't do any checking here return any_cast(a); #endif } }; /// enable printing TestClass. /// This needs operator<< and and ANY_TYPE_SET_PRINTABLE specialization. inline std::ostream& operator<< (std::ostream& os, TestClass a) { return (os << "TestClass\n"); } ANY_TYPE_SET_PRINTABLE(TestClass, true); /// Main function for the test int main() { any_type a1; a1 = 4; std::cerr << a1.to_stream() << "\n"; TestClass b; any_type b1 = b; // std::cerr << b; std::cerr << b1.to_stream() << "\n"; #if !(defined DISABLE_ANY_CONVERT && DISABLE_ANY_CONVERT) any_type a2 = std::string("5.444"); double a2val = 0; std::cerr << "conversion " << (a2.convert(a2val) ? "succeeded" : "failed") << ", "; std::cerr << "value: " << a2val << "\n"; any_type a3 = 6.55; std::string a3val; std::cerr << "conversion " << (a3.convert(a3val) ? "succeeded" : "failed") << ", "; std::cerr << "value: " << a3val << "\n"; any_type a4 = std::string("7"); int a4val = 0; std::cerr << "conversion " << (a4.convert(a4val) ? "succeeded" : "failed") << ", "; std::cerr << "value: " << a4val << "\n"; any_type a5 = 'a'; double a5val = 0; std::cerr << "conversion " << (a5.convert(a5val) ? "succeeded" : "failed") << ", "; std::cerr << "value: " << a5val << "\n"; char b1val = 0; std::cerr << "conversion " << (b1.convert(b1val) ? "succeeded" : "failed") << ", "; std::cerr << "value: " << int(b1val) << "\n"; #endif return 0; } /// @} gsmartcontrol-1.1.4/src/hz/ascii.h000066400000000000000000000414071417717230600171020ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 1992, 1993 The Regents of the University of California License: See LICENSE_bsd-ucb.txt file (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author The Regents of the University of California /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_ASCII_H #define HZ_ASCII_H #include "hz_config.h" // feature macros /// \file /// Part of this file (specifically, ascii_strtoi() implementation) /// is derived from FreeBSD's strtol() and friends. // The original FreeBSD copyright header follows: /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ // Taken from: // __FBSDID("$FreeBSD: src/lib/libc/stdlib/strtol.c,v 1.20 2007/01/09 00:28:10 imp Exp $"); #include // std::numeric_limits #include // errno (not std::errno, it may be a macro) #include // std::localeconv, std::lconv #include // std::strlen, std::memcmp, std::memcpy #include // std::size_t, std::ptrdiff_t #include // for stdlib.h, std::strtod #include // std::isspace #if !(defined DISABLE_STRTOF && DISABLE_STRTOF) \ || !(defined DISABLE_STRTOLD && DISABLE_STRTOLD) #include // strtof, strtold (C99, not in C++98) #endif #include #ifndef ASSERT // assert() is undefined if NDEBUG is defined. #define ASSERT(a) assert(a) #endif #include "type_properties.h" // type_is_*, type_make_unsigned // Disable silly VS warnings #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable: 4800) // 'int' : forcing value to bool 'true' or 'false' (performance warning) #pragma warning(disable: 4804) // unsafe use of type 'bool' in operation #pragma warning(disable: 4146) // unary minus operator applied to unsigned type, result still unsigned #pragma warning(disable: 6328) // 'const char' passed as parameter '1' when 'unsigned char' is required in call to 'isspace' #endif // Locale-independent functions for ascii manipulation. namespace hz { /// Implementation of std::isspace(). /// This function always behaves like the standard function /// does in Classic locale (regardless of current locale). inline bool ascii_isspace(char c) { return (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v'); } /// Implementation of std::strtoX() functions for every native integral type: /// char (numerical value), wchar_t (numerical value), /// int, short, long, long long, plus their signed/unsigned variants. /// This function always behaves like the standard functions do /// in Classic locale. template inline T ascii_strtoi(const char* nptr, char** endptr, int base) { typedef typename hz::type_make_unsigned::type unsigned_type; typedef typename hz::type_make_signed::type signed_type; const char* s = nptr; unsigned_type acc = 0; unsigned_type cutoff = 0; signed_type cutlim = 0; int any = 0; // (-1, 0, 1) char c = 0; bool neg = false; // skip whitespace and pick up leading +/- sign if any. do { c = *s++; } while (ascii_isspace(c)); if (c == '-') { neg = true; c = *s++; } else { if (c == '+') c = *s++; } // This check is actually not present in original version. // So, in original, -1 successfully passes as an unsigned integer. // This is obviously wrong - if 32768 causes int16_t overflow, so // should -1 cause uint16_t underflow. if (neg && !std::numeric_limits::is_signed) { // disallow negative unsigned integers errno = ERANGE; if (endptr != 0) *endptr = const_cast(--s); // where '-' was encountered return std::numeric_limits::min(); // aka 0 } // If base is 0, allow 0x for hex and 0 for octal, else // assume decimal; if base is already 16, allow 0x. if ( (base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X') && ((s[1] >= '0' && s[1] <= '9') || (s[1] >= 'A' && s[1] <= 'F') || (s[1] >= 'a' && s[1] <= 'f')) ) { c = s[1]; s += 2; base = 16; } if (base == 0) base = (c == '0' ? 8 : 10); if (base < 2 || base > 36) { errno = EINVAL; if (endptr != 0) *endptr = const_cast(nptr); return 0; } acc = 0; any = 0; // Compute the cutoff value between legal numbers and illegal // numbers. That is the largest legal value, divided by the // base. An input number that is greater than this value, if // followed by a legal input character, is too big. One that // is equal to this value may be valid or not; the limit // between valid and invalid numbers is then based on the last // digit. For instance, if the range for longs is // [-2147483648..2147483647] and the input base is 10, // cutoff will be set to 214748364 and cutlim to either // 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated // a value > 214748364, or equal but the next digit is > 7 (or 8), // the number is too big, and we will return a range error. // // Set 'any' if any `digits' consumed; make it negative to indicate // overflow. if (std::numeric_limits::is_signed) { #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wint-in-bool-context" #pragma GCC diagnostic ignored "-Woverflow" #endif cutoff = static_cast( neg ? ( static_cast(-(std::numeric_limits::min() + std::numeric_limits::max())) + std::numeric_limits::max() ) : std::numeric_limits::max() ); #ifdef __GNUC__ #pragma GCC diagnostic pop #endif cutlim = static_cast(cutoff % base); cutoff = static_cast(cutoff / base); } else { cutoff = static_cast(std::numeric_limits::max() / base); cutlim = static_cast(std::numeric_limits::max() % base); } // Assumes that the upper and lower case // alphabets and digits are each contiguous. for ( ; ; c = *s++) { if (c >= '0' && c <= '9') { c = static_cast(c - '0'); } else if (c >= 'A' && c <= 'Z') { c = static_cast(c - 'A' + 10); } else if (c >= 'a' && c <= 'z') { c = static_cast(c - 'a' + 10); } else { break; } if (c >= base) { break; } if ((any < 0) || acc > cutoff || (acc == cutoff && static_cast(c) > cutlim)) { any = -1; } else { any = 1; acc = static_cast((acc * base) + c); } } if (any < 0) { if (std::numeric_limits::is_signed) { acc = neg ? std::numeric_limits::min() : std::numeric_limits::max(); } else { acc = std::numeric_limits::max(); } errno = ERANGE; } else if (!any) { errno = EINVAL; } else if (neg) { acc = static_cast(-acc); } if (endptr != 0) *endptr = const_cast(any ? (s - 1) : nptr); return acc; } namespace internal { // proxy functions for system strtod family. /// We use struct because the errors are easier on the user this way. template struct ascii_strtof_impl { }; /// Specialization template<> struct ascii_strtof_impl { static double func(const char* nptr, char** endptr) { return std::strtod(nptr, endptr); } }; // Note: strtold() and strtof() are C99 / _XOPEN_SOURCE >= 600. /// Specialization template<> struct ascii_strtof_impl { static float func(const char* nptr, char** endptr) { #if !(defined DISABLE_STRTOF && DISABLE_STRTOF) return strtof(nptr, endptr); #else // emulate via strtod(). double val = std::strtod(nptr, endptr); // check the overflow condition. note: min() is smallest positive number, not negative huge. if (val > static_cast(std::numeric_limits::max())) { errno = ERANGE; // infinity is defined as builtin hugeval in limits. HUGE_VALF seems to be C99 only. return std::numeric_limits::infinity(); } // negative overflow if (val < -static_cast(std::numeric_limits::max())) { errno = ERANGE; return -std::numeric_limits::infinity(); } // NOTE: We don't check for the underflow. The standards have // a mixed definition of it (and what value should be returned), and // I'm not sure if a flag should be raised at all. // If anyone knows how to do it portably and reliably, please tell me. return float(val); #endif } }; /// Specialization template<> struct ascii_strtof_impl { static long double func(const char* nptr, char** endptr) { #if !(defined DISABLE_STRTOLD && DISABLE_STRTOLD) return strtold(nptr, endptr); #else // This leads to loss of precision, but what else can we do? // nans and infinities are preserved on implicit conversion, // so no need to do anything. return std::strtod(nptr, endptr); #endif } }; } // POSIX / C99 behaviour of strtod() / strtof() / strtold(): // 1. optional leading whitespace (std::isspace()). // 2. optional + or - signs. // 3. one of the following: // a) decimal number: // non-empty sequence of decimal digits, optionally containing radix (decimal // point, locale-dependent). optional decimal exponent: e|E[+|-]decimal_digits. // b) hexadecimal number: // 0x or 0X, then non-empty sequence of hex digits (possibly with radix), // optional binary exponent: p|P[+|-]decimal_digits. C90 (but not C99) requires // that either radix or exponent must be present. // c) infinity: // "inf" or "infinity", case-insensitive. // d) nan: // "nan", case-insensitive. optionally followed by "(", sequence of chars, ")". // Returns: converted value. // if endptr is not 0, it's set to point to the first character of nptr on which // the parser stopped. // if no conversion is performed, it returns 0, sets *endptr to nptr and errno is // set to EINVAL (setting errno is optional). // on overflow, +/-HUGE_VAL[L|F] is returned, and errno is set to ERANGE. // on underflow, 0 is returned and errno is set to ERANGE. /// Implementation of strtod/strtof/strtold for any floating-point type. /// This function always behaves like the standard functions do /// in Classic locale. template inline T ascii_strtof(const char* nptr, char** endptr) { // Basic checks. Detect early, detect often. if (!nptr || nptr[0] == '\0') { errno = EINVAL; // this is optional, but let's do it anyway. if (endptr) *endptr = const_cast(nptr); return 0; } // Only decimal point and spaces are locale-dependent, convert them // to a format strtod() would understand. // Skip leading ascii spaces, so that strtod doesn't have to recognize them. const char* wnptr = nptr; while (ascii_isspace(*wnptr)) ++wnptr; // If after classic whitespaces there are locale whitespaces, error out. // Without this, strtod() would ignore them, which is not ok (they shouldn't be here). if (std::isspace(*wnptr)) { errno = EINVAL; // this is optional, but let's do it anyway. if (endptr) *endptr = const_cast(wnptr); return 0; } std::lconv* locdata = std::localeconv(); ASSERT(locdata); const char* radix = locdata->decimal_point; ASSERT(radix); std::size_t radix_len = (radix ? std::strlen(radix) : 0U); ASSERT(radix_len); // Check if the locale resembles Classic. if (radix[0] == '.' && radix[1] == '\0') { // nothing to convert, just forward to the system function. T val = internal::ascii_strtof_impl::func(wnptr, endptr); if (endptr && *endptr == wnptr) // re-set endptr to nptr if they should be equal *endptr = const_cast(nptr); return val; } // If, e.g., the source string is "12,34" and "," is locale radix, // we need to stop right before it, or else strtod() will return 12.34 // instead of 12. (well, unless radix is space, which is really screwed up anyway). std::size_t wnptr_len = std::strlen(wnptr); const char* end_pos = wnptr; // right after the number is over. for (std::size_t i = 0; i < wnptr_len + 1 - radix_len; ++i) { if (*end_pos == '\0' || std::memcmp(end_pos, radix, radix_len) == 0) break; ++end_pos; } // No real format checks here, but I don't think they are needed // anyway. We're just replacing one char with another, and unless // the locale has really bad conflicts with it, everything should be fine. const char* radix_pos = wnptr; while (!(radix_pos == end_pos || *radix_pos == '.')) { ++radix_pos; } if (*radix_pos == '.') { // classic radix found // even if radix_len is 1, we still have to copy the string, so no special case there. char* copy = new char[end_pos - wnptr + radix_len]; // \0 will take a byte of old '.'. std::memcpy(copy, wnptr, radix_pos - wnptr); // copy up until '.'. char* cpos = copy + (radix_pos - wnptr); std::memcpy(cpos, radix, radix_len); // copy the locale radix cpos += radix_len; std::memcpy(cpos, radix_pos + 1, end_pos - radix_pos - 1); cpos += end_pos - radix_pos - 1; *cpos = '\0'; char* cendptr = 0; T val = internal::ascii_strtof_impl::func(copy, &cendptr); // translate cendptr to *endptr if (endptr) { if (!cendptr || *cendptr == '\0') { *endptr = const_cast(end_pos); // location of \0 or first locale radix in original string } else if (cendptr) { if (cendptr == copy) { *endptr = const_cast(nptr); } else { std::ptrdiff_t coffset = (cendptr - copy); *endptr = const_cast(wnptr) + coffset - ((coffset > (radix_pos - wnptr)) ? (radix_len - 1) : 0); } } } delete[] copy; return val; } if (*end_pos != '\0') { // locale radix found, use a part of nptr before that. char* copy = new char[end_pos - wnptr + 1]; std::memcpy(copy, wnptr, end_pos - wnptr); *(copy + (end_pos - wnptr)) = '\0'; char* cendptr = 0; T val = internal::ascii_strtof_impl::func(copy, &cendptr); // translate cendptr to *endptr if (endptr) { if (!cendptr || *cendptr == '\0') { *endptr = const_cast(end_pos); // location of \0 or first locale radix in original string } else if (cendptr) { if (cendptr == copy) { *endptr = const_cast(nptr); } else { *endptr = const_cast(wnptr) + (cendptr - copy); } } } delete[] copy; return val; } // no classic radix, no locale radix. // nothing to convert, just forward to the system function. T val = internal::ascii_strtof_impl::func(wnptr, endptr); if (endptr && *endptr == wnptr) // re-set endptr to nptr if they should be equal *endptr = const_cast(nptr); return val; } namespace { /// Helper struct to choose the correct implementation template struct ascii_strton_impl { }; /// Specialization template struct ascii_strton_impl { template inline static T func(const char* nptr, char** endptr, int base = 0) { return hz::ascii_strtof(nptr, endptr); } }; /// Specialization template struct ascii_strton_impl { template inline static T func(const char* nptr, char** endptr, int base = 0) { return hz::ascii_strtoi(nptr, endptr, base); } }; } /// Create any number (integral or floating point) from a string. /// Note: Parameter "base" is ignored for floating point types. template inline T ascii_strton(const char* nptr, char** endptr, int base = 0) { return ascii_strton_impl::value, hz::type_is_floating_point::value>::template func(nptr, endptr, base); } } // ns #ifdef _MSC_VER #pragma warning(pop) #endif #endif /// @} gsmartcontrol-1.1.4/src/hz/bad_cast_exception.h000066400000000000000000000126701417717230600216300ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_BAD_CAST_EXCEPTION_H #define HZ_BAD_CAST_EXCEPTION_H #include "hz_config.h" // feature macros // we need these for instantiation, even if exceptions are disabled. #include // std::exception #include #if !(defined DISABLE_RTTI && DISABLE_RTTI) #include // std::type_info #endif #include "exceptions.h" // THROW_FATAL #include "system_specific.h" // type_name_demangle #include "string_sprintf.h" // hz::string_sprintf namespace hz { #if !(defined DISABLE_RTTI && DISABLE_RTTI) /// Children of this class are thrown from various casting functions class bad_cast_except : virtual public std::exception { // from public: /// Constructor /// \param self_name child class name /// \param error_msg error message bad_cast_except(const char* self_name = 0, const char* error_msg = 0) : src_type(typeid(void)), dest_type(typeid(void)), self_name_(self_name ? self_name : "bad_cast_except"), error_msg_(error_msg ? error_msg : "Type cast failed from \"%s\" to \"%s\".") { } /// Constructor bad_cast_except(const std::type_info& src, const std::type_info& dest, const char* self_name = 0, const char* error_msg = 0) : src_type(src), dest_type(dest), self_name_(self_name ? self_name : "bad_cast_except"), error_msg_(error_msg ? error_msg : "Type cast failed from \"%s\" to \"%s\".") // still need %s here for correct arg count for printf { } /// Virtual destructor virtual ~bad_cast_except() throw() { } /// Reimplemented from std::exception virtual const char* what() const throw() { // Note: we do quite a lot of construction here, but since it's not // an out-of-memory exception, what the heck. std::string who = (self_name_.empty() ? "[unknown]" : self_name_); std::string from = (src_type == typeid(void) ? "[unknown]" : hz::type_name_demangle(src_type.name())); if (from.empty()) from = src_type.name(); std::string to = (dest_type == typeid(void) ? "[unknown]" : hz::type_name_demangle(dest_type.name())); if (to.empty()) to = dest_type.name(); return (msg_ = hz::string_sprintf((who + ": " + error_msg_).c_str(), from.c_str(), to.c_str())).c_str(); } const std::type_info& src_type; ///< Cast source type info const std::type_info& dest_type; ///< Cast destination type info private: mutable std::string msg_; ///< This must be a member to avoid its destruction on function call return. use what(). std::string self_name_; ///< The exception class name std::string error_msg_; ///< Error message }; #else // no RTTI variant: /// Children of this class are thrown from various casting functions class bad_cast_except : virtual public std::exception { // from public: /// Constructor /// \param self_name child class name /// \param error_msg error message bad_cast_except(const char* self_name = 0, const char* error_msg = 0) : self_name_(self_name ? self_name : "bad_cast_except"), error_msg_(error_msg ? error_msg : "Type cast failed.") { } /// Virtual destructor virtual ~bad_cast_except() throw() { } /// Reimplemented from std::exception virtual const char* what() const throw() { return (msg_ = (self_name_.empty() ? "[unknown]" : self_name_) + ": " + error_msg_).c_str(); } private: mutable std::string msg_; ///< This must be a member to avoid its destruction on function call return. use what(). std::string self_name_; ///< The exception class name std::string error_msg_; ///< Error message }; #endif } // ns /* // Example: class custom_cast_exception : public hz::bad_cast_except { public: custom_cast_exception() : hz::bad_cast_except("custom_cast_exception", "Cast failed from %s to %s.") { } #if !(defined DISABLE_RTTI && DISABLE_RTTI) custom_cast_exception(const std::type_info& src, const std::type_info& dest) : hz::bad_cast_except(src, dest, "custom_cast_exception", "Cast failed") { } #endif }; */ /// \def DEFINE_BAD_CAST_EXCEPTION(name, rtti_msg, nortti_msg) /// Define an exception class named \c name that is a child of hz::bad_cast_except, /// and shows \c rtti_msg if RTTI is enabled and nortti_msg if RTTI is disabled. #if !(defined DISABLE_RTTI && DISABLE_RTTI) #define DEFINE_BAD_CAST_EXCEPTION(name, rtti_msg, nortti_msg) \ class name : public hz::bad_cast_except { \ public: \ name() : hz::bad_cast_except(#name, rtti_msg) \ { } \ name(const std::type_info& src, const std::type_info& dest) \ : hz::bad_cast_except(src, dest, #name, rtti_msg) \ { } \ } #else // no rtti: #define DEFINE_BAD_CAST_EXCEPTION(name, rtti_msg, nortti_msg) \ class name : public hz::bad_cast_except { \ public: \ name() : hz::bad_cast_except(#name, nortti_msg) \ { } \ } #endif /// \def THROW_CUSTOM_BAD_CAST(name, from, to) /// Throw a bad cast exception of class \c name, with \c from /// and \c to being std::type_info class objects. #if !(defined DISABLE_RTTI && DISABLE_RTTI) #define THROW_CUSTOM_BAD_CAST(name, from, to) \ THROW_FATAL(name(from, to)) #else // no rtti: #define THROW_CUSTOM_BAD_CAST(name, from, to) \ THROW_FATAL(name()) #endif #endif /// @} gsmartcontrol-1.1.4/src/hz/bin2ascii_encoder.h000066400000000000000000000105141417717230600213470ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_BIN2ASCII_ENCODER_H #define HZ_BIN2ASCII_ENCODER_H #include "hz_config.h" // feature macros #include // std::size_t #include #include // std::strchr namespace hz { /// A class to convert a binary string to ascii string, while still retaining /// ascii character readability. The result can be put inside double quotes. class Bin2AsciiEncoder { public: /// Constructor. /// \param url_mode This enables URL-style encoding (not sure if it's correct, though). Bin2AsciiEncoder(bool url_mode = false) : url_mode_(url_mode) { } /// Encode the passed string (src.data()). inline std::string encode(const std::string& src) const; /// Decode the passed string (src.data()). /// \return empty string on failure (provided that src is not empty), decoded string on success. inline std::string decode(const std::string& src) const; /// Check whether the string can be an encoded string. bool string_is_encoded(const std::string& s) const { return (s.find_first_not_of(url_mode_ ? char_holder::encoded_chars_url : char_holder::encoded_chars) == std::string::npos); } /// Check whether a character can occur in an encoded string bool char_is_encoded(char c) const { return std::strchr((url_mode_ ? char_holder::encoded_chars_url : char_holder::encoded_chars), c); } private: /// Static member holder template struct StaticHolder { static const char* const encoded_chars; ///< Characters which may appear in encoded string (no-url mode). static const char* const encoded_chars_url; /// char_holder; ///< Static member holder instantiation bool url_mode_; ///< URL encoding mode (or not) /// Get a byte value of a hex digit char char_from_hex_digit(char c) const { if (c >= '0' && c <= '9') { return static_cast(c - '0'); } if (c >= 'A' && c <= 'F') { return static_cast(c - 'A' + 10); } if (c >= 'a' && c <= 'f') { return static_cast(c - 'a' + 10); } return 16; // aka invalid } }; template const char* const Bin2AsciiEncoder::StaticHolder::encoded_chars = "!^&()_-+=|.<>%" "0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; template const char* const Bin2AsciiEncoder::StaticHolder::encoded_chars_url = "-_.!~*'()+%" "0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; inline std::string Bin2AsciiEncoder::encode(const std::string& src) const { std::size_t src_size = src.size(); std::string dest; dest.reserve(src_size * 3); // maximum static const char* digits = "0123456789ABCDEF"; char c = 0; for (std::size_t i = 0; i < src_size; ++i) { c = src[i]; if (c == ' ') { dest += (url_mode_ ? "+" : "%20"); // don't use isalnum & friends, they're locale-dependent. } else if (c != '%' && std::strchr((url_mode_ ? char_holder::encoded_chars_url : char_holder::encoded_chars), c)) { dest += c; } else { unsigned char uc = static_cast(c); dest += '%'; dest += digits[(uc >> 4) & 0x0F]; dest += digits[uc & 0x0F]; } } return dest; } inline std::string Bin2AsciiEncoder::decode(const std::string& src) const { std::size_t src_size = src.size(); std::string dest; dest.reserve(src_size); // maximum char c = 0; for (std::size_t i = 0; i < src_size; ++i) { c = src[i]; if (c == '+' && url_mode_) { // if not url_mode, treat it as the others dest += ' '; continue; } if (c == '%') { if ((i + 2) < src_size) { const char cv1 = char_from_hex_digit(src[++i]); const char cv2 = char_from_hex_digit(src[++i]); if (cv1 == 16 || cv2 == 16) // invalid, max hex char + 1. return std::string(); dest += static_cast(cv2 + (cv1 << 4)); continue; } return std::string(); } dest += c; } return dest; } } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/bin2ascii_encoder_test.cpp000066400000000000000000000020351417717230600227400ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_unlicense.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz_tests /// \weakgroup hz_tests /// @{ // disable libdebug, we don't link to it #undef HZ_USE_LIBDEBUG #define HZ_USE_LIBDEBUG 0 // enable libdebug emulation through std::cerr #undef HZ_EMULATE_LIBDEBUG #define HZ_EMULATE_LIBDEBUG 1 // The first header should be then one we're testing, to avoid missing // header pitfalls. #include "bin2ascii_encoder.h" #include /// Main function for the test int main() { std::string s = "adbeq 2dd +-23\nqqq#4 $"; hz::Bin2AsciiEncoder enc; std::string e = enc.encode(s); std::string d = enc.decode(e); std::cerr << "Original: \"" << s << "\"\n"; std::cerr << "Encoded: \"" << e << "\"\n"; std::cerr << "Decoded: \"" << d << "\"\n"; return 0; } /// @} gsmartcontrol-1.1.4/src/hz/common_types.h000066400000000000000000000033361417717230600205250ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ #ifndef HZ_COMMON_TYPES_H #define HZ_COMMON_TYPES_H #include "hz_config.h" // feature macros namespace hz { // ---------------------------------- DefaultType /// A type that may be specified for template arguments, /// to avoid specifying the real type. struct DefaultType { }; // dummy class /** Set \c type to \c DefaultT if \c AutoT hz::DefaultType, otherwise set it to \c PassedT. \code // Sample usage: template struct A { // this will select DefT if hz::DefaultType was passed for T. typedef typename hz::type_auto_select::type RealT; }; A::RealT a1; ///< a1 is of std::stringstream type. A::RealT a1; ///< a1 is of std::string type. \endcode */ template struct type_auto_select { typedef PassedT type; ///< Deduced type }; /// Specialization, DefaultType was passed, select default. template struct type_auto_select { typedef DefaultT type; ///< Deduced type }; // ---------------------------------- NullType /// NullType is usually used as default template argument /// to indicate that it's unused. struct NullType { }; /// In case you need multiple unique NullType-style classes, use this /// with different N values. template struct NullTypeUnique { }; } // ns // using hz::DefaultType; // for convenience. #endif /// @} gsmartcontrol-1.1.4/src/hz/cstdint.h000066400000000000000000000016651417717230600174640ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2009 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup applib /// \weakgroup applib /// @{ #ifndef HZ_CSTDINT_H #define HZ_CSTDINT_H #include "hz_config.h" // feature macros // Use this file instead of stdint.h for portability. // It contains all the C99 stdint.h stuff in global namespace. // C++11 defines these automatically for cstdint, so we do it too for compatibility. #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS #endif #ifndef __STDC_CONSTANT_MACROS #define __STDC_CONSTANT_MACROS #endif // Assume stdint.h is present on all platforms except msvc #ifdef _MSC_VER #include "cstdint_impl_msvc.h" #else #include #endif #endif /// @} gsmartcontrol-1.1.4/src/hz/cstdint_impl_msvc.h000066400000000000000000000170721417717230600215340ustar00rootroot00000000000000// ISO C9x compliant stdint.h for Microsoft Visual Studio // Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 // // Copyright (c) 2006-2008 Alexander Chemeris // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // 3. The name of the author may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////////// #ifndef _MSC_VER // [ #error "Use this header only with Microsoft Visual C++ compilers!" #endif // _MSC_VER ] #ifndef _MSC_STDINT_H_ // [ #define _MSC_STDINT_H_ #if _MSC_VER > 1000 #pragma once #endif #include // For Visual Studio 6 in C++ mode and for many Visual Studio versions when // compiling for ARM we should wrap include with 'extern "C++" {}' // or compiler give many errors like this: // error C2733: second C linkage of overloaded function 'wmemchr' not allowed #ifdef __cplusplus extern "C" { #endif # include #ifdef __cplusplus } #endif // Define _W64 macros to mark types changing their size, like intptr_t. #ifndef _W64 # if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 # define _W64 __w64 # else # define _W64 # endif #endif // 7.18.1 Integer types // 7.18.1.1 Exact-width integer types // Visual Studio 6 and Embedded Visual C++ 4 doesn't // realize that, e.g. char has the same size as __int8 // so we give up on __intX for them. #if (_MSC_VER < 1300) typedef char int8_t; typedef short int16_t; typedef int int32_t; typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; #else typedef __int8 int8_t; typedef __int16 int16_t; typedef __int32 int32_t; typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; #endif typedef __int64 int64_t; typedef unsigned __int64 uint64_t; // 7.18.1.2 Minimum-width integer types typedef int8_t int_least8_t; typedef int16_t int_least16_t; typedef int32_t int_least32_t; typedef int64_t int_least64_t; typedef uint8_t uint_least8_t; typedef uint16_t uint_least16_t; typedef uint32_t uint_least32_t; typedef uint64_t uint_least64_t; // 7.18.1.3 Fastest minimum-width integer types typedef int8_t int_fast8_t; typedef int16_t int_fast16_t; typedef int32_t int_fast32_t; typedef int64_t int_fast64_t; typedef uint8_t uint_fast8_t; typedef uint16_t uint_fast16_t; typedef uint32_t uint_fast32_t; typedef uint64_t uint_fast64_t; // 7.18.1.4 Integer types capable of holding object pointers #ifdef _WIN64 // [ typedef __int64 intptr_t; typedef unsigned __int64 uintptr_t; #else // _WIN64 ][ typedef _W64 int intptr_t; typedef _W64 unsigned int uintptr_t; #endif // _WIN64 ] // 7.18.1.5 Greatest-width integer types typedef int64_t intmax_t; typedef uint64_t uintmax_t; // 7.18.2 Limits of specified-width integer types #if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 // 7.18.2.1 Limits of exact-width integer types #define INT8_MIN ((int8_t)_I8_MIN) #define INT8_MAX _I8_MAX #define INT16_MIN ((int16_t)_I16_MIN) #define INT16_MAX _I16_MAX #define INT32_MIN ((int32_t)_I32_MIN) #define INT32_MAX _I32_MAX #define INT64_MIN ((int64_t)_I64_MIN) #define INT64_MAX _I64_MAX #define UINT8_MAX _UI8_MAX #define UINT16_MAX _UI16_MAX #define UINT32_MAX _UI32_MAX #define UINT64_MAX _UI64_MAX // 7.18.2.2 Limits of minimum-width integer types #define INT_LEAST8_MIN INT8_MIN #define INT_LEAST8_MAX INT8_MAX #define INT_LEAST16_MIN INT16_MIN #define INT_LEAST16_MAX INT16_MAX #define INT_LEAST32_MIN INT32_MIN #define INT_LEAST32_MAX INT32_MAX #define INT_LEAST64_MIN INT64_MIN #define INT_LEAST64_MAX INT64_MAX #define UINT_LEAST8_MAX UINT8_MAX #define UINT_LEAST16_MAX UINT16_MAX #define UINT_LEAST32_MAX UINT32_MAX #define UINT_LEAST64_MAX UINT64_MAX // 7.18.2.3 Limits of fastest minimum-width integer types #define INT_FAST8_MIN INT8_MIN #define INT_FAST8_MAX INT8_MAX #define INT_FAST16_MIN INT16_MIN #define INT_FAST16_MAX INT16_MAX #define INT_FAST32_MIN INT32_MIN #define INT_FAST32_MAX INT32_MAX #define INT_FAST64_MIN INT64_MIN #define INT_FAST64_MAX INT64_MAX #define UINT_FAST8_MAX UINT8_MAX #define UINT_FAST16_MAX UINT16_MAX #define UINT_FAST32_MAX UINT32_MAX #define UINT_FAST64_MAX UINT64_MAX // 7.18.2.4 Limits of integer types capable of holding object pointers #ifdef _WIN64 // [ # define INTPTR_MIN INT64_MIN # define INTPTR_MAX INT64_MAX # define UINTPTR_MAX UINT64_MAX #else // _WIN64 ][ # define INTPTR_MIN INT32_MIN # define INTPTR_MAX INT32_MAX # define UINTPTR_MAX UINT32_MAX #endif // _WIN64 ] // 7.18.2.5 Limits of greatest-width integer types #define INTMAX_MIN INT64_MIN #define INTMAX_MAX INT64_MAX #define UINTMAX_MAX UINT64_MAX // 7.18.3 Limits of other integer types #ifdef _WIN64 // [ # define PTRDIFF_MIN _I64_MIN # define PTRDIFF_MAX _I64_MAX #else // _WIN64 ][ # define PTRDIFF_MIN _I32_MIN # define PTRDIFF_MAX _I32_MAX #endif // _WIN64 ] #define SIG_ATOMIC_MIN INT_MIN #define SIG_ATOMIC_MAX INT_MAX #ifndef SIZE_MAX // [ # ifdef _WIN64 // [ # define SIZE_MAX _UI64_MAX # else // _WIN64 ][ # define SIZE_MAX _UI32_MAX # endif // _WIN64 ] #endif // SIZE_MAX ] // WCHAR_MIN and WCHAR_MAX are also defined in #ifndef WCHAR_MIN // [ # define WCHAR_MIN 0 #endif // WCHAR_MIN ] #ifndef WCHAR_MAX // [ # define WCHAR_MAX _UI16_MAX #endif // WCHAR_MAX ] #define WINT_MIN 0 #define WINT_MAX _UI16_MAX #endif // __STDC_LIMIT_MACROS ] // 7.18.4 Limits of other integer types #if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 // 7.18.4.1 Macros for minimum-width integer constants #define INT8_C(val) val##i8 #define INT16_C(val) val##i16 #define INT32_C(val) val##i32 #define INT64_C(val) val##i64 #define UINT8_C(val) val##ui8 #define UINT16_C(val) val##ui16 #define UINT32_C(val) val##ui32 #define UINT64_C(val) val##ui64 // 7.18.4.2 Macros for greatest-width integer constants #define INTMAX_C INT64_C #define UINTMAX_C UINT64_C #endif // __STDC_CONSTANT_MACROS ] #endif // _MSC_STDINT_H_ ] gsmartcontrol-1.1.4/src/hz/data_file.h000066400000000000000000000065121417717230600177200ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2004 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_DATA_FILE_H #define HZ_DATA_FILE_H #include "hz_config.h" // feature macros #include #include #include "fs_path.h" #include "debug.h" namespace hz { /// Static variable holder template struct DataFileStaticHolderTemplate { static std::vector search_directories; ///< Search directories for data files }; // definitions template std::vector DataFileStaticHolderTemplate::search_directories; typedef DataFileStaticHolderTemplate DataFileStaticHolder; ///< Static variable holder (one (and only) specialization) //------------------------------------------------------------------------ /// Add a directory to a search path inline void data_file_add_search_directory(const std::string& path) { if (!path.empty()) DataFileStaticHolder::search_directories.push_back(path); } /// Get currently registered search directories (a copy is returned) inline std::vector data_file_get_search_directories() { return DataFileStaticHolder::search_directories; } /// Set a directory list for a search path inline void data_file_set_search_directories(const std::vector& dirs) { DataFileStaticHolder::search_directories = dirs; } /// Find a data file (using a file name) in a search directory list. /// \return A full path to filename, or empty string on "not found". inline std::string data_file_find(const std::string& filename, bool allow_to_be_directory = false) { if (filename.empty()) return std::string(); hz::FsPath fp(filename); // convert to native if it contains separators. this allows us // to use subdirectories in filenames while always using slashes. fp.to_native(); if (fp.is_absolute()) { // shouldn't happen, but still... if (!fp.exists()) { debug_print_error("app", "%s: Data file \"%s\" not found: File doesn't exist.\n", DBG_FUNC, fp.c_str()); return std::string(); } else if (!allow_to_be_directory && fp.is_dir()) { debug_print_error("app", "%s: Data file \"%s\" is a directory (which is not allowed).\n", DBG_FUNC, fp.c_str()); return std::string(); } return fp.str(); // return as is. } for (unsigned int i = 0; i < DataFileStaticHolder::search_directories.size(); i++) { std::string dirpath = DataFileStaticHolder::search_directories.at(i); hz::FsPath dp(dirpath + hz::DIR_SEPARATOR_S + fp.str()); // debug_print_dump("app", "%s: Searching for data \"%s\" at \"%s\".\n", DBG_FUNC, fp.c_str(), dirpath.c_str()); if (dp.exists()) { if (!allow_to_be_directory && dp.is_dir()) { debug_print_error("app", "%s: Data file \"%s\" file found at \"%s\", but it is a directory.\n", DBG_FUNC, fp.c_str(), dirpath.c_str()); return std::string(); } debug_print_info("app", "%s: Data file \"%s\" found at \"%s\".\n", DBG_FUNC, fp.c_str(), dirpath.c_str()); return dp.str(); } } debug_print_error("app", "%s: Data file \"%s\" not found.\n", DBG_FUNC, fp.c_str()); return std::string(); } } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/debug.h000066400000000000000000000226641417717230600171040ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_DEBUG_H #define HZ_DEBUG_H #include "hz_config.h" // feature macros #include // std::fprintf(), std::vfprintf() #ifndef __GNUC__ #include // std::va_start, va_list macro and friends #endif /* #include #ifndef ASSERT // assert() is undefined if NDEBUG is defined. # define ASSERT(a) assert(a) #endif */ /** \file This file is a link between libhz (and its users) and libdebug. It provides a way to write in libdebug-like API without actually using libdebug. Note that it provides only output functions. The setup functions cannot be emulated (but you probably won't need them in libraries anyway). */ // Use libdebug as is #if defined(HZ_USE_LIBDEBUG) && (HZ_USE_LIBDEBUG) // only output functions #include "libdebug/libdebug_mini.h" #else // undef them in case libdebug was included #ifdef debug_out_dump #undef debug_out_dump #endif #ifdef debug_out_info #undef debug_out_info #endif #ifdef debug_out_warn #undef debug_out_warn #endif #ifdef debug_out_error #undef debug_out_error #endif #ifdef debug_out_fatal #undef debug_out_fatal #endif #ifdef debug_print_dump #undef debug_print_dump #endif #ifdef debug_print_info #undef debug_print_info #endif #ifdef debug_print_warn #undef debug_print_warn #endif #ifdef debug_print_error #undef debug_print_error #endif #ifdef debug_print_fatal #undef debug_print_fatal #endif #ifdef DBG_FILE #undef DBG_FILE #endif #ifdef DBG_LINE #undef DBG_LINE #endif #ifdef DBG_FUNC_NAME #undef DBG_FUNC_NAME #endif #ifdef DBG_FUNC_PRNAME #undef DBG_FUNC_PRNAME #endif #ifdef DBG_FUNC #undef DBG_FUNC #endif #ifdef DBG_FUNC_MSG #undef DBG_FUNC_MSG #endif #ifdef DBG_POS #undef DBG_POS #endif #ifdef DBG_TRACE_POINT_MSG #undef DBG_TRACE_POINT_MSG #endif #ifdef DBG_TRACE_POINT_AUTO #undef DBG_TRACE_POINT_AUTO #endif #ifdef DBG_FUNCTION_ENTER_MSG #undef DBG_FUNCTION_ENTER_MSG #endif #ifdef DBG_FUNCTION_EXIT_MSG #undef DBG_FUNCTION_EXIT_MSG #endif #ifdef DBG_ASSERT_MSG #undef DBG_ASSERT_MSG #endif #ifdef DBG_ASSERT #undef DBG_ASSERT #endif // emulate libdebug API through std::cerr #if defined(HZ_EMULATE_LIBDEBUG) && HZ_EMULATE_LIBDEBUG #include #include #include // cerr / stderr have no buffering, so no need to sync them with each other. #define debug_out_dump(domain, output) \ std::cerr << " [" << (domain) << "] " << output #define debug_out_info(domain, output) \ std::cerr << " [" << (domain) << "] " << output #define debug_out_warn(domain, output) \ std::cerr << " [" << (domain) << "] " << output #define debug_out_error(domain, output) \ std::cerr << " [" << (domain) << "] " << output #define debug_out_fatal(domain, output) \ std::cerr << " [" << (domain) << "] " << output #ifdef __GNUC__ // The "trim trailing comma" and "##" extensions are GNU features (works with intel too). // The ## part is needed to avoid requirement of at least one argument after "format". #define debug_print_dump(domain, format, ...) \ std::fprintf(stderr, (std::string(" [") + (domain) + "] " + format).c_str(), ## __VA_ARGS__) #define debug_print_info(domain, format, ...) \ std::fprintf(stderr, (std::string(" [") + (domain) + "] " + format).c_str(), ## __VA_ARGS__) #define debug_print_warn(domain, format, ...) \ std::fprintf(stderr, (std::string(" [") + (domain) + "] " + format).c_str(), ## __VA_ARGS__) #define debug_print_error(domain, format, ...) \ std::fprintf(stderr, (std::string(" [") + (domain) + "] " + format).c_str(), ## __VA_ARGS__) #define debug_print_fatal(domain, format, ...) \ std::fprintf(stderr, (std::string(" [") + (domain) + "] " + format).c_str(), ## __VA_ARGS__) #else // non-gcc compilers: namespace hz { namespace internal { inline void debug_print_impl(const std::string& header, const char* format, ...) { std::va_list ap; va_start(ap, format); std::vfprintf(stderr, (header + format).c_str(), ap); va_end(ap); } } } #define debug_print_dump(domain, ...) \ hz::internal::debug_print_impl(std::string(" [") + (domain) + "] ", __VA_ARGS__) #define debug_print_info(domain, ...) \ hz::internal::debug_print_impl(std::string(" [") + (domain) + "] ", __VA_ARGS__) #define debug_print_warn(domain, ...) \ hz::internal::debug_print_impl(std::string(" [") + (domain) + "] ", __VA_ARGS__) #define debug_print_error(domain, ...) \ hz::internal::debug_print_impl(std::string(" [") + (domain) + "] ", __VA_ARGS__) #define debug_print_fatal(domain, ...) \ hz::internal::debug_print_impl(std::string(" [") + (domain) + "] ", __VA_ARGS__) #endif #define DBG_FILE __FILE__ #define DBG_LINE __LINE__ #if defined HAVE_CXX___func__ && HAVE_CXX___func__ #define DBG_FUNC_NAME __func__ #elif defined HAVE_CXX___FUNCTION__ && HAVE_CXX___FUNCTION__ #define DBG_FUNC_NAME __FUNCTION__ #else #define DBG_FUNC_NAME "unknown" #endif #ifdef __GNUC__ #define DBG_FUNC_PRNAME __PRETTY_FUNCTION__ #else #define DBG_FUNC_PRNAME DBG_FUNC_NAME #endif #include namespace hz { namespace internal { inline std::string format_function_msg(const std::string& func, bool add_suffix) { // if it's "bool::A::func(int)" or "bool test::A::func(int)", // remove the return type and parameters. std::string::size_type endpos = func.find('('); if (endpos == std::string::npos) endpos = func.size(); // search for first space (after the parameter), or "". std::string::size_type pos = func.find_first_of(" >"); if (pos != std::string::npos) { if (func[pos] == '>') pos += 2; // skip :: ++pos; // skip whatever character we're over // debug_out_info("default", "pos: " << pos << ", endpos: " << endpos << "\n"); return func.substr(pos >= endpos ? 0 : pos, endpos - pos) + (add_suffix ? "(): " : "()"); } return func.substr(0, endpos) + (add_suffix ? "(): " : "()"); } } } #define DBG_FUNC (hz::internal::format_function_msg(DBG_FUNC_PRNAME, false).c_str()) #define DBG_FUNC_MSG (hz::internal::format_function_msg(DBG_FUNC_PRNAME, true).c_str()) // Note: DBG_POS is not an object if emulated or disabled! Only valid for outputting to streams. #define DBG_POS "(function: " << DBG_FUNC_NAME << "(), file: " << DBG_FILE \ << ", line: " << DBG_LINE << ")" #define DBG_TRACE_POINT_MSG(a) debug_out_dump("default", "Trace point \"" << #a << "\" reached at " << DBG_POS << ".\n") #define DBG_TRACE_POINT_AUTO debug_out_dump("default", "Trace point reached at " << DBG_POS << ".\n") #define DBG_FUNCTION_ENTER_MSG debug_out_dump("default", "ENTER: \"" << DBG_FUNC << "\"\n") #define DBG_FUNCTION_EXIT_MSG debug_out_dump("default", "EXIT: \"" << DBG_FUNC << "\"\n") #define DBG_ASSERT_MSG(cond, msg) \ if (true) { \ if (!(cond)) \ debug_out_error("default", (msg) << "\n"); \ } else (void)0 #define DBG_ASSERT(cond) \ if (true) { \ if (!(cond)) \ debug_out_error("default", "ASSERTION FAILED: " << #cond << " at " << DBG_POS << "\n"); \ } else (void)0 // No output at all #else // do/while block is needed to: // 1. make " if(a) debug_out_info(); f(); " work correctly. // 2. require terminating semicolon. #define debug_out_dump(domain, output) if(true){}else(void)0 #define debug_out_info(domain, output) if(true){}else(void)0 #define debug_out_warn(domain, output) if(true){}else(void)0 #define debug_out_error(domain, output) if(true){}else(void)0 #define debug_out_fatal(domain, output) if(true){}else(void)0 #define debug_print_dump(domain, format, ...) if(true){}else(void)0 #define debug_print_info(domain, format, ...) if(true){}else(void)0 #define debug_print_warn(domain, format, ...) if(true){}else(void)0 #define debug_print_error(domain, format, ...) if(true){}else(void)0 #define debug_print_fatal(domain, format, ...) if(true){}else(void)0 #define DBG_FILE "" #define DBG_LINE 0 #define DBG_FUNC_NAME "" #define DBG_FUNC_PRNAME "" #define DBG_FUNC "" #define DBG_FUNC_MSG "" // Note: DBG_POS is not an object if emulated or disabled! #define DBG_POS "" #define DBG_TRACE_POINT_MSG(a) if(true){}else(void)0 #define DBG_TRACE_POINT_AUTO if(true){}else(void)0 #define DBG_FUNCTION_ENTER_MSG if(true){}else(void)0 #define DBG_FUNCTION_EXIT_MSG if(true){}else(void)0 #define DBG_ASSERT_MSG(cond, msg) if(true){}else(void)0 #define DBG_ASSERT(cond) if(true){}else(void)0 #endif // other stuff, emulated or not // #ifdef debug_begin // #undef debug_begin // #endif #define debug_begin() if(true){}else(void)0 // #ifdef debug_end // #undef debug_end // #endif #define debug_end() if(true){}else(void)0 #define debug_indent_inc(...) if(true){}else(void)0 #define debug_indent_dec(...) if(true){}else(void)0 #define debug_indent_reset() if(true){}else(void)0 #define debug_indent "" #define debug_unindent "" #define debug_resindent "" #endif #endif /// @} gsmartcontrol-1.1.4/src/hz/down_cast.h000066400000000000000000000044231417717230600177700ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2003 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_DOWN_CAST_H #define HZ_DOWN_CAST_H #include "hz_config.h" // feature macros #include "static_assert.h" // HZ_STATIC_ASSERT #include "type_properties.h" // type_is_polymorphic, type_is_pointer, type_remove_pointer namespace hz { /// Perform a down cast (pointers only!). /// This cast works with polymorphic and non-polymorphic types. /// If RTTI is disabled, it will revert to static_cast; otherwise, /// dynamic_cast is used for polymorphic types and static_cast /// for non-polymorphic types. template inline Target down_cast(const Source& arg); #if defined DISABLE_RTTI && DISABLE_RTTI template inline Target down_cast(const Source& arg) { // accept pointers only: HZ_STATIC_ASSERT(type_is_pointer::value && type_is_pointer::value, not_a_pointer); return static_cast(arg); } #else namespace internal { /// Helper for down_cast, uses dynamic_cast unless the types are non-polymorphic, /// in which case static_cast is used. template::type>::value && type_is_polymorphic::type>::value > struct down_cast_helper { static Target func(const Source& arg) { return dynamic_cast(arg); } }; /// Non-polymorphic specialization template struct down_cast_helper { static Target func(const Source& arg) { return static_cast(arg); } }; } // ns internal template inline Target down_cast(const Source& arg) { // accept pointers only: HZ_STATIC_ASSERT(type_is_pointer::value && type_is_pointer::value, not_a_pointer); return internal::down_cast_helper::func(arg); } #endif // DISABLE_RTTI } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/down_cast_test.cpp000066400000000000000000000035501417717230600213620ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_unlicense.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz_tests /// \weakgroup hz_tests /// @{ // disable libdebug, we don't link to it #undef HZ_USE_LIBDEBUG #define HZ_USE_LIBDEBUG 0 // enable libdebug emulation through std::cerr #undef HZ_EMULATE_LIBDEBUG #define HZ_EMULATE_LIBDEBUG 1 // #define DISABLE_RTTI // The first header should be then one we're testing, to avoid missing // header pitfalls. #include "down_cast.h" // #include struct TestBase { }; struct TestChild : public TestBase { }; struct TestPoly : public TestBase { virtual ~TestPoly() { } }; struct TestPoly2 : public TestPoly { virtual ~TestPoly2() { } }; /// Main function for the test int main() { // std::cerr << "TestBase: " << hz::type_is_polymorphic::value << "\n"; // std::cerr << "TestChild: " << hz::type_is_polymorphic::value << "\n"; // std::cerr << "TestPoly: " << hz::type_is_polymorphic::value << "\n"; { TestChild c; TestBase* b = &c; hz::down_cast(b); // should use static_cast (both are non-polymorphic) // static_cast(b); } { TestPoly p; TestBase* b = &p; hz::down_cast(b); // should use static_cast (base is non-polymorphic) // static_cast(b); } { TestPoly2 p; TestPoly* b = &p; hz::down_cast(b); // should use dynamic_cast (both are polymorphic) // dynamic_cast(b); // static_cast(b); } /* { TestPoly p; TestBase& b = p; hz::down_cast(b); // should throw an error, only pointers are supported. } */ return 0; } /// @} gsmartcontrol-1.1.4/src/hz/env_tools.h000066400000000000000000000167211417717230600200230ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_ENV_TOOLS_H #define HZ_ENV_TOOLS_H #include "hz_config.h" // feature macros #include #if defined _WIN32 #include // winapi stuff #include "scoped_array.h" #include "win32_tools.h" // win32_* #elif defined ENABLE_GLIB && ENABLE_GLIB #include #else #include // for stdlib.h, std::getenv #include // std::malloc #include // setenv, unsetenv, putenv #endif /** \file Environment manipulation functions. On windows, these always work with utf-8 strings. */ namespace hz { /// Get environment variable value. /// \return false if error or no such variable. inline bool env_get_value(const std::string& name, std::string& value) { if (name.empty()) return false; #if defined _WIN32 // getenv and _wgetenv are not thread-safe under windows, so use winapi. hz::scoped_array wname(hz::win32_utf8_to_utf16(name.c_str())); if (!wname) // conversion error return false; wchar_t dummy[2]; // Determine the size of needed buffer by supplying an undersized one. // If it fits, returns number of chars not including 0. If not, returns the number // of chars needed for full data, including 0. DWORD len = GetEnvironmentVariableW(wname.get(), dummy, 2); if (len == 0) { // failure return false; } if (len == 1) { // whaddayaknow, it fit len = 2; } hz::scoped_array wvalue(new wchar_t[len]); if (GetEnvironmentVariableW(wname.get(), wvalue.get(), len) != len - 1) { // failure return false; } if (wcschr(wvalue.get(), L'%') != 0) { // check for embedded variables // This always includes 0 in the returned length. Go figure. len = ExpandEnvironmentStringsW(wvalue.get(), dummy, 2); // get the needed length if (len > 0) { // not failure hz::scoped_array wvalue_exp(new wchar_t[len]); if (ExpandEnvironmentStringsW(wvalue.get(), wvalue_exp.get(), len) != len) // failure return false; hz::scoped_array val(hz::win32_utf16_to_utf8(wvalue_exp.get())); if (val) { value = val.get(); } return val; } } hz::scoped_array val(hz::win32_utf16_to_utf8(wvalue.get())); if (val) { value = val.get(); } return val; // glib version may be thread-unsafe on win32, so don't use it there. #elif defined ENABLE_GLIB && ENABLE_GLIB const char* v = g_getenv(name.c_str()); if (!v) return false; value = v; return true; #else const char* v = std::getenv(name.c_str()); if (!v) return false; value = v; return true; #endif } /// Set environment variable. If \c overwrite is false, the value won't /// be overwritten if it already exists. /// \return true if the value was written successfully. inline bool env_set_value(const std::string& name, const std::string& value, bool overwrite = true) { if (name.empty() || name.find('=') != std::string::npos) return false; #if defined _WIN32 std::string oldvalue; if (!overwrite && env_get_value(name, oldvalue)) return true; hz::scoped_array wname(hz::win32_utf8_to_utf16(name.c_str())); hz::scoped_array wvalue(hz::win32_utf8_to_utf16(value.c_str())); if (!wname || !wvalue) // conversion error return false; // Since using main() instead of wmain() causes environment tables to be // desynchronized, calling _wputenv() could correct the _wgetenv() table. // However, both of them are not thread-safe (their secure variants too), // so we avoid them. // hz::scoped_array put_arg(hz::win32_utf8_to_utf16((name + "=" + value).c_str())); // if (put_arg) // _wputenv(put_arg.get()); if (wname) { return (SetEnvironmentVariableW(wname.get(), wvalue.get()) != 0); } return false; // glib version may be thread-unsafe on win32, so don't use it there. #elif defined ENABLE_GLIB && ENABLE_GLIB return g_setenv(name.c_str(), value.c_str(), overwrite) != 0; // may be thread-unsafe #elif defined HAVE_SETENV && HAVE_SETENV // setenv returns -1 if there's insufficient space in environment // or it's an invalid name (EINVAL errno is set). return setenv(name.c_str(), value.c_str(), overwrite) == 0; #else std::string oldvalue; if (!overwrite && env_get_value(name, oldvalue)) { return true; } std::string putenv_arg = name + "=" + value; char* buf = (char*)std::malloc(putenv_arg.size() + 1); std::memcpy(buf, putenv_arg.c_str(), putenv_arg.size() + 1); return putenv(buf) == 0; // returns 0 on success. Potentially leaks, but it is the way putenv() behaves. #endif } /// Unset an environment variable inline bool env_unset_value(const std::string& name) { if (name.empty() || name.find('=') != std::string::npos) return false; #if defined _WIN32 hz::scoped_array wname(hz::win32_utf8_to_utf16(name.c_str())); if (wname) { // we could do _wputenv("name=") or _wputenv_s(...) too, but they're not thread-safe. return SetEnvironmentVariableW(wname.get(), 0); } return false; #elif defined ENABLE_GLIB && ENABLE_GLIB g_unsetenv(name.c_str()); // returns void. may not be thread-safe! return true; #elif defined HAVE_SETENV && HAVE_SETENV // unsetenv returns -1 on error with errno EINVAL set (invalid char in name). return unsetenv(name.c_str()) == 0; #else std::string putenv_arg = name + "="; char* buf = (char*)std::malloc(putenv_arg.size() + 1); std::memcpy(buf, putenv_arg.c_str(), putenv_arg.size() + 1); return putenv(buf) == 0; // returns 0 on success. Potentially leaks, but it is the way putenv() behaves. #endif } /// Temporarily change a value of an environment variable (for as long /// as an object of this class exists). class ScopedEnv { public: /// Constructor. /// \param name variable name /// \param value variable value to set /// \param do_change if false, no operation will be performed. This is useful if you need /// to conditionally set a variable (you can't practically declare a scoped variable inside /// a conditional block to be used outside it). /// \param overwrite if false and the variable already exists, don't change it. ScopedEnv(const std::string& name, const std::string& value, bool do_change = true, bool overwrite = true) : name_(name), do_change_(do_change), old_set_(false), error_(false) { if (do_change_) { old_set_ = env_get_value(name_, old_value_); if (old_set_ && !overwrite) { do_change_ = false; } else { error_ = !env_set_value(name_, value, true); } } } /// Destructor, changes back the variable to the old value ~ScopedEnv() { if (do_change_) { if (old_set_) { env_set_value(name_, old_value_, true); } else { env_unset_value(name_); } } } /// If true, there was an error setting the value. bool bad() const { return error_; } /// Check if there was a value before we set it bool get_old_set() const { return old_set_; } /// Get the old variable value std::string get_old_value() const { return old_value_; } private: std::string name_; ///< Variable name std::string old_value_; ///< Old value bool do_change_; ///< If false, don't do anything bool old_set_; ///< If false, there was no variable before we set it bool error_; ///< If true, there was an error setting the value }; } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/errno_string.h000066400000000000000000000074201417717230600205220ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2013 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_ERRNO_STRING_H #define HZ_ERRNO_STRING_H #include "hz_config.h" // feature macros #include #include // std::size_t #include // for stdarg.h, va_start, std::va_list and friends. #if defined ENABLE_GLIB && ENABLE_GLIB #include // g_strerror(), g_strsignal() #else // glibc has two versions of strerror_r: // GNU (may or may not use buf): // char *strerror_r(int errnum, char *buf, size_t buflen); // XSI (posix) (returns 0 on success): // int strerror_r(int errnum, char *buf, size_t buflen); #if defined HAVE_XSI_STRERROR_R && HAVE_XSI_STRERROR_R #include // for string.h #include // strerror_r #include // errno #elif defined HAVE_GNU_STRERROR_R && HAVE_GNU_STRERROR_R #include // for string.h #include // strerror_r #elif defined HAVE_WIN_SE_FUNCS && HAVE_WIN_SE_FUNCS #include // for string.h #include // strerror_s #include "win32_tools.h" #else #include // std::strerror #endif #endif #include "portable_snprintf.h" // portable_snprintf // Compilation options: // - Define ENABLE_GLIB to 1 to enable glib-related code (portable errno messages) namespace hz { // TODO: make messages always in utf-8 (convert from locale). /// Portable version of strerror() for std::string. /// Note: This function may return messages in native language, /// possibly using LC_MESSAGES to select the language. /// If Glib is enabled, it returns messages in UTF-8 format. inline std::string errno_string(int errno_value); // ------------------------------------------ Implementation inline std::string errno_string(int errno_value) { std::string msg; #if defined ENABLE_GLIB && ENABLE_GLIB msg = g_strerror(errno_value); // no need to free. won't return 0. message is in utf8. #else const int buf_size = 128; char buf[buf_size] = {0}; #if defined HAVE_XSI_STRERROR_R && HAVE_XSI_STRERROR_R if (strerror_r(errno_value, buf, buf_size) == 0 && *buf) { // always writes terminating 0 in those buf_size bytes. msg = buf; } else { // errno is set on error if (errno == EINVAL) { // errno may be a macro portable_snprintf(buf, buf_size, "Unknown errno: %d.", errno_value); msg = buf; } else if (errno == ERANGE) { portable_snprintf(buf, buf_size, "Insufficient buffer to store description for errno: %d.", errno_value); msg = buf; } } #elif defined HAVE_GNU_STRERROR_R && HAVE_GNU_STRERROR_R const char* rmsg = strerror_r(errno_value, buf, buf_size); if (rmsg) { msg = rmsg; } else { portable_snprintf(buf, buf_size, "Error while getting description for errno: %d.", errno_value); msg = buf; } #elif defined HAVE_WIN_SE_FUNCS && HAVE_WIN_SE_FUNCS wchar_t msg_buf[128] = {0}; // MS docs don't say exactly, but it's somewhere in 80-100 range. if (_wcserror_s(msg_buf, 128, errno_value) != 0) { // always 0-terminated portable_snprintf(buf, buf_size, "Error while getting description for errno: %d.", errno_value); msg = buf; } msg = hz::win32_utf16_to_utf8_string(msg_buf); #else // win32 and non-gnu/posix systems. // win32 has thread-safe strerror(). const char* m = std::strerror(errno_value); // no need to free. if (m) { msg = m; } else { portable_snprintf(buf, buf_size, "Error while getting description for errno: %d.", errno_value); msg = buf; } #endif #endif return msg; } } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/errno_string_test.cpp000066400000000000000000000025431417717230600221150ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_unlicense.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz_tests /// \weakgroup hz_tests /// @{ // disable libdebug, we don't link to it #undef HZ_USE_LIBDEBUG #define HZ_USE_LIBDEBUG 0 // enable libdebug emulation through std::cerr #undef HZ_EMULATE_LIBDEBUG #define HZ_EMULATE_LIBDEBUG 1 // The first header should be then one we're testing, to avoid missing // header pitfalls. #include "errno_string.h" #include #include #include // for errno.h #include // E* /// Helper macro for the test - declares E* value (e.g. EACCES). #define TEST_DECLARE_EVALUE(a) \ values[a] = #a /// Main function for the test int main() { std::map values; TEST_DECLARE_EVALUE(EACCES); TEST_DECLARE_EVALUE(EAGAIN); TEST_DECLARE_EVALUE(EBUSY); TEST_DECLARE_EVALUE(ENOENT); TEST_DECLARE_EVALUE(EEXIST); // TEST_DECLARE_EVALUE(ELOOP); // not on win32 for(std::map::const_iterator iter = values.begin(); iter != values.end(); ++iter) { std::cout << iter->second << ": " << hz::errno_string(iter->first) << "\n"; } return 0; } /// @} gsmartcontrol-1.1.4/src/hz/error.h000066400000000000000000000175061417717230600171460ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_ERROR_H #define HZ_ERROR_H #include "hz_config.h" // feature macros #include #include // for std::exception specialization #if !(defined DISABLE_RTTI && DISABLE_RTTI) #include // std::type_info #endif // #if defined ENABLE_GLIBMM && ENABLE_GLIBMM // # include // for Glib::Error specialization // #endif #include "debug.h" // DBG_ASSERT #include "errno_string.h" // hz::errno_string #include "process_signal.h" // hz::signal_to_string #include "bad_cast_exception.h" #include "i18n.h" // HZ__ /** \file Compilation options: - Define DISABLE_RTTI to disable RTTI checks and typeinfo-getter functions. NOT recommended. - Define ENABLE_GLIBMM to 1 to enable glibmm-related code (mainly utf8 string messages and Glib::Error specialization). Note that this will also enable GLIB. */ namespace hz { /** \file Predefined error types are: "errno", "signal" (child exited with signal). */ /// Error level (severity) struct ErrorLevel { /// Error level (severity) enum level_t { none = 0, ///< No error dump = 1 << 0, ///< Dump info = 1 << 1, ///< Informational (default) warn = 1 << 2, ///< Warning error = 1 << 3, ///< Error fatal = 1 << 4 ///< Fatal }; }; template class Error; /// Base class for Error class ErrorBase { public: /// Severity level typedef ErrorLevel::level_t level_t; DEFINE_BAD_CAST_EXCEPTION(type_mismatch, "Error type mismatch. Original type: \"%s\", requested type: \"%s\".", "Error type mismatch."); /// Constructor ErrorBase(const std::string& type_, level_t level_, const std::string& msg) : type(type_), level(level_), message(msg) { } /// Constructor ErrorBase(const std::string& type_, level_t level_) : type(type_), level(level_) { } /// Virtual destructor virtual ~ErrorBase() { } /// Clone this object virtual ErrorBase* clone() = 0; // needed for copying by base pointers #if !(defined DISABLE_RTTI && DISABLE_RTTI) /// Get std::type_info for the error code type. virtual const std::type_info& get_code_type() const = 0; #endif /// Get error code of type \c CodeMemberType template CodeMemberType get_code() const // this may throw on bad cast! { #if !(defined DISABLE_RTTI && DISABLE_RTTI) if (get_code_type() != typeid(CodeMemberType)) THROW_CUSTOM_BAD_CAST(type_mismatch, get_code_type(), typeid(CodeMemberType)); #endif return static_cast*>(this)->code; } /// Get error code of type \c CodeMemberType template bool get_code(CodeMemberType& put_it_here) const // this doesn't throw { #if !(defined DISABLE_RTTI && DISABLE_RTTI) if (get_code_type() != typeid(CodeMemberType)) return false; #endif put_it_here = static_cast*>(this)->code; return true; } /// Increase the level (severity) of the error level_t level_inc() { if (level == ErrorLevel::fatal) return level; return (level = static_cast(static_cast(level) << 1)); } /// Decrease the level (severity) of the error level_t level_dec() { if (level == ErrorLevel::none) return level; return (level = static_cast(static_cast(level) >> 1)); } /// Get error level (severity) level_t get_level() const { return level; } /// Get error type std::string get_type() const { return type; } /// Get error message std::string get_message() const { return message; } // no set_type, set_message - we don't allow changing those. protected: std::string type; ///< Error type level_t level; ///< Error severity std::string message; ///< Error message }; // Provides some common stuff for Error to ease template specializations. template class ErrorCodeHolder : public ErrorBase { protected: /// Constructor ErrorCodeHolder(const std::string& type_, ErrorLevel::level_t level_, const CodeType& code_, const std::string& msg) : ErrorBase(type_, level_, msg), code(code_) { } /// Constructor ErrorCodeHolder(const std::string& type_, ErrorLevel::level_t level_, const CodeType& code_) : ErrorBase(type_, level_), code(code_) { } public: #if !(defined DISABLE_RTTI && DISABLE_RTTI) // Reimplemented from ErrorBase const std::type_info& get_code_type() const { return typeid(CodeType); } #endif CodeType code; ///< Error code. We have a class specialization for references too }; // Specialization for void, helpful for custom messages template<> class ErrorCodeHolder : public ErrorBase { protected: /// Constructor ErrorCodeHolder(const std::string& type_, ErrorLevel::level_t level_, const std::string& msg) : ErrorBase(type_, level_, msg) { } public: #if !(defined DISABLE_RTTI && DISABLE_RTTI) // Reimplemented from ErrorBase const std::type_info& get_code_type() const { return typeid(void); } #endif }; /// Error class. Stores an error code of type \c CodeType. /// Instantiate this in user code. template class Error : public ErrorCodeHolder { public: /// Constructor Error(const std::string& type_, ErrorLevel::level_t level_, const CodeType& code_, const std::string& msg) : ErrorCodeHolder(type_, level_, code_, msg) { } // Reimplemented from ErrorBase ErrorBase* clone() { return new Error(ErrorCodeHolder::type, ErrorCodeHolder::level, ErrorCodeHolder::code, ErrorCodeHolder::message); } }; /// Error class specialization for void (helpful for custom messages). template<> class Error : public ErrorCodeHolder { public: Error(const std::string& type_, ErrorLevel::level_t level_, const std::string& msg) : ErrorCodeHolder(type_, level_, msg) { } // Reimplemented from ErrorBase ErrorBase* clone() { return new Error(ErrorCodeHolder::type, ErrorCodeHolder::level, ErrorCodeHolder::message); } }; /// Error class specialization for int (can be used for signals, errno). /// Message is automatically constructed if not provided. template<> class Error : public ErrorCodeHolder { public: /// Constructor Error(const std::string& type_, ErrorLevel::level_t level_, int code_, const std::string& msg) : ErrorCodeHolder(type_, level_, code_, msg) { } /// Constructor Error(const std::string& type_, ErrorLevel::level_t level_, int code_) : ErrorCodeHolder(type_, level_, code) { if (type == "errno") { message = hz::errno_string(code_); } else if (type == "signal") { // hz::signal_string should be translated already message = HZ__("Child exited with signal: ") + hz::signal_to_string(code_); } else { // nothing else supported here. use constructor with a message. DBG_ASSERT(0); } } // Reimplemented from ErrorBase ErrorBase* clone() { return new Error(ErrorCodeHolder::type, ErrorCodeHolder::level, ErrorCodeHolder::code, ErrorCodeHolder::message); } }; /* namespace { Error e1("type1", ErrorLevel::info, 6); Error e2("type2", ErrorLevel::fatal, "asdasd", "aaa"); std::bad_alloc ex3; // Error e3("type3", ErrorBase::info, static_cast(ex3)); Error e3("type3", ErrorLevel::info, ex3); Glib::IOChannelError ex4(Glib::IOChannelError::FILE_TOO_BIG, "message4"); Error e4("type4", ErrorLevel::info, ex4); } */ } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/error_holder.h000066400000000000000000000162161417717230600205000ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_ERROR_HOLDER_H #define HZ_ERROR_HOLDER_H #include "hz_config.h" // feature macros #include #include "ptr_container.h" #include "sync.h" #include "sync_multilock.h" // SyncMultiLock #include "error.h" #include "debug.h" namespace hz { /// A class wishing to implement Error holding storage should inherit this. /// Unless specified otherwise, all methods are thread-safe if the /// thread-safe locking policy is provided. /// \tparam LockPolicy_ A class with methods to lock / unlock current object access /// for use in multi-threaded environments. See sync.h for more info. template class ErrorHolder { public: typedef std::vector error_list_t; ///< A list of ErrorBase* pointers typedef ptr_container ptr_error_list_t; ///< A list of auto-deleted pointers to error_list_t typedef LockPolicy_ ErrorLockPolicy; ///< Locking policy typedef typename ErrorLockPolicy::ScopedLock ErrorScopedLock; ///< Scoped lock class /// Make it its own friend for mutex visibility template friend class ErrorHolder; /// Virtual destructor virtual ~ErrorHolder() { } /// Add an error to the error list template void push_error(const E& e, bool do_lock = true) { ErrorScopedLock locker(error_object_mutex_, do_lock); E* copied = new E(e); // clone e. it will be deleted when the list is destroyed. errors_.push_back(copied); error_warn(copied); } /// Remove last error from the error list template void pop_last_error(E& e, bool do_lock = true) { ErrorScopedLock locker(error_object_mutex_, do_lock); e = static_cast(errors_.pop_back()); } /// Import errors from another object template void import_errors(ErrorHolder& other, bool do_lock_this = true, bool do_lock_other = true) { if (this == reinterpret_cast(&other)) return; // always maintain the same locking order to avoid deadlocks. /* if (&error_object_mutex_ < reinterpret_cast(&(other.error_object_mutex_))) { if (do_lock_this) ErrorLockPolicy::lock(error_object_mutex_); if (do_lock_other) ErrorHolder::ErrorLockPolicy::lock(other.error_object_mutex_); } else { if (do_lock_other) ErrorHolder::ErrorLockPolicy::lock(other.error_object_mutex_); if (do_lock_this) ErrorLockPolicy::lock(error_object_mutex_); } */ SyncMultiLock::ErrorLockPolicy::Mutex> locker(error_object_mutex_, other.error_object_mutex_, do_lock_this, do_lock_other); error_list_t tmp; // non-pointer-basec container other.get_errors_cloned(tmp, false); errors_.insert(errors_.end(), tmp.begin(), tmp.end()); // copies the deletion ownership too /* if (&error_object_mutex_ < reinterpret_cast(&(other.error_object_mutex_))) { if (do_lock_this) ErrorLockPolicy::unlock(error_object_mutex_); if (do_lock_other) ErrorHolder::ErrorLockPolicy::unlock(other.error_object_mutex_); } else { if (do_lock_other) ErrorHolder::ErrorLockPolicy::unlock(other.error_object_mutex_); if (do_lock_this) ErrorLockPolicy::unlock(error_object_mutex_); } */ } /// Check if there are any errors in this class. bool has_errors(bool do_lock = true) const { ErrorScopedLock locker(error_object_mutex_, do_lock); return !errors_.empty(); } /// Get a list of errors. /// NOTE: You MUST do additional locking (and possibly pass do_lock=false here) /// if you intend to use the elements of the returned array. If you don't do that, /// this class may just delete its pointers and you'll be left with dangling pointers. error_list_t get_errors(bool do_lock = true) const { ErrorScopedLock locker(error_object_mutex_, do_lock); // copy the pointers to deque, return it. error_list_t ret(errors_.begin(), errors_.end()); return ret; } /// Get a cloned list of errors. /// You MUST delete the elements of the returned container! template void get_errors_cloned(ReturnedContainer& put_here, bool do_lock = true) const { ErrorScopedLock locker(error_object_mutex_, do_lock); errors_.clone_by_method_to(put_here); // calls clone() on each element } // These functions are the recommended way to access the errors container. /// A begin() function for the error list. error_list_t::iterator errors_begin(bool do_lock = true) { ErrorScopedLock locker(error_object_mutex_, do_lock); return errors_.begin(); } /// A begin() function for the error list (const version). error_list_t::const_iterator errors_begin(bool do_lock = true) const { ErrorScopedLock locker(error_object_mutex_, do_lock); return errors_.begin(); } /// An end() function for the error list. error_list_t::iterator errors_end(bool do_lock = true) { ErrorScopedLock locker(error_object_mutex_, do_lock); return errors_.end(); } /// An end() function for the error list (const version). error_list_t::const_iterator errors_end(bool do_lock = true) const { ErrorScopedLock locker(error_object_mutex_, do_lock); return errors_.end(); } /// Clear the error list void clear_errors(bool do_lock = true) { ErrorScopedLock locker(error_object_mutex_, do_lock); errors_.clear(); } /// Lock the error list void errors_lock() { ErrorLockPolicy::lock(error_object_mutex_); } /// Unlock the error list void errors_unlock() { ErrorLockPolicy::unlock(error_object_mutex_); } /// This function is called every time push_error() is invoked. /// The default implementation prints the message using libdebug. /// Override in children if needed. virtual void error_warn(ErrorBase* e) { std::string msg = e->get_type() + ": " + e->get_message() + "\n"; ErrorLevel::level_t level = e->get_level(); // use debug macros, not functions (to allow complete removal through preprocessor). switch (level) { case ErrorLevel::none: break; case ErrorLevel::dump: debug_out_dump("hz", msg); break; case ErrorLevel::info: debug_out_info("hz", msg); break; case ErrorLevel::warn: debug_out_warn("hz", "Warning: " << msg); break; case ErrorLevel::error: debug_out_error("hz", "Error: " << msg); break; case ErrorLevel::fatal: debug_out_fatal("hz", "Fatal: " << msg); break; } } protected: ptr_error_list_t errors_; ///< Error list. The newest errors at the end. mutable typename ErrorLockPolicy::Mutex error_object_mutex_; ///< Mutex to protect multi-threaded access. }; /// An error holder with no locking. typedef ErrorHolder ErrorHolderST; /// An error holder that does its own locking through mutexes. typedef ErrorHolder ErrorHolderMT; } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/exceptions.h000066400000000000000000000046331417717230600201730ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_EXCEPTIONS_H #define HZ_EXCEPTIONS_H #include "hz_config.h" // feature macros // These headers may be needed for object creation even if exceptions are disabled. // #include // std::exception // #include // standard exceptions, derived from std::exception /** Define DISABLE_EXCEPTIONS=1 to disable exception use (only applicable when using the macros defined below). Useful for e.g. gcc's -fno-exceptions, etc... */ /// \def THROW_FATAL(ex) /// If you use -fno-exceptions gcc switch (or similar), define /// DISABLE_EXCEPTIONS and no throw statement will occur if /// you use THROW_FATAL instead of throw (calling this will cause abort() /// after printing an error message to stderr). /// Otherwise, it's equivalent to a simple throw ex. /// Note: The exceptions MUST have a what() member function for this to work. /// Do NOT put ex into parentheses. gcc-3.3 gives syntax errors about that (huh?). /// \def THROW_WARN(ex) /// Same as THROW_FATAL(ex), but no abort() in case of no-exceptions. #if !(defined DISABLE_EXCEPTIONS && DISABLE_EXCEPTIONS) #define THROW_FATAL(ex) \ throw ex #define THROW_WARN(ex) \ throw ex #else // no-exceptions alternative: #include // std::abort() #include // std::fprintf // #include // std::cerr // We could use std::exit(EXIT_FAILURE), but it's somewhat inconsistent // in regards of stack unwinding and destructors, so use abort() // std::cerr << "Fatal exception thrown (exceptions are disabled): " << ex.what() << std::endl; #define THROW_FATAL(ex) \ if (true) { \ const char* ex_what = ex.what(); \ std::fprintf(stderr, "Fatal exception thrown (exceptions are disabled): %s\n", ex_what ? ex_what : "[unknown]"); \ std::abort(); \ } else (void)0 // std::cerr << "Warn exception thrown (exceptions are disabled): " << ex.what() << std::endl; #define THROW_WARN(ex) \ if (true) { \ std::fprintf(stderr, "Warn exception thrown (exceptions are disabled): %s\n", ex_what ? ex_what : "[unknown]"); \ } else (void)0 #endif #endif /// @} gsmartcontrol-1.1.4/src/hz/format_unit.h000066400000000000000000000211561417717230600203400ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2003 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_FORMAT_UNIT_H #define HZ_FORMAT_UNIT_H #include "hz_config.h" // feature macros #include #include // std::size_t #include // for time.h, std::strftime, std::time, std::localtime, ... #include "cstdint.h" #include "locale_tools.h" // hz::ScopedCLocale #include "string_num.h" // hz::number_to_string #include "i18n.h" // HZ_NC_, HZ_C_ // HAVE_WIN_SE_FUNCS means localtime_s #if (defined HAVE_WIN_SE_FUNCS && HAVE_WIN_SE_FUNCS) \ || !(defined HAVE_REENTRANT_LOCALTIME && HAVE_REENTRANT_LOCALTIME) #include // localtime_r, localtime_s #endif namespace hz { /// Format byte or bit size in human-readable way, e.g. KB, mb, etc... /// Note that kilobit always means 1000 bits, there's no confusion with that (as opposed to kilobyte). /// This function honors the SI rules, e.g. GiB for binary, GB for decimal (as defined by SI). inline std::string format_size(uint64_t size, bool use_decimal = false, bool size_is_bits = false) { const uint64_t multiplier = (use_decimal ? 1000 : 1024); const uint64_t kb_size = multiplier; // kilo const uint64_t mb_size = kb_size * multiplier; // mega const uint64_t gb_size = mb_size * multiplier; // giga const uint64_t tb_size = gb_size * multiplier; // tera const uint64_t pb_size = tb_size * multiplier; // peta const uint64_t eb_size = pb_size * multiplier; // exa // these are beyond the size of uint64_t. // const uint64_t zb_size = eb_size * multiplier; // zetta // const uint64_t yb_size = zb_size * multiplier; // yotta static const char* const names[] = { HZ_NC_("filesize", " B"), // bytes decimal HZ_NC_("filesize", " B"), // bytes binary HZ_NC_("filesize", " bit"), // bits decimal HZ_NC_("filesize", " bit"), // bits binary. note: 2 bit, not 2 bits. HZ_NC_("filesize", " KB"), HZ_NC_("filesize", " KiB"), HZ_NC_("filesize", " Kbit"), HZ_NC_("filesize", " Kibit"), HZ_NC_("filesize", " MB"), HZ_NC_("filesize", " MiB"), HZ_NC_("filesize", " Mbit"), HZ_NC_("filesize", " Mibit"), HZ_NC_("filesize", " GB"), HZ_NC_("filesize", " GiB"), HZ_NC_("filesize", " Gbit"), HZ_NC_("filesize", " Gibit"), HZ_NC_("filesize", " TB"), HZ_NC_("filesize", " TiB"), HZ_NC_("filesize", " Tbit"), HZ_NC_("filesize", " Tibit"), HZ_NC_("filesize", " PB"), HZ_NC_("filesize", " PiB"), HZ_NC_("filesize", " Pbit"), HZ_NC_("filesize", " Pibit"), HZ_NC_("filesize", " EB"), HZ_NC_("filesize", " EiB"), HZ_NC_("filesize", " Ebit"), HZ_NC_("filesize", " Eibit") }; const int addn = static_cast(!use_decimal) + (static_cast(size_is_bits) * 2); if (size >= eb_size) { // exa return hz::number_to_string(static_cast(size) / static_cast(eb_size), 2, true) + HZ_RC_("filesize", names[(6 * 4) + addn]); } if (size >= pb_size) { // peta return hz::number_to_string(static_cast(size) / static_cast(pb_size), 2, true) + HZ_RC_("filesize", names[(5 * 4) + addn]); } if (size >= tb_size) { // tera return hz::number_to_string(static_cast(size) / static_cast(tb_size), 2, true) + HZ_RC_("filesize", names[(4 * 4) + addn]); } if (size >= gb_size) { // giga return hz::number_to_string(static_cast(size) / static_cast(gb_size), 2, true) + HZ_RC_("filesize", names[(3 * 4) + addn]); } if (size >= mb_size) { // mega return hz::number_to_string(static_cast(size) / static_cast(mb_size), 2, true) + HZ_RC_("filesize", names[(2 * 4) + addn]); } if (size >= kb_size) { // kilo return hz::number_to_string(static_cast(size) / static_cast(kb_size), 2, true) + HZ_RC_("filesize", names[(1 * 4) + addn]); } return hz::number_to_string(size) + HZ_RC_("filesize", names[(0 * 4) + addn]); } /// Format time length (e.g. 330 seconds) in a human-readable manner /// (e.g. "5 min 30 sec"). inline std::string format_time_length(int64_t secs) { // don't use uints here - there bring bugs. const int64_t min_size = 60; const int64_t hour_size = min_size * 60; const int64_t day_size = hour_size * 24; if (secs >= 100 * hour_size) { int64_t days = (secs + day_size / 2) / day_size; // time in days (rounded to nearest) int64_t sec_diff = secs - days * day_size; // difference between days and actual time (may be positive or negative) if (days < 10) { // if less than 10 days, display hours too // if there's more than half an hour missing from complete day, add a day. // then add half an hour and convert to hours. int64_t hours = ((sec_diff < (-hour_size / 2) ? sec_diff + day_size : sec_diff) + hour_size / 2) / hour_size; if (hours > 0 && sec_diff < (-hour_size / 2)) days--; return hz::number_to_string(days) + " " + HZ_C_("time", "d") + " " + hz::number_to_string(hours) + " " + HZ_C_("time", "h"); } else { // display days only return hz::number_to_string(days) + " " + HZ_C_("time", "d"); } } else if (secs >= 100 * min_size) { int64_t hours = (secs + hour_size / 2) / hour_size; // time in hours (rounded to nearest) int64_t sec_diff = secs - hours * hour_size; if (hours < 10) { // if less than 10 hours, display minutes too int64_t minutes = ((sec_diff < (-min_size / 2) ? sec_diff + hour_size : sec_diff) + min_size / 2) / min_size; if (minutes > 0 && sec_diff < (-min_size / 2)) hours--; return hz::number_to_string(hours) + " " + HZ_C_("time", "h") + " " + hz::number_to_string(minutes) + " " + HZ_C_("time", "min"); } else { // display hours only return hz::number_to_string(hours) + " " + HZ_C_("time", "h"); } } else if (secs >= 100) { int64_t minutes = (secs + min_size / 2) / min_size; // time in minutes (rounded to nearest) return hz::number_to_string(minutes) + " " + HZ_C_("time", "min"); } return hz::number_to_string(secs) + " " + HZ_C_("time", "sec"); } /// Format a date specified by \c ltmp (pointer to tm structure). /// See strftime() documentation for format details. To print ISO datetime /// use "%Y-%m-%d %H:%M:%S" (sometimes 'T' is used instead of space). inline std::string format_date(const std::string& format, const struct std::tm* ltmp, bool use_locale = true) { if (!ltmp || format.empty()) return std::string(); // try to guess the appropriate buffer size for strftime std::size_t buf_size = format.size() + (format.size() / 2); // this should be enough for most of them std::size_t written = 0; std::string res; int iterations = 0; // strftime is locale-dependent. unless use_locale is true, we switch to classic locale. const ScopedCLocale classic(!use_locale); do { char* buf = new char[buf_size]; // note: this actually may return 0 on success (e.g. for %p), but I don't know a // correct way to distinguish between error and legal 0. complain to strftime designers. written = std::strftime(buf, buf_size, format.c_str(), ltmp); if (written) { buf[written] = '\0'; // strftime specifically leaves a place for this. res = buf; } else { buf_size *= 2; } delete[] buf; ++iterations; } while (!written && iterations <= 4); // limit to 4 iterations (that's 2^4 original size) to avoid infinite loops. return res; } /// Format a date specified by \c timet (seconds since Epoch). /// See strftime() documentation for format details. inline std::string format_date(const std::string& format, std::time_t timet, bool use_locale = true) { #if defined HAVE_WIN_SE_FUNCS && HAVE_WIN_SE_FUNCS struct std::tm ltm; if (localtime_s(<m, &timet) != 0) // shut up msvc (it thinks std::localtime() is unsafe) return std::string(); const struct std::tm* ltmp = <m; #elif defined HAVE_REENTRANT_LOCALTIME && HAVE_REENTRANT_LOCALTIME const struct std::tm* ltmp = std::localtime(&timet); #else struct std::tm ltm; if (!localtime_r(&timet, <m)) // use reentrant localtime_r (posix/bsd and related) return std::string(); const struct std::tm* ltmp = <m; #endif return format_date(format, ltmp, use_locale); } /// Format current date. /// See strftime() documentation for format details. inline std::string format_date(const std::string& format, bool use_locale = true) { const std::time_t timet = std::time(NULL); if (timet == static_cast(-1)) return std::string(); return format_date(format, timet, use_locale); } } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/format_unit_test.cpp000066400000000000000000000053331417717230600217310ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_unlicense.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz_tests /// \weakgroup hz_tests /// @{ // disable libdebug, we don't link to it #undef HZ_USE_LIBDEBUG #define HZ_USE_LIBDEBUG 0 // enable libdebug emulation through std::cerr #undef HZ_EMULATE_LIBDEBUG #define HZ_EMULATE_LIBDEBUG 1 // The first header should be then one we're testing, to avoid missing // header pitfalls. #include "format_unit.h" #include #include "cstdint.h" /// Main function for the test int main() { std::cerr << hz::format_size(3 * 1024 * 1024) << "\n"; // 3 MiB std::cerr << hz::format_size(4LL * 1000 * 1000, true, true) << "\n"; // 4 Mbit std::cerr << hz::format_size(100LL * 1000 * 1000 * 1000) << "\n"; // 100 GB in GiB (aka how hard disk manufactures screw you) std::cerr << hz::format_size(100LL * 1024 * 1024, true) << "\n"; // 100 MiB in decimal GB std::cerr << hz::format_size(5) << "\n"; // 5 bytes std::cerr << hz::format_size(6, true, true) << "\n"; // 6 bits std::cerr << hz::format_size(uint64_t(2.5 * 1024)) << "\n"; // 2.5 KiB std::cerr << hz::format_size(uint64_t(2.5 * 1024 * 1024)) << "\n"; // 2.5 MiB std::cerr << hz::format_size(uint64_t(2.5 * 1024 * 1024 * 1024)) << "\n"; // 2.5 GiB std::cerr << hz::format_size(uint64_t(2.5 * 1024 * 1024 * 1024 * 1024)) << "\n"; // 2.5 TiB std::cerr << hz::format_size(uint64_t(2.5 * 1024 * 1024 * 1024 * 1024 * 1024)) << "\n"; // 2.5 PiB std::cerr << hz::format_size(uint64_t(2.5 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024)) << "\n"; // 2.5 EiB std::cerr << hz::format_size(uint64_t(1000204886016ULL), true) << "\n"; // common size of 1 TiB hdd in decimal const uint64_t min_size = 60; const uint64_t hour_size = min_size * 60; const uint64_t day_size = hour_size * 24; std::cerr << hz::format_time_length(5) << "\n"; // 5 sec std::cerr << hz::format_time_length(uint64_t(5.5 * min_size)) << "\n"; // 5.5 min std::cerr << hz::format_time_length(130 * min_size) << "\n"; // 130 min std::cerr << hz::format_time_length(uint64_t(5.5 * hour_size)) << "\n"; // 5.5 hours std::cerr << hz::format_time_length(uint64_t(24.33 * hour_size)) << "\n"; // 24 hours, 20 minutes std::cerr << hz::format_time_length(130 * hour_size + 30 * min_size) << "\n"; // 130.5 hours std::cerr << hz::format_time_length(5 * day_size + 15 * hour_size + 30 * min_size) << "\n"; // 5 days, 15 hours, 30 minutes std::cerr << hz::format_time_length(20 * day_size - 8 * hour_size) << "\n"; // 19 days, 16 hours return 0; } /// @} gsmartcontrol-1.1.4/src/hz/fs_common.h000066400000000000000000000024161417717230600177670ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2013 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_FS_COMMON_H #define HZ_FS_COMMON_H #include "hz_config.h" // feature macros // #if defined ENABLE_GLIB && ENABLE_GLIB // #include // lotsa stuff... // #endif // This header is mainly internal. namespace hz { /// \var const char DIR_SEPARATOR /// Directory path separator (character) /// \var static const char* const DIR_SEPARATOR_S /// Directory path separator (string) // these have internal linkage (const integral types). // there's no point in keeping glib here only for this // #if defined ENABLE_GLIB && ENABLE_GLIB // const char DIR_SEPARATOR = G_DIR_SEPARATOR; // static const char* const DIR_SEPARATOR_S = G_DIR_SEPARATOR_S; #if defined _WIN32 const char DIR_SEPARATOR = '\\'; // no need to be static, it's integral static const char* const DIR_SEPARATOR_S = "\\"; #else const char DIR_SEPARATOR = '/'; static const char* const DIR_SEPARATOR_S = "/"; #endif } // ns hz #endif /// @} gsmartcontrol-1.1.4/src/hz/fs_dir.h000066400000000000000000000475071417717230600172670ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_FS_DIR_H #define HZ_FS_DIR_H #include "hz_config.h" // feature macros #include #include #include // dirent needs this #include "local_algo.h" // shell_sort #include "string_wcmatch.h" // string_wcmatch #include "fs_path.h" // Path #include "fs_dir_platform.h" // directory_* /** \file Filesystem directory access. This API accepts/gives utf-8 filenames/paths on win32, current locale filenames/paths on others (just like glib). */ namespace hz { // -------------------------------------- Iterators class Dir; namespace internal { /// Directory iterator. /// Note: There is no const version of this because of technical limitations. /// All iterators to one Dir point to the same entry. /// Note: Don't forget that there are "." and ".." entries. /// Use is_special() to verify. class DirIterator { public: typedef void iterator_category; ///< STL compatibility typedef std::string value_type; ///< STL compatibility typedef int difference_type; ///< STL compatibility typedef void pointer; ///< STL compatibility typedef value_type reference; ///< STL compatibility /// Native handle type typedef directory_entry_handle_type handle_type; /// Constructor DirIterator() : dir_(NULL), entry_(NULL) { } /// Constructor DirIterator(Dir* dir, handle_type entry) : dir_(dir), entry_(entry) { } /// Copy constructor DirIterator(const DirIterator& other) { dir_ = other.dir_; entry_ = other.entry_; } /// Assignment operator DirIterator& operator= (const DirIterator& other) { dir_ = other.dir_; entry_ = other.entry_; return *this; } /// Comparison operator bool operator== (const DirIterator& other) const { return entry_ == other.entry_; } /// Comparison operator bool operator!= (const DirIterator& other) const { return !(*this == other); } /// Advance to next entry inline DirIterator& operator++ (); // prefix /// Advance to next entry (postfix operator). /// Returns void because of technical limitations. inline void operator++ (int); /// Same as name() (dereference operator) inline std::string operator* () const; /// Provides operations on return value of name() inline std::string operator-> () const; /// Get entry name inline std::string name() const; /// Get full path inline std::string path() const; /// Returns true if it's "." or "..". inline bool is_special() const; /// Get native handle inline handle_type get_handle(); private: Dir* dir_; ///< Directory we're operating with handle_type entry_; ///< Native handle }; } // ns // -------------------------------------- Sorting and filtering functors /// Filtering functor for Dir::list(). No filtering - leave all entries. struct DirFilterNone { /// Whether to use operator() with FsPaths instead of strings. bool use_path_objects() { return false; } /// Entry filterer bool operator() (const std::string& entry_name) { return true; } /// Path filterer bool operator() (FsPath& path) { return true; } }; enum { // flags // these may be ORed DIR_LEAVE_FILE = 1 << 0, ///< Leave files DIR_LEAVE_DIR = 1 << 1, ///< Leave directories DIR_LEAVE_REGULAR = 1 << 2, ///< Leave regular files DIR_LEAVE_SYMLINK = 1 << 3, ///< Leave symlinks DIR_LEAVE_ALL = DIR_LEAVE_FILE | DIR_LEAVE_DIR ///< Leave everything (any entry is either file or directory) }; /// Filtering functor for Dir::list(). This one uses flag-based filtering. /// Note: If an error occurs while checking flags, the entry is filtered out. struct DirFilterByFlags { /// Constructor. Flags are OR-ed combinations of DIR_LEAVE_*. DirFilterByFlags(int flags) : flags_(flags) { } /// Whether to use operator() with FsPaths instead of strings. bool use_path_objects() { return true; } /// Entry filterer bool operator() (const std::string& entry_name) { return false; } /// Path filterer bool operator() (FsPath& path) { if ((flags_ & DIR_LEAVE_FILE) && path.is_file()) return true; if ((flags_ & DIR_LEAVE_DIR) && path.is_dir()) return true; if ((flags_ & DIR_LEAVE_REGULAR) && path.is_regular()) return true; if ((flags_ & DIR_LEAVE_SYMLINK) && path.is_symlink()) return true; return false; } private: int flags_; ///< Flags }; /// Filtering functor for Dir::list(). This one uses glob (aka wildcard: ?, *, []) filtering. /// See hz::string_wcmatch for exact pattern syntax. struct DirFilterWc { /// Constructor DirFilterWc(const std::string& pattern) : pattern_(pattern) { } /// whether to use operator() with FsPaths instead of strings. bool use_path_objects() { return false; } /// Entry filterer bool operator() (const std::string& entry_name) { return hz::string_wcmatch(pattern_, entry_name); } /// Path filterer bool operator() (FsPath& path) { return true; } private: std::string pattern_; ///< Wildcard pattern }; // ----------------- Sorting /// Directory sort flags enum dir_sort_flag_t { DIR_SORT_DIRS_FIRST, ///< List directories first DIR_SORT_FILES_FIRST, ///< List files first DIR_SORT_MIXED ///< List files and directories in an interleaved way }; /// A sorting functor for Dir::list(). This one does no sorting. struct DirSortNone { /// This is called before the less function void set_dir(const std::string& dir) { } /// Whether to use operator() with FsPaths instead of strings. bool use_path_objects() { return false; } /// "less" function using entry names bool operator() (const std::string& entry_name1, const std::string& entry_name2) { return true; // always return "less", which should cause no sorting. } /// "less" function using path objects bool operator() (FsPath& path1, FsPath& path2) { return true; // always return "less", which should cause no sorting. } }; /// Base class for various sorters of Dir::list(). template struct DirSortBase { /// Constructor. DirSortBase(dir_sort_flag_t flag) : flag_(flag) { } /// This is called before the less function void set_dir(const std::string& dir) { dir_ = dir; } /// "less" function using entry names. /// Use for DIR_SORT_MIXED only! bool operator() (const std::string& entry_name1, const std::string& entry_name2) { return static_cast(this)->compare(entry_name1, entry_name2); } /// "less" function using path objects bool operator() (FsPath& path1, FsPath& path2) { bool e1_dir = path1.is_dir(), e2_dir = path2.is_dir(); if (e1_dir == e2_dir) // same type return static_cast(this)->compare(path1, path2); if (flag_ == DIR_SORT_DIRS_FIRST) return e1_dir; // (e1 < e2) if e1 is dir if (flag_ == DIR_SORT_FILES_FIRST) return !e1_dir; // (e1 < e2) if e1 is file return true; // won't reach this } protected: dir_sort_flag_t flag_; ///< Directory sort flag std::string dir_; ///< The directory name }; /// A sorting functor for Dir::list(). This one does alphanumeric sorting. struct DirSortAlpha : public DirSortBase { /// Constructor DirSortAlpha(dir_sort_flag_t flag = DIR_SORT_DIRS_FIRST) : DirSortBase(flag) { } /// Whether to use operator() with FsPaths instead of strings. bool use_path_objects() { // for mixed comparison we don't need to construct the FsPaths. return (flag_ != DIR_SORT_MIXED); } /// "less" function (called by parent) bool compare(const std::string& entry_name1, const std::string& entry_name2) { return entry_name1 < entry_name2; } /// "less" function (called by parent) bool compare(FsPath& path1, FsPath& path2) { return path1.str() < path2.str(); } }; /// A sorting functor for Dir::list(). This one does timestamp-based /// (descending order) sorting. struct DirSortMTime : public DirSortBase { /// Constructor DirSortMTime(dir_sort_flag_t flag = DIR_SORT_DIRS_FIRST) : DirSortBase(flag) { } /// Whether to use operator() with FsPaths instead of strings. bool use_path_objects() { return true; } /// "less" function (called by parent) bool compare(const std::string& entry_name1, const std::string& entry_name2) { return true; // dummy } // "less" function (called by parent) bool compare(FsPath& path1, FsPath& path2) { time_t e1_ts = 0, e2_ts = 0; if (!path1.get_last_modified(e1_ts) || !path2.get_last_modified(e2_ts) || (e1_ts == e2_ts)) path1.str() < path2.str(); // error or similar timestamps, fall back to this. return e1_ts < e2_ts; } }; // -------------------------------------- Main Dir class /// This class represents a directory which is opened in /// constructor and closed in destructor. class Dir : public FsPath { public: typedef internal::DirIterator iterator; ///< Iterator for traversing the directory entries typedef directory_handle_type handle_type; ///< Native handle type for directory typedef directory_entry_handle_type entry_handle_type; ///< Native handle type for directory entries /// Create a Dir object. Dir() : dir_(NULL), entry_(NULL) { } /// Create a Dir object. This will NOT open the directory. Dir(const FsPath& path) : dir_(NULL), entry_(NULL) { set_path(path.get_path()); } /// Create a Dir object. This will NOT open the directory. Dir(const std::string& path) : dir_(NULL), entry_(NULL) { set_path(path); } private: // Between move semantics (confusing and error-prone) and denying copying, // I choose to deny. /// Private copy constructor to deny copying. Dir(const Dir& other); /// Private assignment operator to deny copying Dir& operator= (const Dir& other); public: /* // Copy constructor. Move semantics are implemented - // the handle ownership is transferred exclusively. Dir(Dir& other) : dir_(NULL), entry_(NULL) { *this = other; // operator= } // Move semantics, as with copy constructor. inline Dir& operator= (Dir& other); */ /// Destructor which invokes close() if needed. virtual ~Dir() { if (dir_) close(); } /// Open the directory. The path must be already set. inline bool open(); /// Open the directory with path "path". inline bool open(const std::string& path); /// Close the directory manually (automatically invoked in destructor). inline bool close(); /// Get native handle of a directory inline handle_type get_handle(); // ------------ directory entry functions /// Read the next entry. Returns false when the end is reached /// or if the directory is not open. If entry read error occurs, _true_ /// is returned - to check for error, see bad(). /// This function will open the directory if needed. inline bool entry_next(); /// Rewind the entry pointer to the beginning, so that entry_next() /// will read the first entry. /// If the directory was changed while open, this should re-read it. /// This function will open the directory if needed. inline bool entry_reset(); /// Get the name of current entry inline std::string entry_get_name(); /// Get full path of current entry inline std::string entry_get_path(); /// Get native handle of a directory entry inline entry_handle_type entry_get_handle(); // ------------- entry iterator functions /// Returns an iterator pointing to the first entry. /// This function will open the directory if needed. iterator begin() // this resets the position! { entry_reset(); entry_next(); // set to first entry return iterator(this, entry_); } /// Returns an iterator pointing to the entry one past the last one. iterator end() { return iterator(this, NULL); } // -------------- entry listing /// Put directory entries into container. Each entry will be filtered through /// filter_func. The final list will be sorted using sort_func. /// Container must be a random-access container supporting push_back() method. /// put_with_path indicates whether the Dir's path should be prepended to /// entry name before putting it into the container. /// This function will open the directory if needed. template inline bool list(Container& put_here, bool put_with_path, SortFunctor sort_func, FilterFunctor filter_func); /// Same as the other list() functions, but defaulting to no filtering. template bool list(Container& put_here, bool put_with_path, SortFunctor sort_func) { return this->list(put_here, put_with_path, sort_func, DirFilterNone()); } /// Same as list(), but with default sorting. template bool list_filtered(Container& put_here, bool put_with_path, FilterFunctor filter_func) { return this->list(put_here, put_with_path, DirSortAlpha(DIR_SORT_DIRS_FIRST), filter_func); } /// Same as the other list() functions, but defaulting to no filtering /// and alphanumeric sort (dirs first). template bool list(Container& put_here, bool put_with_path = false) { return this->list(put_here, put_with_path, DirSortAlpha(DIR_SORT_DIRS_FIRST), DirFilterNone()); } private: // for move semantics: // Dir(const Dir& other); // don't allow it. allow only from non-const. // const Dir& operator=(const Dir& other); // don't allow it. allow only from non-const. handle_type dir_; ///< Native handle (DIR*) entry_handle_type entry_; ///< Current entry (dirent*) }; // ------------------------------------------- Implementation namespace internal { inline DirIterator& DirIterator::operator++ () // prefix { if (dir_) { dir_->entry_next(); entry_ = dir_->entry_get_handle(); } else { entry_ = NULL; } return *this; } inline void DirIterator::operator++ (int) // postfix. returns void because of technical limitations. { ++(*this); } inline std::string DirIterator::operator* () const // dereference { return name(); } inline std::string DirIterator::operator-> () const { return name(); } inline std::string DirIterator::name() const { if (!entry_ || !dir_) return std::string(); return dir_->entry_get_name(); } inline std::string DirIterator::path() const { if (!entry_ || !dir_) return std::string(); return dir_->entry_get_path(); } inline bool DirIterator::is_special() const { std::string n = name(); return (n == "." || n == ".."); } inline DirIterator::handle_type DirIterator::get_handle() { if (!entry_ || !dir_) return NULL; return dir_->entry_get_handle(); } } // ns // -------------------------------------------- /* // move semantics, as with copy constructor. inline Dir& Dir::operator= (Dir& other) { dir_ = other.dir_; entry_ = other.entry_; // clear other's members, so everything is clear. other.dir_ = NULL; other.entry_ = NULL; other.set_path(""); other.set_error(HZ__("The directory handle ownership was transferred from this object.")); return *this; } */ inline bool Dir::open() { if (dir_) { // already open set_error(std::string(HZ__("Error while opening directory \"/path1/\": ")) + HZ__("Another directory is open already. Close it first."), 0, this->get_path()); return false; } if (this->empty()) { set_error(std::string(HZ__("Error while opening directory: ")) + HZ__("Supplied path is empty.")); return false; } clear_error(); dir_ = directory_open(this->c_str()); if (!dir_) { set_error(HZ__("Error while opening directory \"/path1/\": /errno/."), errno, this->get_path()); return false; } entry_ = NULL; return true; } inline bool Dir::open(const std::string& path) { set_path(path); return open(); } inline bool Dir::close() { clear_error(); if (dir_ && directory_close(dir_) != 0) { // error set_error(HZ__("Error while closing directory \"/path1/\": /errno/."), errno, this->get_path()); dir_ = NULL; entry_ = NULL; return false; } dir_ = NULL; entry_ = NULL; return true; // even if already closed } inline Dir::handle_type Dir::get_handle() { return dir_; } inline bool Dir::entry_next() { clear_error(); entry_ = NULL; // just in case if (!dir_) { // open if needed if (!open()) return false; } // if (!dir_) { // set_error(HZ__("Error while reading directory entry: Directory is not open."); // return false; // an indicator to stop the caller's loop. // } errno = 0; // reset errno, because readdir may return NULL even if it's OK. entry_ = directory_read(dir_); if (errno != 0) { set_error(HZ__("Error while reading directory entry of \"/path1/\": /errno/."), errno, this->get_path()); return true; // you may continue reading anyway } if (entry_) return true; return false; // end is reached } inline bool Dir::entry_reset() { clear_error(); entry_ = NULL; // since it's invalid anyway if (!dir_) { // open if needed if (!open()) return false; } // if (!dir_) { // set_error(HZ__("Error while reading directory entry: Directory is not open."); // return false; // } directory_rewind(dir_); // no return value here return true; } inline std::string Dir::entry_get_name() { clear_error(); if (!dir_) { set_error(std::string(HZ__("Error while reading directory entry of \"/path1/\": ")) + HZ__("Directory is not open."), 0, this->get_path()); return std::string(); } if (!entry_) { set_error(std::string(HZ__("Error while reading directory entry of \"/path1/\": ")) + HZ__("Entry is not set."), 0, this->get_path()); return std::string(); } return directory_entry_name(entry_); } inline std::string Dir::entry_get_path() { return get_path() + DIR_SEPARATOR_S + entry_get_name(); } inline Dir::entry_handle_type Dir::entry_get_handle() { return entry_; } template inline bool Dir::list(Container& put_here, bool put_with_path, SortFunctor sort_func, FilterFunctor filter_func) { clear_error(); if (!dir_) { // open if needed if (!open()) // this sets the error if needed return false; } entry_reset(); bool filter_using_paths = filter_func.use_path_objects(); bool sort_using_paths = sort_func.use_path_objects(); typedef std::vector list_path_list_t; typedef std::vector list_string_list_t; list_path_list_t path_results; // for FsPaths list_string_list_t string_results; // for strings while (entry_next()) { if (bad()) continue; std::string name = entry_get_name(); if (bad()) continue; if (filter_using_paths) { FsPath p(this->get_path()); p.append(name); if (filter_func(p)) { if (sort_using_paths) { path_results.push_back(p); } else { string_results.push_back(name); } } } else { // string-based filtering if (filter_func(name)) { if (sort_using_paths) { FsPath p(this->get_path()); p.append(name); path_results.push_back(p); } else { string_results.push_back(name); } } } } sort_func.set_dir(this->get_path()); if (sort_using_paths) { hz::shell_sort(path_results.begin(), path_results.end(), sort_func); for (list_path_list_t::const_iterator iter = path_results.begin(); iter != path_results.end(); ++iter) { if (put_with_path) { put_here.push_back(iter->str()); } else { put_here.push_back(iter->get_basename()); } } } else { hz::shell_sort(string_results.begin(), string_results.end(), sort_func); for (list_string_list_t::const_iterator iter = string_results.begin(); iter != string_results.end(); ++iter) { if (put_with_path) { FsPath p(this->get_path()); p.append(*iter); put_here.push_back(p.str()); } else { put_here.push_back(*iter); } } } return true; } } // ns hz #endif /// @} gsmartcontrol-1.1.4/src/hz/fs_dir_platform.h000066400000000000000000000171761417717230600211720ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2009 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_FS_DIR_PLATFORM_H #define HZ_FS_DIR_PLATFORM_H #include "hz_config.h" // feature macros #include #ifdef _WIN32 #include // std::nothrow #include // _* :) #include // std::size_t #include // for string.h #include // wcslen() #else #include // dirent needs this #include #endif #ifdef _WIN32 #include "win32_tools.h" // hz::win32_utf8_to_utf16(), hz::win32_utf16_to_utf8 #include "scoped_array.h" // hz::scoped_array #endif // Limited dirent-like API to hide platform differences. // This API accepts/gives utf-8 filenames/paths on win32, // current locale filenames/paths on others (just like glib). namespace hz { #ifndef _WIN32 typedef DIR* directory_handle_type; ///< Native directory handle typedef struct dirent* directory_entry_handle_type; ///< Native directory entry handle // Linux man pages say off_t, but POSIX and glibc have long. // typedef off_t directory_offset_type; typedef long directory_offset_type; ///< Directory entry offset type /// Open directory. NULL on error, also sets errno. inline directory_handle_type directory_open(const char* path) { return opendir(path); } /// Close directory. 0 on success, -1 on error (also sets errno). inline int directory_close(directory_handle_type dir) { return closedir(dir); } /// Rewind directory inline void directory_rewind(directory_handle_type dir) { return rewinddir(dir); } /// Read current entry. On error, returns NULL and sets errno. inline directory_entry_handle_type directory_read(directory_handle_type dir) { return readdir(dir); } /// Return current position as a native offset. inline directory_offset_type directory_tell(directory_handle_type dir) { return telldir(dir); } /// Go to the position given by directory_tell(). inline void directory_seek(directory_handle_type dir, directory_offset_type pos) { seekdir(dir, pos); } /// Get current entry name inline std::string directory_entry_name(directory_entry_handle_type entry) { return (entry && entry->d_name) ? entry->d_name : ""; } #else // win32: // Note: Windows implementation is based on MinGW's dirent implementation // (public domain). namespace internal { struct DirectoryEntry { const wchar_t* d_name; // this is owned by Directory's data field. utf-16. }; struct Directory { struct _wfinddata_t data; // entry data as given by _wfindfirst(). DirectoryEntry entry; // current directory entry intptr_t handle; // directory handle as returned by _wfindfirst(). long status; // 0 - not started, -1 - off the end, positive - 0-based index of next entry. wchar_t full_pattern[MAX_PATH + 3]; // directory search pattern + "\\*" }; } typedef internal::Directory* directory_handle_type; typedef internal::DirectoryEntry* directory_entry_handle_type; typedef long directory_offset_type; // that's what they use... inline directory_handle_type directory_open(const char* path) // NULL on error, also sets errno. { errno = 0; if (!path) { errno = ENOENT; // not specified by opendir (3), not sure about this. return 0; } if (path[0] == '\0') { errno = ENOENT; // // Directory does not exist, or name is an empty string. return 0; } hz::scoped_array wpath(hz::win32_utf8_to_utf16(path)); if (!wpath) { errno = ENOENT; // not specified by opendir (3), not sure about this. return 0; } struct _stat s; if (_wstat(wpath.get(), &s) == -1) { errno = ENOENT; // Directory does not exist, or name is an empty string. return 0; } if (!(s.st_mode & _S_IFDIR)) { errno = ENOTDIR; // Name is not a directory. return 0; } internal::Directory* dir = new(std::nothrow) internal::Directory(); if (!dir) { errno = ENOMEM; // Out of memory return 0; } // We store an absolute path so that we don't depend on current directory // (after rewind, for example). _wfullpath(dir->full_pattern, wpath.get(), MAX_PATH); // make an absolute path std::size_t len = wcslen(dir->full_pattern); if (len == 0 || (len+1) >= MAX_PATH) { // not sure about the conditions, but just in case. errno = ENOTDIR; // // Directory does not exist, or name is an empty string. return 0; } // ms says that it can be both slash and backslash if (dir->full_pattern[len - 1] != '/' && dir->full_pattern[len - 1] != '\\') { dir->full_pattern[len] = L'\\'; dir->full_pattern[len+1] = L'*'; } else { dir->full_pattern[len] = L'*'; } dir->handle = -1; // not actually opened yet dir->status = 0; // not started yet return dir; } inline int directory_close(directory_handle_type dir) // 0 on success, -1 on error (also sets errno). { errno = 0; if (!dir) { errno = EBADF; // Invalid directory stream descriptor return -1; } int result = 0; if (dir->handle != -1) { result = _findclose(dir->handle); // -1 on error, 0 on success. // It sets ENOENT (no matching entries found) on error, which doesn't // really make sense. } delete dir; return result; } inline void directory_rewind(directory_handle_type dir) { // rewinddir (3p) defines no errors. // errno = 0; if (!dir) { // errno = EFAULT; return; } if (dir->handle != -1) { _findclose(dir->handle); } dir->handle = -1; dir->status = 0; } inline directory_entry_handle_type directory_read(directory_handle_type dir) { errno = 0; if (!dir) { errno = EBADF; // Invalid directory stream descriptor. return 0; } if (dir->status < 0) { // we already reached the end return 0; } if (dir->status == 0) { // haven't started yet dir->handle = _wfindfirst(dir->full_pattern, &(dir->data)); if (dir->handle == -1) { // no files in directory dir->status = -1; if (errno == ENOENT) { // no entries, it's not an error in readdir(). errno = 0; } return 0; } dir->status = 1; } else { // already started, get the next entry if (_wfindnext(dir->handle, &(dir->data))) { // error or no more files if (errno == ENOENT) { errno = 0; } _findclose(dir->handle); dir->handle = -1; dir->status = -1; } else { // next entry read ok ++(dir->status); } } if (dir->status > 0) { dir->entry.d_name = (dir->data.name ? dir->data.name : L""); return &(dir->entry); } return 0; } inline directory_offset_type directory_tell(directory_handle_type dir) { errno = 0; // POSIX doesn't specify errno on telldir(), Linux does. if (!dir || dir->status == -1) { errno = EBADF; // Invalid directory stream descriptor return -1; } return dir->status; // just the position } // return to the position given by directory_tell(). inline void directory_seek(directory_handle_type dir, directory_offset_type pos) { // seekdir (3p) defines no errors. // errno = 0; if (!dir) { // errno = EFAULT; return; } if (pos < -1) { // invalid position // errno = EINVAL; return; } if (pos == -1) { // past the end if (dir->handle != -1) { _findclose (dir->handle); // close it like directory_read does. } dir->handle = -1; dir->status = -1; } else { // ok, rewind and read in a loop directory_rewind(dir); while (dir->status < pos) { if (!directory_read(dir)) break; } } } inline std::string directory_entry_name(directory_entry_handle_type entry) { if (!entry->d_name) return std::string(); return win32_utf16_to_utf8_string(entry->d_name); } #endif // win32 } // ns hz #endif /// @} gsmartcontrol-1.1.4/src/hz/fs_dir_test.cpp000066400000000000000000000051561417717230600206530ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz_tests /// \weakgroup hz_tests /// @{ // disable libdebug, we don't link to it #undef HZ_USE_LIBDEBUG #define HZ_USE_LIBDEBUG 0 // enable libdebug emulation through std::cerr #undef HZ_EMULATE_LIBDEBUG #define HZ_EMULATE_LIBDEBUG 1 // The first header should be then one we're testing, to avoid missing // header pitfalls. #include "fs_dir.h" #include #include /// Main function for the test int main() { using namespace hz; { Dir dir("."); // open the directory. closed in destructor. while (dir.entry_next()) { if (!dir.bad()) // one-entry failure std::cerr << dir.entry_get_name() << "\n"; } if (dir.bad()) { // general failure std::cerr << "Directory error: " << dir.get_error_locale() << "\n"; } else { std::cerr << "All OK.\n"; } } // iterator interface { std::cerr << "\nListing through iterator interface:\n"; Dir dir(".."); // open the directory. closed in destructor. for (Dir::iterator iter = dir.begin(); iter != dir.end(); ++iter) { if (!dir.bad()) // one-entry failure std::cerr << *iter << "\n"; } if (dir.bad()) { // general failure std::cerr << "Directory error: " << dir.get_error_locale() << "\n"; } else { std::cerr << "All OK.\n"; } } // listing entries { std::cerr << "\nListing through list():\n"; hz::Dir dir(".."); // open the directory. closed in destructor. { std::cerr << "\nSorted by name (dirs first):\n"; std::vector v; dir.list(v, false); for (unsigned int i = 0; i < v.size(); ++i) { std::cerr << v[i] << "\n"; } } { std::cerr << "\nSorted by timestamp (mixed):\n"; std::vector v; dir.list(v, true, hz::DirSortMTime(DIR_SORT_MIXED)); for (unsigned int i = 0; i < v.size(); ++i) { std::cerr << v[i] << "\n"; } } { std::cerr << "\nSorted by name (dirs first), filtered by wildcard:\n"; std::vector v; dir.list_filtered(v, false, hz::DirFilterWc("*.o")); for (unsigned int i = 0; i < v.size(); ++i) { std::cerr << v[i] << "\n"; } } } // error handling { hz::Dir dir("/nonexistent/directory"); if (!dir.open()) { std::cerr << dir.get_error_locale() << "\n"; } else { std::cerr << "Directory \"" << dir.get_path() << "\" opened successfully.\n"; } } return 0; } /// @} gsmartcontrol-1.1.4/src/hz/fs_error_holder.h000066400000000000000000000165331417717230600211720ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2013 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_FS_ERROR_HOLDER_H #define HZ_FS_ERROR_HOLDER_H #include "hz_config.h" // feature macros #include // cerrno is not directly used here, but will be needed in children. #include // errno (not std::errno, it may be a macro) #if defined ENABLE_GLIB && ENABLE_GLIB #include // g_* #endif #include "errno_string.h" // hz::errno_string #include "debug.h" // debug_* #include "i18n.h" // HZ__ #include "string_algo.h" // string_replace #ifdef _WIN32 #include "win32_tools.h" #endif namespace hz { /// Filesystem operation error holder. class FsErrorHolder { public: /// Constructor FsErrorHolder() : error_errno_(0), bad_(false) { } /// Virtual destructor virtual ~FsErrorHolder() { } /// Check if the last operation was unsuccessful bool bad() const { return bad_; } /// check if the last operation was successful bool ok() const { return !bad_; } // for easy function result checks // operator bool () // cast to bool // { // return !bad(); // } /// Get last operation's errno if it was unsuccessful. int get_errno() const { return error_errno_; } /// Get error message in current locale. Useful for outputting to console, etc... std::string get_error_locale(); /// Get error message in UTF-8. Use in GUI messages, etc... std::string get_error_utf8(); protected: /// Set error (formatted message, errno, up to two affected paths). /// The formatted message may contain the following special strings: /// - /path1/ - replaced by \c path1; /// - /path2/ - replaced by \c path2; /// - /errno/ - replaced by errno string. void set_error(const std::string& error_format, int error_errno = 0, const std::string& path1 = "", const std::string path2 = "") { error_format_ = error_format; error_errno_ = error_errno; error_path1_ = path1; error_path2_ = path2; bad_ = true; warn(); } /// Clear the previously set error void clear_error() { bad_ = false; error_format_.clear(); error_errno_ = 0; error_path1_.clear(); error_path2_.clear(); } /// Import error from another object void import_error(const FsErrorHolder& other) { bad_ = other.bad_; error_format_ = other.error_format_; error_errno_ = other.error_errno_; error_path1_ = other.error_path1_; error_path2_ = other.error_path2_; } /// This is called on every set_error(). Override to e.g. enable GUI notifications. /// The default implementation prints a warning using libdebug. virtual void warn() { debug_out_warn("hz", "FS warning: " + this->get_error_locale() + "\n"); } // I thought about making these mutable, but it's a bad solution. std::string error_format_; ///< Last error message format (gettext-translated). std::string error_path1_; ///< This replaces /path1/ parameter in message format std::string error_path2_; ///< This replaces /path2/ parameter in message format int error_errno_; ///< If not 0, this holds the last error's errno. bool bad_; ///< Error flag }; // -------------------------- Implementation #if defined ENABLE_GLIB && ENABLE_GLIB inline std::string FsErrorHolder::get_error_locale() { // Note: Paths are in filesystem charset. On win32 it's always utf-8. // Errno string is in libc locale charset or utf8 (if using glib). gchar* loc_errno = g_locale_from_utf8(hz::errno_string(error_errno_).c_str(), -1, NULL, NULL, NULL); std::string loc_errno_str = (loc_errno ? loc_errno : (HZ__("[charset conv error] ") + error_errno_ + hz::errno_string(error_errno_))); g_free(loc_errno); gchar* p1_utf8 = g_filename_display_name(error_path1_.c_str()); // fs -> utf8. always non-null. gchar* p1_locale = g_locale_from_utf8(p1_utf8, -1, NULL, NULL, NULL); // utf8 -> locale std::string p1 = (p1_locale ? p1_locale : (HZ__("[charset conv error] ") + error_path1_)); g_free(p1_utf8); g_free(p1_locale); gchar* p2_utf8 = g_filename_display_name(error_path2_.c_str()); // fs -> utf8. always non-null. gchar* p2_locale = g_locale_from_utf8(p2_utf8, -1, NULL, NULL, NULL); // utf8 -> locale std::string p2 = (p2_locale ? p2_locale : (HZ__("[charset conv error] ") + error_path2_)); g_free(p2_utf8); g_free(p2_locale); std::string msg = error_format_; hz::string_replace(msg, "/path1/", p1, 1); hz::string_replace(msg, "/path2/", p2, 1); hz::string_replace(msg, "/errno/", loc_errno_str, 1); return msg; } inline std::string FsErrorHolder::get_error_utf8() { // Paths are in filesystem charset, convert to utf8. // gchar* cp1utf8 = g_filename_to_utf8(error_path1_.c_str(), -1, NULL, NULL, NULL); // gchar* cp2utf8 = g_filename_to_utf8(error_path2_.c_str(), -1, NULL, NULL, NULL); // unlike g_filename_to_utf8, this always returns something. (available since glib 2.6). gchar* p1_utf8 = g_filename_display_name(error_path1_.c_str()); std::string p1 = (p1_utf8 ? p1_utf8 : (HZ__("[charset conv error] ") + error_path1_)); g_free(p1_utf8); gchar* p2_utf8 = g_filename_display_name(error_path2_.c_str()); std::string p2 = (p2_utf8 ? p2_utf8 : (HZ__("[charset conv error] ") + error_path2_)); g_free(p2_utf8); std::string msg = error_format_; hz::string_replace(msg, "/path1/", p1, 1); hz::string_replace(msg, "/path2/", p2, 1); hz::string_replace(msg, "/errno/", hz::errno_string(error_errno_), 1); // Errno string is already utf8 if using glib. return msg; } #else // no glib // Without glib, the messages may be really screwed up. // We still provide these, but we don't make any guarantees. // This _should_ work on windows, but since we're using this function // for console and console in windows is screwed up (uses DOS charsets(!)), // we can't promise anything. inline std::string FsErrorHolder::get_error_locale() { std::string msg = error_format_; // gettext-supplied charset (locale, probably) #ifdef _WIN32 // files are in system locale. we're using console, don't use thread locale. char* err_path1 = hz::win32_utf8_to_locale(error_path1_.c_str(), 0, false); char* err_path2 = hz::win32_utf8_to_locale(error_path2_.c_str(), 0, false); std::string errno_str = hz::errno_string(error_errno_); char* errno_cstr = hz::win32_utf8_to_locale(errno_str.c_str(), 0, false); hz::string_replace(msg, "/path1/", (err_path1 ? err_path1 : error_path1_), 1); hz::string_replace(msg, "/path2/", (err_path2 ? err_path2 : error_path2_), 1); hz::string_replace(msg, "/errno/", (errno_cstr ? errno_cstr : errno_str), 1); // utf-8 delete[] err_path1; delete[] err_path2; delete[] errno_cstr; #else // filesystem charset (possibly locale). screwed up, probably. hz::string_replace(msg, "/path1/", error_path1_, 1); hz::string_replace(msg, "/path2/", error_path2_, 1); hz::string_replace(msg, "/errno/", hz::errno_string(error_errno_), 1); // locale charset #endif return msg; } // This works correctly in windows, since we use only utf-8 paths there. inline std::string FsErrorHolder::get_error_utf8() { return this->get_error_locale(); } #endif } // ns hz #endif /// @} gsmartcontrol-1.1.4/src/hz/fs_file.h000066400000000000000000000641041417717230600174200ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2013 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_FS_FILE_H #define HZ_FS_FILE_H #include "hz_config.h" // feature macros /** \file This API accepts/gives utf-8 filenames/paths on win32, current locale filenames/paths on others (just like glib). Notes on 64-bit file support: Glibc/Linux: Glibc/Linux, Solaris and (to some extent) QNX implement "transitional" interface when used on 32-bit processors. http://www.gnu.org/software/libc/manual/html_mono/libc.html ftello() and fseeko() are available in glibc if _LARGEFILE_SOURCE is defined, which is also enabled by (_XOPEN_SOURCE >= 500 || _GNU_SOURCE). ftello64() is available in glibc if _LARGEFILE64_SOURCE is defined, which is enabled with _GNU_SOURCE. Defining _FILE_OFFSET_BITS=64 replaces fopen() & friends with fopen64() and friends transparently. If ftello() is available, it's replaced by ftello64() too (same for fseeko()). Note that this won't make ftello64() available for applications. Note that the replacement happens on asm level if the compiler supports it, so, for example, libstdc++'s cstdio will correctly provide fopen64 as std::fopen(). GCC, Sun and Intel support the transparent conversion. If it's unsupported, glibc falls back to macro-based substitution, which will fail since cstdio undefines those macros. Solaris: http://docs.sun.com/app/docs/doc/817-3946/6mjgmt4mp?l=en&a=view http://docs.sun.com/app/docs/doc/816-5175/standards-5?l=en&a=view Solaris seems to have the same rules regarding _LARGEFILE*_SOURCE and _FILE_OFFSET_BITS as glibc. _LARGEFILE64_SOURCE implies _LARGEFILE_SOURCE as well. Both _LARGEFILE*_SOURCE macros are automatically defined if: * No explicit standards-conforming environment is requested (neither _POSIX_SOURCE nor _XOPEN_SOURCE is defined and the value of __STDC__ does not imply standards conformance), OR: * Extended system interfaces are explicitly requested (__EXTENSIONS__ is defined). NetBSD: (_LARGEFILE_SOURCE || _XOPEN_SOURCE >= 500 || _NETBSD_SOURCE) enables fseeko() and ftello(). If none of the major feature macros are defined, _NETBSD_SOURCE is assumed. fopen() and friends use 64-bit offsets by default (off_t is always 64 bits). No *64() functions are present. OpenBSD: fseeko() and ftello() are always defined. fopen() and friends use 64-bit offsets (off_t is always 64 bits). No *64() functions are present. FreeBSD: __POSIX_VISIBLE >= 200112 (enabled by default) enables fseeko() / ftello(). off_t and friends always operate in 64 bits. No *64() functions are present. QNX: There is no _LARGEFILE_SOURCE, fseeko() / ftello() are always available. _LARGEFILE64_SOURCE enables *64() variants. _FILE_OFFSET_BITS=64 replaces ordinary functions with *64() equivalents (through asm conversion). _QNX_SOURCE (default) enables everything. Darwin: off_t is 64-bit. Seems to be lifted from FreeBSD. Windows: All off_t-related functions use long on Windows. There is no fseeko() / ftello(). One has to use _fseeki64() and friends explicitly. */ #include #include // std::size_t #include // std::FILE, std::fopen() and friends (this doesn't break LFS), std::rename() #include // off_t, fileno(), _fileno(), _wfopen(), _wrename() #include // errno #include // *stat*() needs this #include // *stat*() needs this; chmod() #ifdef _WIN32 #include // _fstat*(), _wunlink(), _wchmod() #else #include // stat(), unlink() #endif #include "fs_path.h" // FsPath #ifdef _WIN32 #include "scoped_array.h" // hz::scoped_array #include "win32_tools.h" // hz::win32_utf8_to_utf16 #endif // Filesystem file manipulation namespace hz { /// \typedef file_size_t /// Offset & size type. may be uint32_t or uint64_t, depending on system and compilation flags. /// Note: It is usually discouraged to use this type in headers, because the library may be /// compiled with one size and the application with another. However, this problem is rather /// limited if using header-only approach, like we do. #if defined HAVE_POSIX_OFF_T_FUNCS && HAVE_POSIX_OFF_T_FUNCS typedef off_t file_size_t; // off_t is in stdio.h, available in all self-respecting unix systems. #elif defined HAVE_WIN_LFS_FUNCS && HAVE_WIN_LFS_FUNCS typedef __int64 file_size_t; // ms stuff, _fseeki64() and friends use it. #else typedef long file_size_t; // fseek() and friends use long. win32 doesn't have off_t. #endif /* TODO Seekless read support in hz::File. should be able to accept a fixed buffer, an std::string of max length, optional ticker callback. e.g. // callback inline bool append_func(std::string& put_here, const std::string& chunk, bool last) { put_here += chunk; return true; // continue } // user code void f (hz::File& file) { std::string put_here; file.get_contents(put_here, 1024, append_func); // 3rd param should have a default func. } Should have similar thing with static buffers. */ /// A class that represents a file. This can be thought of as a /// wrapper around std::FILE*. class File : public FsPath { public: /// Native handle type. typedef std::FILE* handle_type; // std::FILE must be identical to ::FILE in C++. /// Constructor File() : file_(NULL) { } /// Create a File object with path "path". This will NOT open the file. File(const std::string& path) : file_(NULL) { this->set_path(path); } /// Create a File object with path "path". This will NOT open the file. File(const FsPath& path) : file_(NULL) { this->set_path(path.get_path()); } /// Create a File object and open a file "path" points to. /// You should check the success status with bad(). File(const std::string& path, const std::string& open_mode) : file_(NULL) { this->set_path(path); this->open(open_mode); } /// Create a File object and open a file "path" points to. /// You should check the success status with bad(). File(const FsPath& path, const std::string& open_mode) : file_(NULL) { this->set_path(path.get_path()); this->open(open_mode); } private: // Between move semantics (confusing and error-prone) and denying copying, // I choose to deny. /// Private copy constructor to deny copying File(const File& other); /// Private assignment operator to deny copying File& operator= (const File& other); public: /* // Copy constructor. Move semantics are implemented - the ownership is transferred // exclusively. File(File& other) : file_(NULL) { *this = other; // operator= } // move semantics, as with copy constructor. inline File& operator= (File& other); */ /// Destructor which invokes close() if needed. virtual ~File() { if (file_) this->close(); } // --- these may set bad() status /// Open the file with open_mode (standard std::fopen open mode). inline bool open(const std::string& open_mode); /// Close the previously opened file. inline bool close(); /// Get native file handle (obtained using std::fopen() or similar) handle_type get_handle() { return file_; } /// Get file contents. \c put_data_here will be allocated to whatever /// size is needed to contain all the data. The size is written to \c put_size_here. /// You must call "delete[] put_data_here" afterwards. /// If the file is larger than \c max_size (100M by default), the function refuses to load it. /// Note: No additional trailing 0 is written to data! /// \return false on failure (error is also set). inline bool get_contents(unsigned char*& put_data_here, file_size_t& put_size_here, file_size_t max_size = 104857600); /// Same as other versions of get_contents(), but puts data into an already /// allocated buffer of size \c buf_size. /// If the size is insufficient, false is returned and buffer is left untouched. /// If any other error occurs, the buffer is left in unspecified state. /// Internal usage only: If buf_size is -1, the buffer will be automatically allocated. /// TODO: Support files which are not seekable and don't have a size attribute (e.g. /proc/*). /// \return false on failure (error is also set). inline bool get_contents_noalloc(unsigned char*& put_data_here, file_size_t buf_size, file_size_t& put_size_here, file_size_t max_size = 104857600); /// Same as other versions of get_contents(), but for std::string /// (no terminating 0 is needed inside the file, the string is 0-terminated anyway). /// \return false on failure (error is also set). inline bool get_contents(std::string& put_data_here, file_size_t max_size = 104857600); /// Write data to file, creating or truncating it beforehand. /// \c data may or may not be 0-terminated (it's irrelevant). /// \return false on failure (error is also set). inline bool put_contents(const unsigned char* data, file_size_t data_size); /// Same as the other version of put_contents(), but writes data from std::string. /// No terminating 0 is written to the file. /// \return false on failure (error is also set). inline bool put_contents(const std::string& data); /// Get file size. Do NOT assign the result to int - you'll break LFS support. /// If \c use_read is true, the file is read completely to determine its size. /// This is needed for special files (like in /proc), which have 0 size if queried /// the standard way. /// \return false on failure (error is also set). inline bool get_size(file_size_t& put_here, bool use_read = false); /// Move (rename) a file to \c to. The destination will be overwritten if it exists and /// it's not a directory (this is true even for win32, where renaming usually fails if /// the destination exists). This function is subject to rename() limitations. /// \return false on failure (error is also set). inline bool move(const std::string& to); /// Copy the file to a destination file specified by \c to. /// If \c to already exists, overwrite it. /// \return false on failure (error is also set). inline bool copy(const std::string& to); // remove() is in Path (parent). // --- Standard functions for portable implementation of operations // These are similar to their system equivalents. Additionally, they // use the best OS-dependent function if available. /// Same as std::fopen(), but platform-independent (properly handles charsets, etc...). static inline handle_type platform_fopen(const char* file, const char* open_mode); /// Same as fseek[o](), but platform-independent. static inline int platform_fseek(handle_type stream, file_size_t offset, int whence); /// Same as ftell[o](), but platform-independent. static inline file_size_t platform_ftell(handle_type stream); private: // for move semantics: // File(const File& other); // don't allow it. allow only from non-const. // const File& operator=(const File& other); // don't allow it. allow only from non-const. handle_type file_; ///< File handle (FILE*) }; // ------------------------------------------- Implementation /* // move semantics, as with copy constructor. inline File& File::operator= (File& other) { file_ = other.file_; // clear other's members, so everything is clear. other.file_ = NULL; other.set_path(""); other.set_error(HZ__("The file handle ownership was transferred from this object.")); return *this; } */ inline bool File::open(const std::string& open_mode) { clear_error(); if (file_) { // already open set_error(std::string(HZ__("Error while opening file \"/path1/\": ")) + HZ__("Another file is open already. Close it first."), 0, this->get_path()); return false; } if (this->empty()) { set_error(std::string(HZ__("Error while opening file: ")) + HZ__("Supplied path is empty.")); return false; } // this creates a 0 size file if it doesn't exist! file_ = platform_fopen(this->c_str(), open_mode.c_str()); if (!file_) { set_error(HZ__("Error while opening file \"/path1/\": /errno/."), errno, this->get_path()); return false; } return true; } inline bool File::close() { clear_error(); if (file_ && std::fclose(file_) != 0) { // error set_error(HZ__("Error while closing file \"/path1/\": /errno/."), errno, this->get_path()); file_ = NULL; return false; } file_ = NULL; return true; // even if already closed } inline bool File::get_contents(unsigned char*& put_data_here, file_size_t& put_size_here, file_size_t max_size) { return get_contents_noalloc(put_data_here, static_cast(-1), put_size_here, max_size); } inline bool File::get_contents_noalloc(unsigned char*& put_data_here, file_size_t buf_size, file_size_t& put_size_here, file_size_t max_size) { clear_error(); if (this->empty()) { set_error(std::string(HZ__("Unable to open file for reading: ")) + HZ__("Supplied path is empty.")); return false; } handle_type f = platform_fopen(this->c_str(), "rb"); if (!f) { set_error(HZ__("Unable to open file \"/path1/\" for reading: /errno/."), errno, this->get_path()); return false; } do { // goto emulation if (platform_fseek(f, 0, SEEK_END) != 0) { set_error(HZ__("Unable to read file \"/path1/\": /errno/."), errno, this->get_path()); break; // goto cleanup } const file_size_t size = platform_ftell(f); if (size == static_cast(-1)) { // size may be unsigned, it's the way it works set_error(HZ__("Unable to read file \"/path1/\": /errno/."), errno, this->get_path()); break; // goto cleanup } if (size > max_size) { set_error(std::string(HZ__("Unable to read file \"/path1/\": ")) + HZ__("File size is larger than allowed."), 0, this->get_path()); break; // goto cleanup } // automatically allocate the buffer if buf_size is -1. bool auto_alloc = (buf_size == static_cast(-1)); if (!auto_alloc && buf_size < size) { set_error(std::string(HZ__("Unable to read file \"/path1/\": ")) + HZ__("Supplied buffer is too small."), 0, this->get_path()); break; // goto cleanup } std::rewind(f); // returns void unsigned char* buf = put_data_here; if (auto_alloc) buf = new unsigned char[static_cast(size)]; // this may throw! // We don't need large file support here because we read into memory, // which is limited at 31 bits anyway (on 32-bit systems). // I really hope this is not a byte-by-byte operation std::size_t read_bytes = std::fread(buf, 1, static_cast(size), f); if (size != static_cast(read_bytes)) { set_error(std::string(HZ__("Unable to read file \"/path1/\": ")) + HZ__("Unexpected number of bytes read."), 0, this->get_path()); if (auto_alloc) delete[] buf; break; // goto cleanup } // All OK put_size_here = size; if (auto_alloc) put_data_here = buf; } while (false); // cleanup: if (std::fclose(f) != 0) { if (!bad()) // don't overwrite the previous error set_error(HZ__("Error while closing file \"/path1/\": /errno/."), errno, this->get_path()); } return ok(); } inline bool File::get_contents(std::string& put_data_here, file_size_t max_size) { file_size_t size = 0; unsigned char* buf = 0; get_contents(buf, size, max_size); if (bad()) return false; // Note: No need for appending 0, it's all automatic in string. put_data_here.reserve(static_cast(size)); // string takes size without trailing 0. put_data_here.append(reinterpret_cast(buf), static_cast(size)); delete[] buf; return ok(); } inline bool File::put_contents(const unsigned char* data, file_size_t data_size) { clear_error(); if (this->empty()) { set_error(std::string(HZ__("Unable to open file for for writing: ")) + HZ__("Supplied path is empty.")); return false; } handle_type f = platform_fopen(this->c_str(), "wb"); if (!f) { set_error(HZ__("Unable to open file \"/path1/\" for writing: /errno/."), errno, this->get_path()); return false; } // We write in chunks to support large files. const std::size_t chunk_size = 32*1024; // 32K block devices will be happy. file_size_t left_to_write = data_size; bool write_error = false; while (left_to_write >= static_cast(chunk_size)) { // better loop than specify all in one, this way we can support _really_ large files. if (std::fwrite(data + data_size - left_to_write, chunk_size, 1, f) != 1) { write_error = true; break; } left_to_write -= static_cast(chunk_size); } // write the remainder if (!write_error && left_to_write > 0 && std::fwrite(data + data_size - left_to_write, static_cast(left_to_write), 1, f) != 1) write_error = true; if (write_error) { set_error(std::string(HZ__("Unable to write file \"/path1/\": ")) + HZ__("Number of written bytes doesn't match the data size."), 0, this->get_path()); std::fclose(f); // don't check anything, it's too late return false; } if (std::fclose(f) != 0) set_error(HZ__("Error while closing file \"/path1/\": /errno/."), errno, this->get_path()); return ok(); } inline bool File::put_contents(const std::string& data) { return put_contents(reinterpret_cast(data.data()), static_cast(data.size())); } inline bool File::get_size(file_size_t& put_here, bool use_read) { clear_error(); if (this->empty()) { set_error(std::string(HZ__("Unable to get file size: ")) + HZ__("Supplied path is empty.")); return false; } if (!use_read) { // we could use _filelengthi64() for windows, but it doesn't seem to have // enough error reporting. #if defined HAVE_WIN_LFS_FUNCS && HAVE_WIN_LFS_FUNCS struct _stati64 s; // this contains 64-bit file size const int stat_result = _wstati64(this->get_utf16(), &s); #elif defined _WIN32 // The _stat* family may return stale info. _fstat*() doesn't have this problem // but requires opening the file, which may be bad (no permissions, etc...). struct _stat s; const int stat_result = _wstat(this->get_utf16(), &s); #else struct stat s; const int stat_result = stat(this->c_str(), &s); #endif if (stat_result == -1) { set_error(HZ__("Unable to get file size of \"/path1/\": /errno/."), errno, this->get_path()); return false; } #ifdef _WIN32 if (!(s.st_mode & _S_IFREG) || (s.st_mode & _S_IFCHR)) // see FsPath::is_regular() for remarks. #else if (!S_ISREG(s.st_mode)) // no need for S_ISLNK - we're using stat(), not lstat(). #endif { set_error("Unable to get file size of \"/path1/\": Supplied path is not a regular file.", 0, this->get_path()); return false; } // For symlinks st_size is a size in bytes of the pointed file. // To get the size of a symlink itself, use lstat(). Size of a symlink // is basically strlen(pointed_path). put_here = s.st_size; return true; } // Force reading the file, assume it's non-seekable. handle_type f = platform_fopen(this->c_str(), "rb"); if (!f) { set_error(HZ__("Unable to open file \"/path1/\" for reading: /errno/."), errno, this->get_path()); return false; } const std::size_t buf_size = 32*1024; // 32K block devices will be happy char buf[buf_size] = {0}; (void)std::fread(buf, buf_size, 1, f); if (std::ferror(f)) { set_error(HZ__("Unable to read file \"/path1/\": /errno/."), errno, this->get_path()); } else { // file read completely const file_size_t size = platform_ftell(f); if (size == static_cast(-1)) { // size may be unsigned, it's the way it works set_error(HZ__("Unable to read file \"/path1/\": /errno/."), errno, this->get_path()); } else { // All OK put_here = size; } } // cleanup: if (std::fclose(f) != 0) { if (!bad()) // don't overwrite the previous error set_error(HZ__("Error while closing file \"/path1/\": /errno/."), errno, this->get_path()); } return ok(); } inline bool File::move(const std::string& to) { clear_error(); if (this->empty()) { set_error(std::string(HZ__("Unable to move filesystem entry: ")) + HZ__("Source path is empty.")); return false; } if (to.empty()) { set_error(std::string(HZ__("Unable to move filesystem entry \"/path1/\": ")) + HZ__("Destination path is empty."), 0, this->get_path()); return false; } if (this->get_path() == to) { // this is not bulletproof set_error(std::string(HZ__("Unable to move filesystem entry \"/path1/\": ")) + HZ__("Source path is the same as destination path."), 0, this->get_path()); return false; } bool success = false; #ifdef _WIN32 success = (_wrename(this->get_utf16(), FsPath(to).get_utf16()) == 0); // win32 rename() doesn't replace contents if newpath exists and says "file exists". // Try to rename first, then unlink/rename again. This way we have at least some atomicity. if (!success && errno == EACCES) { File dest_file(to); if (dest_file.is_file()) { dest_file.remove(); // no error tracking here, rename() will report the needed error. success = (_wrename(this->get_utf16(), FsPath(to).get_utf16()) == 0); } } #else success = (std::rename(this->c_str(), to.c_str()) == 0); #endif if (!success) set_error(HZ__("Unable to move filesystem entry \"/path1/\" to \"/path2/\": /errno/."), errno, this->get_path(), to); return ok(); } inline bool File::copy(const std::string& to) { clear_error(); if (this->empty()) { set_error(std::string(HZ__("Unable to copy file: ")) + HZ__("Source path is empty.")); return false; } if (to.empty()) { set_error(std::string(HZ__("Unable to copy file \"/path1/\": ")) + HZ__("Destination path is empty."), 0, this->get_path()); return false; } if (this->get_path() == to) { set_error(std::string(HZ__("Unable to copy file \"/path1/\": ")) + HZ__("Source path is the same as destination path."), 0, this->get_path()); return false; } handle_type fsrc = platform_fopen(this->c_str(), "rb"); if (!fsrc) { set_error(std::string(HZ__("Unable to copy file \"/path1/\" to \"/path2/\": ")) + HZ__("Unable to open source file: /errno/."), errno, this->get_path(), to); return false; } // Remember permissions in case the file is deleted while copying. #if defined HAVE_WIN_LFS_FUNCS && HAVE_WIN_LFS_FUNCS struct _stati64 st; // this contains 64-bit file size const int stat_result = _fstati64(_fileno(fsrc), &st); #elif defined _WIN32 struct _stat st; const int stat_result = _fstat(_fileno(fsrc), &st); // win32 deprecated fstat, so use this instead. #else struct stat st; const int stat_result = fstat(fileno(fsrc), &st); #endif handle_type fdest = platform_fopen(to.c_str(), "wb"); // this truncates it if (!fdest) { std::fclose(fsrc); // don't check status - there's nothing we can do anyway set_error(std::string(HZ__("Unable to copy file \"/path1/\" to \"/path2/\": ")) + HZ__("Unable to create destination file: /errno/."), errno, this->get_path(), to); return false; } const std::size_t buf_size = 32*1024; // 32K unsigned char buf[buf_size] = {0}; while (true) { // let's hope these are not byte-by-byte operations std::size_t read_bytes = std::fread(buf, 1, buf_size, fsrc); if (read_bytes != buf_size && std::ferror(fsrc)) { // error set_error(std::string(HZ__("Unable to copy file \"/path1/\" to \"/path2/\": ")) + HZ__("Error while reading source file: /errno/."), errno, this->get_path(), to); break; } std::size_t written_bytes = std::fwrite(buf, 1, read_bytes, fdest); if (read_bytes != written_bytes) { // error set_error(std::string(HZ__("Unable to copy file \"/path1/\" to \"/path2/\": ")) + HZ__("Error while writing to destination file: /errno/."), errno, this->get_path(), to); break; } } if (bad()) { // error was set // don't check these - there's nothing we can do about them std::fclose(fsrc); std::fclose(fdest); #ifdef _WIN32 (void)_wunlink(FsPath(to).get_utf16()); #else unlink(to.c_str()); #endif return false; } if (std::fclose(fsrc) == -1) { std::fclose(fdest); #ifdef _WIN32 (void)_wunlink(FsPath(to).get_utf16()); #else unlink(to.c_str()); #endif set_error(std::string(HZ__("Unable to copy file \"/path1/\" to \"/path2/\": ")) + HZ__("Error while closing source file: /errno/."), errno, this->get_path(), to); return false; } if (std::fclose(fdest) == -1) { // the OS may delay writing until this point (or even further). #ifdef _WIN32 (void)_wunlink(FsPath(to).get_utf16()); #else unlink(to.c_str()); #endif set_error(std::string(HZ__("Unable to copy file \"/path1/\" to \"/path2/\": ")) + HZ__("Error while closing source file: /errno/."), errno, this->get_path(), to); return false; } // copy permissions. don't check for errors here - they are harmless. if (stat_result == 0) { #ifdef _WIN32 (void)_wchmod(FsPath(to).get_utf16(), st.st_mode & (_S_IREAD | _S_IWRITE)); // it won't accept anything else. #else chmod(to.c_str(), st.st_mode & 07777); // don't transfer unneeded stuff (like "is directory"). #endif } return true; } File::handle_type inline File::platform_fopen(const char* file, const char* open_mode) { // Don't validate parameters, they will be validated by the called functions. #if defined HAVE_WIN_SE_FUNCS && HAVE_WIN_SE_FUNCS handle_type f = 0; errno = _wfopen_s(&f, hz::scoped_array(hz::win32_utf8_to_utf16(file)).get(), hz::scoped_array(hz::win32_utf8_to_utf16(open_mode)).get() ); return f; #elif defined _WIN32 return _wfopen(hz::scoped_array(hz::win32_utf8_to_utf16(file)).get(), hz::scoped_array(hz::win32_utf8_to_utf16(open_mode)).get()); #else return std::fopen(file, open_mode); #endif } int inline File::platform_fseek(handle_type stream, file_size_t offset, int whence) { // Don't validate parameters, they will be validated by the called functions. #if defined HAVE_POSIX_OFF_T_FUNCS && HAVE_POSIX_OFF_T_FUNCS return fseeko(stream, offset, whence); // POSIX #elif defined HAVE_WIN_LFS_FUNCS && HAVE_WIN_LFS_FUNCS return _fseeki64(stream, offset, whence); #else return std::fseek(stream, offset, whence); #endif } file_size_t inline File::platform_ftell(handle_type stream) { #if defined HAVE_POSIX_OFF_T_FUNCS && HAVE_POSIX_OFF_T_FUNCS return ftello(stream); // POSIX #elif defined HAVE_WIN_LFS_FUNCS && HAVE_WIN_LFS_FUNCS return _ftelli64(stream); #else return std::ftell(stream); #endif } } // ns hz #endif /// @} gsmartcontrol-1.1.4/src/hz/fs_file_test.cpp000066400000000000000000000023111417717230600210020ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz_tests /// \weakgroup hz_tests /// @{ // disable libdebug, we don't link to it #undef HZ_USE_LIBDEBUG #define HZ_USE_LIBDEBUG 0 // enable libdebug emulation through std::cerr #undef HZ_EMULATE_LIBDEBUG #define HZ_EMULATE_LIBDEBUG 1 // The first header should be then one we're testing, to avoid missing // header pitfalls. #include "fs_file.h" #include #include /// Main function for the test int main() { std::vector files; files.push_back("/usr/bin/ar"); files.push_back("/proc/partitions"); for (unsigned int i = 0; i < files.size(); ++i) { hz::File file(files[i]); hz::file_size_t size1 = 0, size2 = 0; file.get_size(size1); // stat() method file.get_size(size2, true); // fread() method std::cerr << "File " << file.str() << "\n"; std::cerr << "stat size: " << size1 << ", fread size: " << size2 << "\n\n"; } return 0; } /// @} gsmartcontrol-1.1.4/src/hz/fs_path.h000066400000000000000000000644051417717230600174410ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2013 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_FS_PATH_H #define HZ_FS_PATH_H #include "hz_config.h" // feature macros #include #include // errno (not std::errno, it may be a macro) #include // for stdio.h; std::FILE, std::fclose, std::remove. #include // _wfopen* #include // std::time_t #include // *stat() needs this; utime.h needs this; mode_t #include // *stat() needs this; mkdir() #ifdef _WIN32 #include // _waccess*(), _wstat(), _wunlink(), _wrmdir(), _wmkdir() #include // _wutime() #else #include // std::size_t #include // access(), stat(), unlink(), readlink() #include // utime() #endif #include "fs_common.h" // separator #include "fs_path_utils.h" // path_* functions #include "fs_error_holder.h" // FsErrorHolder #include "fs_dir_platform.h" // directory_* functions #include "win32_tools.h" // hz::win32_* charset conversion /** \file Filesystem path and file manipulation. This API accepts/gives utf-8 filenames/paths on win32, current locale filenames/paths on others (just like glib). */ namespace hz { /// The sole purpose of this class is to enforce privacy of its members. /// You can not construct an object of this class. class FsPathHolder { protected: // construct only from children /// Constructor FsPathHolder() #ifdef _WIN32 : utf16_path_(0) #endif { } /// Constructor. FsPathHolder(const std::string& path) : path_(path) #ifdef _WIN32 , utf16_path_(0) #endif { } public: /// Virtual destructor virtual ~FsPathHolder() { #ifdef _WIN32 delete[] utf16_path_; #endif } // --- these will _not_ set bad() status /// Set current path. void set_path(const std::string& path) { path_ = path; #ifdef _WIN32 delete[] utf16_path_; utf16_path_ = 0; // reset #endif } /// Get current path std::string get_path() const { return path_; } /// Same as get_path() std::string str() const { return path_; } /// Same as get_path().c_str() const char* c_str() const { return path_.c_str(); } /// Check if current path string is empty bool empty() const { return path_.empty(); } // These are sort-of internal #ifdef _WIN32 /// Get path in UTF-16 format and store it (convert it from UTF-8) /// Note: This may actually return 0 if it's unsupported or the encoded string is invalid. const wchar_t* get_utf16() const { if (!utf16_path_) utf16_path_ = hz::win32_utf8_to_utf16(path_.c_str()); return utf16_path_; } /// Set path in UTF-16 format. Also convert it to UTF-8 and store it. bool set_utf16(const wchar_t* path) { char* utf8 = win32_utf16_to_utf8(path); if (utf8) { set_path(utf8); // this correctly resets utf16_path_ delete[] utf8; return true; } return false; } #endif private: // don't let children modify path_ directly, it will desync utf16_path_. std::string path_; ///< Current path. Always UTF-8 in windows. #ifdef _WIN32 mutable wchar_t* utf16_path_; ///< Same as path_, but in UTF-16. #endif }; /// A class representing a filesystem path. class FsPath : public FsPathHolder, public FsErrorHolder { public: /// \typedef mode_type /// mkdir() mode type. #ifdef _WIN32 typedef int mode_type; #else typedef mode_t mode_type; #endif /// Constructor FsPath() { } /// Constructor, sets current path FsPath(const std::string& path) : FsPathHolder(path) { } /// Destructor virtual ~FsPath() { } // --- these will _not_ set bad() status /// Convert path from unknown format to native (e.g. unix paths to win32). /// The current object is also modified. inline FsPath& to_native(); /// Remove trailing separators in path (unless they are parts of the root component). /// The current object is also modified. inline FsPath& trim_trailing(); /// Go up \c steps steps. The current object is also modified. inline FsPath& go_up(unsigned int steps = 1); /// Append a partial (e.g. relative) path. It doesn't matter if it starts with a /// separator. The current object is also modified. inline FsPath& append(const std::string& partial_path); /// Compress a path - remove double separators, trailing /// separator, "/./" components, and deal with "/../" if possible. /// Note: This function performs its operations on strings, not real paths. /// The current object is also modified. inline FsPath& compress(); /// Make the path absolute (if it's not already) by prepending \c base_path. inline FsPath& make_absolute(const std::string& base_path); /// Get the path truncated by 1 level, e.g. /usr/local/ -> /usr. inline std::string get_dirname() const; /// Get the basename of path, e.g. /usr/local/ -> local; /a/b/c -> c. inline std::string get_basename() const; /// Get root path of current path. e.g. '/' or 'D:\'. /// May not work with relative paths under win32. inline std::string get_root() const; /// Check if the path corresponds to root (drive / share in win32). inline bool is_root() const; /// Get an extension of the last component, e.g. /local/archive.tar.gz -> gz inline std::string get_extension() const; /// Get a full extension of the last component, e.g. /local/archive.tar.gz -> tar.gz inline std::string get_full_extension() const; /// Get the last component without extension, e.g. /local/archive.tar.gz -> archive.tar inline std::string get_noext_basename() const; /// Get the last component without extension, e.g. /local/archive.tar.gz -> archive inline std::string get_noext_min_basename() const; /// Check if the path is absolute (only for native paths). returns 0 if it's not. /// the returned value is a position past the root component (e.g. 3 for C:\\temp). inline std::string::size_type is_absolute() const; /// Check if the current path is a subpath of supplied argument. /// e.g. /usr/local/bin is a subpath of /usr. Note: This doesn't check real paths, only strings. inline bool is_subpath_of(const std::string& superpath) const; // --- these may set bad() status /// Check if the existing file can be fopen'ed with "rb", or the directory has read perms. /// Note: This function should be use only as an utility function (e.g. for GUI notification); /// other uses are not logically concurrent-safe (and therefore, insecure). /// bad() status is set on failure. inline bool is_readable(); /// Check if the existing or soon to be created file is writable, or if files can be created in this dir. /// Note: The same security considerations apply to this function as to is_readable(). /// bad() status is set on failure. inline bool is_writable(); /// Check if anything exists at this path. /// Note: The same security considerations apply to this function as to is_readable(). /// bad() status is set on failure. inline bool exists(); /// Check if it's a file (any type, including block, etc..,). Will also match for symlinks to files. /// Note: The same security considerations apply to this function as to is_readable(). /// bad() status is set on error. inline bool is_file(); /// Check if it's a regular file. Will also match for symlinks to files. /// Note: The same security considerations apply to this function as to is_readable(). /// bad() status is set on failure. inline bool is_regular(); /// Check if it's a directory. /// Note: The same security considerations apply to this function as to is_readable(). /// bad() status is set on error. inline bool is_dir(); /// Check if it's a symlink. /// Note: The same security considerations apply to this function as to is_readable(). /// bad() status is set on failure. inline bool is_symlink(); /// If current path is a symbolic link, put its destination into \c dest. /// Note: You should avoid calling this in loop, you may end up with an /// infinite loop if the links point to each other. /// Note: The returned path may be relative to the link's directory. /// If error occurs, false is returned and bad() status is set. /// If current path is not a symbolic link, false is returned, bad() is _not_ set. /// If false is returned, dest is untouched. inline bool get_link_destination(std::string& dest); /// Get "last modified time" property. /// Do NOT assign the result to int - std::time_t is implementation-defined. /// Usually it's seconds since epoch, see time(2) for details. /// bad() status is set on failure and false is returned. inline bool get_last_modified(std::time_t& put_here); /// Set the last modified time. It will also change the last access /// time as a side effect. /// bad() status is set on failure and false is returned. inline bool set_last_modified(std::time_t t); /// Create a directory (assuming that the parent directory already exists). /// octal_mode parameter is ignored on Windows. /// Note: If creating with parents, when failing to create one of the directories, /// it won't remove the previously created ones. /// Note: If creating with parents, the supplied path _must_ be absolute. /// bad() status is set on failure and false is returned. inline bool make_dir(mode_type octal_mode, bool with_parents); /// Remove a file or directory. /// bad() status is set on failure and false is returned. inline bool remove(bool recursive = false); }; // ------------------------------------------- Implementation inline FsPath& FsPath::to_native() { this->set_path(path_to_native(this->get_path())); return *this; } inline FsPath& FsPath::trim_trailing() { this->set_path(path_trim_trailing_separators(this->get_path())); return *this; } inline FsPath& FsPath::go_up(unsigned int steps) { std::string p = this->get_path(); while (steps--) p = get_dirname(); this->set_path(p); return *this; } inline FsPath& FsPath::append(const std::string& partial_path) { std::string p = this->get_path(); // trim leading separators of partial_path std::string::size_type index = partial_path.find_first_not_of(DIR_SEPARATOR); if (index != std::string::npos) { // if no non-separator characters found, do nothing. trim_trailing(); if (!is_root()) p += DIR_SEPARATOR_S; p += partial_path.substr(index); } this->set_path(p); return *this; } inline FsPath& FsPath::compress() { this->set_path(path_compress(this->get_path())); return *this; } FsPath& FsPath::make_absolute(const std::string& base_path) { if (!is_absolute()) { set_path(FsPath(base_path).append(get_path()).get_path()); } return compress(); } inline std::string FsPath::get_dirname() const { return path_get_dirname(this->get_path()); } inline std::string FsPath::get_basename() const { return path_get_basename(this->get_path()); } inline std::string FsPath::get_root() const { return path_get_root(this->get_path()); } inline bool FsPath::is_root() const { std::string p = path_trim_trailing_separators(this->get_path()); return (path_is_absolute(p) == p.size()); } inline std::string FsPath::get_extension() const { std::string base = get_basename(); std::string::size_type pos = base.rfind('.'); if (pos != std::string::npos) return base.substr(pos + 1); return std::string(); } inline std::string FsPath::get_full_extension() const { std::string base = get_basename(); std::string::size_type pos = base.find('.'); if (pos != std::string::npos) return base.substr(pos + 1); return std::string(); } std::string FsPath::get_noext_basename() const { std::string base = get_basename(); std::string::size_type pos = base.rfind('.'); return base.substr(0, pos); } std::string FsPath::get_noext_min_basename() const { std::string base = get_basename(); std::string::size_type pos = base.find('.'); return base.substr(0, pos); } inline std::string::size_type FsPath::is_absolute() const { return path_is_absolute(this->get_path()); } inline bool FsPath::is_subpath_of(const std::string& superpath) const { return (this->get_path().compare(0, superpath.length(), superpath) == 0); } inline bool FsPath::is_readable() { clear_error(); if (this->empty()) { set_error(std::string(HZ__("Unable to check if a file or directory is readable: ")) + HZ__("Supplied path is empty.")); return false; } #if defined HAVE_WIN_SE_FUNCS && HAVE_WIN_SE_FUNCS if (_waccess_s(this->get_utf16(), 04)) // msvc uses integers instead (R_OK == 04 anyway). #elif defined _WIN32 if (_waccess(this->get_utf16(), 04) == -1) // *access*() may not work with < win2k with directories. #else if (access(this->c_str(), R_OK) == -1) // from unistd.h #endif { set_error(HZ__("File or directory \"/path1/\" is not readable: /errno/."), errno, this->get_path()); } return ok(); } inline bool FsPath::is_writable() { clear_error(); if (this->empty()) { set_error(std::string(HZ__("Unable to check if a file or directory is writable: ")) + HZ__("Supplied path is empty.")); return false; } bool is_directory = is_dir(); bool path_exists = exists(); std::string dirname = (is_directory ? path_trim_trailing_separators(this->get_path()) : get_dirname()); clear_error(); #ifdef _WIN32 // win32 doesn't get access() (it just doesn't work with writing) // If it doesn't exist, try to create it. // If it exists and is a file, try to open it for writing. // If it exists and is a directory, try to create a test file in it. // Note: This method is possibly non-suitable for symlink-capable filesystems. FsPath path_to_check(this->get_path()); path_to_check.trim_trailing(); if (path_exists && is_directory) { path_to_check.set_path(path_to_check.get_path() += std::string(DIR_SEPARATOR_S) + "__test.txt"); path_exists = path_to_check.exists(); } // pcheck either doesn't exist, or it's a file. try to open it. std::FILE* f = 0; #if defined HAVE_WIN_SE_FUNCS && HAVE_WIN_SE_FUNCS errno = _wfopen_s(&f, path_to_check.get_utf16(), L"ab"); #else f = _wfopen(path_to_check.get_utf16(), L"ab"); // this creates a 0 size file if it doesn't exist! #endif if (!f) { set_error(HZ__("File or directory \"/path1/\" is not writable: /errno/."), errno, this->get_path()); return false; } if (std::fclose(f) != 0) { set_error(std::string(HZ__("Unable to check if a file or directory \"/path1/\" is writable: ")) + HZ__("Error while closing file: /errno/."), errno, this->get_path()); return false; } // remove the created file if (path_exists && _wunlink(path_to_check.get_utf16()) == -1) { set_error(std::string(HZ__("Unable to check if a file or directory \"/path1/\" is writable: ")) + HZ__("Error while removing file: /errno/."), errno, this->get_path()); return false; } // All OK #else if (path_exists && is_directory) { if (access(dirname.c_str(), W_OK) == -1) { set_error(HZ__("File or directory \"/path1/\" is not writable: /errno/."), errno, this->get_path()); } } else { // no such path or it's a file if (path_exists) { // checking an existing file if (access(this->c_str(), W_OK) == -1) set_error(HZ__("File or directory \"/path1/\" is not writable: /errno/."), errno, this->get_path()); } else { // no such path, check parent dir's access mode if (access(dirname.c_str(), W_OK) == -1) set_error(HZ__("File or directory \"/path1/\" is not writable: /errno/."), errno, this->get_path()); } } #endif return ok(); } inline bool FsPath::exists() { clear_error(); if (this->empty()) { set_error(std::string(HZ__("Unable to check if a file or directory exists: ")) + HZ__("Supplied path is empty.")); return false; } #if defined HAVE_WIN_SE_FUNCS && HAVE_WIN_SE_FUNCS if (_waccess_s(this->get_utf16(), 00) != 0) // msvc uses integers instead (F_OK == 00 anyway). #elif defined _WIN32 if (_waccess(this->get_utf16(), 00) != 0) // msvc uses integers instead (F_OK == 00 anyway). #else if (access(this->c_str(), F_OK) == -1) #endif { if (errno != ENOENT) // ENOENT (No such file or directory) shouldn't be reported as error. set_error(HZ__("File or directory \"/path1/\" doesn't exist: /errno/."), errno, this->get_path()); return false; } return ok(); } inline bool FsPath::is_file() { clear_error(); if (this->empty()) { set_error(std::string(HZ__("Unable to check if a path points to a file: ")) + HZ__("Supplied path is empty.")); return false; } #ifdef _WIN32 struct _stat s; const int stat_result = _wstat(this->get_utf16(), &s); #else struct stat s; const int stat_result = stat(this->c_str(), &s); #endif if (stat_result == -1) { set_error(HZ__("Unable to check if a path \"/path1/\" points to a file: /errno/."), errno, this->get_path()); return false; } #ifdef _WIN32 if (s.st_mode & _S_IFDIR) #else if (S_ISDIR(s.st_mode)) // we check for dir. anything else is a file. #endif { // set_error("The path \"" + path_ + "\" points to directory."); return false; } return true; } inline bool FsPath::is_regular() { clear_error(); if (this->empty()) { set_error(std::string(HZ__("Unable to check if a path points to a regular file: ")) + HZ__("Supplied path is empty.")); return false; } #ifdef _WIN32 struct _stat s; const int stat_result = _wstat(this->get_utf16(), &s); #else struct stat s; const int stat_result = stat(this->c_str(), &s); #endif if (stat_result == -1) { set_error(HZ__("Unable to check if a path \"/path1/\" points to a regular file: /errno/."), errno, this->get_path()); return false; } #ifdef _WIN32 // MS documentation contradicts itself about when _S_IFREG is set // (see _fstat() and _stat()). _stat() says it's a regular file or a char device, // _fstat() says it's a regular file and char device is represented by _S_IFCHR. // We check both, just in case. if (!(s.st_mode & _S_IFREG) || (s.st_mode & _S_IFCHR)) // if it's not a regular file or it's a char device. #else if (!S_ISREG(s.st_mode)) // we check for dir. anything else is a file. #endif { // set_error("The path \"" + path_ + "\" points to a non-regular file."); return false; } return true; } inline bool FsPath::is_dir() { clear_error(); if (this->empty()) { set_error(std::string(HZ__("Unable to check if a path points to directory: ")) + HZ__("Supplied path is empty.")); return false; } #ifdef _WIN32 struct _stat s; const int stat_result = _wstat(this->get_utf16(), &s); #else struct stat s; const int stat_result = stat(this->c_str(), &s); #endif if (stat_result == -1) { set_error(HZ__("Unable to check if a path \"/path1/\" points to directory: /errno/."), errno, this->get_path()); return false; } #ifdef _WIN32 if (!(s.st_mode & _S_IFDIR)) #else if (!S_ISDIR(s.st_mode)) #endif { // set_error("The path \"" + path_ + "\" points to a file."); return false; } return true; } inline bool FsPath::is_symlink() { clear_error(); if (this->empty()) { set_error(std::string(HZ__("Unable to check if a path points to a symbolic link: ")) + HZ__("Supplied path is empty.")); return false; } #ifdef _WIN32 // well, win32 and all... // Although there are symlinks there (theoretically), we don't want to get // our hands dirty (glib doesn't). return false; #else struct stat s; if (lstat(this->c_str(), &s) == -1) { set_error(HZ__("Unable to check if a path \"/path1/\" points to a symbolic link: /errno/."), errno, this->get_path()); return false; } if (!S_ISLNK(s.st_mode)) { return false; } return true; #endif } inline bool FsPath::get_link_destination(std::string& dest) { clear_error(); if (this->empty()) { set_error(std::string(HZ__("Unable to get link destination: ")) + HZ__("Supplied path is empty.")); return false; } #ifdef _WIN32 return false; // not a link (see is_symlink()) #else std::size_t buf_size = 256; // size_t, as accepted by readlink(). do { char* buf = new char[buf_size]; // readlink() returns the number of written bytes, not including terminating 0. ssize_t written = readlink(this->c_str(), buf, buf_size); if (written == -1) { // error if (errno != EINVAL) { // EINVAL: The named file is not a symbolic link. set_error(HZ__("Unable to get link destination of path \"/path1/\": /errno/."), errno, this->get_path()); } delete[] buf; return false; } if (written != static_cast(buf_size)) { // means we have enough room buf[written] = '\0'; // there's a place for this. dest = buf; delete[] buf; break; } else { // doesn't fit, increase size. buf_size *= 4; } delete[] buf; } while (true); return true; #endif } inline bool FsPath::get_last_modified(std::time_t& put_here) { clear_error(); if (this->empty()) { set_error(std::string(HZ__("Unable to get the last modification time of a path: ")) + HZ__("Supplied path is empty.")); return false; } #ifdef _WIN32 struct _stat s; const int stat_result = _wstat(this->get_utf16(), &s); #else struct stat s; const int stat_result = stat(this->c_str(), &s); #endif if (stat_result == -1) { set_error(HZ__("Unable to get the last modification time of path \"/path1/\": /errno/."), errno, this->get_path()); return false; } put_here = s.st_mtime; return ok(); } inline bool FsPath::set_last_modified(std::time_t t) { clear_error(); if (this->empty()) { set_error(std::string(HZ__("Unable to set the last modification time of a filesystem entry: ")) + HZ__("Supplied path is empty.")); return false; } #ifdef _WIN32 struct _utimbuf tb; #else struct utimbuf tb; #endif tb.actime = t; // this is a side effect - change access time too tb.modtime = t; #ifdef _WIN32 if (_wutime(this->get_utf16(), &tb) == -1) #else if (utime(this->c_str(), &tb) == -1) #endif { set_error(HZ__("Unable to set the last modification time of path \"/path1/\": /errno/."), errno, this->get_path()); return false; } return ok(); } inline bool FsPath::make_dir(mode_type octal_mode, bool with_parents) { // debug_out_dump("hz", DBG_FUNC_MSG << "Path: \"" << path_ << "\"\n"); clear_error(); if (this->empty()) { set_error(std::string(HZ__("Unable to create directory: ")) + HZ__("Supplied path is empty.")); return false; } if (is_root()) return true; if (with_parents) { if (!is_absolute()) { set_error(std::string(HZ__("Unable to create directory with parents at path \"/path1/\": ")) + HZ__("Supplied path must be absolute."), 0, this->get_path()); return false; } FsPath p(get_dirname()); if (!p.make_dir(octal_mode, with_parents)) { import_error(p); return false; } } #ifdef _WIN32 int status = _wmkdir(this->get_utf16()); #else int status = mkdir(this->c_str(), octal_mode); #endif if (status == -1) { if (errno == EEXIST && is_dir()) { return true; // already exists } set_error(HZ__("Unable to create directory at path \"/path1/\": /errno/."), errno, this->get_path()); return false; } return ok(); } namespace internal { /// Helper function, internal. /// Remove Remove directory recursively. /// \return the number of not removed files. 0 on success. pass directory only. inline int path_remove_dir_recursive(const std::string& path) { hz::FsPath p(path); if (!p.exists()) return 1; // couldn't remove 1 file. maybe doesn't exist - it's still an error. // if (! p.is_file().bad()) { // file // #ifdef _WIN32 // return static_cast(_wunlink(p.get_utf16()) == -1); // #else // return static_cast(unlink(p.c_str()) == -1); // #endif // } int error_count = 0; // It's a directory, iterate it, then remove it directory_handle_type dir = directory_open(path.c_str()); if (dir) { while (true) { errno = 0; directory_entry_handle_type entry = directory_read(dir); if (errno) { // error while reading entry. try next one. error_count++; continue; } if (!entry) { // no error and entry is null - means we reached the end. break; } std::string entry_name = directory_entry_name(entry); if (entry_name == "." || entry_name == "..") // won't delete those continue; std::string entry_path = path + DIR_SEPARATOR_S + directory_entry_name(entry); FsPath ep(entry_path); if (ep.is_dir() && !ep.is_symlink()) { // if it's a directory and not a symlink, go recursive error_count += path_remove_dir_recursive(entry_path); } else { // just remove it #ifdef _WIN32 if (_wunlink(ep.get_utf16()) == -1) #else if (unlink(ep.c_str()) == -1) #endif { error_count++; } } } directory_close(dir); } // try to remove dir even if it's non-readable. #ifdef _WIN32 const int rmdir_result = _wrmdir(p.get_utf16()); #else const int rmdir_result = rmdir(p.c_str()); #endif if (rmdir_result == -1) return ++error_count; return error_count; } } // ns inline bool FsPath::remove(bool recursive) { clear_error(); if (this->empty()) { set_error(std::string(HZ__("Unable to remove file or directory: ")) + HZ__("Supplied path is empty.")); return false; } if (path_trim_trailing_separators(this->get_path()) == get_root()) { set_error(std::string(HZ__("Unable to remove file or directory \"/path1/\": ")) + HZ__("Cannot remove root directory."), 0, this->get_path()); return false; } if (recursive && !is_file()) { clear_error(); // clear previous function call if (internal::path_remove_dir_recursive(this->get_path()) > 0) { // supply dirs here only. set_error(HZ__("Unable to remove directory \"/path1/\" completely: Some files couldn't be deleted."), 0, this->get_path()); } return false; } #ifdef _WIN32 // win2k (maybe later wins too) remove() says "permission denied" (!) on directories. int status = 0; if (is_dir()) { status = _wrmdir(this->get_utf16()); // empty dir only } else { status = _wunlink(this->get_utf16()); // files only } if (status == -1) { set_error(HZ__("Unable to remove file or directory \"/path1/\": /errno/."), errno, this->get_path()); } #else if (std::remove(this->c_str()) == -1) { // In case of not empty directory, POSIX says the error will be EEXIST // or ENOTEMPTY. Linux uses ENOTEMPTY, Solaris uses EEXIST. // ENOTEMPTY makes more sense for error messages, so convert. if (errno == EEXIST) errno = ENOTEMPTY; set_error(HZ__("Unable to remove file or directory \"/path1/\": /errno/."), errno, this->get_path()); } #endif return ok(); } } // ns hz #endif /// @} gsmartcontrol-1.1.4/src/hz/fs_path_test.cpp000066400000000000000000000067261417717230600210350ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz_tests /// \weakgroup hz_tests /// @{ // disable libdebug, we don't link to it #undef HZ_USE_LIBDEBUG #define HZ_USE_LIBDEBUG 0 // enable libdebug emulation through std::cerr #undef HZ_EMULATE_LIBDEBUG #define HZ_EMULATE_LIBDEBUG 1 // The first header should be then one we're testing, to avoid missing // header pitfalls. #include "fs_path.h" #include "fs_file.h" #include #include /// Main function for the test int main() { using namespace hz; { std::string s = "\\\\\\\\asd\\www"; std::cerr << FsPath(s).to_native().str() << "\n"; } { File p("Makeafile", "rb"); if (p.bad()) { std::cerr << p.get_error_locale() << "\n"; } } { std::string s; File p("Makeafile"); if (p.get_contents(s)) { std::cerr << "Read " << s.size() << " bytes.\n"; } else { std::cerr << p.get_error_locale() << "\n"; } } { std::string s = "\\12/2$&! a23412"; std::cerr << "Safe file: " << filename_make_safe(s) << ", safe path " << path_make_safe(s) << "\n"; } { std::vector paths; #ifdef _WIN32 paths.push_back("A:\\temp\\ab"); // dir: A:\temp, base: ab paths.push_back("B:\\temp\\ab\\\\"); // dir: B:\temp, base: ab paths.push_back("\\\\host\\"); // dir: \\host\, base: \\host\ ; paths.push_back("C:\\"); // dir: C:\, base: C:\; paths.push_back("D:\\a\\\\b\\\\c"); // dir: D:\a\\b, base: c paths.push_back("\\a\\b\\c"); // dir: \a\b, base: c paths.push_back("d\\e\\f"); // dir: \d\e, base: f paths.push_back("\\f"); // dir: ., base: f; We can't use the \-paths, because there's no "current" drive. paths.push_back("g"); // dir: ., base: g paths.push_back("C:\\temp"); // dir: temp, base: C:\ ; paths.push_back("C:\\temp\\"); // dir: temp, base: C:\ ; paths.push_back("C:\\Documents and Settings\\whatever\\My Documents\\hello.conf"); paths.push_back("."); // dir: ., base: . paths.push_back(".."); // dir: ., base: .. paths.push_back(""); // dir: ., base: . #else paths.push_back("C:\\22da\\a\\"); paths.push_back("/usr/local/bin//"); // dir: /usr/local, base: bin paths.push_back("/a/dd//e/"); // dir: /a/dd, base: e paths.push_back("a/b/c/d/"); // dir: a/b/c, base: d // examples from man 2 dirname paths.push_back("/usr/local/lib"); // dir: /usr/local, base: lib paths.push_back("/usr/"); // dir: /, base: usr paths.push_back("usr"); // dir: ., base: usr paths.push_back("/"); // dir: /, base: / paths.push_back("."); // dir: ., base: . paths.push_back(".."); // dir: ., base: .. paths.push_back(""); // dir: ., base: . paths.push_back("./hello/a/b.././c/d/../e"); paths.push_back("/a/../.././../b/"); paths.push_back("../a/./b/.."); paths.push_back("//.programrc"); #endif for (unsigned int i = 0; i < paths.size(); ++i) { FsPath p = paths[i]; std::cerr << p.str() << ":\n" << "\t" << "dir: " << p.get_dirname() << ", " << " " << "base: " << p.get_basename() << ", " << " " << "root: " << p.get_root() << "\n" << "\t" << "abs: " << p.is_absolute() << ", " << " " << "trim: " << path_trim_trailing_separators(p.str()) << ", " << " " << "compress: " << path_compress(p.str()) << "\n"; } } return 0; } /// @} gsmartcontrol-1.1.4/src/hz/fs_path_utils.h000066400000000000000000000210711417717230600206510ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2013 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_FS_PATH_UTILS_H #define HZ_FS_PATH_UTILS_H #include "hz_config.h" // feature macros #include #if !defined _WIN32 #include // dirname, basename #include // strncpy #include // std::size_t #endif #include "fs_common.h" // separator /** \file Filesystem path string manipulation. For windows, always supply utf-8 or current locale-encoded strings. Paths like \\.\ and \\?\ are not supported for windows (yet). */ namespace hz { /// Convert path from unknown format to native (e.g. unix paths to win32). /// Same as FsPath(path).to_native().str() inline std::string path_to_native(const std::string& path); /// Remove trailing slashes in path (unless they are part of root component). inline std::string path_trim_trailing_separators(const std::string& path); /// Check if the path is absolute (only for native paths). Return 0 if it's not. /// The returned value is a position past the root component (e.g. 3 for C:\\temp). inline std::string::size_type path_is_absolute(const std::string& path); /// Get the path truncated by 1 level, e.g. /usr/local/ -> /usr. inline std::string path_get_dirname(const std::string& path); /// Get the basename of path, e.g. /usr/local/ -> local; /a/b -> b. inline std::string path_get_basename(const std::string& path); /// Get root path of current path. e.g. '/' or 'D:\'. /// May not work with relative paths under win32. inline std::string path_get_root(const std::string& path); /// Change the supplied filename so that it's safe to create it /// (remove any potentially harmful characters from it). inline std::string filename_make_safe(const std::string& filename); /// Change the supplied path so that it's safe to create it /// (remove any potentially harmful characters from it). inline std::string path_make_safe(const std::string& path); // ------------------------------------------- Implementation inline std::string path_to_native(const std::string& path) { std::string s(path); std::string::size_type pos = 0; char from = '\\'; if (DIR_SEPARATOR == '\\') from = '/'; while ((pos = s.find(from, pos)) != std::string::npos) { s[pos] = DIR_SEPARATOR; ++pos; } return s; } inline std::string path_trim_trailing_separators(const std::string& path) { std::string::size_type apos = path_is_absolute(path); // first position of non-abs portion if (apos >= path.size()) // / or similar return path; std::string::size_type pos = path.find_last_not_of(DIR_SEPARATOR); // remove trailing slashes if (pos == std::string::npos) // / ? return path.substr(0, apos); return path.substr(0, pos + 1); } inline std::string::size_type path_is_absolute(const std::string& path) { #ifndef _WIN32 if (!path.empty() && path[0] == '/') return 1; #else // win32 if (path.size() >= 3 && path.substr(1, 2) == ":\\") // 'D:\' return 3; if (path.size() >= 4 && path.substr(0, 2) == "\\\\") { // '\\host\' std::string::size_type pos = path.rfind('\\'); if (pos >= 3 && pos != std::string::npos) return pos + 1; } #endif return 0; } inline std::string path_get_dirname(const std::string& path) { // GLib (as of 2.14.1) has a bug in g_path_get_dirname() implementation: // "/usr/local/" returns "/usr/local", not "/usr". Don't use it. #if !defined _WIN32 std::size_t buf_size = path.size() + 1; char* buf = new char[buf_size]; std::strncpy(buf, path.c_str(), buf_size); std::string ret = dirname(buf); // dirname may modify buf, that's why we needed a copy of it. delete[] buf; return ret; #else if (path.empty()) return "."; std::string::size_type apos = path_is_absolute(path); // first position of non-abs portion if (apos >= path.size()) // / or similar return path; std::string::size_type pos2 = path.find_last_not_of(DIR_SEPARATOR); // remove trailing slashes if (pos2 == std::string::npos) // / ? return path.substr(0, apos); std::string::size_type pos1 = path.find_last_of(DIR_SEPARATOR, pos2); // next slash from the end if (pos1 == std::string::npos) { return "."; // one-component relative dir } if (apos && pos1 == apos - 1) { // it's a root subdir return path.substr(0, apos); } pos1 = path.find_last_not_of(DIR_SEPARATOR, pos1); // skip duplicate slashes if (pos1 == std::string::npos && apos) // it's root subdir return path.substr(0, apos); std::string dir = path.substr(0, pos1+1); if (dir.empty()) return "."; return dir; #endif } inline std::string path_get_basename(const std::string& path) { #if !defined _WIN32 std::size_t buf_size = path.size() + 1; char* buf = new char[buf_size]; std::strncpy(buf, path.c_str(), buf_size); std::string ret = basename(buf); // basename may modify buf, that's why we needed a copy of it. delete[] buf; return ret; #else if (path.empty()) return "."; std::string::size_type apos = path_is_absolute(path); // first position of non-abs portion if (apos >= path.size()) // / or similar return path; // / -> /, as per basename manpage std::string::size_type pos2 = path.find_last_not_of(DIR_SEPARATOR); // remove trailing slashes std::string::size_type pos1 = path.find_last_of(DIR_SEPARATOR, pos2); pos1 = (pos1 == std::string::npos ? 0 : (pos1 + 1)); pos2 = (pos2 == std::string::npos ? path.size() : (pos2 + 1)); return path.substr(pos1, pos2 - pos1); #endif } inline std::string path_get_root(const std::string& path) { #if !defined _WIN32 return "/"; // easy #else // hard // don't use path_is_absolute(), we have slightly more error-checking. if (path.size() >= 3 && path.substr(1, 2) == ":\\") // 'D:\' return path.substr(0, 3); if (path.size() >= 4 && path.substr(0, 2) == "\\\\") { // '\\host\', '\\.\', '\\?\' std::string::size_type pos = path.rfind('\\'); if (pos >= 3 && pos != std::string::npos) return path.substr(0, pos+1); } return std::string(); // cannot detect #endif } inline std::string path_compress(const std::string& path) { std::string::size_type rel_pos = path_is_absolute(path); std::string rel = path.substr(rel_pos); // retrieve relative component only std::string::size_type curr = 0, last = 0; std::string::size_type end = rel.size(); std::string result, component; while (true) { if (last >= end) // last is past the end break; curr = rel.find(DIR_SEPARATOR, last); if (curr != last) { component = rel.substr(last, (curr == std::string::npos ? curr : (curr - last))); if (component == ".") { if (result == "" && rel_pos == 0) // don't un-relativise result += (std::string(".") + DIR_SEPARATOR_S); // else, nothing } else if (component == "..") { // retain ".." when previous component is ".." or ".". if (result == "" || result == (std::string(".") + DIR_SEPARATOR_S) || (result.size() >= 3 && result.substr(result.size() - 3) == (std::string("..") + DIR_SEPARATOR_S))) { result += (std::string("..") + DIR_SEPARATOR_S); } else { // std::cerr << "Getting dirname on \"" << result << "\"\n"; std::string up = path_get_dirname(result); // go up if (up == ".") { result = ""; } else { result = (up + DIR_SEPARATOR_S); } } } else { result += (component + DIR_SEPARATOR_S); } } if (curr == std::string::npos) break; last = curr + 1; } return path_trim_trailing_separators(path.substr(0, rel_pos) + result); } inline std::string filename_make_safe(const std::string& filename) { std::string s(filename); std::string::size_type pos = 0; while ((pos = s.find_first_not_of( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890._-", pos)) != std::string::npos) { s[pos] = '_'; ++pos; } // win32 kernel (heh) has trouble with space and dot-ending files if (!s.empty() && (s[s.size() - 1] == '.' || s[s.size() - 1] == ' ')) { s[s.size() - 1] = '_'; } return s; } inline std::string path_make_safe(const std::string& path) { std::string s(path); std::string::size_type pos = 0; while ((pos = s.find_first_not_of( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890._-", pos)) != std::string::npos) { if (s[pos] != DIR_SEPARATOR) s[pos] = '_'; ++pos; } // win32 kernel (heh) has trouble with space and dot-ending files if (!s.empty() && (s[s.size() - 1] == '.' || s[s.size() - 1] == ' ')) { s[s.size() - 1] = '_'; } return s; } } // ns hz #endif /// @} gsmartcontrol-1.1.4/src/hz/fs_tools.h000066400000000000000000000103021417717230600176300ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_FS_TOOLS_H #define HZ_FS_TOOLS_H #include "hz_config.h" // feature macros #include #include // std::size_t #ifdef _WIN32 #include // _wgetcwd, _wchdir #include // free #else #include // getcwd, chdir #endif #include "env_tools.h" // hz::env_get_value #ifdef _WIN32 #include "win32_tools.h" // win32_* stuff #include "scoped_array.h" // hz::scoped_array #endif /** \file Filesystem utilities */ namespace hz { /// Get the current user's configuration file directory (in native fs encoding /// for UNIX, utf-8 for windows). E.g. "$HOME/.config" in UNIX. inline std::string get_user_config_dir(); /// Get the current user's home directory (in native fs encoding for UNIX, /// utf-8 for windows). This function always returns something, but /// note that the directory may not actually exist at all. inline std::string get_home_dir(); /// Get current working directory inline std::string get_current_dir(); /// Change current directory. inline bool set_current_dir(const std::string& dir); /// Get temporary directory of a system (can be user-specific). /// For windows it seems to be in UTF-8, for others - in fs encoding. inline std::string get_tmp_dir(); // ------------------------- Implementation inline std::string get_user_config_dir() { std::string dir; #ifdef _WIN32 // that's "C:\documents and settings\username\application data". dir = win32_get_special_folder(CSIDL_APPDATA); if (dir.empty()) { dir = get_home_dir(); // fallback, always non-empty. } #else if (!hz::env_get_value("XDG_CONFIG_HOME", dir)) { // default to $HOME/.config dir = get_home_dir() + DIR_SEPARATOR + ".config"; } #endif return dir; } inline std::string get_home_dir() { // Do NOT use g_get_home_dir, it doesn't work consistently in win32 // between glib versions. std::string dir; #ifdef _WIN32 // For windows we usually get "C:\documents and settings\username". // Try $USERPROFILE, then CSIDL_PROFILE, then Windows directory // (glib uses it, not sure why though). hz::env_get_value("USERPROFILE", dir); // in utf-8 if (dir.empty()) { dir = win32_get_special_folder(CSIDL_PROFILE); } if (dir.empty()) { dir = win32_get_windows_directory(); // always returns something. } #else // linux, etc... // We use $HOME to allow the user to override it. // Other solutions involve getpwuid_r() to read from passwd. if (!hz::env_get_value("HOME", dir)) { // works well enough // HOME may be empty in some situations (limited shells // and rescue logins). // We could use /tmp/, but obtaining the username // is too complicated and unportable. dir = get_tmp_dir(); } #endif return dir; } inline std::string get_current_dir() { std::string dir; #ifdef _WIN32 wchar_t* buf = 0; if (!(buf = _wgetcwd(NULL, 0))) { // allocates buf with malloc() return std::string(); // error } dir = hz::win32_utf16_to_utf8_string(buf); free(buf); #else std::size_t buf_size = 1024; char* buf = new char[buf_size]; while (getcwd(buf, buf_size) == 0) { delete[] buf; buf = new char[buf_size *= 2]; } dir = buf; delete[] buf; #endif return dir; } inline bool set_current_dir(const std::string& dir) { #ifdef _WIN32 // Dont check the parameter, let the function do it. return (_wchdir(hz::scoped_array(hz::win32_utf8_to_utf16(dir.c_str())).get()) == 0); #else return (chdir(dir.c_str()) == 0); #endif } inline std::string get_tmp_dir() { std::string dir; hz::env_get_value("TMPDIR", dir); if (dir.empty()) { hz::env_get_value("TMP", dir); } if (dir.empty()) { hz::env_get_value("TEMP", dir); } #ifdef _WIN32 if (dir.empty()) { // not sure about this, but that's what glib does... dir = win32_get_windows_directory(); // always returns something } #else if (dir.empty()) { dir = "/tmp"; } #endif return dir; } } // ns hz #endif /// @} gsmartcontrol-1.1.4/src/hz/hz_config.h000066400000000000000000000211371417717230600177560ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2013 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_HZ_CONFIG_H #define HZ_HZ_CONFIG_H /** \file \brief This file is included from all hz headers. You should include this file if you're using any of: - macros from autoconf-generated config.h - macros from global_macros.h The purpose of this file is to provide some common compatibility solutions for the whole hz, while not depending on pasting autoconf's config.h code into all files. */ /** \namespace hz HZ library, system-level abstraction and general utilities. */ /** \namespace hz::internal HZ library internal implementation helpers. */ #ifdef DOXYGEN_ONLY /// Define HZ_USE_GLOBAL_MACROS=1 from a compiler option to enable /// auto-inclusion of global_macros.h. #define HZ_USE_GLOBAL_MACROS #endif #if defined HZ_USE_GLOBAL_MACROS && HZ_USE_GLOBAL_MACROS // define manually #include "global_macros.h" #define HZ_NO_GLOBAL_MACROS 0 #else #define HZ_NO_GLOBAL_MACROS 1 #endif // if there was no global_macros.h included, do our own mini version #if defined HZ_NO_GLOBAL_MACROS && HZ_NO_GLOBAL_MACROS // Autoconf's config.h. The macro is defined by ./configure automatically. #ifdef HAVE_CONFIG_H #include #endif // No assumptions on config.h contents - it may not do any of these // checks, so don't rely on them. You should use global_macros.h instead, // or manually define them. #endif // Functions / feature checks #if defined _WIN32 && defined __GNUC__ #include <_mingw.h> // mingw feature macros, used below #endif /// \def DISABLE_RTTI /// Defined to 0 or 1. If 1, RTTI is disabled. #ifndef DISABLE_RTTI // No auto-detection here... // There's __GXX_RTTI (not used here; since gcc >= 4.3 (?)), but I'm not sure if it's valid. #define DISABLE_RTTI 0 #endif /// \def DISABLE_EXCEPTIONS /// Defined to 0 or 1. If 1, exceptions are disabled. #ifndef DISABLE_EXCEPTIONS // some auto-detection (gcc 3.3 or later (I think)) #if defined __GNUC__ && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) && !defined __EXCEPTIONS #define DISABLE_EXCEPTIONS 0 #else #define DISABLE_EXCEPTIONS 1 #endif #endif /// \def HAVE_CXX_EXTERN_C_OVERLOAD /// Defined to 0 or 1. If 1, compiler supports overloading on extern "C" function pointer arguments. #ifndef HAVE_CXX_EXTERN_C_OVERLOAD #ifdef __SUNPRO_CC #define HAVE_CXX_EXTERN_C_OVERLOAD 1 #else #define HAVE_CXX_EXTERN_C_OVERLOAD 0 #endif #endif /// \def HAVE_VERBOSE_TERMINATE_HANDLER /// Defined to 0 or 1. If 1, compiler supports __gnu_cxx::__verbose_terminate_handler. #ifndef HAVE_VERBOSE_TERMINATE_HANDLER #if defined __GNUC__ #define HAVE_VERBOSE_TERMINATE_HANDLER 1 #else #define HAVE_VERBOSE_TERMINATE_HANDLER 0 #endif #endif /// \def HAVE_GCC_ABI_DEMANGLE /// Defined to 0 or 1. If 1, compiler supports \::abi::__cxa_demangle. #ifndef HAVE_GCC_ABI_DEMANGLE // This also works with intel/linux (__GNUC__ is defined by default in it). #if defined __GNUC__ #define HAVE_GCC_ABI_DEMANGLE 1 #else #define HAVE_GCC_ABI_DEMANGLE 0 #endif #endif /// \def HAVE_CXX___func__ /// Defined to 0 or 1. If 1, compiler supports __func__. #ifndef HAVE_CXX___func__ // this is a C99 thing, but I don't know of any other compiler which supports it #if defined __GNUC__ #define HAVE_CXX___func__ 1 #else #define HAVE_CXX___func__ 0 #endif #endif /// \def HAVE_CXX___FUNCTION__ /// Defined to 0 or 1. If 1, compiler supports __FUNCTION__. #ifndef HAVE_CXX___FUNCTION__ // suncc supports this, but only with extensions enabled (can we check those?) #if defined __GNUC__ || defined _MSC_VER #define HAVE_CXX___FUNCTION__ 1 #else #define HAVE_CXX___FUNCTION__ 0 #endif #endif /// \def HAVE_WIN_SE_FUNCS /// Defined to 0 or 1. If 1, compiler supports Win32's "secure" *_s() functions (since msvc 2005/8.0). /// If some of the functions have different requirements, they are listed separately. #ifndef HAVE_WIN_SE_FUNCS #if defined _MSC_VER && _MSC_VER >= 1400 #define HAVE_WIN_SE_FUNCS 1 #else #define HAVE_WIN_SE_FUNCS 0 #endif #endif /// \def HAVE_POSIX_OFF_T_FUNCS /// Defined to 0 or 1. If 1, compiler supports fseeko/ftello. See fs_file.h for details. #ifndef HAVE_POSIX_OFF_T_FUNCS // It's quite hard to detect, so we just enable for any non-win32. #if defined _WIN32 #define HAVE_POSIX_OFF_T_FUNCS 0 #else #define HAVE_POSIX_OFF_T_FUNCS 1 #endif #endif /// \def HAVE_WIN_LFS_FUNCS /// Defined to 0 or 1. If 1, compiler supports LFS (large file support) functions on win32. #ifndef HAVE_WIN_LFS_FUNCS // enable if it's msvc >= 2005, or we're linking to newer msvcrt with mingw. #if defined _WIN32 && ( (defined _MSC_VER >= 1400) \ || (defined __GNUC__ && defined __MSVCRT_VERSION__ && __MSVCRT_VERSION__ >= 0x800) ) #define HAVE_WIN_LFS_FUNCS 1 #else #define HAVE_WIN_LFS_FUNCS 0 #endif #endif /// \def HAVE_XSI_STRERROR_R /// Defined to 0 or 1. If 1, compiler supports XSI-style strerror_r(). #ifndef HAVE_XSI_STRERROR_R #if ((defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L) \ || (defined _XOPEN_SOURCE && _XOPEN_SOURCE >= 600)) && !defined _GNU_SOURCE #define HAVE_XSI_STRERROR_R 1 #else #define HAVE_XSI_STRERROR_R 0 #endif #endif /// \def HAVE_GNU_STRERROR_R /// Defined to 0 or 1. If 1, compiler supports GNU-style strerror_r(). #ifndef HAVE_GNU_STRERROR_R // _GNU_SOURCE excludes HAVE_XSI_STRERROR_R #if defined _GNU_SOURCE #define HAVE_GNU_STRERROR_R 1 #else #define HAVE_GNU_STRERROR_R 0 #endif #endif /// \def HAVE_ISO_STDIO /// Defined to 0 or 1. If 1, the *printf() family (but not _*printf() or *printf_s()) behaves /// according to ISO specification in terms of 0-termination, return value /// and accepting %lld, %llu and %Lf format specifiers. /// Note: If using mingw runtime >= 3.15 and __USE_MINGW_ANSI_STDIO, /// mingw supports both C99/POSIX and msvcrt format specifiers. /// This includes proper printing of long double (%Lf), %lld and %llu, etc... /// This does _not_ affect _snprintf() and similar non-standard functions. #ifndef HAVE_ISO_STDIO // On win32, assume only with mingw with ansi/iso extensions. // for others, assume it's always there. #if !defined _WIN32 || ( defined __GNUC__ && \ (!defined __NO_ISOCEXT || defined __USE_MINGW_ANSI_STDIO) ) #define HAVE_ISO_STDIO 1 #else #define HAVE_ISO_STDIO 0 #endif #endif /// \def HAVE__SNPRINTF /// Defined to 0 or 1. If 1, the compiler has Win32's _snprintf (broken 0-termination). #ifndef HAVE__SNPRINTF #if defined _WIN32 #define HAVE__SNPRINTF 1 #else #define HAVE__SNPRINTF 0 #endif #endif /// \def HAVE__VSNPRINTF /// Defined to 0 or 1. If 1, the compiler has Win32's _vsnprintf (broken 0-termination). #ifndef HAVE__VSNPRINTF #if defined _WIN32 #define HAVE__VSNPRINTF 1 #else #define HAVE__VSNPRINTF 0 #endif #endif /// \def HAVE_SNPRINTF_S /// Defined to 0 or 1. If 1, the compiler has snprintf_s (msvc 2005/8.0). #ifndef HAVE_SNPRINTF_S #if defined _MSC_VER && _MSC_VER >= 1400 #define HAVE_SNPRINTF_S 1 #else #define HAVE_SNPRINTF_S 0 #endif #endif /// \def HAVE_VSNPRINTF_S /// Defined to 0 or 1. If 1, the compiler has vsnprintf_s (msvc 2005/8.0). #ifndef HAVE_VSNPRINTF_S #if defined _MSC_VER && _MSC_VER >= 1400 #define HAVE_VSNPRINTF_S 1 #else #define HAVE_VSNPRINTF_S 0 #endif #endif /// \def HAVE_VASPRINTF /// Defined to 0 or 1. If 1, the compiler has vasprintf() (GNU/glibc extension). #ifndef HAVE_VASPRINTF #if defined _GNU_SOURCE #define HAVE_VASPRINTF 1 #else #define HAVE_VASPRINTF 0 #endif #endif /// \def HAVE_REENTRANT_LOCALTIME /// Defined to 0 or 1. If 1, localtime() is reentrant. #ifndef HAVE_REENTRANT_LOCALTIME // win32 and solaris localtime() is reentrant #if defined _WIN32 || defined sun || defined __sun #define HAVE_REENTRANT_LOCALTIME 1 #else #define HAVE_REENTRANT_LOCALTIME 0 #endif #endif /// \def HAVE_SETENV /// Defined to 0 or 1. If 1, the compiler has setenv() and unsetenv(). #ifndef HAVE_SETENV // setenv(), unsetenv() glibc feature test macros: // _BSD_SOURCE || _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600. // However, since they are available almost anywhere except windows, // we just test for that. #if defined _WIN32 #define HAVE_SETENV 0 #else #define HAVE_SETENV 1 #endif #endif #if !defined DISABLE_STRTOF && defined _MSC_VER #define DISABLE_STRTOF 1 #endif #if !defined DISABLE_STRTOLD && defined _MSC_VER #define DISABLE_STRTOLD 1 #endif #endif /// @} gsmartcontrol-1.1.4/src/hz/i18n.h000066400000000000000000000065051417717230600165710ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_I18N_H #define HZ_I18N_H #include "hz_config.h" // feature macros /** \file Gettext bridge for internationalization. Sort of like gettext.h, but a lot simpler / lighter. This file is for internal use in hz. The application is expected to use gettext.h or similar mechanisms. Note: If you are using UTF-8 to display messages in your application but system locale is not UTF-8, then you need to call gettext's bind_textdomain_codeset(package, "UTF-8"); to enable locale -> UTF-8 conversion for translated messages. */ namespace hz { /// \def ENABLE_NLS /// Defined to 0 or 1. If 1, enable native language support. /// Usually NLS can be disabled through the configure --disable-nls option. #if defined ENABLE_NLS && ENABLE_NLS #include // gettext functions #include // std::size_t #include // std::strlen, std::memcpy namespace internal { inline const char* i18n_C_helper(const char* msg_with_context, const char* clean_msg) { const char* res = gettext(msg_with_context); return ((res == msg_with_context) ? clean_msg : res); } inline const char* i18n_R_helper(const char* context, const char* clean_msg) { std::size_t context_len = std::strlen(context) + 1; std::size_t clean_msg_len = std::strlen(clean_msg) + 1; char[] msg_with_context = new char[context_len + clean_msg_len]; std::memcpy(msg_with_context, context, context_len - 1); msg_with_context[context_len - 1] = '\004'; std::memcpy(msg_with_context + context_len, clean_msg, clean_msg_len); const char* res = gettext(msg_with_context); if (res == msg_with_context) { delete[] msg_with_context; return clean_msg; } delete[] msg_with_context; return res; } } // ------- Mark and translate /// The main gettext function. Marks and translates at runtime. /// You need to pass --keyword=HZ__ to xgettext when extracting messages. #define HZ__(String) gettext(String) /// Same as HZ__(), but specifies a context too, to e.g. /// disambiguate two "Open" menu entries as ("File", "Open") and ("Printer", "Open"). /// You MUST pass --keyword=C_:1c,2 to xgettext when extracting messages. #define HZ_C_(Context, String) i18n_C_helper((Context "\004" String), (String)) // ------- Mark only /// The no-op marking of a string for translation. /// You MUST pass --keyword=HZ_N_ to xgettext when extracting messages. #define HZ_N_(String) (String) /// Same as HZ_N_(), but accepts context too. /// --keyword=HZ_NC_:1c,2 #define HZ_NC_(Context, String) (String) // ------- Translate only /// Translate a dynamic string. #define HZ_R_(String) gettext(String) /// Same as HZ_R_(), but accepts context too. #define HZ_RC_(Context, String) i18n_R_helper((Context), (String)) #else #define HZ__(String) (String) #define HZ_C_(Context, String) (String) #define HZ_N_(String) (String) #define HZ_NC_(Context, String) (String) #define HZ_R_(String) (String) #define HZ_RC_(Context, String) (String) #endif } // ns hz #endif /// @} gsmartcontrol-1.1.4/src/hz/initialized.h000066400000000000000000000046611417717230600203200ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_INITIALIZED_H #define HZ_INITIALIZED_H #include "hz_config.h" // feature macros namespace hz { /// This template is useful for default-initializing POD class members. template class initialized { public: initialized() : x() { } initialized(const initialized& arg) : x(arg.x) { } explicit initialized(const T& arg) : x(arg) { } initialized& operator=(const initialized& arg) { x = arg.x; return *this; } initialized& operator=(const T& arg) { x = arg; return *this; } operator const T&() const { return x; } operator T&() { return x; } const T& data() const { return x; } T& data() { return x; } protected: T x ; } ; /// This template specialization is useful for default-initializing raw pointers. template class initialized { public: initialized() : x() { } initialized(const initialized& arg) : x(arg.x) { } explicit initialized(T* arg) : x(arg) { } initialized& operator=(const initialized& arg) { x = arg.x; return *this; } initialized& operator=(T* arg) { x = arg; return *this; } T* operator->() { return x; } operator const T*() const { return x; } // Avoid gcc warning about choosing one cast over another (-Wconversion) // operator T*&() // { // return x; // } const T*& data() const { return x; } T*& data() { return x; } protected: T* x ; } ; /// This template is useful for initializing enum-type and integral variables. template class value_initialized : public initialized { public : value_initialized() : initialized(initial_value) { } value_initialized(const initialized& arg) : initialized(arg) { } explicit value_initialized(const T& arg) : initialized(arg) { } value_initialized& operator=(const initialized& arg) { initialized::operator=(arg); return *this; } value_initialized& operator=(const T& arg) { initialized::operator=(arg); return *this; } } ; } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/instance_manager.h000066400000000000000000000122341417717230600213040ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_INSTANCE_MANAGER_H #define HZ_INSTANCE_MANAGER_H #include "hz_config.h" // feature macros #include "sync.h" // SyncPolicyNone #include "noncopyable.h" namespace hz { /** Inherit this class to have a single- or multi-instance objects, e.g. windows. Single-instance variant uses locks to protect the data when manipulating it through its functions. If you intend to write to the pointed object and use it in separate threads, you will need to lock it manually. Multi-instance variant doesn't store any instances itself, so you are expected to perform any necessary locking with the pointers you hold. \c SingleLockPolicy has no effect if MultiInstance is true. */ template class InstanceManager : public hz::noncopyable { // disallow copying protected: /// Can't construct / delete this directly! use create() and destroy() InstanceManager() { } /// Virtual destructor (protected, can't delete this directly, use destroy()). virtual ~InstanceManager() { } public: /// Create a new instance or return an already created one if single-instance. /// If single-instance, the call will be serialized. static Child* create() { Child* o = new Child; o->obj_create(); return o; } /// Destroy an instance. \c instance must be passed if using /// multi-instance object. If single-instance, \c instance has no effect. /// If single-instance, the call will be serialized. static void destroy(Child* instance = 0) { if (instance) { instance->obj_destroy(); delete instance; // zeroify yourself } } protected: // These functions are called when the object instance is // created or destroyed through ::create() and ::destroy(). /// Called from create(), right after constructor. virtual void obj_create() { } /// Called from destroy(), right before destructor. virtual void obj_destroy() { } // We have these functions for multi-instance variant too to // support transparently switching between them. /// Returns true if there is a valid single-instance object. /// In multi-instance version this always returns false. static bool has_single_instance(bool do_lock = true) { return false; } /// Returns the instance if there is a valid single-instance one. /// In multi-instance version this always returns 0. static Child* get_single_instance(bool do_lock = true) { return 0; } /// Set single-instance object. /// This function has no effect in multi-instance version. static void set_single_instance(Child* instance, bool do_lock = true) { } }; /// Single-instance specialization template class InstanceManager : public hz::noncopyable { // disallow copying protected: InstanceManager() { } virtual ~InstanceManager() { } public: static Child* create() { typename SingleLockPolicy::ScopedLock lock(instance_mutex_); if (instance_) // for single-instance objects return instance_; Child* o = new Child; o->obj_create(); instance_ = o; // for single-instance objects return o; } static void destroy(Child* instance = 0) { typename SingleLockPolicy::ScopedLock lock(instance_mutex_); if (instance_) { instance_->obj_destroy(); delete instance_; instance_ = 0; } } protected: virtual void obj_create() { } virtual void obj_destroy() { } static bool has_single_instance(bool do_lock = true) { typename SingleLockPolicy::ScopedLock lock(instance_mutex_, do_lock); return instance_; } static Child* get_single_instance(bool do_lock = true) { typename SingleLockPolicy::ScopedLock lock(instance_mutex_, do_lock); return instance_; } static void set_single_instance(Child* instance, bool do_lock = true) { typename SingleLockPolicy::ScopedLock lock(instance_mutex_, do_lock); instance_ = instance; } // if an object is allowed to have a single instance only, these are needed. static Child* instance_; ///< Single instance pointer static typename SingleLockPolicy::Mutex instance_mutex_; ///< Single instance pointer mutex // static const bool multi_instance_ = false; }; /// Single instance pointer. /// This is a definition of class template's static member variable /// (it can be in a header (as opposed to cpp) if it's class template member). /// This will generate different instances as long as the class type is different. /// By passing the child's class type as template parameter we guarantee that. template Child* InstanceManager::instance_ = 0; template typename SingleLockPolicy::Mutex InstanceManager::instance_mutex_; } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/intrusive_ptr.h000066400000000000000000000405511417717230600207260ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2001 - 2010 Peter Dimov (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_boost_1_0.txt file ***************************************************************************/ /// \file /// \author Peter Dimov /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_INTRUSIVE_PTR_H #define HZ_INTRUSIVE_PTR_H #include "hz_config.h" // feature macros /** \file Intrusive reference-counting smart pointer, based on boost::intrusive_ptr. Original notes and copyright info follow: intrusive_ptr.hpp Copyright (c) 2001, 2002 Peter Dimov Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) See http://www.boost.org/libs/smart_ptr/intrusive_ptr.html for documentation. */ #include // std::size_t #include // strncpy / strlen #include // std::exception // #include // std::ostream (for operator<<) #if !(defined DISABLE_RTTI && DISABLE_RTTI) #include // std::type_info #endif #include "exceptions.h" // THROW_FATAL /// \def INTRUSIVE_PTR_TRACING /// Defined to 0 (default if undefined) or 1. This enables reference count /// and lock tracing (very verbose). #ifndef INTRUSIVE_PTR_TRACING #define INTRUSIVE_PTR_TRACING 0 #endif /// \def INTRUSIVE_PTR_RUNTIME_CHECKS /// Defined to 0 or 1 (default if undefined). This enables runtime checks /// for errors (with exception throwing). #ifndef INTRUSIVE_PTR_RUNTIME_CHECKS #define INTRUSIVE_PTR_RUNTIME_CHECKS 1 #endif #if defined INTRUSIVE_PTR_TRACING && INTRUSIVE_PTR_TRACING // Don't use libdebug here - it uses us and bad things may happen // if there's something wrong with it. // Use plain std::cerr. It will be disabled by default anyway. #include // std::cerr #endif namespace hz { /// This is thrown on refcount / null pointer errors, if \c DEBUG_BUILD /// or \ref INTRUSIVE_PTR_TRACING are defined. struct intrusive_ptr_error : virtual public std::exception { // from /// Constructor intrusive_ptr_error(const char* msg) #if !(defined DISABLE_RTTI && DISABLE_RTTI) : type_(typeid(void)) #endif { std::size_t buf_len = std::strlen(msg) + 1; msg_ = std::strncpy(new char[buf_len], msg, buf_len); } #if !(defined DISABLE_RTTI && DISABLE_RTTI) /// Constructor intrusive_ptr_error(const char* msg, const std::type_info& type) : msg_(0), type_(type) { const char* tname = type.name(); const char* tmsg = " Type: "; std::size_t msg_len = std::strlen(msg); std::size_t tmsg_len = std::strlen(tmsg); std::size_t tname_len = std::strlen(tname); msg_ = new char[msg_len + tmsg_len + tname_len + 1]; std::strncpy(msg_, msg, msg_len); std::strncpy(msg_ + msg_len, tmsg, tmsg_len); std::strncpy(msg_ + msg_len + tmsg_len, tname, tname_len + 1); } #endif /// Virtual destructor virtual ~intrusive_ptr_error() throw() { delete[] msg_; msg_ = 0; // protect from double-deletion compiler bugs } /// Reimplemented from std::exception. /// Note: messages in exceptions are not newline-terminated. virtual const char* what() const throw() { return msg_; } char* msg_; ///< Error message #if !(defined DISABLE_RTTI && DISABLE_RTTI) const std::type_info& type_; ///< Type information of the pointee #endif }; /// \def INTRUSIVE_PTR_THROW(cond, type, msg) /// If \ref INTRUSIVE_PTR_RUNTIME_CHECKS is 1, check /// \c cond and throw \ref intrusive_ptr_error. If \ref INTRUSIVE_PTR_RUNTIME_CHECKS /// is 0, ignore \c cond and don't throw anything. /// If \ref INTRUSIVE_PTR_TRACING is 1, print message and some /// additional information to std::cerr. #if !(defined DISABLE_RTTI && DISABLE_RTTI) #if defined INTRUSIVE_PTR_TRACING && INTRUSIVE_PTR_TRACING \ && defined INTRUSIVE_PTR_RUNTIME_CHECKS && INTRUSIVE_PTR_RUNTIME_CHECKS #define INTRUSIVE_PTR_THROW(cond, type, msg) \ if (cond) { \ std::cerr << (msg) << " Type: " << type.name() << "\n"; \ THROW_FATAL(intrusive_ptr_error(msg, type)); \ } #elif defined INTRUSIVE_PTR_TRACING && INTRUSIVE_PTR_TRACING #define INTRUSIVE_PTR_THROW(cond, type, msg) \ { \ std::cerr << (msg) << " Type: " << type.name() << "\n"; \ } #elif defined INTRUSIVE_PTR_RUNTIME_CHECKS && INTRUSIVE_PTR_RUNTIME_CHECKS #define INTRUSIVE_PTR_THROW(cond, type, msg) \ if (cond) { \ THROW_FATAL(intrusive_ptr_error(msg, type)); \ } #else #define INTRUSIVE_PTR_THROW(cond, type, msg) { } #endif #else // no rtti #if defined INTRUSIVE_PTR_TRACING && INTRUSIVE_PTR_TRACING \ && defined INTRUSIVE_PTR_RUNTIME_CHECKS && INTRUSIVE_PTR_RUNTIME_CHECKS #define INTRUSIVE_PTR_THROW(cond, type, msg) \ if (cond) { \ std::cerr << (msg) << "\n"; \ THROW_FATAL(intrusive_ptr_error(msg)); \ } #elif defined INTRUSIVE_PTR_TRACING && INTRUSIVE_PTR_TRACING #define INTRUSIVE_PTR_THROW(cond, type, msg) \ { \ std::cerr << (msg) << "\n"; \ } #elif defined INTRUSIVE_PTR_RUNTIME_CHECKS && INTRUSIVE_PTR_RUNTIME_CHECKS #define INTRUSIVE_PTR_THROW(cond, type, msg) \ if (cond) { \ THROW_FATAL(intrusive_ptr_error(msg)); \ } #else #define INTRUSIVE_PTR_THROW(cond, type, msg) { } #endif #endif /// \def INTRUSIVE_PTR_TRACING(msg) /// Print a message to std::cerr if \ref INTRUSIVE_PTR_TRACING is 1. #if defined INTRUSIVE_PTR_TRACING && INTRUSIVE_PTR_TRACING #define INTRUSIVE_PTR_TRACE_MSG(msg) { std::cerr << msg << "\n"; } #else #define INTRUSIVE_PTR_TRACE_MSG(msg) { } #endif /// Default threading policy for intrusive_ptr wrapped classes. /// The wrapped class MUST provide: inc_ref(), dec_ref(), ref_count(). template struct IntrusivePtrRefFunctionsDefault { /// Increase reference count static int inc_ref(PtrType* p) { INTRUSIVE_PTR_THROW(!p, typeid(PtrType), "IntrusivePtrRefFunctionsDefault::inc_ref(): Error: NULL pointer passed!"); INTRUSIVE_PTR_TRACE_MSG("IntrusivePtrRefFunctionsDefault::inc_ref(): increasing from " << p->ref_count()); return p->inc_ref(); } /// Decrease reference count static int dec_ref(PtrType* p) { INTRUSIVE_PTR_THROW(!p, typeid(PtrType), "IntrusivePtrRefFunctionsDefault::dec_ref(): Error: NULL pointer passed!"); INTRUSIVE_PTR_TRACE_MSG("IntrusivePtrRefFunctionsDefault::dec_ref(): decreasing from " << p->ref_count()); int c = p->dec_ref(); if (c == 0) { INTRUSIVE_PTR_TRACE_MSG("IntrusivePtrRefFunctionsDefault::dec_ref(): delete " << p); delete p; } return c; } }; /// If using intrusive_ptr_referenced_self_locked, this provides /// the default functions to use when locking the wrapped object. template struct IntrusivePtrChildLockFunctionsDefault { /// Increase reference count static void ref_lock(Child* p) { INTRUSIVE_PTR_THROW(!p, typeid(Child), "IntrusivePtrChildLockFunctionsDefault::ref_lock(): Error: NULL pointer passed!"); INTRUSIVE_PTR_TRACE_MSG("IntrusivePtrChildLockFunctionsDefault::ref_lock() called."); p->ref_lock(); } /// Decrease reference count static void ref_unlock(Child* p) { INTRUSIVE_PTR_THROW(!p, typeid(Child), "IntrusivePtrChildLockFunctionsDefault::ref_unlock(): Error: NULL pointer passed!"); INTRUSIVE_PTR_TRACE_MSG("IntrusivePtrChildLockFunctionsDefault::ref_unlock() called."); p->ref_unlock(); } }; /// Convenience class to use as parent for user classes to provide /// intrusive_ptr support (reference counting functions). class intrusive_ptr_referenced { public: /// Constructor intrusive_ptr_referenced() : ref_count_(0) { } // Note: methods are const to allow construction of intrusive_ptr // from const data while still incrementing the refcount. /// Increase reference count int inc_ref() const { return (++ref_count_); } /// Decrease reference count int dec_ref() const { INTRUSIVE_PTR_THROW(ref_count_ <= 0, typeid(void), "intrusive_ptr_referenced::dec_ref(): ref_count <= 0 and decrease request received!"); return (--ref_count_); } /// Get reference count int ref_count() const { return ref_count_; } protected: mutable int ref_count_; ///< Reference count }; /// Same as intrusive_ptr_referenced, but with locking multi-threading policy /// (mutex included) for thread-safe reference counting. template class intrusive_ptr_referenced_locked { public: typedef LockPolicy intrusive_ptr_lock_policy; ///< Locking policy typedef typename LockPolicy::ScopedLock intrusive_ptr_scoped_lock_type; ///< Scoped lock type /// Constructor intrusive_ptr_referenced_locked() : ref_count_(0) { } /// Increase reference count int inc_ref() const { intrusive_ptr_scoped_lock_type locker(ref_mutex_); return (++ref_count_); } /// Decrease reference count int dec_ref() const { intrusive_ptr_scoped_lock_type locker(ref_mutex_); INTRUSIVE_PTR_THROW(ref_count_ <= 0, typeid(void), "intrusive_ptr_referenced_locked::dec_ref(): ref_count <= 0 and decrease request received!"); return (--ref_count_); } /// Get reference count int ref_count() const { intrusive_ptr_scoped_lock_type locker(ref_mutex_); return ref_count_; } /// Get protecting mutex typename LockPolicy::Mutex& get_ref_mutex() { return ref_mutex_; } protected: mutable int ref_count_; ///< Reference count mutable typename LockPolicy::Mutex ref_mutex_; ///< Protecting mutex }; /// Same as intrusive_ptr_referenced_locked, but with locking functions and /// mutex inside the child class. This is useful if the child already has them. template > class intrusive_ptr_referenced_self_locked { public: /// Constructor intrusive_ptr_referenced_self_locked() : ref_count_(0) { } /// Increase reference count int inc_ref() const { LockFunctions::ref_lock(static_cast(this)); // this uses less memory than Locker. int c = ++ref_count_; LockFunctions::ref_unlock(static_cast(this)); return c; } /// Decrease reference count int dec_ref() const { Locker locker(this); // we have to use this here because of its fuzzy scope INTRUSIVE_PTR_THROW(ref_count_ <= 0, typeid(void), "intrusive_ptr_referenced_self_locked::dec_ref(): ref_count <= 0 and decrease request received!"); return (--ref_count_); } /// Get reference count int ref_count() const { LockFunctions::ref_lock(static_cast(this)); int c = ref_count_; LockFunctions::ref_unlock(static_cast(this)); return c; } protected: mutable int ref_count_; /// Reference count private: /// Scoped locker object struct Locker { /// Constructor, locks \c self_ Locker(intrusive_ptr_referenced_self_locked* self_) : self(self_) { LockFunctions::ref_lock(static_cast(self)); } /// Destructor, unlocks \c self_ ~Locker() { LockFunctions::ref_unlock(static_cast(self)); } intrusive_ptr_referenced_self_locked* self; ///< An object to lock }; }; /// Intrusive reference-counting smart pointer. /// \tparam T Pointee type (without *). /// \tparam RefFunctions Policy for reference counting template > class intrusive_ptr { private: typedef intrusive_ptr this_type; ///< Borland-specific workaround typedef T* this_type::*unspecified_bool_type; ///< Helper type for if(p)-style casts. public: /// Wrapped type typedef T element_type; /// Constructor, creates a null pointer intrusive_ptr() : p_(0) { } /// Constructor. Note that the reference count of \c p is preserved /// and possibly increased if \c add_ref is true. intrusive_ptr(T* p, bool add_ref = true) : p_(p) { if (p_ != 0 && add_ref) RefFunctions::inc_ref(p_); } /// Construct from other intrusive_ptr with implicitly-convertible pointer type, /// increases reference count. template intrusive_ptr(const intrusive_ptr& rhs) : p_(rhs.get()) { if (p_ != 0) RefFunctions::inc_ref(p_); } /// Copy constructor, increases reference count. intrusive_ptr(const intrusive_ptr& rhs) : p_(rhs.p_) { if (p_ != 0) RefFunctions::inc_ref(p_); } /// Destructor, decreases reference count. ~intrusive_ptr() { if (p_ != 0) RefFunctions::dec_ref(p_); } /// Assignment operator from implicitly-convertible pointer type, /// increases reference count. template intrusive_ptr& operator=(const intrusive_ptr& rhs) { this_type(rhs).swap(*this); return *this; } /// Assignment operator, increases reference count. intrusive_ptr& operator=(const intrusive_ptr& rhs) { this_type(rhs).swap(*this); return *this; } /// Assignment operator from plain pointer intrusive_ptr& operator=(T* rhs) { this_type(rhs).swap(*this); return *this; } /// Get the pointed object T* get() const { return p_; } /// Dereference operator. /// \throw intrusive_ptr_error if this is a null pointer. T& operator*() const { INTRUSIVE_PTR_THROW(!p_, typeid(T), "intrusive_ptr::operator*(): Attempting to dereference NULL pointer!"); return *p_; } /// Arrow operator. /// \throw intrusive_ptr_error if this is a null pointer. T* operator->() const { INTRUSIVE_PTR_THROW(!p_, typeid(T), "intrusive_ptr::operator->(): Attempting to dereference NULL pointer!"); return p_; } /// Bool-like conversion for null checks operator unspecified_bool_type () const { return p_ == 0 ? 0 : &this_type::p_; } /// Operator! is a Borland-specific workaround bool operator! () const { return p_ == 0; } /// Swap with other pointer void swap(intrusive_ptr& rhs) { T* tmp = p_; p_ = rhs.p_; rhs.p_ = tmp; } private: T* p_; ///< Pointed object }; /// Comparison operator template inline bool operator==(const intrusive_ptr& a, const intrusive_ptr& b) { return a.get() == b.get(); } /// Comparison operator template inline bool operator!=(const intrusive_ptr& a, const intrusive_ptr& b) { return a.get() != b.get(); } /// Comparison operator template inline bool operator==(const intrusive_ptr& a, U* b) { return a.get() == b; } /// Comparison operator template inline bool operator!=(const intrusive_ptr& a, U* b) { return a.get() != b; } /// Comparison operator template inline bool operator==(T* a, const intrusive_ptr& b) { return a == b.get(); } /// Comparison operator template inline bool operator!=(T* a, const intrusive_ptr& b) { return a != b.get(); } /// Comparison operator template inline bool operator<(const intrusive_ptr& a, const intrusive_ptr& b) { // return std::less()(a.get(), b.get()); // needs return a.get() < b.get(); } /// Swap two pointers template inline void swap(intrusive_ptr& lhs, intrusive_ptr& rhs) { lhs.swap(rhs); } /// Get pointed object. This is for mem_fn support. template inline T* get_pointer(const intrusive_ptr& p) { return p.get(); } /// Perform a static cast on intrusive_ptr template inline intrusive_ptr ptr_static_cast(const intrusive_ptr& p) { return static_cast(p.get()); } /// Perform a const cast on intrusive_ptr template inline intrusive_ptr ptr_const_cast(const intrusive_ptr& p) // return type's second parameter defaulted. { return const_cast(p.get()); } /// Perform a const cast on intrusive_ptr template inline intrusive_ptr ptr_const_cast(const intrusive_ptr& p) { return const_cast(p.get()); } #if !(defined DISABLE_RTTI && DISABLE_RTTI) /// Perform a dynamic cast on intrusive_ptr template inline intrusive_ptr ptr_dynamic_cast(const intrusive_ptr& p) { return dynamic_cast(p.get()); } #endif // Don't define this - it may conflict with actual intrusive_ptr overloads // (especially with sun compiler). /* // operator<< template inline std::ostream& operator<< (std::ostream& os, const intrusive_ptr& p) { os << p.get(); return os; } */ } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/intrusive_scoped_lock.h000066400000000000000000000061061417717230600224040ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_INTRUSIVE_SCOPED_LOCK_H #define HZ_INTRUSIVE_SCOPED_LOCK_H #include "hz_config.h" // feature macros namespace hz { /// Locking policy for intrusive_scoped_lock. The template parameter /// class is expected to have lock() and unlock() functions. template struct IntrusiveLockingPolicyDefault { /// Call obj.lock() static void lock(T& obj) { obj.lock(); } /// Call obj.unlock() static void unlock(T& obj) { obj.unlock(); } }; /// Specialization for pointers template struct IntrusiveLockingPolicyDefault { /// Call obj->lock() static void lock(T obj) { obj->lock(); } /// Call obj->unlock() static void unlock(T obj) { obj->unlock(); } }; /// Same as IntrusiveLockingPolicyDefault, but call the lock through /// "->", not ".". This allows its usage with smart pointers which transfer /// their locking to pointee objects. template struct IntrusiveLockingPolicySmart { /// Call (&obj)->lock() static void lock(T& obj) { (&obj)->lock(); } /// Cal (&obj)->unlock() static void unlock(T& obj) { (&obj)->unlock(); } }; /// Locking policy for intrusive_scoped_lock which does no locking at all. template struct IntrusiveLockingPolicyNone { /// Do nothing static void lock(T& obj) { } /// Do nothing static void unlock(T& obj) { } }; /** Intrusive scoped lock class. The locking is done by calling lock() and unlock() methods of parameter object. LockingPolicy template parameter may be used to control which functions are called, as well as how. Example usage: \code { SharedData* sd = get_some_shared_data(); // SharedData must have lock() and unlock(). intrusive_scoped_lock locker(sd); // this will invoke sd->lock() // at the end of the scope, sd->unlock() will be called. } \endcode */ template > class intrusive_scoped_lock { public: /// Constructor, locks the object. Note that the object is stored by reference /// and therefore must exist as long as this object itself exists. intrusive_scoped_lock(T& r, bool do_lock_ = true) : ref(r), do_lock(do_lock_) { if (do_lock) LockingPolicy::lock(ref); } /// Destructor, unlocks the object. ~intrusive_scoped_lock() { if (do_lock) LockingPolicy::unlock(ref); } /// Get the object we're operating with T& get() { return ref; } private: // Private assignment operator (disallow copying) intrusive_scoped_lock& operator=(const intrusive_scoped_lock&); // Private copy constructor (disallow copying) intrusive_scoped_lock(const intrusive_scoped_lock&); T& ref; ///< The object we're operating with bool do_lock; ///< If we're locking or not }; } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/launch_url.h000066400000000000000000000037561417717230600201530ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_LAUNCH_URL_H #define HZ_LAUNCH_URL_H #include "hz_config.h" // feature macros #include #include #ifdef _WIN32 #include // seems to be needed by shellapi.h #include // ShellExecuteW() #include "scoped_array.h" #include "win32_tools.h" // hz::win32_utf8_to_utf16 #else #include "scoped_ptr.h" #endif namespace hz { /// Open URL in browser or mailto: link in mail client. /// Return error message on error, empty string otherwise. /// The link is in utf-8 in windows. inline std::string launch_url(GtkWindow* window, const std::string& link) { // For some reason, gtk_show_uri() crashes on windows, so use our manual implementation. #ifdef _WIN32 hz::scoped_array wlink(hz::win32_utf8_to_utf16(link.c_str())); if (!wlink) return "Error while executing a command: The specified URI contains non-UTF-8 characters."; HINSTANCE inst = ShellExecuteW(0, L"open", wlink.get(), NULL, NULL, SW_SHOWNORMAL); if (inst > reinterpret_cast(32)) { return std::string(); } return "Error while executing a command: Internal error."; #else hz::scoped_ptr error(0, g_error_free); #if GTK_CHECK_VERSION(3, 22, 0) bool status = gtk_show_uri_on_window(window, link.c_str(), GDK_CURRENT_TIME, &error.get_ref()); #else GdkScreen* screen = (window ? gtk_window_get_screen(window) : 0); bool status = gtk_show_uri(screen, link.c_str(), GDK_CURRENT_TIME, &error.get_ref()); #endif if (!status) { return std::string("Cannot open URL: ") + ((error && error->message) ? (std::string(": ") + error->message) : "."); } return std::string(); #endif } } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/local_algo.h000066400000000000000000000065321417717230600201060ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_LOCAL_ALGO_H #define HZ_LOCAL_ALGO_H #include "hz_config.h" // feature macros #include // std::ptrdiff_t namespace hz { /** \file These standard-like algorithms are here to avoid including huge standard headers in a header-only library. */ // -------------------------------- Shell Sort namespace internal { /// Helper for shell_sort template struct shell_sort_helper { // works for operators typedef typename T::value_type value_type; }; /// Specialization template struct shell_sort_helper { // needed for C arrays typedef T value_type; }; } // ns internal /// Shell sort. Works for most STL containers. /// Shell sort is one of the fastest on small datasets (<1000), /// and has a small memory footprint. template inline void shell_sort(RandomIter first, RandomIter last) { const std::ptrdiff_t n = last - first; for (std::ptrdiff_t gap = n / 2; 0 < gap; gap /= 2) { for (std::ptrdiff_t i = gap; i < n; i++) { for (std::ptrdiff_t j = i - gap; 0 <= j; j -= gap) { if ( *(first+j+gap) < *(first+j) ) { // swap them typename internal::shell_sort_helper::value_type temp = *(first+j); *(first+j) = *(first+j+gap); *(first+j+gap) = temp; } } } } } /// Same as the other shell_sort(), but with custom comparator function template inline void shell_sort(RandomIter first, RandomIter last, Comparator less_than_func) { const std::ptrdiff_t n = last - first; for (std::ptrdiff_t gap = n / 2; 0 < gap; gap /= 2) { for (std::ptrdiff_t i = gap; i < n; i++) { for (std::ptrdiff_t j = i - gap; 0 <= j; j -= gap) { if ( less_than_func(*(first+j+gap), *(first+j)) ) { // swap them typename internal::shell_sort_helper::value_type temp = *(first+j); *(first+j) = *(first+j+gap); *(first+j+gap) = temp; } } } } } /// Binary search. /// \return iterator to found element, or \c end if not found. template inline RandomIter returning_binary_search(RandomIter begin, RandomIter end, const ValueType& value) { RandomIter initial_end = end; RandomIter middle; while (begin < end) { middle = begin + ((end - begin - 1) / 2); if (value < *middle) { end = middle; } else if (*middle < value) { begin = middle + 1; } else { return middle; } } return initial_end; } /// Binary search with custom comparator. /// \return iterator to found element, or \c end if not found. template inline RandomIter returning_binary_search(RandomIter begin, RandomIter end, const ValueType& value, LessComparator comp) { RandomIter initial_end = end; RandomIter middle; while (begin < end) { middle = begin + ((end - begin - 1) / 2); if (comp(value, *middle)) { end = middle; } else if (comp(*middle, value)) { begin = middle + 1; } else { return middle; } } return initial_end; } } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/locale_tools.h000066400000000000000000000203261417717230600204660ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_LOCALE_TOOLS_H #define HZ_LOCALE_TOOLS_H #include "hz_config.h" // feature macros #include #include #include // std::setlocale #include // std::runtime_error namespace hz { /** \file Locale manipulation facilities Note: The POSIX man page for setlocale (which libstdc++ is based on) states: "The locale state is common to all threads within a process." This may have rather serious implications for thread-safety. However, for glibc, libstdc++ uses the "uselocale" extension, which sets locale on per-thread basis. */ /// Set the C standard library locale, storing the previous one into \c old_locale. /// \return false on failure inline bool locale_c_set(const std::string& loc, std::string& old_locale) { // even though undocumented, glibc returns 0 when the locale string is invalid. char* old = std::setlocale(LC_ALL, loc.c_str()); if (old) { old_locale = old; return true; } return false; } /// Set the C standard library locale. /// \return false on failure inline bool locale_c_set(const std::string& loc) { return std::setlocale(LC_ALL, loc.c_str()); // returns 0 if invalid } /// Get current C standard library locale. inline std::string locale_c_get() { return std::setlocale(LC_ALL, 0); } /// Set the C++ standard library locale (may also set the C locale depending /// on implementation), storing the previous one into \c old_locale. /// \return false on failure (no exception is thrown) inline bool locale_cpp_set(const std::locale& loc, std::locale& old_locale) { #if !(defined DISABLE_EXCEPTIONS && DISABLE_EXCEPTIONS) try { #endif // under FreeBSD (at least 6.x) and OSX, this may throw on anything // except "C" and "POSIX" with message: // "locale::facet::_S_create_c_locale name not valid". // Blame inadequate implementations. old_locale = std::locale::global(loc); #if !(defined DISABLE_EXCEPTIONS && DISABLE_EXCEPTIONS) } catch (const std::runtime_error& e) { return false; } #endif return true; } /// Set the C++ standard library locale (may also set the C locale depending /// on implementation), storing the previous one's name into \c old_locale. /// \return false on failure (no exception is thrown) inline bool locale_cpp_set(const std::locale& loc, std::string& old_locale) { #if !(defined DISABLE_EXCEPTIONS && DISABLE_EXCEPTIONS) try { #endif old_locale = std::locale::global(loc).name(); // not sure, but name() may not be unique (?) #if !(defined DISABLE_EXCEPTIONS && DISABLE_EXCEPTIONS) } catch (const std::runtime_error& e) { return false; } #endif return true; } /// Set the C++ standard library locale (may also set the C locale depending /// on implementation). /// \return false on failure (no exception is thrown) inline bool locale_cpp_set(const std::locale& loc) { #if !(defined DISABLE_EXCEPTIONS && DISABLE_EXCEPTIONS) try { #endif std::locale::global(loc); #if !(defined DISABLE_EXCEPTIONS && DISABLE_EXCEPTIONS) } catch (const std::runtime_error& e) { return false; } #endif return true; } /// Set the C++ standard library locale name (may also set the C locale depending /// on implementation), storing the previous one into \c old_locale. /// \return false on failure (no exception is thrown) inline bool locale_cpp_set(const std::string& loc, std::locale& old_locale) { #if !(defined DISABLE_EXCEPTIONS && DISABLE_EXCEPTIONS) try { #endif old_locale = std::locale::global(std::locale(loc.c_str())); #if !(defined DISABLE_EXCEPTIONS && DISABLE_EXCEPTIONS) } catch (const std::runtime_error& e) { return false; } #endif return true; } /// Set the C++ standard library locale name (may also set the C locale depending /// on implementation), storing the previous one's name into \c old_locale. /// \return false on failure (no exception is thrown) inline bool locale_cpp_set(const std::string& loc, std::string& old_locale) { #if !(defined DISABLE_EXCEPTIONS && DISABLE_EXCEPTIONS) try { #endif old_locale = std::locale::global(std::locale(loc.c_str())).name(); // not sure, but name() may not be unique (?) #if !(defined DISABLE_EXCEPTIONS && DISABLE_EXCEPTIONS) } catch (const std::runtime_error& e) { return false; } #endif return true; } /// Set the C++ standard library locale name (may also set the C locale depending /// on implementation). /// \return false on failure (no exception is thrown) inline bool locale_cpp_set(const std::string& loc) { #if !(defined DISABLE_EXCEPTIONS && DISABLE_EXCEPTIONS) try { #endif std::locale::global(std::locale(loc.c_str())); #if !(defined DISABLE_EXCEPTIONS && DISABLE_EXCEPTIONS) } catch (const std::runtime_error& e) { return false; } #endif return true; } /// Get current C++ standard library locale. This has no definition, /// use one of the specializations. template ReturnType locale_cpp_get(); /// Get current C++ standard library locale as a string. /// May return "*" if it's not representable as a string. template<> inline std::string locale_cpp_get() { return std::locale().name(); } /// Get current C++ standard library locale std::locale object. template<> inline std::locale locale_cpp_get() { return std::locale(); } /// Temporarily change the C standard library locale as long /// as this object lives. class ScopedCLocale { public: /// Change to classic locale (aka "C") ScopedCLocale(bool do_change = true) : do_change_(do_change), bad_(false) { if (do_change_) { // avoid unnecessary const char* -> string conversion overhead bad_ = locale_c_set("C", old_locale_); } } /// Change to user-specified locale loc. ScopedCLocale(const std::string& loc, bool do_change = true) : do_change_(do_change), bad_(false) { if (do_change_) { bad_ = locale_c_set(loc, old_locale_); } } /// Change back the locale ~ScopedCLocale() { this->restore(); } /// Get the old locale std::string old() const { return old_locale_; } /// Return true if locale setting was unsuccessful bool bad() const { return bad_; } /// Restore the locale. Invoked by destructor. bool restore() { if (do_change_ && !bad_) { bad_ = locale_c_set(old_locale_); do_change_ = false; // avoid invoking again } return bad_; } private: std::string old_locale_; ///< Old locale bool do_change_; ///< Whether we changed something or not bool bad_; ///< If true, there was some error }; /// Temporarily change the C++ standard library locale, as long as this /// object lives. This may also set the C locale depending on implementation. class ScopedCppLocale { public: /// Change to classic locale (aka "C") ScopedCppLocale(bool do_change = true) : do_change_(do_change), bad_(false) { if (do_change) { bad_ = locale_cpp_set(std::locale::classic(), old_locale_); } } /// Change to user-specified locale loc. ScopedCppLocale(const std::string& loc, bool do_change = true) : do_change_(do_change), bad_(false) { if (do_change_) { bad_ = locale_cpp_set(loc, old_locale_); } } /// Change to user-specified locale loc. ScopedCppLocale(const std::locale& loc, bool do_change = true) : do_change_(do_change), bad_(false) { if (do_change_) { bad_ = locale_cpp_set(loc, old_locale_); } } /// Change the locale back to the old one ~ScopedCppLocale() { this->restore(); } /// Get the old locale std::locale old() const { return old_locale_; } /// Return true if locale setting was unsuccessful bool bad() const { return bad_; } /// Restore the locale. Invoked by destructor. bool restore() { if (do_change_ && !bad_) { bad_ = locale_cpp_set(old_locale_); do_change_ = false; // avoid invoking again } return bad_; } private: std::locale old_locale_; ///< The old locale bool do_change_; ///< Whether we changed the locale or not bool bad_; ///< If true, there was an error setting the locale }; } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/noncopyable.h000066400000000000000000000030511417717230600203140ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 1999 - 2010 Beman Dawes (C) 2008 - 2013 Alexander Shaduri License: See BOOST_LICENSE_1_0.txt file ***************************************************************************/ /// \file /// \author Dave Abrahams /// \author Beman Dawes /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_NONCOPYABLE_H #define HZ_NONCOPYABLE_H #include "hz_config.h" // feature macros /** \file Original notes and copyright info follow: Boost noncopyable.hpp header file (C) Copyright Beman Dawes 1999-2003. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) See http://www.boost.org/libs/utility for documentation. */ namespace hz { // Contributed by Dave Abrahams /// This namespace is for protection from unintended ADL /// (Argument-dependent lookup; this avoids unintended lookups in hz namespace). namespace noncopyable_ { /// Private copy constructor and copy assignment ensure classes derived from /// class noncopyable cannot be copied. class noncopyable { protected: noncopyable() {} // ~noncopyable() {} private: // emphasize the following members are private noncopyable( const noncopyable& ); const noncopyable& operator=( const noncopyable& ); }; } /// Inherit this to make your objects non-copyable typedef noncopyable_::noncopyable noncopyable; } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/optional_value.h000066400000000000000000000063231417717230600210310ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2013 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_OPTIONAL_VALUE_H #define HZ_OPTIONAL_VALUE_H #include "hz_config.h" // feature macros #include // std::ostream, operator<<(ostream, const char*). iosfwd is not enough. #if __cplusplus > 201100L #include // move #endif namespace hz { /// A simple wrapper around type T, providing additional state - defined / undefined. template class OptionalValue { public: /// Wrapped type typedef T value_type; /// Constructor. Initial value is default-constructed, and the state is undefined. OptionalValue() : value_(), defined_(false) { } #if __cplusplus > 201100L /// Construct from value \c v, setting the state to defined. explicit OptionalValue(T v) : value_(std::move(v)), defined_(true) { } #else /// Construct from value \c v, setting the state to defined. OptionalValue(const T& v) : value_(v), defined_(true) { } #endif // Don't Define any of the following: move, copy, destruct. // This will make the compiler generate the defaults. // /// Assignment operator // OptionalValue& operator= (const OptionalValue& other) // { // value_ = other.value_; // defined_ = other.defined_; // return *this; // } // /// Assignment operator // OptionalValue& operator= (OptionalValue& other) // { // value_ = other.value_; // defined_ = other.defined_; // return *this; // } #if __cplusplus > 201100L /// Assignment operator. /// \post state is defined. OptionalValue& operator= (T v) { value_ = std::move(v); defined_ = true; return *this; } #else /// Assignment operator. /// \post state is defined. OptionalValue& operator= (const T& v) { value_ = v; defined_ = true; return *this; } #endif // /// Assignment operator. // /// \post state is defined. // OptionalValue& operator= (T& v) // { // value_ = v; // defined_ = true; // return *this; // } /// Comparison operator bool operator== (const OptionalValue& v) { return (defined_ == v.defined_ && value_ == v.value_); } /// Comparison operator bool operator== (const T& v) { return (defined_ && value_ == v); } /// Set the state to undefined, set internal value to a default-constructed one. void reset() { defined_ = false; value_ = T(); // how else? (it should be needed, e.g. to clear ref-ptr) } /// Get the stored value. You may use this function only if defined() returns true. const T& value() const { return value_; } /// Check the defined state. bool defined() const { return defined_; } private: T value_; ///< Wrapped value bool defined_; ///< "Defined" state }; /// Stream output operator template std::ostream& operator<< (std::ostream& os, const OptionalValue& v) { if (v.defined()) { os << v.value(); } else { os << "[value undefined]"; } return os; } } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/portable_snprintf.h000066400000000000000000000135521417717230600215450ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2009 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_PORTABLE_SNPRINTF_H #define HZ_PORTABLE_SNPRINTF_H #include "hz_config.h" // feature macros /** \file Compilation options: - Define \c ENABLE_GLIB to 1 to enable glib-related code (portable ISO-compatible (v)snprintf). Keep in mind that these format types are non-portable (the first one is MS variant, the second one is standard): - %I64d, %lld (long long int), - %I64u, %llu (unsigned long long int), - %f, %Lf (long double; needs casting to double under non-ANSI mingw if using %f). */ namespace hz { // Note: There are macros, so they are not namespaced. /// \def HAVE_PORTABLE_SNPRINTF_MS /// If 1, portable_snprintf() and portable_vsnprintf() accept I64d, I64u. /// Users are expected to check this macro to see if the format is supported. /// \def HAVE_PORTABLE_SNPRINTF_ISO /// If 1, portable_snprintf() and portable_vsnprintf() accept lld, llu, Lf. /// Users are expected to check this macro to see if the format is supported. /// \def portable_snprintf(buf, buf_size, ...) /// snprintf() wrapper that always behaves according to ISO standard in terms of 0-termination. /// Note that this is a macro, and therefore not namespaced. /// Use it like this: /// \code void portable_snprintf(char *str, size_t size, const char *format, ...); \endcode #if defined ENABLE_GLIB && ENABLE_GLIB #include // g_snprintf // It's ISO-compatible #define portable_snprintf(buf, buf_size, ...) \ (void)g_snprintf((buf), (buf_size), __VA_ARGS__) #define HAVE_PORTABLE_SNPRINTF_MS 0 #define HAVE_PORTABLE_SNPRINTF_ISO 1 #elif defined HAVE_ISO_STDIO && HAVE_ISO_STDIO // mingw/ISO, all non-windows platforms. #include // for cstdio #include // snprintf #define portable_snprintf(buf, buf_size, ...) \ (void)snprintf((buf), (buf_size), __VA_ARGS__) // mingw/ISO has both the MS and ISO specifiers #if defined _WIN32 && defined __GNUC__ #define HAVE_PORTABLE_SNPRINTF_MS 1 #else #define HAVE_PORTABLE_SNPRINTF_MS 0 #endif #define HAVE_PORTABLE_SNPRINTF_ISO 1 #elif defined HAVE_WIN_SE_FUNCS && HAVE_WIN_SE_FUNCS #include // for cstdio #include // _snprintf_s // writes at most buf_size bytes, always including 0. #define portable_snprintf(buf, buf_size, ...) \ (void)_snprintf_s((buf), (buf_size), _TRUNCATE, __VA_ARGS__) #define HAVE_PORTABLE_SNPRINTF_MS 1 #define HAVE_PORTABLE_SNPRINTF_ISO 0 #elif defined HAVE__SNPRINTF && HAVE__SNPRINTF // Prefer snprintf() to _snprintf() if we're sure it's good. // Note that old versions of mingw used to define snprintf() as _snprintf(), // which is not proper. #include // for cstdio #include // _snprintf // _snprintf() writes at most buf_size-1 bytes. the buffer should be zeroed out // beforehand. #define portable_snprintf(buf, buf_size, ...) \ (void)((_snprintf((buf), (buf_size) - 1, __VA_ARGS__) == ((buf_size) - 1)) && ((buf)[(buf_size)-1] = '\0')) #define HAVE_PORTABLE_SNPRINTF_MS 0 #define HAVE_PORTABLE_SNPRINTF_ISO 1 #else #error Cannot find suitable snprintf() implementation for portable_snprintf() #endif /// \def portable_vsnprintf(buf, buf_size, ...) /// vsnprintf() wrapper that always behaves according to ISO standard in terms of 0-termination. /// Note that this is a macro, and therefore not namespaced. /// Use it like this: /// \code void portable_vsnprintf(char *str, size_t size, const char *format, va_list ap); \endcode #if defined ENABLE_GLIB && ENABLE_GLIB #include // g_vsnprintf // It's ISO-compatible #define portable_vsnprintf(buf, buf_size, format, ap) \ (void)g_vsnprintf((buf), (buf_size), (format), (ap)) #define HAVE_PORTABLE_VSNPRINTF_MS 0 #define HAVE_PORTABLE_VSNPRINTF_ISO 1 #elif defined HAVE_ISO_STDIO && HAVE_ISO_STDIO #include // for cstdio #include // vsnprintf (mingw, posix(?)) #include // for cstdarg #include // vsnprintf (gnu) #define portable_vsnprintf(buf, buf_size, format, ap) \ (void)vsnprintf((buf), (buf_size), (format), (ap)) // mingw/ISO has both the MS and ISO specifiers #if defined _WIN32 && defined __GNUC__ #define HAVE_PORTABLE_VSNPRINTF_MS 1 #else #define HAVE_PORTABLE_VSNPRINTF_MS 0 #endif #define HAVE_PORTABLE_VSNPRINTF_ISO 1 #elif defined HAVE_WIN_SE_FUNCS && HAVE_WIN_SE_FUNCS #include // for cstdio #include // _vsnprintf_s // writes at most buf_size bytes, always including 0. #define portable_vsnprintf(buf, buf_size, format, ap) \ (void)vsnprintf_s((buf), (buf_size), _TRUNCATE, (format), (ap)) #define HAVE_PORTABLE_VSNPRINTF_MS 1 #define HAVE_PORTABLE_VSNPRINTF_ISO 0 #elif defined HAVE__VSNPRINTF && HAVE__VSNPRINTF // prefer snprintf() to _snprintf() if we're sure it's good. #include // for cstdio #include // _vsnprintf #include // for cstdarg #include // _vsnprintf // _vsnprintf() writes at most buf_size-1 bytes. the buffer should be zeroed out // beforehand. // Note that MS's vsnprintf() (which is the same as their _vsnprintf()) // doesn't always write 0. // We use _vsnprintf() instead of vsnprintf() to avoid using the proper vsnprintf() // by chance (_vsnprintf() is always broken, vsnprintf() may not be). #define portable_vsnprintf(buf, buf_size, format, ap) \ (void)((_vsnprintf((buf), (buf_size) - 1, (format), (ap)) == ((buf_size) - 1)) && ((buf)[(buf_size)-1] = '\0')) #define HAVE_PORTABLE_VSNPRINTF_MS 1 #define HAVE_PORTABLE_VSNPRINTF_ISO 0 #else #error Cannot find suitable vsnprintf() implementation for portable_vsnprintf() #endif } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/process_signal.h000066400000000000000000000152621417717230600210250ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_PROCESS_SIGNAL_H #define HZ_PROCESS_SIGNAL_H #include "hz_config.h" // feature macros #include #if defined ENABLE_GLIB && ENABLE_GLIB #include // g_strsignal() #else #include // for string.h #include // strsignal() #endif #ifdef _WIN32 // Note: #define WINVER 0x0501 (aka winxp) before this! // This is needed for GetProcessId() (winxp or later). // Just writing a prototype doesn't work - we get undefined symbols. #include // all that winapi stuff #include // errno #else #include // pid_t #include // for signal.h #include // kill() #endif #include "portable_snprintf.h" // portable_snprintf /** \file Compilation options: - Define ENABLE_GLIB to 1 to enable glib-related code (portable signal messages). */ // win32 doesn't have signal W* macros, define them (this is _very_ rough, // but win32 doesn't provide any better way AFAIK). // checking one macro is enough. #if defined _WIN32 && !defined WIFEXITED #define WIFEXITED(w) (((w) & 0XFFFFFF00) == 0) #define WIFSIGNALED(w) (!WIFEXITED(w)) #define WEXITSTATUS(w) (w) #define WTERMSIG(w) (w) #endif namespace hz { /// Portable strsignal() version for std::string. /// Note: This function may return messages in native language, /// possibly using LC_MESSAGES to select the language. /// If Glib is enabled, it returns messages in UTF-8 format. inline std::string signal_string(int signal_value); /// \typedef process_id_t /// Process handle /// \enum signal_t /// Sendable signals /// \var signal_t SIGNAL_SIGNONE /// Verify that the process exists /// \var signal_t SIGNAL_SIGTERM /// Ask the process to terminate itself /// \var signal_t SIGNAL_SIGKILL /// Nuke the process #ifdef _WIN32 typedef HANDLE process_id_t; // process handle, not process id enum signal_t { SIGNAL_SIGNONE, SIGNAL_SIGTERM, SIGNAL_SIGKILL }; #else typedef pid_t process_id_t; enum signal_t { SIGNAL_SIGNONE = 0, SIGNAL_SIGTERM = SIGTERM, SIGNAL_SIGKILL = SIGKILL }; #endif /// Portable kill(). Works with signal_t signals only. /// Process groups are not supported under win32. inline int process_signal_send(process_id_t process_handle, signal_t sig); // ------------------------------------------ Implementation inline std::string signal_to_string(int signal_value) { std::string msg; #if defined ENABLE_GLIB && ENABLE_GLIB msg = g_strsignal(signal_value); // no need to free. won't return 0. message is in utf8. // mingw doesn't have strsignal()! #elif defined _WIN32 char buf[64] = {0}; portable_snprintf(buf, 64, "Unknown signal: %d.", signal_value); msg = buf; #else // no glib and not win32 const char* m = strsignal(signal_value); // this may return 0, but not on Linux. if (m) { msg = m; } else { char buf[64] = {0}; portable_snprintf(buf, 64, "Unknown signal: %d.", signal_value); msg = buf; } #endif return msg; } #ifdef _WIN32 namespace internal { // structure used to pass parameters to process_signal_find_by_pid. struct process_signal_find_by_pid_arg { process_signal_find_by_pid_arg(DWORD pid_) : pid(pid_), hwnd(0) { } DWORD pid; // pid we're looking from HWND hwnd; // hwnd used to return the result }; // signal_send() helper inline BOOL CALLBACK process_signal_find_by_pid(HWND hwnd, LPARAM cb_arg) { DWORD pid = 0; GetWindowThreadProcessId(hwnd, &pid); process_signal_find_by_pid_arg* arg = reinterpret_cast(cb_arg); if (pid == arg->pid) { arg->hwnd = hwnd; return FALSE; // stop the caller } return TRUE; // continue searching } } #endif #ifndef _WIN32 inline int process_signal_send(process_id_t process_handle, signal_t sig) { return kill(process_handle, static_cast(sig)); // aah, the beauty of simplicity... } #else inline int process_signal_send(process_id_t process_handle, signal_t sig) { if (process_handle == 0) { errno = ESRCH; // The pid or process group does not exist. return -1; } // just check if the process exists if (sig == SIGNAL_SIGNONE) { // Warning: GetProcessId() requires winxp or higher. // Without it we can't do anything meaningful. #if defined(WINVER) && WINVER >= 0x0501 if (GetProcessId(process_handle) == 0) { // this may indicate many things, but let's not be picky. errno = ESRCH; // The pid or process group does not exist. return -1; } #endif return 0; // everything ok // unconditionall kill, no cleanups } else if (sig == SIGNAL_SIGKILL) { // This is an ugly way of murder, but such is the life of processes in win32... // GetExitCodeProcess() will return UINT(-1) as exit code. if (TerminateProcess(process_handle, static_cast(-1)) == 0) { errno = ESRCH; // The pid or process group does not exist. return -1; } // try euthanasia } else if (sig == SIGNAL_SIGTERM) { // Warning: GetProcessId() requires winxp or higher. // Without it we can't do anything meaningful. #if defined(WINVER) && WINVER >= 0x0501 internal::process_signal_find_by_pid_arg arg(GetProcessId(process_handle)); if (EnumWindows(&internal::process_signal_find_by_pid, reinterpret_cast(&arg)) != 0) { if (arg.hwnd) { // we found something // tell it to close. // not sure what's the difference between ANSI/UNICODE here. PostMessageA(arg.hwnd, WM_QUIT, 0, 0); // check the status later } else { // error, not found errno = EPERM; // no permission return -1; } } else { // no windows for this pid, can't kill without them... errno = EPERM; // no permission return -1; } #else errno = EPERM; // no permission return -1; #endif // huh? some bad enum / int screwup happened } else { errno = EINVAL; // invalid signal return -1; } // The signal was sent, wait for confirmation or something. DWORD exit_code = STILL_ACTIVE; // wait for its status to change for 500 msec. if (WaitForSingleObject(process_handle, 500) == WAIT_OBJECT_0) { // condition reached. // this puts STILL_ACTIVE into exit_code if it's not terminated yet. if (GetExitCodeProcess(process_handle, &exit_code) != 0) { errno = EPERM; // no permission return -1; } } if (exit_code == STILL_ACTIVE) { errno = EPERM; // no permission return -1; } return 0; } #endif } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/ptr_container.h000066400000000000000000000146061417717230600206620ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Yonat Sharon /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_PTR_CONTAINER_H #define HZ_PTR_CONTAINER_H #include "hz_config.h" // feature macros #include "type_properties.h" namespace hz { /** A pointer-container wrapper class which auto-deletes its elements. Heavily based on ptr_container class by Yonat Sharon , from http://ootips.org/yonat/4dev/ . Example usage: \code { ptr_container > v; // v can be manipulated like any std::vector. v.push_back(new int(42)); v.push_back(new int(17)); // v now owns the allocated int-s. Don't free them elsewhere! v.erase(v.begin()); // frees the memory allocated for the int 42, and then removes the // first element of v. } // v's destructor is called, and it frees the memory allocated for the int 17. \endcode Notes: 1. Assumes that all elements are unique (you don't have two elements pointing to the same object, otherwise you might delete it twice). 2. Not usable with pair associative containers (map and multimap). */ template class ptr_container : public Container { public: // parent's stuff typedef typename Container::size_type size_type; ///< STL compatibility typedef typename Container::difference_type difference_type; ///< STL compatibility typedef typename Container::reference reference; ///< STL compatibility typedef typename Container::const_reference const_reference; ///< STL compatibility typedef typename Container::value_type value_type; ///< STL compatibility typedef typename Container::iterator iterator; ///< STL compatibility typedef typename Container::const_iterator const_iterator; ///< STL compatibility typedef typename Container::reverse_iterator reverse_iterator; ///< STL compatibility typedef typename Container::const_reverse_iterator const_reverse_iterator; ///< STL compatibility typedef ptr_container self_type; ///< Type of this class typedef Container wrapped_type; ///< Wrapped container type using Container::begin; using Container::end; using Container::size; /// Constructor ptr_container() { } /// Construct from existing container ptr_container(const Container& c) : Container(c) { } /// Destructor, deletes all elements ~ptr_container() { clean_all(); } /// Assignment operator (from existing container) self_type& operator= (const Container& c) { Container::operator=(c); return *this; } private: /// Private copy constructor (disallows copying and therefore double-deletion) ptr_container(const self_type&); /// Private assignment operator (disallows copying and therefore double-deletion) self_type& operator= (const self_type&); public: /// Delete all elements, clear the container void clear() { clean_all(); // delete pointers Container::clear(); // clear the container } /// Delete one element, remove it from container iterator erase(iterator i) { clean(i); return Container::erase(i); } /// Delete a range of elements, remove them from container iterator erase(iterator f, iterator l) { clean(f,l); return Container::erase(f,l); } /// For associative containers: erase() a value size_type erase(const value_type& v) { iterator i = Container::find(v); size_type found(i != end()); // can't have more than 1 if (found) erase(i); return found; } /// Delete the first element, remove it from container. void pop_front() { clean(begin()); Container::pop_front(); } /// Delete the last element, remove it from container. void pop_back() { iterator i(end()); clean(--i); Container::pop_back(); } /// Resize the container. If the new size is smaller than the current /// one, delete the extra elements and remove them. void resize(size_type s, value_type c = value_type()) { if (s < size()) clean(begin()+s, end()); Container::resize(s, c); } /// Clear the container (deleting everything) and assign a new range to it. template void assign(InputIterator first, InputIterator last) { clean_all(); Container::assign(first, last); } /// Clear the container (deleting everything) and create a range of \c n elements. template void assign(Size n, const T& t = T()) { clean_all(); Container::assign(n, t); } /// For std::list: remove by value (delete if found) void remove(const value_type& v) { for (iterator i = begin(); i != end(); ++i) { if ((*i) == v) { clean(i); break; } } // clean( std::find(begin(), end(), v) ); Container::remove(v); } /// For std::list: remove by predicate (delete if found) template void remove_if(Pred pr) { for (iterator i = begin(); i != end(); ++i) { if (pr(*i)) clean(i); } Container::remove_if(pr); } // Additional, non-Container-emulating functions: /// Deep-clone the container and its elements. /// You MUST delete the elements of \c put_here after you're done with them. template void clone_to(ReturnedContainer& put_here) const { put_here.assign(this->begin(), this->end()); for (typename ReturnedContainer::iterator i = put_here.begin(); i != put_here.end(); ++i) { *i = new typename hz::type_remove_pointer::type(*i); } } /// Same as clone_to(), but calls the clone() method of objects-to clone. /// This is needed if we hold the base pointers of child classes. /// You MUST delete the elements of put_here. template void clone_by_method_to(ReturnedContainer& put_here) const // deep-clone { put_here.assign(this->begin(), this->end()); for (typename ReturnedContainer::iterator i = put_here.begin(); i != put_here.end(); ++i) { *i = (*i)->clone(); } } private: /// Delete element void clean(iterator i) { delete *i; } /// Delete range void clean(iterator first, iterator last) { while (first != last) clean(first++); } /// Delete all elements void clean_all() { clean(begin(), end()); } }; } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/res_data.h000066400000000000000000000106531417717230600175730ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_RES_DATA_H #define HZ_RES_DATA_H #include "hz_config.h" // feature macros #include #include "cstdint.h" #include "data_file.h" #include "fs_file.h" /** \file When loading file resources, this file allows you to choose between compiled-in buffers (e.g. generated by file2csource.sh script and linked in), and runtime-loaded data files (searched and loaded dynamically at runtime). Configuration macros: HZ_ENABLE_COMPILED_RES_DATA (0 | 1). */ // Compiled-in buffers: /// Declare that we have a binary chunk res_name. /// (You may use this e.g. with GtkBuilder xml contents, putting it into your window class). #define HZ_RES_DATA_COMPILED_INIT_NAMED(res_name, dummy, class_name) \ struct class_name { \ class_name() \ { \ extern const unsigned char res_name[]; \ extern const unsigned int res_name##_size; \ buf = res_name; \ size = res_name##_size; \ root_name = #res_name; \ } \ std::string get_string() const \ { \ if (!buf || !size) \ return std::string(); \ return std::string(reinterpret_cast(buf), size); \ } \ const unsigned char* buf; \ unsigned int size; \ const char* root_name; \ } /// Same as HZ_RES_DATA_COMPILED_INIT_NAMED, with the default name as ResData. #define HZ_RES_DATA_COMPILED_INIT(res_name, dummy) \ HZ_RES_DATA_COMPILED_INIT_NAMED(res_name, dummy, ResData) /// Use this for auto-loaded data files. /// The generated API is the same as with compiled-in buffers. /// Don't forget to call hz::data_file_add_search_directory() somewhere /// to register the data file search paths. #define HZ_RES_DATA_LOADED_INIT_NAMED(res_name, data_file, class_name) \ struct class_name { \ class_name() : buf(0), size(0), root_name(0) \ { \ std::string path = hz::data_file_find(data_file); \ if (path.empty()) \ return; \ hz::File file(path); \ hz::file_size_t file_size = 0, loaded_size = 0; \ if (!file.get_size(file_size)) \ return; \ unsigned char* tmpbuf = new unsigned char[file_size + 1]; \ if (!file.get_contents_noalloc(tmpbuf, file_size + 1, loaded_size)) { \ delete[] tmpbuf; \ return; \ } \ tmpbuf[file_size] = '\0'; /* add 0-termination, like with compiled-in buffers */ \ buf = tmpbuf; \ size = static_cast(loaded_size); \ root_name = #res_name; \ } \ ~class_name() { \ delete[] const_cast(buf); \ buf = 0; \ } \ std::string get_string() const \ { \ if (!buf || !size) \ return std::string(); \ return std::string(reinterpret_cast(buf), size); \ } \ const unsigned char* buf; \ unsigned int size; \ const char* root_name; \ } /// Same as HZ_RES_DATA_LOADED_INIT_NAMED, with the default name as ResData. #define HZ_RES_DATA_LOADED_INIT(res_name, data_file) \ HZ_RES_DATA_LOADED_INIT_NAMED(res_name, (data_file), ResData) /// Dummy fallback #define HZ_RES_DATA_DUMMY_INIT_NAMED(res_name, dummy, class_name) \ struct class_name { \ class_name() : buf(0), size(0), root_name(#res_name) \ { } \ std::string get_string() const \ { \ return std::string(); \ } \ const unsigned char* buf; \ unsigned int size; \ const char* root_name; \ } /// Dummy fallback #define HZ_RES_DATA_DUMMY_INIT(res_name, dummy) \ HZ_RES_DATA_DUMMY_INIT_NAMED(res_name, (dummy), ResData) /// \def HZ_RES_DATA_INIT_NAMED /// Declare compiled or runtime-loaded named data, the exact method will be chosen automatically. /// \def HZ_RES_DATA_INIT /// Declare compiled or runtime-loaded data, the exact method will be chosen automatically. #if defined HZ_ENABLE_COMPILED_RES_DATA && HZ_ENABLE_COMPILED_RES_DATA #define HZ_RES_DATA_INIT_NAMED(res_name, dummy, class_name) \ HZ_RES_DATA_COMPILED_INIT_NAMED(res_name, dummy, class_name) #define HZ_RES_DATA_INIT(res_name, dummy) \ HZ_RES_DATA_COMPILED_INIT(res_name, dummy) #else // loaded buffers #define HZ_RES_DATA_INIT_NAMED(res_name, data_file, class_name) \ HZ_RES_DATA_LOADED_INIT_NAMED(res_name, data_file, class_name) #define HZ_RES_DATA_INIT(res_name, data_file) \ HZ_RES_DATA_LOADED_INIT(res_name, data_file) #endif #endif /// @} gsmartcontrol-1.1.4/src/hz/scoped_array.h000066400000000000000000000066061417717230600204670ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 1998 - 2010 Greg Colvin and Beman Dawes (C) 2001 - 2010 Peter Dimov (C) 2008 - 2013 Alexander Shaduri License: See LICENSE_boost_1_0.txt file ***************************************************************************/ /// \file /// \author Greg Colvin and Beman Dawes /// \author Peter Dimov /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_SCOPED_ARRAY_H #define HZ_SCOPED_ARRAY_H #include "hz_config.h" // feature macros /** \file Scoped non-reference-counting auto-deletign array. Based on boost::scoped_array. Original notes and copyright info follow: // (C) Copyright Greg Colvin and Beman Dawes 1998, 1999. // Copyright (c) 2001, 2002 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // http://www.boost.org/libs/smart_ptr/scoped_array.htm // */ #include // std::ptrdiff_t #include #ifndef ASSERT // assert() is undefined if NDEBUG is defined. #define ASSERT(a) assert(a) #endif namespace hz { /// scoped_array extends scoped_ptr to arrays. Deletion of the array pointed to /// is guaranteed, either on destruction of the scoped_array or via an explicit /// reset(). Use shared_array or std::vector if your needs are more complex. template class scoped_array { // noncopyable private: T* ptr; ///< The underlying array pointer /// Private copy constructor scoped_array(const scoped_array&); /// Private assignment operator scoped_array& operator=(const scoped_array&); typedef scoped_array this_type; ///< Own type typedef T* this_type::*unspecified_bool_type; ///< Helper for bool conversions /// Private operator void operator==(const scoped_array&) const; /// Private operator void operator!=(const scoped_array&) const; public: /// Underlying type (without pointer) typedef T element_type; /// Constructor, gets hold of the array ownership explicit scoped_array(T* p = 0) : ptr(p) // never throws { } /// Destructor, deletes the array ~scoped_array() // never throws { delete[] (ptr); ptr = 0; // exception-safety (needed here?) } /// Delete the old array and switch to the new one void reset(T* p = 0) // never throws { ASSERT(p == 0 || p != ptr); // catch self-reset errors this_type(p).swap(*this); } /// Element access T& operator[](std::ptrdiff_t i) const // never throws { ASSERT(ptr != 0); ASSERT(i >= 0); return ptr[i]; } /// Get the underlying pointer T* get() const // never throws { return ptr; } /// Get a reference to the stored pointer. /// Useful for passing to functions who expect T**. T*& get_ref() // never throws { return ptr; } /// Bool-like conversion operator unspecified_bool_type () const { return ptr == 0 ? 0 : &this_type::ptr; } /// Null-check bool operator! () const // never throws { return ptr == 0; } /// Swap with another array void swap(scoped_array& b) // never throws { T * tmp = b.ptr; b.ptr = ptr; ptr = tmp; } }; /// Swap two arrays template inline void swap(scoped_array& a, scoped_array& b) // never throws { a.swap(b); } } // ns #endif // hg /// @} gsmartcontrol-1.1.4/src/hz/scoped_ptr.h000066400000000000000000000104411417717230600201460ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 1998 - 2010 Greg Colvin and Beman Dawes (C) 2001 - 2010 Peter Dimov (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_boost_1_0.txt file ***************************************************************************/ /// \file /// \author Greg Colvin and Beman Dawes /// \author Peter Dimov /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_SCOPED_PTR_H #define HZ_SCOPED_PTR_H #include "hz_config.h" // feature macros /** \file Scoped non-reference-counting smart pointer with custom cleanup function. Based on boost::scoped_ptr. Original notes and copyright info follow: // (C) Copyright Greg Colvin and Beman Dawes 1998, 1999. // Copyright (c) 2001, 2002 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // http://www.boost.org/libs/smart_ptr/scoped_ptr.htm // */ #include #ifndef ASSERT // assert() is undefined if NDEBUG is defined. #define ASSERT(a) assert(a) #endif namespace hz { namespace internal { /// Deleter base template struct scoped_ptr_cleaner_base { /// Virtual destructor virtual ~scoped_ptr_cleaner_base() { } /// Delete the pointer. The actual method of deletion is chosen by the overrider. virtual void cleanup(T* p) = 0; }; /// Deleter which calls F(p), where p is the pointer. template struct scoped_ptr_cleaner : public scoped_ptr_cleaner_base { /// Constructor scoped_ptr_cleaner(F f) : clean_func(f) { } /// Virtual destructor virtual ~scoped_ptr_cleaner() { } void cleanup(T* p) { clean_func(p); } F clean_func; }; } template class scoped_ptr { // non-copyable private: T* ptr; internal::scoped_ptr_cleaner_base* cleaner; scoped_ptr (const scoped_ptr&); scoped_ptr& operator= (const scoped_ptr&); typedef scoped_ptr this_type; typedef T* this_type::*unspecified_bool_type; void operator== (const scoped_ptr&) const; void operator!= (const scoped_ptr&) const; public: typedef T element_type; /// Constructor with default cleaner explicit scoped_ptr(T* p = 0) // never throws : ptr(p), cleaner(0) { } /// Constructor. Takes ownership of \c p. During destruction /// \c cleanup_func will be called with p. template explicit scoped_ptr(T* p, F cleanup_func) : ptr(p), cleaner(new internal::scoped_ptr_cleaner(cleanup_func)) { } /// Destructor - deletes the underlying pointer ~scoped_ptr() // never throws { if (cleaner) { if (ptr) cleaner->cleanup(ptr); delete cleaner; } else { #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdelete-incomplete" #endif delete ptr; #ifdef __GNUC__ #pragma GCC diagnostic pop #endif } } /// Delete the old pointer and switch to the new one void reset(T* p = 0) // never throws { ASSERT(p == 0 || p != ptr); // catch self-reset errors this_type(p).swap(*this); } /// Dereference operator T& operator*() const // never throws { ASSERT(ptr); return *ptr; } /// Arrow operator T* operator->() const // never throws { ASSERT(ptr); return ptr; } /// Get the underlying pointer T* get() const // never throws { return ptr; } /// Get a reference to the stored pointer. /// Useful for passing to functions who expect T**. T*& get_ref() // never throws { return ptr; } // Bool-like conversion operator unspecified_bool_type () const { return ptr == 0 ? 0 : &this_type::ptr; } /// Null-check bool operator! () const // never throws { return ptr == 0; } /// Swap with another pointer void swap(scoped_ptr& b) // never throws { T * tmp = b.ptr; b.ptr = ptr; ptr = tmp; } }; /// Swap two pointers template inline void swap(scoped_ptr& a, scoped_ptr& b) // never throws { a.swap(b); } /// get_pointer(p) is a generic way to say p.get() template inline T* get_pointer(const scoped_ptr& p) { return p.get(); } } // ns #endif // hg /// @} gsmartcontrol-1.1.4/src/hz/static_assert.h000066400000000000000000000037161417717230600206630ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2003 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_STATIC_ASSERT_H #define HZ_STATIC_ASSERT_H #include "hz_config.h" // feature macros /** \file Note: Please specify the hz namespace explicitly (i.e. hz::static_assertion<>) to avoid conflicts with various other implementations; Better yet, use HZ_STATIC_ASSERT macro. */ namespace hz { /// Static assertion - similar to C++11's std::static_assert, but for C++98. template struct static_assertion; template<> struct static_assertion { }; // Use this when making an explicit static_assertion. // Without T, Intel C++ explicitly instantiates the static_assertion type. // Usage: HZ_STATIC_ASSERT(hz::static_false::value, cannot_do_this); // where T is an instantiation-dependent type. template struct static_false { static const bool value = false; }; } // ns /// Use this macro for static checks with messages. /// \c msg_identifier doesn't have to be defined as a C++ identifier, /// it's just a message that will appear in compiler error output. #define HZ_STATIC_ASSERT(cond, msg_identifier) \ if (true) { \ hz::static_assertion<((cond) != 0)> ERROR_##msg_identifier; \ (void)ERROR_##msg_identifier; \ } else (void)0 // Maybe use typedef? // Alternate version: /* namespace hz { template struct STATIC_ASSERTION_FAILURE; template<> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; template struct static_assertion_test { }; } // ns #define HZ_STATIC_ASSERT(cond) \ typedef hz::static_assertion_test)> \ hz_static_assertion_ ## __LINE__ */ #endif /// @} gsmartcontrol-1.1.4/src/hz/stream_cast.h000066400000000000000000000070601417717230600203140ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2000 - 2010 Kevlin Henney (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_boost_1_0.txt file ***************************************************************************/ #ifndef HZ_LEXICAL_CAST_H #define HZ_LEXICAL_CAST_H #include "hz_config.h" // feature macros #include // std::size_t #include #include #include // std::numeric_limits #if __cplusplus > 201100L #include #else #include "type_properties.h" // type_is_pointer #endif #include "bad_cast_exception.h" /** \file stream_cast library, based on boost::lexical_cast. Convert any type supporting "operator<<" to any other type supporting "operator>>". Original notes and copyright info follow: // what: lexical_cast custom keyword cast // who: contributed by Kevlin Henney, // enhanced with contributions from Terje Slettebø, // with additional fixes and suggestions from Gennaro Prota, // Beman Dawes, Dave Abrahams, Daryle Walker, Peter Dimov, // and other Boosters // when: November 2000, March 2003, June 2005 // Copyright Kevlin Henney, 2000-2005. All rights reserved. // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) */ namespace hz { namespace internal { /// stream_cast helper stream template class lexical_stream { public: // Constructor lexical_stream() { stream.unsetf(std::ios::skipws); // disable whitespace skipping if (std::numeric_limits::is_specialized) { stream.precision(std::numeric_limits::digits10 + 1); } else if (std::numeric_limits::is_specialized) { stream.precision(std::numeric_limits::digits10 + 1); } } /// Output operator bool operator<<(const Source &input) { return !(stream << input).fail(); } /// Input operator template bool operator>>(InputStreamable &output) { #if __cplusplus > 201100L return !std::is_pointer::value && stream >> output && stream.get() == std::char_traits::eof(); #else return !hz::type_is_pointer::value && stream >> output && stream.get() == std::char_traits::eof(); #endif } /// Input operator bool operator>>(std::string &output) { output = stream.str(); return true; } private: std::stringstream stream; ///< The actual string stream }; // call-by-const reference version template struct array_to_pointer_decay { typedef T type; }; template struct array_to_pointer_decay { typedef const T* type; }; } // ns internal /// This is thrown in case of cast failure DEFINE_BAD_CAST_EXCEPTION(bad_stream_cast, "Failed stream_cast from \"%s\" to \"%s\".", "Failed stream_cast."); /// Stream cast - cast Source to Target using stream operators. template inline Target stream_cast(const Source& arg) { typedef typename internal::array_to_pointer_decay::type NewSource; internal::lexical_stream interpreter; Target result; if (!(interpreter << arg && interpreter >> result)) { THROW_CUSTOM_BAD_CAST(bad_stream_cast, typeid(NewSource), typeid(Target)); } return result; } } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/string_algo.h000066400000000000000000000550021417717230600203160ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_STRING_ALGO_H #define HZ_STRING_ALGO_H #include "hz_config.h" // feature macros #include #include // std::tolower, std::toupper namespace hz { // --------------------------------------------- Split /// Split a string into components by character (delimiter), appending the /// components (without delimiters) to container "append_here". /// If skip_empty is true, then empty components will be omitted. /// If "limit" is more than 0, only put a maximum of "limit" number of /// elements into vector, with the last one containing the rest of the string. template inline void string_split(const std::string& str, char delimiter, Container& append_here, bool skip_empty = false, typename Container::difference_type limit = 0) { std::string::size_type curr = 0, last = 0; std::string::size_type end = str.size(); typename Container::difference_type num = 0; // number inserted while (true) { if (last >= end) { // last is past the end if (!skip_empty) // no need to check num here append_here.push_back(std::string()); break; } curr = str.find(delimiter, last); if (!skip_empty || (curr != last)) { if (++num == limit) { append_here.push_back(str.substr(last, std::string::npos)); break; } else { append_here.push_back(str.substr(last, (curr == std::string::npos ? curr : (curr - last)))); } } if (curr == std::string::npos) break; last = curr + 1; } } /// Split a string into components by another string (delimiter), appending the /// components (without delimiters) to container "append_here". /// If skip_empty is true, then empty components will be omitted. /// If "limit" is more than 0, only put a maximum of "limit" number of /// elements into vector, with the last one containing the rest of the string. template inline void string_split(const std::string& str, const std::string& delimiter, Container& append_here, bool skip_empty = false, typename Container::difference_type limit = 0) { std::string::size_type skip_size = delimiter.size(); if (str.size() < skip_size) { append_here.push_back(str); return; } std::string::size_type curr = 0, last = 0; std::string::size_type end = str.size(); typename Container::difference_type num = 0; // number inserted while (true) { if (last >= end) { // last is past the end if (!skip_empty) // no need to check num here append_here.push_back(std::string()); break; } curr = str.find(delimiter, last); std::string component(str, last, (curr == std::string::npos ? curr : (curr - last))); if (!skip_empty || !component.empty()) { if (++num == limit) { append_here.push_back(str.substr(last, std::string::npos)); break; } else { append_here.push_back(component); } } if (curr == std::string::npos) break; last = curr + skip_size; } } /// Split a string into components by any of the characters (delimiters), appending the /// components (without delimiters) to container "append_here". /// If skip_empty is true, then empty components will be omitted. /// If "limit" is more than 0, only put a maximum of "limit" number of /// elements into vector, with the last one containing the rest of the string. template inline void string_split_by_chars(const std::string& str, const std::string& delimiter_chars, Container& append_here, bool skip_empty = false, typename Container::difference_type limit = 0) { std::string::size_type curr = 0, last = 0; std::string::size_type end = str.size(); typename Container::difference_type num = 0; // number inserted while (true) { if (last >= end) { // last is past the end if (!skip_empty) // no need to check num here append_here.push_back(std::string()); break; } curr = str.find_first_of(delimiter_chars, last); if (!skip_empty || (curr != last)) { if (++num == limit) { append_here.push_back(str.substr(last, std::string::npos)); break; } else { append_here.push_back(str.substr(last, (curr == std::string::npos ? curr : (curr - last)))); } } if (curr == std::string::npos) break; last = curr + 1; } } // --------------------------------------------- Join /// Join elements of container v into a string, using glue between them. template inline std::string string_join(const Container& v, char glue) { typename Container::const_iterator begin = v.begin(); typename Container::const_iterator iter = begin; std::string ret; for (; iter != v.end(); ++iter) { if (iter != begin) ret += glue; ret += (*iter); } return ret; } /// Join elements of container v into a string, using glue between them. template inline std::string string_join(const Container& v, const std::string& glue) { typename Container::const_iterator begin = v.begin(); typename Container::const_iterator iter = begin; std::string ret; for (; iter != v.end(); ++iter) { if (iter != begin) ret += glue; ret += (*iter); } return ret; } // --------------------------------------------- Trim /// Trim a string s from both sides (modifying s). Return true if s was modified. /// Trimming removes all trim_chars that occur on either side of the string s. inline bool string_trim(std::string& s, const std::string& trim_chars = " \t\r\n") { if (trim_chars.empty()) return false; const std::string::size_type s_size = s.size(); std::string::size_type index = s.find_last_not_of(trim_chars); if (index != std::string::npos) s.erase(index + 1); // from index+1 to the end index = s.find_first_not_of(trim_chars); if (index != std::string::npos) s.erase(0, index); else s.clear(); return s_size != s.size(); // true if s was modified } /// Trim a string s from both sides (not modifying s), returning the changed string. /// Trimming removes all trim_chars that occur on either side of the string s. inline std::string string_trim_copy(const std::string& s, const std::string& trim_chars = " \t\r\n") { std::string ret(s); string_trim(ret, trim_chars); return ret; } /// Trim a string s from the left (modifying s). Return true if s was modified. /// Trimming removes all trim_chars that occur on the left side of the string s. inline bool string_trim_left(std::string& s, const std::string& trim_chars = " \t\r\n") { if (trim_chars.empty()) return false; const std::string::size_type s_size = s.size(); std::string::size_type index = s.find_first_not_of(trim_chars); if (index != std::string::npos) s.erase(0, index); else s.clear(); return s_size != s.size(); // true if s was modified } /// Trim a string s from the left (not modifying s), returning the changed string. /// Trimming removes all trim_chars that occur on the left side of the string s. inline std::string string_trim_left_copy(const std::string& s, const std::string& trim_chars = " \t\r\n") { std::string ret(s); string_trim_left(ret, trim_chars); return ret; } /// Trim a string s from the right (modifying s). Return true if s was modified. /// Trimming removes all trim_chars that occur on the right side of the string s. inline bool string_trim_right(std::string& s, const std::string& trim_chars = " \t\r\n") { if (trim_chars.empty()) return false; const std::string::size_type s_size = s.size(); std::string::size_type index = s.find_last_not_of(trim_chars); if (index != std::string::npos) s.erase(index + 1); // from index+1 to the end return s_size != s.size(); // true if s was modified } /// Trim a string s from the right (not modifying s), returning the changed string. /// Trimming removes all trim_chars that occur on the right side of the string s. inline std::string string_trim_right_copy(const std::string& s, const std::string& trim_chars = " \t\r\n") { std::string ret(s); string_trim_right(ret, trim_chars); return ret; } // --------------------------------------------- Erase /// Erase the left side of string s if it contains substring_to_erase, /// modifying s. Returns true if s was modified. inline bool string_erase_left(std::string& s, const std::string& substring_to_erase) { if (substring_to_erase.empty()) return false; std::string::size_type sub_size = substring_to_erase.size(); if (s.compare(0, sub_size, substring_to_erase) == 0) { s.erase(0, sub_size); return true; } return false; } /// Erase the left side of string s if it contains substring_to_erase, /// not modifying s, returning the changed string. inline std::string string_erase_left_copy(const std::string& s, const std::string& substring_to_erase) { std::string ret(s); string_erase_left(ret, substring_to_erase); return ret; } /// Erase the right side of string s if it contains substring_to_erase, /// modifying s. Returns true if s was modified. inline bool string_erase_right(std::string& s, const std::string& substring_to_erase) { std::string::size_type sub_size = substring_to_erase.size(); if (sub_size == 0) return false; std::string::size_type s_size = s.size(); if (sub_size > s_size) return false; if (s.compare(s_size - sub_size, sub_size, substring_to_erase) == 0) { s.erase(s_size - sub_size, sub_size); return true; } return false; } /// Erase the right side of string s if it contains substring_to_erase, /// not modifying s, returning the changed string. inline std::string string_erase_right_copy(const std::string& s, const std::string& substring_to_erase) { std::string ret(s); string_erase_right(ret, substring_to_erase); return ret; } // --------------------------------------------- Misc. Transformations /// remove adjacent duplicate chars inside s (modifying s). /// returns true if s was modified. /// useful for e.g. removing extra spaces inside the string. inline bool string_remove_adjacent_duplicates(std::string& s, char c, unsigned int max_out_adjacent = 1) { if (s.size() <= max_out_adjacent) return false; bool changed = false; std::string::size_type pos1 = 0, pos2; while ((pos1 = s.find(c, pos1)) != std::string::npos) { pos2 = s.find_first_not_of(c, pos1); if (pos2 == std::string::npos) pos2 = s.size(); // just past the last char if (pos2 - pos1 > max_out_adjacent) { s.erase(pos1 + max_out_adjacent, pos2 - pos1 - max_out_adjacent); changed = true; } pos1 += max_out_adjacent; } return changed; } /// remove adjacent duplicate chars inside s, not modifying s, returning the changed string. inline std::string string_remove_adjacent_duplicates_copy(const std::string& s, char c, unsigned int max_out_adjacent = 1) { std::string ret(s); string_remove_adjacent_duplicates(ret, c, max_out_adjacent); return ret; } // --------------------------------------------- Replace // TODO: Add string_replace_linear(), where multiple strings are replaced into the // original string (as opposed to previous result). /// Replace from with to inside s (modifying s). Return number of replacements made. inline std::string::size_type string_replace(std::string& s, const std::string& from, const std::string& to, int max_replacements = -1) { if (from.empty()) return std::string::npos; if (max_replacements == 0 || from == to) return 0; const std::string::size_type from_len(from.size()); const std::string::size_type to_len(to.size()); std::string::size_type cnt = 0; std::string::size_type pos = 0; while ((pos = s.find(from, pos)) != std::string::npos) { s.replace(pos, from_len, to); pos += to_len; if (static_cast(++cnt) >= max_replacements && max_replacements != -1) break; } return cnt; } /// Replace from with to inside s, not modifying s, returning the changed string. inline std::string string_replace_copy(const std::string& s, const std::string& from, const std::string& to, int max_replacements = -1) { std::string ret(s); string_replace(ret, from, to, max_replacements); return ret; } /// Replace from with to inside s (modifying s). char version. inline std::string::size_type string_replace(std::string& s, char from, char to, int max_replacements = -1) { if (max_replacements == 0 || from == to) return 0; std::string::size_type cnt = 0, pos = 0; while ((pos = s.find(from, pos)) != std::string::npos) { s[pos] = to; ++pos; if (static_cast(++cnt) >= max_replacements && max_replacements != -1) break; } return cnt; } /// Replace from with to inside s, not modifying s, returning the changed string. char version. inline std::string string_replace_copy(const std::string& s, char from, char to, int max_replacements = -1) { std::string ret(s); string_replace(ret, from, to); return ret; } /// Replace from_chars[0] with to_chars[0], from_chars[1] with to_chars[1], etc... in s (modifying s). /// from_chars.size() must be equal to to_chars.size(). /// Note: This is a multi-pass algorithm (there are from_chars.size() iterations). inline std::string::size_type string_replace_chars(std::string& s, const std::string& from_chars, const std::string& to_chars, int max_replacements = -1) { const std::string::size_type from_size = from_chars.size(); if (from_size != to_chars.size()) return std::string::npos; if (max_replacements == 0 || from_chars == to_chars) return 0; std::string::size_type cnt = 0, pos = 0; for(std::string::size_type i = 0; i < from_size; ++i) { pos = 0; char from = from_chars[i], to = to_chars[i]; while ((pos = s.find(from, pos)) != std::string::npos) { s[pos] = to; ++pos; if (static_cast(++cnt) >= max_replacements && max_replacements != -1) break; } if (max_replacements != -1 && static_cast(cnt) >= max_replacements) break; } return cnt; } /// Replace from_chars[0] with to_chars[0], from_chars[1] with to_chars[1], etc... in s, /// not modifying s, returning the changed string. /// from_chars.size() must be equal to to_chars.size(). /// Note: This is a multi-pass algorithm (there are from_chars.size() iterations). inline std::string string_replace_chars_copy(const std::string& s, const std::string& from_chars, const std::string& to_chars, int max_replacements = -1) { std::string ret(s); string_replace_chars(ret, from_chars, to_chars); return ret; } /// Replace all chars from from_chars with to_char (modifying s). inline std::string::size_type string_replace_chars(std::string& s, const std::string& from_chars, char to_char, int max_replacements = -1) { if (from_chars.empty()) return std::string::npos; if (max_replacements == 0) return 0; std::string::size_type cnt = 0, pos = 0; while ((pos = s.find_first_of(from_chars, pos)) != std::string::npos) { s[pos] = to_char; ++pos; if (static_cast(++cnt) >= max_replacements && max_replacements != -1) break; } return cnt; } /// Replace all chars from from_chars with to_char, not modifying s, returning the changed string. inline std::string string_replace_chars_copy(const std::string& s, const std::string& from_chars, char to_char, int max_replacements = -1) { std::string ret(s); string_replace_chars(ret, from_chars, to_char); return ret; } /// Replace from_strings[0] with to_strings[0], from_strings[1] with to_strings[1], etc... /// in s (modifying s). Returns total number of replacements performed. /// from_strings.size() must be equal to to_strings.size(). /// Note: This is a multi-pass algorithm (there are from_strings.size() iterations). /// Implementation note: We cannot use "template C1>", because /// it appears that it was a gcc extension, removed in 4.1 (C1 cannot bind to std::vector, /// which has 2 (or more, implementation dependent) template parameters. gcc 4.1 /// allowed this because vector's other parameters have defaults). template inline std::string::size_type string_replace_array(std::string& s, const Container1& from_strings, const Container2& to_strings, int max_replacements = -1) { const std::string::size_type from_array_size = from_strings.size(); if (from_array_size != to_strings.size()) return std::string::npos; if (max_replacements == 0) return 0; // if (from_strings == to_strings) // don't check this, it's too expensive. it will work anyway. // return 0; std::string::size_type cnt = 0, pos = 0; for(std::string::size_type i = 0; i < from_array_size; ++i) { pos = 0; while ((pos = s.find(from_strings[i], pos)) != std::string::npos) { s.replace(pos, from_strings[i].size(), to_strings[i]); pos += to_strings[i].size(); if (static_cast(++cnt) >= max_replacements && max_replacements != -1) break; } if (max_replacements != -1 && static_cast(cnt) > max_replacements) break; } return cnt; } /// Eeplace from_strings[0] with to_strings[0], from_strings[1] with to_strings[1], etc... in s, /// not modifying s, returning the changed string. /// from_strings.size() must be equal to to_strings.size(). /// Note: This is a multi-pass algorithm (there are from_strings.size() iterations). template inline std::string string_replace_array_copy(const std::string& s, const Container1& from_strings, const Container2& to_strings, int max_replacements = -1) { std::string ret(s); string_replace_array(ret, from_strings, to_strings); return ret; } /// Replace all strings in from_strings with to_string in s (modifying s). /// Returns total number of replacements performed. /// Note: This is a one-pass algorithm. template inline std::string::size_type string_replace_array(std::string& s, const Container& from_strings, const std::string& to_string, int max_replacements = -1) { const std::string::size_type from_array_size = from_strings.size(); const std::string::size_type to_str_size = to_string.size(); if (from_array_size == 0) return std::string::npos; if (max_replacements == 0) return 0; std::string::size_type cnt = 0, pos = 0; for(std::string::size_type i = 0; i < from_array_size; ++i) { pos = 0; while ((pos = s.find(from_strings[i], pos)) != std::string::npos) { s.replace(pos, from_strings[i].size(), to_string); pos += to_str_size; if (static_cast(++cnt) >= max_replacements && max_replacements != -1) break; } if (max_replacements != -1 && static_cast(cnt) > max_replacements) break; } return cnt; } /// Replace all strings in from_strings with to_string in s, not modifying s, returning the changed string. /// Note: This is a one-pass algorithm. template inline std::string string_replace_array_copy(const std::string& s, const Container& from_strings, const std::string& to_string, int max_replacements = -1) { std::string ret(s); string_replace_array(ret, from_strings, to_string); return ret; } /// Same as the other overloads, but needed to avoid conflict with all-template version template inline std::string::size_type string_replace_array(std::string& s, const Container& from_strings, const char* to_string, int max_replacements = -1) { return string_replace_array(s, from_strings, std::string(to_string), max_replacements); } // Same as the other overloads, but needed to avoid conflict with all-template version template inline std::string string_replace_array_copy(const std::string& s, const Container& from_strings, const char* to_string, int max_replacements = -1) { return string_replace_array_copy(s, from_strings, std::string(to_string), max_replacements); } // --------------------------------------------- Matching /// Check whether a string begins with another string inline bool string_begins_with(const std::string& str, const std::string& substr) { if (str.length() >= substr.length()) { return (str.compare(0, substr.length(), substr) == 0); } return false; } /// Check whether a string begins with a character inline bool string_begins_with(const std::string& str, char ch) { return !str.empty() && str[0] == ch; } /// Check whether a string ends with another string inline bool string_ends_with(const std::string& str, const std::string& substr) { if (str.length() >= substr.length()) { return (str.compare(str.length() - substr.length(), substr.length(), substr) == 0); } return false; } /// Check whether a string ends with a character inline bool string_ends_with(const std::string& str, char ch) { return !str.empty() && str[str.size() - 1] == ch; } // --------------------------------------------- Utility /// Auto-detect and convert mac/dos/unix newline formats in s (modifying s) to unix format. /// Returns true if \c s was changed. inline bool string_any_to_unix(std::string& s) { std::string::size_type n = hz::string_replace(s, "\r\n", "\n"); // dos n += hz::string_replace(s, '\r', '\n'); // mac return bool(n); } /// Auto-detect and convert mac/dos/unix newline formats in s to unix format. /// Returns the result string. inline std::string string_any_to_unix_copy(const std::string& s) { std::string ret(s); string_any_to_unix(ret); return ret; } /// Auto-detect and convert mac/dos/unix newline formats in s (modifying s) to dos format. /// Returns true if \c s was changed. inline bool string_any_to_dos(std::string& s) { bool changed = string_any_to_unix(s); std::string::size_type n = hz::string_replace(s, "\n", "\r\n"); // dos return bool(n) || changed; // may not really work } /// Auto-detect and convert mac/dos/unix newline formats in s to dos format. /// Returns the result string. inline std::string string_any_to_dos_copy(const std::string& s) { std::string ret(s); string_any_to_dos(ret); return ret; } /// Convert s to lowercase (modifying s). Return size of the string. inline std::string::size_type string_to_lower(std::string& s) { const std::string::size_type len = s.size(); for(std::string::size_type i = 0; i != len; ++i) { s[i] = static_cast(std::tolower(s[i])); } return len; } /// Convert s to lowercase, not modifying s, returning the changed string. inline std::string string_to_lower_copy(const std::string& s) { std::string ret(s); string_to_lower(ret); return ret; } /// Convert s to uppercase (modifying s). Return size of the string. inline std::string::size_type string_to_upper(std::string& s) { const std::string::size_type len = s.size(); for(std::string::size_type i = 0; i != len; ++i) { s[i] = static_cast(std::toupper(s[i])); } return len; } /// Convert s to uppercase, not modifying s, returning the changed string. inline std::string string_to_upper_copy(const std::string& s) { std::string ret(s); string_to_upper(ret); return ret; } } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/string_algo_test.cpp000066400000000000000000000041571417717230600217150ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_unlicense.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz_tests /// \weakgroup hz_tests /// @{ // disable libdebug, we don't link to it #undef HZ_USE_LIBDEBUG #define HZ_USE_LIBDEBUG 0 // enable libdebug emulation through std::cerr #undef HZ_EMULATE_LIBDEBUG #define HZ_EMULATE_LIBDEBUG 1 // The first header should be then one we're testing, to avoid missing // header pitfalls. #include "string_algo.h" #include #include /// Main function for the test int main() { using namespace hz; { std::string s = "/aa/bbb/ccccc//dsada//"; std::vector v; string_split(s, '/', v, false); for (unsigned int i = 0; i < v.size(); i++) std::cerr << (v[i] == "" ? "[empty]" : v[i]) << "\n"; } { std::string s = "//aa////bbb/ccccc//dsada////"; std::vector v; string_split(s, "//", v, false); for (unsigned int i = 0; i < v.size(); i++) std::cerr << (v[i] == "" ? "[empty]" : v[i]) << "\n"; } { std::string s = " a b bb c d "; std::cerr << string_remove_adjacent_duplicates_copy(s, ' ') << "\n"; std::cerr << string_remove_adjacent_duplicates_copy(s, ' ', 2) << "\n"; } { std::string s = "/a/b/c/dd//e/"; string_replace(s, '/', ':'); std::cerr << s << "\n"; } { std::string s = "112/2123412"; string_replace(s, "12", "AB"); std::cerr << s << "\n"; } { std::vector from, to; from.push_back("12"); from.push_back("abc"); to.push_back("345"); to.push_back("de"); std::string s = "12345678abcdefg abc ab"; std::cerr << s << "\n"; string_replace_array(s, from, to); std::cerr << s << "\n"; } { std::vector from, to; from.push_back("12"); from.push_back("abc"); std::string s = "12345678abcdefg abc ab"; std::cerr << s << "\n"; string_replace_array(s, from, ":"); std::cerr << s << "\n"; } return 0; } /// @} gsmartcontrol-1.1.4/src/hz/string_num.h000066400000000000000000000227631417717230600202030ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_STRING_NUM_H #define HZ_STRING_NUM_H #include "hz_config.h" // feature macros #include #include #include // setbase, setprecision, setw #include // std::fixed, std::internal #include // std::locale::classic() #include // std::numeric_limits #include // errno (not std::errno, it may be a macro) #include // std::strncmp #include "type_properties.h" // type_is_* #include "type_categories.h" // type_check_category // #include "static_assert.h" // HZ_STATIC_ASSERT #include "ascii.h" // ascii_* /** \file String to number and number to string conversions */ namespace hz { /// Check whether a string represents a numeric value (the value must /// be represented in C locale). /// strict == true indicates that the whole string must represent a number exactly. /// strict == false allows the string to contain the number only in its beginning /// (ignores any leading spaces and trailing garbage). /// base has an effect only for integral types. For bool it specifies whether to /// accept "true" and "false" (as opposed to 1 and 0). For others, base should /// be between 2 and 36 inclusive. /// This function has no definition, only specializations. template inline bool string_is_numeric(const std::string& s, T& number, bool strict, int base_or_boolalpha); /// Short version with default base. (Needed because default base is different for bool and int). template inline bool string_is_numeric(const std::string& s, T& number, bool strict = true); /// A convenience string_is_numeric wrapper. /// Note that in strict mode, T() is returned for invalid values. template inline T string_to_number(const std::string& s, bool strict, int base_or_boolalpha); /// Short version with default base. (Needed because default base is different for bool and int). template inline T string_to_number(const std::string& s, bool strict = true); /// Convert numeric value to string. alpha_or_base_or_precision means: /// for bool, 0 means 1/0, 1 means true/false; /// for int family (including char), it's the base to format in (8, 10, 16 are definitely supported); /// for float family, it controls the number of digits after comma. template inline std::string number_to_string(T number, int alpha_or_base_or_precision, bool fixed_prec = false); /// Short version with default base / precision. template inline std::string number_to_string(T number); } // ------------------------------------------- Implementation namespace hz { template inline T string_to_number(const std::string& s, bool strict, int base_or_boolalpha) { T value = T(); hz::string_is_numeric(s, value, strict, base_or_boolalpha); return value; } template inline T string_to_number(const std::string& s, bool strict) { T value = T(); hz::string_is_numeric(s, value, strict); return value; } // Definition of the one without base parameter. template inline bool string_is_numeric(const std::string& s, T& number, bool strict) { int base = 0; if (type_is_same::value) { base = 1; // use alpha (true / false), as opposed to 1 / 0. } else if (type_is_integral::value) { base = 0; // auto-detect base 10, 16 (0xNUM), 8 (0NUM). } // base is ignored for other types return string_is_numeric(s, number, strict, base); } // Note: Parameter "base" is ignored for floating point types. #define DEFINE_STRING_IS_NUMERIC(type) \ template<> inline \ bool string_is_numeric(const std::string& s, type& number, bool strict, int base) \ { \ if (s.empty() || (strict && hz::ascii_isspace(s[0]))) /* ascii_strtoi() skips the leading spaces */ \ return false; \ const char* str = s.c_str(); \ char* end = 0; \ errno = 0; \ type tmp = hz::ascii_strton(str, &end, base); \ if ( (strict ? (*end == '\0') : (end != str)) && errno == 0 ) { /* if end is 0 byte, then the whole string was parsed */ \ number = tmp; \ return true; \ } \ return false; \ } // Define string_is_numeric() function specializations for various types: // Specialization for bool template<> inline bool string_is_numeric(const std::string& s, bool& number, bool strict, int boolalpha_enabled) { if (s.empty() || (strict && hz::ascii_isspace(s[0]))) // ascii_strtoi() skips the leading spaces return false; const char* str = s.c_str(); if (boolalpha_enabled) { // skip spaces. won't do anything in strict mode (we already ruled out spaces there) while (hz::ascii_isspace(*str)) { ++str; } // contains "true" at start, or equals to "true" if strict. if (std::strncmp(str, "true", 4) == 0 && (!strict || str[4] == '\0')) { // str is 0-terminated, so no violation here number = true; return true; } // same for "false" if (std::strncmp(str, "false", 5) == 0 && (!strict || str[5] == '\0')) { number = false; return true; } return false; } char* end = 0; errno = 0; // we use ascii_strtoi here to support of +001, etc... bool tmp = hz::ascii_strtoi(str, &end, 0); // auto-base if ( (strict ? (*end == '\0') : (end != str)) && errno == 0 ) { // if end is 0 byte, then the whole string was parsed number = tmp; return true; } return false; } DEFINE_STRING_IS_NUMERIC(char) DEFINE_STRING_IS_NUMERIC(signed char) DEFINE_STRING_IS_NUMERIC(unsigned char) DEFINE_STRING_IS_NUMERIC(wchar_t) DEFINE_STRING_IS_NUMERIC(short int) DEFINE_STRING_IS_NUMERIC(unsigned short int) DEFINE_STRING_IS_NUMERIC(int) DEFINE_STRING_IS_NUMERIC(unsigned int) DEFINE_STRING_IS_NUMERIC(long int) DEFINE_STRING_IS_NUMERIC(unsigned long int) #if !(defined DISABLE_LL_INT && DISABLE_LL_INT) DEFINE_STRING_IS_NUMERIC(long long int) #endif #if !(defined DISABLE_ULL_INT && DISABLE_ULL_INT) DEFINE_STRING_IS_NUMERIC(unsigned long long int) #endif DEFINE_STRING_IS_NUMERIC(float) DEFINE_STRING_IS_NUMERIC(double) DEFINE_STRING_IS_NUMERIC(long double) #undef DEFINE_STRING_IS_NUMERIC // ------------------------------- number_to_string namespace internal { template::type> struct number_to_string_impl { // static std::string func(T number, int alpha_or_base_or_precision, bool ignored_param) // { // HZ_STATIC_ASSERT(hz::static_false::value, not_a_number); // return std::string(); // } }; // bool spec template struct number_to_string_impl { static std::string func(T number, int boolalpha_enabled, bool ignored_param) { if (boolalpha_enabled) return (number ? "true" : "false"); return (number ? "1" : "0"); } }; // int spec template struct number_to_string_impl { static std::string func(T number, int base, bool ignored_param) { if (number == 0) { if (base == 16) { return "0x" + std::string(sizeof(T) * 2, '0'); // 0 doesn't print as 0x0000, but as 000000. fix that. } else if (base == 8) { // same here, 0 prints as 0. return "00"; // better than simply 0 (at least it's clearly octal). } // base 10 can possibly have some funny formatting, so continue... } std::ostringstream ss; ss.imbue(std::locale::classic()); // make it use classic locale if (base == 16) { // setfill & internal: leading 0s between 0x and XXXX. // setw: e.g. for int32, we need 4*2 (size * 2 chars for byte) + 2 (0x) width. ss << std::setfill('0') << std::internal << std::setw(static_cast((sizeof(T) * 2) + 2)); } ss << std::showbase << std::setbase(base) << number; return ss.str(); } }; // char spec template struct number_to_string_impl { static std::string func(T number, int base, bool ignored_param) { return number_to_string(static_cast(number), base); // long int should be > (u)char } }; // floats spec template struct number_to_string_impl { static std::string func(T number, int precision, bool fixed_prec) { std::ostringstream ss; ss.imbue(std::locale::classic()); // make it use classic locale // without std::fixed, precision is counted as all digits, as opposed to only after comma. if (fixed_prec) ss << std::fixed; ss << std::setprecision(precision) << number; return ss.str(); } }; } // ns internal // public function with 2 parameters template inline std::string number_to_string(T number, int boolalpha_or_base_or_precision, bool fixed_prec) { return internal::number_to_string_impl::func(number, boolalpha_or_base_or_precision, fixed_prec); } // public function - short version with default base / precision template inline std::string number_to_string(T number) { int base = 0; if (type_is_same::value) { base = 1; // alpha (true / false), as opposed to 1 / 0. } else if (type_is_integral::value) { base = 10; // default base - 10 } else if (type_is_floating_point::value) { base = std::numeric_limits::digits10 + 1; // precision. 1 is for sign } // don't use fixed prec here, digits10 is for the whole number return internal::number_to_string_impl::func(number, base, false); } } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/string_num_test.cpp000066400000000000000000000131431417717230600215650ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_unlicense.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz_tests /// \weakgroup hz_tests /// @{ // disable libdebug, we don't link to it #undef HZ_USE_LIBDEBUG #define HZ_USE_LIBDEBUG 0 // enable libdebug emulation through std::cerr #undef HZ_EMULATE_LIBDEBUG #define HZ_EMULATE_LIBDEBUG 1 // The first header should be then one we're testing, to avoid missing // header pitfalls. #include "string_num.h" #include #include #include "cstdint.h" /// Main function for the test int main() { using namespace hz; // int i = 0; // double d = 0; // std::cerr << (string_is_numeric("-1", i) ? "true" : "false"); // std::cerr << ", parsed: i=" << i << "\n"; { int16_t int16_; std::cerr << (string_is_numeric("32768", int16_) ? "true" : "false"); // overflow, false std::cerr << "\n"; uint16_t uint16_; std::cerr << (string_is_numeric("65535", uint16_) ? "true" : "false"); // true std::cerr << "\n"; int32_t int32_; std::cerr << (string_is_numeric(" 1.33", int32_) ? "true" : "false"); // strict, false std::cerr << "\n"; int32_t int32_2; std::cerr << (string_is_numeric("1.33", int32_2) ? "true" : "false"); // strict, false std::cerr << "\n"; uint32_t uint32_; std::cerr << (string_is_numeric("-1", uint32_) ? "true" : "false"); // underflow, false std::cerr << "\n"; int64_t int64_; std::cerr << (string_is_numeric("-1", int64_) ? "true" : "false"); // true std::cerr << "\n"; uint64_t uint64_; std::cerr << (string_is_numeric("1", uint64_) ? "true" : "false"); // true std::cerr << "\n"; int int_; std::cerr << (string_is_numeric("-1", int_) ? "true" : "false"); // true std::cerr << "\n"; unsigned int uint_; std::cerr << (string_is_numeric("-1", uint_) ? "true" : "false"); // underflow, false std::cerr << "\n"; long long_int_; std::cerr << (string_is_numeric("-1", long_int_) ? "true" : "false"); // true std::cerr << "\n"; unsigned long long_uint_; std::cerr << (string_is_numeric("-1", long_uint_) ? "true" : "false"); // underflow, false std::cerr << "\n"; char char_; std::cerr << (string_is_numeric("4", char_) ? "true" : "false"); // true std::cerr << "\n"; unsigned char uchar_; std::cerr << (string_is_numeric("315", uchar_) ? "true" : "false"); // overflow, false std::cerr << "\n"; signed char schar_2; std::cerr << (string_is_numeric("128", schar_2) ? "true" : "false"); // overflow, false std::cerr << "\n"; signed char schar_3; std::cerr << (string_is_numeric("-128", schar_3) ? "true" : "false"); // true std::cerr << "\n"; unsigned char uchar_2; std::cerr << (string_is_numeric("128", uchar_2) ? "true" : "false"); // true std::cerr << "\n"; bool b; std::cerr << (string_is_numeric("true", b) ? "true" : "false"); // true std::cerr << "\n"; } // std::cerr << (string_is_numeric("-1.3", i) ? "true" : "false"); // std::cerr << ", parsed: i=" << i << "\n"; { double d = 0; std::cerr << (string_is_numeric("-1.3", d) ? "true" : "false"); std::cerr << ", parsed: d=" << d << "\n"; std::cerr << (string_is_numeric(" inf", d) ? "true" : "false"); std::cerr << ", parsed: d=" << d << "\n"; std::cerr << (string_is_numeric("infinity", d) ? "true" : "false"); std::cerr << ", parsed: d=" << d << "\n"; std::cerr << (string_is_numeric("NAn", d) ? "true" : "false"); std::cerr << ", parsed: d=" << d << "\n"; std::cerr << (string_is_numeric("3.e+4", d) ? "true" : "false"); std::cerr << ", parsed: d=" << d << "\n"; std::cerr << (string_is_numeric("123 ", d) ? "true" : "false"); std::cerr << ", parsed: d=" << d << "\n"; std::cerr << (string_is_numeric("e+3", d) ? "true" : "false"); std::cerr << ", parsed: d=" << d << "\n"; } { // Note: suffixes are case-insensitive. // Long int and long double are differentiated by '.' in constant. std::cerr << number_to_string(true) << "\n"; // bool std::cerr << number_to_string('a') << "\n"; // char std::cerr << number_to_string(1.L) << "\n"; // long double std::cerr << number_to_string(2L) << "\n"; // long int std::cerr << number_to_string(6) << "\n"; // int #if !(defined DISABLE_LL_INT && DISABLE_LL_INT) std::cerr << number_to_string(3LL) << "\n"; // long long int #endif #if !(defined DISABLE_ULL_INT && DISABLE_ULL_INT) std::cerr << number_to_string(4ULL) << "\n"; // unsigned long long int #endif std::cerr << number_to_string(5.f) << "\n"; // float std::cerr << number_to_string(1.33) << "\n"; // double std::cerr << number_to_string(2.123456789123456789123456789L) << "\n"; std::cerr << number_to_string(std::numeric_limits::quiet_NaN()) << "\n"; std::cerr << number_to_string(std::numeric_limits::signaling_NaN()) << "\n"; std::cerr << number_to_string(std::numeric_limits::infinity()) << "\n"; std::cerr << number_to_string(uint16_t(0x1133), 16) << "\n"; std::cerr << number_to_string(uint32_t(0x00001234), 16) << "\n"; std::cerr << number_to_string(uint64_t(0x00001234), 16) << "\n"; std::cerr << number_to_string(uint16_t(0), 16) << "\n"; std::cerr << number_to_string(uint16_t(0xffff), 8) << "\n"; std::cerr << number_to_string(uint16_t(0), 8) << "\n"; // std::cerr << number_to_string("abc", 8) << "\n"; // std::cerr << number_to_string(uint16_t(0xffff), 2) << "\n"; // won't work // std::cerr << number_to_string(uint16_t(0), 2) << "\n"; } return 0; } /// @} gsmartcontrol-1.1.4/src/hz/string_sprintf.h000066400000000000000000000153771417717230600210740ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2013 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_STRING_SPRINTF_H #define HZ_STRING_SPRINTF_H #include "hz_config.h" // feature macros // Define ENABLE_GLIB to 1 to enable glib string functions (more portable?). #include #include // for stdarg.h, va_start, std::va_list and friends. #if defined ENABLE_GLIB && ENABLE_GLIB #include // g_strdup_vprintf(), g_printf_string_upper_bound() #elif defined HAVE_VASPRINTF && HAVE_VASPRINTF && defined HAVE_ISO_STDIO && HAVE_ISO_STDIO // GNU #include // for stdio.h #include // vasprintf() #include // std::free #elif defined HAVE_ISO_STDIO && HAVE_ISO_STDIO // POSIX, mingw/ISO #include // for stdio.h #include // vsnprintf() #include // vsnprintf() #elif defined HAVE_WIN_SE_FUNCS && HAVE_WIN_SE_FUNCS // msvc 2005 and later #include // for stdio.h #include // vsnprintf_s() #include // vsnprintf_s() #else // mingw/no-ISO, msvc, possibly other old systems #include // for stdio.h #include // _vsnprintf() #include // _vsnprintf() #endif #include "string_sprintf_macros.h" /** \file \brief sprintf()-like formatting to std::string with automatic allocation. \note These functions use system *printf family of functions. \note If using mingw runtime >= 3.15 and __USE_MINGW_ANSI_STDIO, mingw supports both C99/POSIX and msvcrt format specifiers. This includes proper printing of long double, %lld and %llu, etc... This does _not_ affect _snprintf() and similar non-standard functions. Note that you may still get warnings from gcc regarding non-MS format specifiers (see HZ_FUNC_PRINTF_CHECK). \note There types are non-portable (the first one is MS variant, the second one is standard): %I64d, %lld (long long int), %I64u, %llu (unsigned long long int), %f, %Lf (long double; needs casting to double under non-ANSI mingw if using %f). See string_sprintf_macros.h on how to test the feature availability. */ namespace hz { /// Get the buffer size required to safely allocate a buffer (including terminating 0) and call vsnprintf(). inline int string_vsprintf_get_buffer_size(const char* format, std::va_list ap) HZ_FUNC_STRING_SPRINTF_CHECK(1, 0); /// Same as string_vsprintf_get_buffer_size(), but using varargs. inline int string_sprintf_get_buffer_size(const char* format, ...) HZ_FUNC_STRING_SPRINTF_CHECK(1, 2); /// Auto-allocating std::string-returning portable vsprintf inline std::string string_vsprintf(const char* format, std::va_list ap) HZ_FUNC_STRING_SPRINTF_CHECK(1, 0); /// Same as string_vsprintf(), but using varargs inline std::string string_sprintf(const char* format, ...) HZ_FUNC_STRING_SPRINTF_CHECK(1, 2); // ------------------------------------------- Implementation inline int string_vsprintf_get_buffer_size(const char* format, std::va_list ap) { #if defined ENABLE_GLIB && ENABLE_GLIB // the glib version returns gsize return static_cast(g_printf_string_upper_bound(format, ap)); #elif defined HAVE_ISO_STDIO && HAVE_ISO_STDIO // In C99/POSIX, vsnprintf() returns the number of bytes it could have written // (without \0) if buffer was large enough. // This includes mingw/ISO. char c = 0; const int size = vsnprintf(&c, 1, format, ap); // C99 and others if (size < 0) return -1; return size + 1; // that +1 is for \0. #else // Unfortunately, MS (maybe some very old systems as well) return -1 if buffer // is not large enough, so we have to use incremental reallocation until it fits. errno = 0; // reset int buf_size = 32; int ret = -1; do { char* buf = new char[buf_size]; #if defined HAVE_WIN_SE_FUNCS && HAVE_WIN_SE_FUNCS const int size = vsnprintf_s(buf, buf_size, buf_size - 1, format, ap); // writes (buf_size - 1) chars, appends 0. #elif defined HAVE__VSNPRINTF && HAVE__VSNPRINTF // Prefer _vsnprintf() to vsnprintf() as MS may change the latter eventually. const int size = _vsnprintf(buf, buf_size, format, ap); // writes buf_size chars, including 0 (if it fits). #else const int size = vsnprintf(buf, buf_size, format, ap); // assume same as above #endif if (size == -1) { if (errno != 0) { // error break; // -1 } else { // doesn't fit, increase size. buf_size *= 4; // continue } } else if (size <= buf_size) { ret = size + 1; // for 0, as the returned value doesn't contain the 0. break; } else { // huh? break; // -1, treat as error } delete[] buf; } while (true); return ret; #endif } inline int string_sprintf_get_buffer_size(const char* format, ...) { std::va_list ap; va_start(ap, format); int ret = string_vsprintf_get_buffer_size(format, ap); va_end(ap); return ret; } inline std::string string_vsprintf(const char* format, std::va_list ap) { #if defined ENABLE_GLIB && ENABLE_GLIB // there's also g_vasprintf(), but only since glib 2.4. gchar* buf = g_strdup_vprintf(format, ap); std::string ret = (buf ? buf : ""); if (buf) g_free(buf); #elif defined HAVE_VASPRINTF && HAVE_VASPRINTF && defined HAVE_ISO_STDIO && HAVE_ISO_STDIO // We check both vasprintf() and ISO compliance above to ensure that // string_vsnprintf() and string_vsprintf_get_buffer_size() can work with // the same format string. std::string ret; char* str = 0; if (vasprintf(&str, format, ap) > 0) // GNU extension ret = (str ? str : ""); std::free(str); #else std::string ret; int size = string_vsprintf_get_buffer_size(format, ap); if (size > 0) { char* buf = new char[size]; int written = 0; #if defined HAVE_ISO_STDIO && HAVE_ISO_STDIO // we could use vsprintf(), but it's deemed unsafe in many environments. written = vsnprintf(buf, size, format, ap); // writes size chars, including 0. #elif defined HAVE_WIN_SE_FUNCS && HAVE_WIN_SE_FUNCS written = vsnprintf_s(buf, size, size - 1, format, ap); // writes (size - 1) chars, appends 0. #elif defined HAVE__VSNPRINTF && HAVE__VSNPRINTF written = _vsnprintf(buf, size, format, ap); // writes size chars, including 0. #else // Same as the first one, to ensure that the order matches that of string_vsprintf_get_buffer_size(). written = vsnprintf(buf, size, format, ap); // writes size chars, including 0. #endif if (written > 0) ret = buf; delete[] buf; } #endif return ret; } inline std::string string_sprintf(const char* format, ...) { std::va_list ap; va_start(ap, format); std::string ret = string_vsprintf(format, ap); va_end(ap); return ret; } } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/string_sprintf_macros.h000066400000000000000000000041141417717230600224230ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_STRING_SPRINTF_MACROS_H #define HZ_STRING_SPRINTF_MACROS_H #include "hz_config.h" // feature macros #include "system_specific.h" // HZ_FUNC_PRINTF_*_CHECK /** \file This is a helper file for string_sprintf.h. It is useful if you need the macros only, but not the implementation (useful for header files). */ /// \def HAVE_STRING_SPRINTF_MS /// whether the MS (%I64d and %I64u) specifiers are supported. /// \def HAVE_STRING_SPRINTF_ISO /// whether the ISO (%lld, %llu, %Lf) specifiers are supported. #if defined ENABLE_GLIB && ENABLE_GLIB #define HAVE_STRING_SPRINTF_MS 0 #define HAVE_STRING_SPRINTF_ISO 1 #elif defined HAVE_ISO_STDIO && HAVE_ISO_STDIO // mingw/ISO has both the MS and ISO specifiers #if defined _WIN32 && defined __GNUC__ #define HAVE_STRING_SPRINTF_MS 1 #else #define HAVE_STRING_SPRINTF_MS 0 #endif #define HAVE_STRING_SPRINTF_ISO 1 #elif defined HAVE_WIN_SE_FUNCS && HAVE_WIN_SE_FUNCS #define HAVE_STRING_SPRINTF_MS 1 #define HAVE_STRING_SPRINTF_ISO 0 #elif defined HAVE__VSNPRINTF && HAVE__VSNPRINTF #define HAVE_STRING_SPRINTF_MS 0 #define HAVE_STRING_SPRINTF_ISO 1 #else #warning Cannot detect vsnprintf() availability #define HAVE_STRING_SPRINTF_MS 0 #define HAVE_STRING_SPRINTF_ISO 0 #endif // HZ_FUNC_STRING_SPRINTF_CHECK - gcc printf format attribute checker, // configured according to MS or ISO features (prefers ISO over MS). #if HAVE_STRING_SPRINTF_ISO #define HZ_FUNC_STRING_SPRINTF_CHECK(format_idx, check_idx) HZ_FUNC_PRINTF_ISO_CHECK(format_idx, check_idx) #elif HAVE_STRING_SPRINTF_MS #define HZ_FUNC_STRING_SPRINTF_CHECK(format_idx, check_idx) HZ_FUNC_PRINTF_MS_CHECK(format_idx, check_idx) #else #define HZ_FUNC_STRING_SPRINTF_CHECK(format_idx, check_idx) #endif #endif /// @} gsmartcontrol-1.1.4/src/hz/string_wcmatch.h000066400000000000000000000221501417717230600210200ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 1989, 1993, 1994 The Regents of the University of California (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_bsd-ucb.txt file ***************************************************************************/ /// \file /// \author Guido van Rossum /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_STRING_WCMATCH_H #define HZ_STRING_WCMATCH_H #include "hz_config.h" // feature macros /** \file Shell-like pattern matching (wildcards). Parts of this file are derived from Sudo's fnmatch(). The original copyright header follows: * Copyright (c) 1989, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Guido van Rossum. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include // std::tolower #include // std::strchr namespace hz { namespace internal { // internal constants. HZ_ prefix is needed to avoid conflicts with libc version. const int HZ_FNM_NOMATCH = 1; /* Match failed. */ const int HZ_FNM_NOESCAPE = 0x01; /* Disable backslash escaping. */ const int HZ_FNM_PATHNAME = 0x02; /* Slash must be matched by slash. */ const int HZ_FNM_PERIOD = 0x04; /* Period must be matched by period. */ const int HZ_FNM_LEADING_DIR = 0x08; /* Ignore / after Imatch. */ const int HZ_FNM_CASEFOLD = 0x10; /* Case insensitive search. */ const int HZ_FNM_IGNORECASE = HZ_FNM_CASEFOLD; const int HZ_FNM_FILE_NAME= HZ_FNM_PATHNAME; #define HZ_WC_ISSET(t, f) ((t) & (f)) enum wc_range_status_t { HZ_RANGE_MATCH = 1, HZ_RANGE_NOMATCH = 0, HZ_RANGE_ERROR = -1 }; /// Helper function inline wc_range_status_t wc_rangematch(const char* pattern, char test, int flags, const char** newp) { int negate = 0, ok = 0; char c = 0, c2 = 0; /* * A bracket expression starting with an unquoted circumflex * character produces unspecified results (IEEE 1003.2-1992, * 3.13.2). This implementation treats it like '!', for * consistency with the regular expression syntax. * J.T. Conklin (conklin@ngai.kaleida.com) */ if ((negate = (*pattern == '!' || *pattern == '^'))) ++pattern; if (HZ_WC_ISSET(flags, HZ_FNM_CASEFOLD)) test = static_cast(std::tolower(static_cast(test))); /* * A right bracket shall lose its special meaning and represent * itself in a bracket expression if it occurs first in the list. * -- POSIX.2 2.8.3.2 */ c = *pattern++; do { if (c == '\\' && !HZ_WC_ISSET(flags, HZ_FNM_NOESCAPE)) c = *pattern++; if (c == '\0') return (HZ_RANGE_ERROR); if (c == '/' && HZ_WC_ISSET(flags, HZ_FNM_PATHNAME)) return (HZ_RANGE_NOMATCH); if (HZ_WC_ISSET(flags, HZ_FNM_CASEFOLD)) c = static_cast(std::tolower(static_cast(c))); if (*pattern == '-' && (c2 = *(pattern+1)) != '\0' && c2 != ']') { pattern += 2; if (c2 == '\\' && !HZ_WC_ISSET(flags, HZ_FNM_NOESCAPE)) c2 = *pattern++; if (c2 == '\0') return (HZ_RANGE_ERROR); if (HZ_WC_ISSET(flags, HZ_FNM_CASEFOLD)) c2 = static_cast(std::tolower((unsigned char)c2)); if (c <= test && test <= c2) ok = 1; } else if (c == test) { ok = 1; } } while ((c = *pattern++) != ']'); *newp = pattern; return (ok == negate ? HZ_RANGE_NOMATCH : HZ_RANGE_MATCH); } /** * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6. * Compares a filename or pathname to a pattern. */ inline int wc_fnmatch(const char* pattern, const char* str, int flags) { const char* strstart = 0; char c = 0, test = 0; for (strstart = str;;) { switch (c = *pattern++) { case '\0': if (HZ_WC_ISSET(flags, HZ_FNM_LEADING_DIR) && *str == '/') return (0); return (*str == '\0' ? 0 : HZ_FNM_NOMATCH); case '?': if (*str == '\0') return (HZ_FNM_NOMATCH); if (*str == '/' && HZ_WC_ISSET(flags, HZ_FNM_PATHNAME)) return (HZ_FNM_NOMATCH); if (*str == '.' && HZ_WC_ISSET(flags, HZ_FNM_PERIOD) && (str == strstart || (HZ_WC_ISSET(flags, HZ_FNM_PATHNAME) && *(str - 1) == '/'))) { return (HZ_FNM_NOMATCH); } ++str; break; case '*': c = *pattern; /* Collapse multiple stars. */ while (c == '*') c = *++pattern; if (*str == '.' && HZ_WC_ISSET(flags, HZ_FNM_PERIOD) && (str == strstart || (HZ_WC_ISSET(flags, HZ_FNM_PATHNAME) && *(str - 1) == '/'))) return (HZ_FNM_NOMATCH); /* Optimize for pattern with * at end or before /. */ if (c == '\0') { if (HZ_WC_ISSET(flags, HZ_FNM_PATHNAME)) { return (HZ_WC_ISSET(flags, HZ_FNM_LEADING_DIR) || std::strchr(str, '/') == NULL ? 0 : HZ_FNM_NOMATCH); } else { return (0); } } else if (c == '/' && HZ_WC_ISSET(flags, HZ_FNM_PATHNAME)) { if ((str = std::strchr(str, '/')) == NULL) return (HZ_FNM_NOMATCH); break; } /* General case, use recursion. */ while ((test = *str) != '\0') { if (!wc_fnmatch(pattern, str, flags & ~HZ_FNM_PERIOD)) return (0); if (test == '/' && HZ_WC_ISSET(flags, HZ_FNM_PATHNAME)) break; ++str; } return (HZ_FNM_NOMATCH); case '[': { if (*str == '\0') return (HZ_FNM_NOMATCH); if (*str == '/' && HZ_WC_ISSET(flags, HZ_FNM_PATHNAME)) return (HZ_FNM_NOMATCH); if (*str == '.' && HZ_WC_ISSET(flags, HZ_FNM_PERIOD) && (str == strstart || (HZ_WC_ISSET(flags, HZ_FNM_PATHNAME) && *(str - 1) == '/'))) { return (HZ_FNM_NOMATCH); } const char* newp = 0; switch (wc_rangematch(pattern, *str, flags, &newp)) { case HZ_RANGE_ERROR: /* not a good range, treat as normal text */ goto normal; case HZ_RANGE_MATCH: pattern = newp; break; case HZ_RANGE_NOMATCH: return (HZ_FNM_NOMATCH); } ++str; break; } case '\\': if (!HZ_WC_ISSET(flags, HZ_FNM_NOESCAPE)) { if ((c = *pattern++) == '\0') { c = '\\'; --pattern; } } /* FALLTHROUGH */ default: normal: // goto label if (c != *str && !(HZ_WC_ISSET(flags, HZ_FNM_CASEFOLD) && (std::tolower(static_cast(c)) == std::tolower(static_cast(*str))))) return (HZ_FNM_NOMATCH); ++str; break; } } /* NOTREACHED */ return (HZ_FNM_NOMATCH); // just in case } #undef HZ_WC_ISSET /// Return true if pattern has any glob chars in it inline bool wc_fnmatch_test(const char* pattern) { bool in_bracket = false; while (*pattern) { switch (*pattern) { case '?': case '*': return true; case '\\': if (*pattern++ == '\0') return false; break; case '[': // a string containing '[' is a glob only if it has a matching ']' in_bracket = true; break; case ']': if (in_bracket) return true; break; } ++pattern; } return false; } } // ns internal // -------------- std::string-style API /// Check whether str matches pattern, which is a shell wildcard. /// Supported special symbols are *, ?, [ ], and backslash (for escaping these). /// On non-match or failure false is returned; otherwise, true. inline bool string_wcmatch(const std::string& pattern, const std::string& str) { return (hz::internal::wc_fnmatch(pattern.c_str(), str.c_str(), internal::HZ_FNM_PATHNAME | internal::HZ_FNM_PERIOD) == 0); } /// Test whether pattern contains any glob characters in it. inline bool string_is_wc_pattern(const std::string& pattern) { return static_cast(hz::internal::wc_fnmatch_test(pattern.c_str())); } } // ns hz #endif /// @} gsmartcontrol-1.1.4/src/hz/string_wcmatch_test.cpp000066400000000000000000000024261417717230600224160ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_unlicense.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz_tests /// \weakgroup hz_tests /// @{ // disable libdebug, we don't link to it #undef HZ_USE_LIBDEBUG #define HZ_USE_LIBDEBUG 0 // enable libdebug emulation through std::cerr #undef HZ_EMULATE_LIBDEBUG #define HZ_EMULATE_LIBDEBUG 1 // The first header should be then one we're testing, to avoid missing // header pitfalls. #include "string_wcmatch.h" #include #include /// Main function for the test int main() { std::cerr << std::boolalpha << hz::string_wcmatch("*aa", "21345") << "\n"; // false std::cerr << std::boolalpha << hz::string_wcmatch("*aa", "21345aaa") << "\n"; // true std::cerr << std::boolalpha << hz::string_wcmatch("2??45", "21345") << "\n"; // true std::cerr << std::boolalpha << hz::string_wcmatch("*a*", "abcd") << "\n"; // true std::cerr << std::boolalpha << hz::string_wcmatch("[123]45", "245") << "\n"; // true std::cerr << std::boolalpha << hz::string_wcmatch("\\*aa", "aaa") << "\n"; // false return 0; } /// @} gsmartcontrol-1.1.4/src/hz/sync.h000066400000000000000000000463471417717230600167760ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_SYNC_H #define HZ_SYNC_H #include "hz_config.h" // feature macros #include #include #include "noncopyable.h" #include "exceptions.h" // THROW_FATAL #include "sync_part_get_policy.h" // internal header, SyncGetPolicy struct. /** \file Threading policies providing synchronization primitives through wrapping existing thread libraries. Note that this is NOT a full-blown threading library. It merely allows the library writers to make their libraries thread-safe by using these primitives. Use SyncPolicyNone for "doing nothing" and SyncPolicyMtDefault for default MT policy. The default is controlled through HZ_SYNC_DEFAULT_POLICY_* macros (see below). ------------------------------ Policy API ------------------------------ Every policy class provides: Mutex Types: - Mutex type. Possibly a wrapper around native mutex type. May or may not be recursive. No guaranteed methods. Guaranteed freeing of resource when destroyed. - NativeMutex type. The underlying mutex type, if available. If not available, it's typedefed as Mutex. No guaranteed methods. Don't declare objects of this type - it is there solely for interaction with existing native mutexes. - RecMutex type. Same as Mutex, but recursive. No guaranteed methods. Guaranteed freeing of resource when destroyed. - NativeRecMutex type. Same as NativeMutex, but recursive. No guaranteed methods. - RWMutex type. A mutex which supports read / write locking. No guaranteed methods. Guaranteed freeing of resource when destroyed. Note: There is no recursion or lock escalation with RWLock. That is, locking it multiple times (read or write), or write-locking while holding a read-lock may work in some situations with some backends, but can also cause a deadlock. - NativeRWMutex type. A cross between NativeMutex and RWMutex. The Native types are needed to give an explicit guarantee that these types are lockable via respective ScopedLock classes and static methods. Static Methods: bool init()\n Initialize the thread backend. The application MUST call this function prior to locking any mutexes from this policy. True is returned in case the backend is initialized successfully, or has been initialized already. Note that with some backends, failure cannot be trapped and the program may abort. If failure condition is detected, false is returned and sync_resource_error exception is thrown. The following static methods accept any mutex type declared above (including Native types). If preconditions aren't met, their behaviour is completely undefined (silent failure, hang, thrown exception - any one of these may be observed). In some cases, errors may be detected (not guaranteed though). If an error is detected, sync_resource_error exception is thrown. The reason for this is that the system is already in unstable state, so throwing a fatal exception seems to be the right choice. void lock(MutexType& m)\n Preconditions: m must not be locked by current thread. Locks the mutex via the mutex's defined locking facilities. This function will block until m is available for locking. bool trylock(MutexType& m)\n Preconditions: m must not be locked by current thread. Tries to lock the mutex via the mutex's defined locking facilities. This function will return false if lock could not be obtained, and true otherwise. void unlock(MutexType& m)\n Preconditions: m must be locked by current thread. Unlocks the previously locked mutex immediately. void lock(MutexType& m, bool for_write = false)\n bool trylock(MutexType& m, bool for_write = false)\n void unlock(MutexType& m, bool for_write = false)\n Same as above, but for RWMutex and NativeRWMutex types. The second parameter must be equal for any lock / unlock and trylock / unlock pair. If not, the behaviour is undefined. Lock Object Types: Caution: These types are NOT thread-safe, and they aren't meant to be passed around between threads. Note: We do not provide non-template typedefed names for these, because when dealing with unknown mutex types, the user cannot reliably determine the needed lock type. For example, the user cannot know if the supplied mutex is a simple mutex, or native recursive one. That's why it's best to avoid specific lock types altogether and use the templated ones. It's easy enough for the users to typedef them themselves if such need arises. In case of error, sync_resource_error is thrown. Note that this exception may be thrown from constructor. In that case, destruction is still necessary. template\\n class GenericScopedLock\n Constructor:\n GenericScopedLock(MutexType& mutex, bool do_lock = true)\n Guaranteed to accept Mutex, NativeMutex, RecMutex, NativeRecMutex types. If do_lock is true, locks mutex when constructed and unlocks when destroyed. If do_lock is false, no operations are performed. Uses policy's static methods lock() and unlock() for doing it. template\\n class GenericScopedTryLock\n Constructor:\n GenericScopedTryLock(MutexType& mutex, bool do_lock = true)\n Guaranteed to accept Mutex, NativeMutex, RecMutex, NativeRecMutex types. Behaves like ScopedTryLock, but provides an additional bool() operator, which returns false if do_lock was true and locking couldn't be performed. Also provides retry() method, which retries the operation (the lock must not be obtained at that point yet). Uses policy's static methods trylock() and unlock() for doing it. class GenericScopedRecLock, class GenericScopedRecTryLock\n There are no such types, use GenericScopedLock and GenericScopedTryLock instead. They accept recursive mutex types too. This is done to avoid requiring knowledge of the exact mutex type (non-recursive or recursive, non-native or native) when locking them. template\\n class GenericScopedRWLock\n Constructor:\n ScopedRWLock(MutexType& mutex, bool for_write = false, bool do_lock = true)\n Guaranteed to accept RWMutex, NativeRWMutex types. Behaves similarily to ScopedLock, but works with read / write mutexes instead. template\\n class GenericScopedRWTryLock\n Constructor:\n ScopedRWTryLock(MutexType& mutex, bool for_write = false, bool do_lock = true)\n A cross between ScopedRWLock and ScopedTryLock. ScopedLock, ScopedTryLock, ScopedNativeLock, ScopedNativeTryLock, ScopedRecLock, ScopedRecTryLock, ScopedNativeRecLock, ScopedNativeRecTryLock, ScopedRWLock, ScopedRWTryLock, ScopedNativeRWLock, ScopedNativeRWTryLock\n These are typedefs for respective (mutex, generic scoped lock) pairs. Out-of-policy Class templates: template\\n struct SyncGetPolicy { typedef unspecified_policy_type type; };\n This struct provides a way to retrieve a policy class type by supplying mutex type. Any mutex type is supported, provided that such mutex type exists in a policy somewhere. For example, to do a locking in a function template where the exact policy or mutex type is unknown, one could write: \code template void f(MutexType& m) { typename SyncGetPolicy::type::ScopedLock lock(m); // ... // lock is released at the end of scope. } \endcode This also provides safe defaults for class templates which accept mutexes as their template parameters, for example: \code template::type> class C { // use Policy::ScopedLock, etc... }; \endcode */ namespace hz { /// This is thrown in case of really bad problems struct sync_resource_error : virtual public std::exception { // from /// Constructor sync_resource_error(const std::string& why) : why_(why) { } /// Virtual destructor virtual ~sync_resource_error() throw() { } // Reimplemented from std::exception virtual const char* what() const throw() { msg = "hz::sync_resource_error: " + why_; return msg.c_str(); } // yes, they should not be strings, to avoid memory allocations. // no, I don't know how to return const char* without memory allocations. const std::string why_; ///< The reason. mutable std::string msg; ///< The message (used in implementation of what()). This must be a member to avoid its destruction on function call return. use what(). }; // -------------------------------- Helpers // This class provides commonly used classes for policies. template struct SyncScopedLockProvider { template class GenericScopedLock : public hz::noncopyable { public: GenericScopedLock(MutexType& mutex, bool do_lock = true) : mutex_(mutex), do_lock_(do_lock) { if (do_lock_) Policy::lock(mutex_); } ~GenericScopedLock() { if (do_lock_) Policy::unlock(mutex_); } private: MutexType& mutex_; bool do_lock_; }; template class GenericScopedTryLock : public hz::noncopyable { private: // Pointer to member function, used in bool() overloading. // We use this instead of bool to avoid its promotion to integer. // Pointers aren't promoted. typedef bool (GenericScopedTryLock::*unspecified_bool_type)() const; public: GenericScopedTryLock(MutexType& mutex, bool do_lock = true) : mutex_(mutex), do_lock_(do_lock), locked_(false) { if (do_lock_) locked_ = Policy::trylock(mutex_); } ~GenericScopedTryLock() { if (do_lock_ && locked_) Policy::unlock(mutex_); } // for repeating the lock request bool retry() { if (locked_) { THROW_FATAL(sync_resource_error("GenericScopedTryLock::trylock(): Attempting to lock an already locked mutex.")); return false; } if (do_lock_) return (locked_= Policy::trylock(mutex_)); return true; // it's always success if do_lock was false. } // This returns whether the operation could not be considered a success. // Not strictly required for bool conversion, but nice to have anyway. bool operator!() const { return !do_lock_ || locked_; // if we're not locking, or if we're locking but it's not locked. } // aka operator bool(). operator unspecified_bool_type() const { // in this case, &operator!, being a valid pointer-to-member-function, // serves as a "true" value, while 0 (being a null-pointer) serves as false. return (operator!() ? &GenericScopedTryLock::operator! : 0); } private: MutexType& mutex_; bool do_lock_; bool locked_; }; template class GenericScopedRWLock : public hz::noncopyable { public: GenericScopedRWLock(MutexType& mutex, bool for_write = false, bool do_lock = true) : mutex_(mutex), do_lock_(do_lock), for_write_(for_write) { if (do_lock_) Policy::lock(mutex_, for_write_); } ~GenericScopedRWLock() { if (do_lock_) Policy::unlock(mutex_, for_write_); } private: MutexType& mutex_; bool do_lock_; bool for_write_; }; template class GenericScopedRWTryLock : public hz::noncopyable { private: typedef bool (GenericScopedRWTryLock::*unspecified_bool_type)() const; public: GenericScopedRWTryLock(MutexType& mutex, bool for_write = false, bool do_lock = true) : mutex_(mutex), do_lock_(do_lock), for_write_(for_write), locked_(false) { if (do_lock_) locked_ = Policy::trylock(mutex_, for_write); } ~GenericScopedRWTryLock() { if (do_lock_ && locked_) Policy::unlock(mutex_, for_write_); } // for repeating the lock request with the same options as in constructor bool retry() { if (locked_) // it's a fatal error, so no point in returning anything. THROW_FATAL(sync_resource_error("GenericScopedRWTryLock::trylock(): Attempting to lock an already locked mutex.")); if (do_lock_) return (locked_= Policy::trylock(mutex_, for_write_)); return true; // it's always success if do_lock was false. } // This returns whether the operation could not be considered a success. // Not strictly required for bool conversion, but nice to have anyway. bool operator!() const { return do_lock_ && !locked_; } // aka operator bool(). operator unspecified_bool_type() const { // in this case, &operator!, being a valid pointer-to-member-function, // serves as a "true" value, while 0 (being a null-pointer) serves as false. return (operator!() ? 0 : &GenericScopedRWTryLock::operator!); } private: MutexType& mutex_; bool do_lock_; bool for_write_; bool locked_; }; }; /// A type useful for dummy mutexes, etc... /// TypeChanger may be used to generate different (non-inter-convertible) /// types, for e.g. function overloading. template struct SyncEmptyType { }; /// Classes may use this in single-threaded or non-locking environments. struct SyncPolicyNone { // We use different types here to avoid user errors such as mixing // types when developing with single-threaded version. typedef SyncEmptyType<1> Mutex; typedef SyncEmptyType<2> NativeMutex; typedef SyncEmptyType<3> RecMutex; typedef SyncEmptyType<4> NativeRecMutex; typedef SyncEmptyType<5> RWMutex; typedef SyncEmptyType<6> NativeRWMutex; static bool init() { return true; } static void lock(Mutex& m) { } static bool trylock(Mutex& m) { return true; } // behave as it was successful static void unlock(Mutex& m) { } static void lock(NativeMutex& m) { } static bool trylock(NativeMutex& m) { return true; } static void unlock(NativeMutex& m) { } static void lock(RecMutex& m) { } static bool trylock(RecMutex& m) { return true; } static void unlock(RecMutex& m) { } static void lock(NativeRecMutex& m) { } static bool trylock(NativeRecMutex& m) { return true; } static void unlock(NativeRecMutex& m) { } static void lock(RWMutex& m, bool for_write = false) { } static bool trylock(RWMutex& m, bool for_write = false) { return true; } static void unlock(RWMutex& m, bool for_write = false) { } static void lock(NativeRWMutex& m, bool for_write = false) { } static bool trylock(NativeRWMutex& m, bool for_write = false) { return true; } static void unlock(NativeRWMutex& m, bool for_write = false) { } /// Dummy scoped lock, does absolutely nothing. Works with all the mutex types. template class GenericScopedLock : public hz::noncopyable { public: GenericScopedLock(MutexType& mutex, bool do_lock = true) { } ~GenericScopedLock() { } }; template class GenericScopedTryLock : public hz::noncopyable { private: typedef bool (GenericScopedTryLock::*unspecified_bool_type)() const; public: GenericScopedTryLock(MutexType& mutex, bool do_lock = true) : do_lock_(do_lock) { } ~GenericScopedTryLock() { } bool retry() { if (do_lock_) // assume that constructor locked it successfully (api-wise). THROW_FATAL(sync_resource_error("GenericScopedTryLock::trylock(): Attempting to lock an already locked mutex.")); return true; } bool operator!() const { return false; // always success, which translates to false for this operator. } operator unspecified_bool_type() const { // in this case, &operator!, being a valid pointer-to-member-function, // serves as a "true" value, while 0 (being a null-pointer) serves as false. return &GenericScopedTryLock::operator!; // always success } private: bool do_lock_; }; template class GenericScopedRWLock : public hz::noncopyable { public: GenericScopedRWLock(MutexType& mutex, bool for_write = false, bool do_lock = true) { } ~GenericScopedRWLock() { } }; template class GenericScopedRWTryLock : public hz::noncopyable { private: typedef bool (GenericScopedRWTryLock::*unspecified_bool_type)() const; public: GenericScopedRWTryLock(MutexType& mutex, bool for_write = false, bool do_lock = true) : do_lock_(do_lock) { } ~GenericScopedRWTryLock() { } bool retry() { if (do_lock_) // assume that constructor locked it successfully (api-wise). THROW_FATAL(sync_resource_error("GenericScopedRWTryLock::trylock(): Attempting to lock an already locked mutex.")); return true; } bool operator!() const { return false; // always success, which translates to false for this operator. } operator unspecified_bool_type() const { // in this case, &operator!, being a valid pointer-to-member-function, // serves as a "true" value, while 0 (being a null-pointer) serves as false. return &GenericScopedRWTryLock::operator!; // always success } private: bool do_lock_; }; typedef GenericScopedLock ScopedLock; typedef GenericScopedTryLock ScopedTryLock; typedef GenericScopedLock ScopedNativeLock; typedef GenericScopedTryLock ScopedNativeTryLock; typedef GenericScopedLock ScopedRecLock; typedef GenericScopedTryLock ScopedRecTryLock; typedef GenericScopedLock ScopedNativeRecLock; typedef GenericScopedTryLock ScopedNativeRecTryLock; typedef GenericScopedRWLock ScopedRWLock; typedef GenericScopedRWTryLock ScopedRWTryLock; typedef GenericScopedRWLock ScopedNativeRWLock; typedef GenericScopedRWTryLock ScopedNativeRWTryLock; }; // mutex -> policy template<> struct SyncGetPolicy { typedef SyncPolicyNone type; }; template<> struct SyncGetPolicy { typedef SyncPolicyNone type; }; template<> struct SyncGetPolicy { typedef SyncPolicyNone type; }; template<> struct SyncGetPolicy { typedef SyncPolicyNone type; }; template<> struct SyncGetPolicy { typedef SyncPolicyNone type; }; template<> struct SyncGetPolicy { typedef SyncPolicyNone type; }; } // ns #if defined HZ_SYNC_DEFAULT_POLICY_GLIBMM #include "sync_policy_glibmm.h" namespace hz { typedef SyncPolicyGlibmm SyncPolicyMtDefault; } #elif defined HZ_SYNC_DEFAULT_POLICY_GLIB #include "sync_policy_glib.h" namespace hz { typedef SyncPolicyGlib SyncPolicyMtDefault; } #elif defined HZ_SYNC_DEFAULT_POLICY_BOOST #include "sync_policy_boost.h" namespace hz { typedef SyncPolicyBoost SyncPolicyMtDefault; } #elif defined HZ_SYNC_DEFAULT_POLICY_POCO #include "sync_policy_poco.h" namespace hz { typedef SyncPolicyPoco SyncPolicyMtDefault; } #elif defined HZ_SYNC_DEFAULT_POLICY_PTHREAD #include "sync_policy_pthread.h" namespace hz { typedef SyncPolicyPthread SyncPolicyMtDefault; } #elif defined HZ_SYNC_DEFAULT_POLICY_WIN32 #include "sync_policy_win32.h" namespace hz { typedef SyncPolicyWin32 SyncPolicyMtDefault; } #else // default: NONE // #elif defined HZ_SYNC_DEFAULT_POLICY_NONE // default: namespace hz { typedef SyncPolicyNone SyncPolicyMtDefault; } #endif #endif /// @} gsmartcontrol-1.1.4/src/hz/sync_lock_ptr.h000066400000000000000000000103161417717230600206560ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_SYNC_LOCK_PTR_H #define HZ_SYNC_LOCK_PTR_H #include "hz_config.h" // feature macros #include "type_properties.h" // type_* #include "intrusive_ptr.h" namespace hz { namespace internal { /// Generic data holder to allow references and pointers only. template struct sync_lock_ptr_data { }; /// Reference specialization template struct sync_lock_ptr_data : public hz::intrusive_ptr_referenced { typedef Obj ref_type; typedef typename type_add_const::type const_ref_type; typedef typename hz::type_remove_reference::type* ptr_type; typedef typename hz::type_add_const::type const_ptr_type; sync_lock_ptr_data(Obj o, ScopedLock* l) : obj(o), lock(l) { } ~sync_lock_ptr_data() { release_lock(); } void release_lock() { delete lock; lock = 0; } ptr_type operator->() { return &obj; } const_ptr_type operator->() const { return &obj; } ref_type operator*() { return obj; } const_ref_type operator*() const { return obj; } Obj obj; ScopedLock* lock; }; /// Pointer specialization template struct sync_lock_ptr_data : public hz::intrusive_ptr_referenced { typedef typename hz::type_add_reference::type>::type ref_type; typedef typename type_add_const::type const_ref_type; typedef Obj ptr_type; typedef typename hz::type_add_const::type const_ptr_type; sync_lock_ptr_data(Obj o, ScopedLock* l) : obj(o), lock(l) { } ~sync_lock_ptr_data() { release_lock(); } void release_lock() { delete lock; lock = 0; } ptr_type operator->() { return obj; } const_ptr_type operator->() const { return obj; } ref_type operator*() { return *obj; } const_ref_type operator*() const { return *obj; } Obj obj; ScopedLock* lock; }; } /// This is a reference-counting smart pointer which: /// - Accepts an object reference or pointer and scoped lock pointer pair. /// - Overloads operator->() to access the object. /// - Releases the scoped lock (via delete) when it dies. /// /// This allows you to return a locked sync_lock_ptr from /// functions which would return Object& had there been no locking. /// As soon as last copy of that is destroyed (out of scope, etc...), /// the lock is released. template class sync_lock_ptr { private: typedef internal::sync_lock_ptr_data data_type; ///< Implementation type typedef bool (sync_lock_ptr::*unspecified_bool_type)() const; ///< Bool conversion helper public: /// Constructor sync_lock_ptr(Obj o, ScopedLock* lock) : data_(new data_type(o, lock)) { } /// Copy constructor sync_lock_ptr(const sync_lock_ptr& other) : data_(other.data_) { } /// Assignment operator sync_lock_ptr& operator=(const sync_lock_ptr& other) { this->data_ = other.data_; // copy intrusive_ptr return *this; } /// Release the lock void release_lock() { if (data_) (*data_).release_lock(); } /// Get a copy of the object Obj get() const { return (*data_).obj; } /// Arrow operator typename data_type::ptr_type operator->() const { return (*data_).operator->(); } /// Dereference operator typename data_type::ref_type operator*() const { return (*data_).operator*(); } /// Null pointer check operator operator unspecified_bool_type() const { // in this case, &operator!, being a valid pointer-to-member-function, // serves as a "true" value, while 0 (being a null-pointer) serves as false. return (operator->() ? &sync_lock_ptr::operator-> : 0); } private: hz::intrusive_ptr data_; ///< The data (the object and the lock pointer) }; } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/sync_multilock.h000066400000000000000000000772111417717230600210530ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_SYNC_MULTILOCK_H #define HZ_SYNC_MULTILOCK_H #include "hz_config.h" // feature macros #include // std::size_t #include "common_types.h" // NullType #include "local_algo.h" // shell_sort #include "sync_part_get_policy.h" // internal header, SyncGetPolicy struct. #include "system_specific.h" // HZ_GCC_CHECK_VERSION namespace hz { /** \file This is a mutex order-based multi-locking facility. This link: http://lists.boost.org/Archives/boost/2008/02/132853.php has an interesting discussion about several approaches to multi-lock. However, the faster the method, the more details it uses from an underlying implementation. In such light, the most generic (straightforward) seems to be the order-based one. If the user requires a very fast method, he/she should roll out their own multi-lock implementation, depending on their needs. */ // TODO: // SyncMultiTryLock (.status() returns 0-based index of first failed lock, or -1 on success, as in boost), // SyncMultiTryLock should unlock the locked mutexes in case of exception during locking. // SyncMultiTryLockUniType, and SyncMultiLockEmpty variants. /// A helper internal struct struct MultiLockNullPolicy // used only in typedef below { }; /// Specialization for SyncGetPolicy on NullType, to avoid errors. template<> struct SyncGetPolicy { typedef MultiLockNullPolicy type; }; namespace internal { /// Helper class: index / mutex pointer holder struct MultiLockPair { /// Constructor MultiLockPair() : m(0), index(0) // for default construction { } /// Constructor MultiLockPair(void* m_, int index_) : m(m_), index(index_) { } void* m; ///< mutex int index; ///< Index, 1-based }; /// Comparison operator, compares by address inline bool operator< (const MultiLockPair& m1, const MultiLockPair& m2) { return m1.m < m2.m; // compare by address } /// Base class for SyncMultiLock. It provides mutex member, /// and lock/unlock facility. Its specialization for NullType /// should be eliminated through empty-base-optimization by compiler. template class MultiLockBase { protected: /// Constructor MultiLockBase(Mutex* m) : mutex(m) { } /// Lock the mutex void lock() { LockPolicy::lock(*mutex); } /// Try locking the mutex bool trylock() { return LockPolicy::trylock(*mutex); } /// Unlock the mutex void unlock() { LockPolicy::unlock(*mutex); } private: Mutex* mutex; ///< The mutex }; /// Specialization for NullType, to disable all activity. template class MultiLockBase { protected: /// Dummy implementation void lock() { } /// Dummy implementation bool trylock() { return true; // no errors here } /// Dummy implementation void unlock() { } }; } /// SyncMultiLock<> is a scoped lock supporting multiple mutexes (up to 10), /// locking them always in the same order. /// Note: Mutexes don't have to be of the same type. template< class Mutex1, class Mutex2 = NullType, class Mutex3 = NullType, class Mutex4 = NullType, class Mutex5 = NullType, class Mutex6 = NullType, class Mutex7 = NullType, class Mutex8 = NullType, class Mutex9 = NullType, class Mutex10 = NullType, class LockPolicy1 = typename SyncGetPolicy::type, class LockPolicy2 = typename SyncGetPolicy::type, class LockPolicy3 = typename SyncGetPolicy::type, class LockPolicy4 = typename SyncGetPolicy::type, class LockPolicy5 = typename SyncGetPolicy::type, class LockPolicy6 = typename SyncGetPolicy::type, class LockPolicy7 = typename SyncGetPolicy::type, class LockPolicy8 = typename SyncGetPolicy::type, class LockPolicy9 = typename SyncGetPolicy::type, class LockPolicy10 = typename SyncGetPolicy::type > class SyncMultiLock : public internal::MultiLockBase<1, Mutex1, LockPolicy1>, public internal::MultiLockBase<2, Mutex2, LockPolicy2>, public internal::MultiLockBase<3, Mutex3, LockPolicy3>, public internal::MultiLockBase<4, Mutex4, LockPolicy4>, public internal::MultiLockBase<5, Mutex5, LockPolicy5>, public internal::MultiLockBase<6, Mutex6, LockPolicy6>, public internal::MultiLockBase<7, Mutex7, LockPolicy7>, public internal::MultiLockBase<8, Mutex8, LockPolicy8>, public internal::MultiLockBase<9, Mutex9, LockPolicy9>, public internal::MultiLockBase<10, Mutex10, LockPolicy10> { public: /// Constructor SyncMultiLock(Mutex1& m1, bool do_lock1 = true) : internal::MultiLockBase<1, Mutex1, LockPolicy1>(&m1), mutexes_(0), size_(0) { if (do_lock1) { mutexes_ = new internal::MultiLockPair[1]; mutexes_[size_++] = internal::MultiLockPair(static_cast(&m1), size_+1); internal::MultiLockBase<1, Mutex1, LockPolicy1>::lock(); } } /// Constructor SyncMultiLock(Mutex1& m1, Mutex2& m2, bool do_lock1 = true, bool do_lock2 = true) : internal::MultiLockBase<1, Mutex1, LockPolicy1>(&m1), internal::MultiLockBase<2, Mutex2, LockPolicy2>(&m2), mutexes_(0), size_(0) { mutexes_ = new internal::MultiLockPair[2]; if (do_lock1) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m1), size_+1); if (do_lock2) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m2), size_+1); this->sort_lock(); } /// Constructor SyncMultiLock(Mutex1& m1, Mutex2& m2, Mutex3& m3, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true) : internal::MultiLockBase<1, Mutex1, LockPolicy1>(&m1), internal::MultiLockBase<2, Mutex2, LockPolicy2>(&m2), internal::MultiLockBase<3, Mutex3, LockPolicy3>(&m3), mutexes_(0), size_(0) { mutexes_ = new internal::MultiLockPair[3]; if (do_lock1) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m1), size_+1); if (do_lock2) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m2), size_+1); if (do_lock3) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m3), size_+1); this->sort_lock(); } /// Constructor SyncMultiLock(Mutex1& m1, Mutex2& m2, Mutex3& m3, Mutex4& m4, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true, bool do_lock4 = true) : internal::MultiLockBase<1, Mutex1, LockPolicy1>(&m1), internal::MultiLockBase<2, Mutex2, LockPolicy2>(&m2), internal::MultiLockBase<3, Mutex3, LockPolicy3>(&m3), internal::MultiLockBase<4, Mutex4, LockPolicy4>(&m4), mutexes_(0), size_(0) { mutexes_ = new internal::MultiLockPair[4]; if (do_lock1) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m1), size_+1); if (do_lock2) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m2), size_+1); if (do_lock3) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m3), size_+1); if (do_lock4) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m4), size_+1); this->sort_lock(); } /// Constructor SyncMultiLock(Mutex1& m1, Mutex2& m2, Mutex3& m3, Mutex4& m4, Mutex5& m5, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true, bool do_lock4 = true, bool do_lock5 = true) : internal::MultiLockBase<1, Mutex1, LockPolicy1>(&m1), internal::MultiLockBase<2, Mutex2, LockPolicy2>(&m2), internal::MultiLockBase<3, Mutex3, LockPolicy3>(&m3), internal::MultiLockBase<4, Mutex4, LockPolicy4>(&m4), internal::MultiLockBase<5, Mutex5, LockPolicy5>(&m5), mutexes_(0), size_(0) { mutexes_ = new internal::MultiLockPair[5]; if (do_lock1) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m1), size_+1); if (do_lock2) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m2), size_+1); if (do_lock3) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m3), size_+1); if (do_lock4) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m4), size_+1); if (do_lock5) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m5), size_+1); this->sort_lock(); } /// Constructor SyncMultiLock(Mutex1& m1, Mutex2& m2, Mutex3& m3, Mutex4& m4, Mutex5& m5, Mutex6& m6, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true, bool do_lock4 = true, bool do_lock5 = true, bool do_lock6 = true) : internal::MultiLockBase<1, Mutex1, LockPolicy1>(&m1), internal::MultiLockBase<2, Mutex2, LockPolicy2>(&m2), internal::MultiLockBase<3, Mutex3, LockPolicy3>(&m3), internal::MultiLockBase<4, Mutex4, LockPolicy4>(&m4), internal::MultiLockBase<5, Mutex5, LockPolicy5>(&m5), internal::MultiLockBase<6, Mutex6, LockPolicy6>(&m6), mutexes_(0), size_(0) { mutexes_ = new internal::MultiLockPair[6]; if (do_lock1) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m1), size_+1); if (do_lock2) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m2), size_+1); if (do_lock3) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m3), size_+1); if (do_lock4) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m4), size_+1); if (do_lock5) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m5), size_+1); if (do_lock6) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m6), size_+1); this->sort_lock(); } /// Constructor SyncMultiLock(Mutex1& m1, Mutex2& m2, Mutex3& m3, Mutex4& m4, Mutex5& m5, Mutex6& m6, Mutex7& m7, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true, bool do_lock4 = true, bool do_lock5 = true, bool do_lock6 = true, bool do_lock7 = true) : internal::MultiLockBase<1, Mutex1, LockPolicy1>(&m1), internal::MultiLockBase<2, Mutex2, LockPolicy2>(&m2), internal::MultiLockBase<3, Mutex3, LockPolicy3>(&m3), internal::MultiLockBase<4, Mutex4, LockPolicy4>(&m4), internal::MultiLockBase<5, Mutex5, LockPolicy5>(&m5), internal::MultiLockBase<6, Mutex6, LockPolicy6>(&m6), internal::MultiLockBase<7, Mutex7, LockPolicy7>(&m7), mutexes_(0), size_(0) { mutexes_ = new internal::MultiLockPair[7]; if (do_lock1) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m1), size_+1); if (do_lock2) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m2), size_+1); if (do_lock3) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m3), size_+1); if (do_lock4) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m4), size_+1); if (do_lock5) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m5), size_+1); if (do_lock6) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m6), size_+1); if (do_lock7) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m7), size_+1); this->sort_lock(); } /// Constructor SyncMultiLock(Mutex1& m1, Mutex2& m2, Mutex3& m3, Mutex4& m4, Mutex5& m5, Mutex6& m6, Mutex7& m7, Mutex8& m8, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true, bool do_lock4 = true, bool do_lock5 = true, bool do_lock6 = true, bool do_lock7 = true, bool do_lock8 = true) : internal::MultiLockBase<1, Mutex1, LockPolicy1>(&m1), internal::MultiLockBase<2, Mutex2, LockPolicy2>(&m2), internal::MultiLockBase<3, Mutex3, LockPolicy3>(&m3), internal::MultiLockBase<4, Mutex4, LockPolicy4>(&m4), internal::MultiLockBase<5, Mutex5, LockPolicy5>(&m5), internal::MultiLockBase<6, Mutex6, LockPolicy6>(&m6), internal::MultiLockBase<7, Mutex7, LockPolicy7>(&m7), internal::MultiLockBase<8, Mutex8, LockPolicy8>(&m8), mutexes_(0), size_(0) { mutexes_ = new internal::MultiLockPair[8]; if (do_lock1) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m1), size_+1); if (do_lock2) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m2), size_+1); if (do_lock3) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m3), size_+1); if (do_lock4) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m4), size_+1); if (do_lock5) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m5), size_+1); if (do_lock6) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m6), size_+1); if (do_lock7) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m7), size_+1); if (do_lock8) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m8), size_+1); this->sort_lock(); } /// Constructor SyncMultiLock(Mutex1& m1, Mutex2& m2, Mutex3& m3, Mutex4& m4, Mutex5& m5, Mutex6& m6, Mutex7& m7, Mutex8& m8, Mutex9& m9, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true, bool do_lock4 = true, bool do_lock5 = true, bool do_lock6 = true, bool do_lock7 = true, bool do_lock8 = true, bool do_lock9 = true) : internal::MultiLockBase<1, Mutex1, LockPolicy1>(&m1), internal::MultiLockBase<2, Mutex2, LockPolicy2>(&m2), internal::MultiLockBase<3, Mutex3, LockPolicy3>(&m3), internal::MultiLockBase<4, Mutex4, LockPolicy4>(&m4), internal::MultiLockBase<5, Mutex5, LockPolicy5>(&m5), internal::MultiLockBase<6, Mutex6, LockPolicy6>(&m6), internal::MultiLockBase<7, Mutex7, LockPolicy7>(&m7), internal::MultiLockBase<8, Mutex8, LockPolicy8>(&m8), internal::MultiLockBase<9, Mutex9, LockPolicy9>(&m9), mutexes_(0), size_(0) { mutexes_ = new internal::MultiLockPair[9]; if (do_lock1) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m1), size_+1); if (do_lock2) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m2), size_+1); if (do_lock3) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m3), size_+1); if (do_lock4) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m4), size_+1); if (do_lock5) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m5), size_+1); if (do_lock6) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m6), size_+1); if (do_lock7) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m7), size_+1); if (do_lock8) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m8), size_+1); if (do_lock9) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m9), size_+1); this->sort_lock(); } /// Constructor SyncMultiLock(Mutex1& m1, Mutex2& m2, Mutex3& m3, Mutex4& m4, Mutex5& m5, Mutex6& m6, Mutex7& m7, Mutex8& m8, Mutex9& m9, Mutex10& m10, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true, bool do_lock4 = true, bool do_lock5 = true, bool do_lock6 = true, bool do_lock7 = true, bool do_lock8 = true, bool do_lock9 = true, bool do_lock10 = true) : internal::MultiLockBase<1, Mutex1, LockPolicy1>(&m1), internal::MultiLockBase<2, Mutex2, LockPolicy2>(&m2), internal::MultiLockBase<3, Mutex3, LockPolicy3>(&m3), internal::MultiLockBase<4, Mutex4, LockPolicy4>(&m4), internal::MultiLockBase<5, Mutex5, LockPolicy5>(&m5), internal::MultiLockBase<6, Mutex6, LockPolicy6>(&m6), internal::MultiLockBase<7, Mutex7, LockPolicy7>(&m7), internal::MultiLockBase<8, Mutex8, LockPolicy8>(&m8), internal::MultiLockBase<9, Mutex9, LockPolicy9>(&m9), internal::MultiLockBase<10, Mutex10, LockPolicy10>(&m10), mutexes_(0), size_(0) { mutexes_ = new internal::MultiLockPair[10]; if (do_lock1) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m1), size_+1); if (do_lock2) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m2), size_+1); if (do_lock3) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m3), size_+1); if (do_lock4) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m4), size_+1); if (do_lock5) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m5), size_+1); if (do_lock6) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m6), size_+1); if (do_lock7) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m7), size_+1); if (do_lock8) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m8), size_+1); if (do_lock9) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m9), size_+1); if (do_lock10) mutexes_[size_++] = internal::MultiLockPair(static_cast(&m10), size_+1); this->sort_lock(); } /// Destructor, unlocks the mutexes ~SyncMultiLock() { for (std::size_t i = size_; i > 0; --i) { // unlock reverse order switch (mutexes_[i - 1].index) { case 1: internal::MultiLockBase<1, Mutex1, LockPolicy1>::unlock(); break; case 2: internal::MultiLockBase<2, Mutex2, LockPolicy2>::unlock(); break; case 3: internal::MultiLockBase<3, Mutex3, LockPolicy3>::unlock(); break; case 4: internal::MultiLockBase<4, Mutex4, LockPolicy4>::unlock(); break; case 5: internal::MultiLockBase<5, Mutex5, LockPolicy5>::unlock(); break; case 6: internal::MultiLockBase<6, Mutex6, LockPolicy6>::unlock(); break; case 7: internal::MultiLockBase<7, Mutex7, LockPolicy7>::unlock(); break; case 8: internal::MultiLockBase<8, Mutex8, LockPolicy8>::unlock(); break; case 9: internal::MultiLockBase<9, Mutex9, LockPolicy9>::unlock(); break; case 10: internal::MultiLockBase<10, Mutex10, LockPolicy10>::unlock(); break; } } delete[] mutexes_; } /// Sort the mutexes and lock them void sort_lock() { shell_sort(mutexes_, mutexes_ + size_); // sort by mutex addresses for (std::size_t i = 0; i < size_; ++i) { switch (mutexes_[i].index) { case 1: internal::MultiLockBase<1, Mutex1, LockPolicy1>::lock(); break; case 2: internal::MultiLockBase<2, Mutex2, LockPolicy2>::lock(); break; case 3: internal::MultiLockBase<3, Mutex3, LockPolicy3>::lock(); break; case 4: internal::MultiLockBase<4, Mutex4, LockPolicy4>::lock(); break; case 5: internal::MultiLockBase<5, Mutex5, LockPolicy5>::lock(); break; case 6: internal::MultiLockBase<6, Mutex6, LockPolicy6>::lock(); break; case 7: internal::MultiLockBase<7, Mutex7, LockPolicy7>::lock(); break; case 8: internal::MultiLockBase<8, Mutex8, LockPolicy8>::lock(); break; case 9: internal::MultiLockBase<9, Mutex9, LockPolicy9>::lock(); break; case 10: internal::MultiLockBase<10, Mutex10, LockPolicy10>::lock(); break; } } } private: internal::MultiLockPair* mutexes_; ///< array of MultiLockPair* for sorting std::size_t size_; ///< array size /// Forbid copying SyncMultiLock(const SyncMultiLock& from); /// Forbid copying SyncMultiLock& operator= (const SyncMultiLock& from); }; /// A scoped lock supporting multiple mutexes (up to 10 with positional /// parameters, unlimited with array-based constructors), locking them /// always in the same order. The mutexes must have the same type. /// Compared to SyncMultiLock, this one has a slightly less memory /// footprint, is slightly faster and handles unlimited number /// (for all practical purposes) of mutexes, all at the /// cost of requiring mutexes to be of the same type. template::type> class SyncMultiLockUniType { public: // -------------------------------- passed as an array /// Constructor template SyncMultiLockUniType(Mutex* (&mutexes)[size], bool do_lock = true) : mutexes_(0), size_(0) { if (size && do_lock) { size_ = size; mutexes_ = new Mutex*[size_]; for (std::size_t i = 0; i < size_; ++i) mutexes_[i] = mutexes[i]; this->sort_lock(); } } /// Constructor. /// For gcc earlier than 4.2 this will be selected for STL containers. template class Container> SyncMultiLockUniType(const Container& mutexes, bool do_lock = true) : mutexes_(0), size_(0) { if (do_lock) { size_ = mutexes.size(); mutexes_ = new Mutex*[size_]; std::size_t i = 0; for (typename Container::const_iterator iter = mutexes.begin(); iter != mutexes.end(); ++iter) mutexes_[i++] = *iter; this->sort_lock(); } } // gcc versions earlier than 4.2 have an undocumented extension which // allowed templates with default parameters to be bound to template template // parameters with fewer parameters. This causes ambiguity errors with // this versions. Resolve that. #if defined(__INTEL_COMPILER) || (!defined __GNUC__) || HZ_GCC_CHECK_VERSION(4, 2, 0) // STL containers mainly fall into this one. template class Container> SyncMultiLockUniType(const Container& mutexes, bool do_lock = true) : mutexes_(0), size_(0) { if (do_lock) { size_ = mutexes.size(); mutexes_ = new Mutex*[size_]; std::size_t i = 0; for (typename Container::const_iterator iter = mutexes.begin(); iter != mutexes.end(); ++iter) mutexes_[i++] = *iter; this->sort_lock(); } } template class Container> SyncMultiLockUniType(const Container& mutexes, bool do_lock = true) : mutexes_(0), size_(0) { if (do_lock) { size_ = mutexes.size(); mutexes_ = new Mutex*[size_]; std::size_t i = 0; for (typename Container::const_iterator iter = mutexes.begin(); iter != mutexes.end(); ++iter) mutexes_[i++] = *iter; this->sort_lock(); } } #endif // -------------------------------- passed directly /// Constructor SyncMultiLockUniType(Mutex& mutex1, bool do_lock1 = true) : mutexes_(0), size_(0) { if (do_lock1) { mutexes_ = new Mutex*[1]; mutexes_[size_++] = &mutex1; // for unlock to work LockPolicy::lock(mutex1); } } /// Constructor SyncMultiLockUniType(Mutex& mutex1, Mutex& mutex2, bool do_lock1 = true, bool do_lock2 = true) : size_(0) { mutexes_ = new Mutex*[2]; if (do_lock1) mutexes_[size_++] = &mutex1; if (do_lock2) mutexes_[size_++] = &mutex2; this->sort_lock(); } /// Constructor SyncMultiLockUniType(Mutex& mutex1, Mutex& mutex2, Mutex& mutex3, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true) : size_(0) { mutexes_ = new Mutex*[3]; if (do_lock1) mutexes_[size_++] = &mutex1; if (do_lock2) mutexes_[size_++] = &mutex2; if (do_lock3) mutexes_[size_++] = &mutex3; this->sort_lock(); } /// Constructor SyncMultiLockUniType(Mutex& mutex1, Mutex& mutex2, Mutex& mutex3, Mutex& mutex4, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true, bool do_lock4 = true) : size_(0) { mutexes_ = new Mutex*[4]; if (do_lock1) mutexes_[size_++] = &mutex1; if (do_lock2) mutexes_[size_++] = &mutex2; if (do_lock3) mutexes_[size_++] = &mutex3; if (do_lock4) mutexes_[size_++] = &mutex4; this->sort_lock(); } /// Constructor SyncMultiLockUniType(Mutex& mutex1, Mutex& mutex2, Mutex& mutex3, Mutex& mutex4, Mutex& mutex5, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true, bool do_lock4 = true, bool do_lock5 = true) : size_(0) { mutexes_ = new Mutex*[5]; if (do_lock1) mutexes_[size_++] = &mutex1; if (do_lock2) mutexes_[size_++] = &mutex2; if (do_lock3) mutexes_[size_++] = &mutex3; if (do_lock4) mutexes_[size_++] = &mutex4; if (do_lock5) mutexes_[size_++] = &mutex5; this->sort_lock(); } /// Constructor SyncMultiLockUniType(Mutex& mutex1, Mutex& mutex2, Mutex& mutex3, Mutex& mutex4, Mutex& mutex5, Mutex& mutex6, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true, bool do_lock4 = true, bool do_lock5 = true, bool do_lock6 = true) : size_(0) { mutexes_ = new Mutex*[6]; if (do_lock1) mutexes_[size_++] = &mutex1; if (do_lock2) mutexes_[size_++] = &mutex2; if (do_lock3) mutexes_[size_++] = &mutex3; if (do_lock4) mutexes_[size_++] = &mutex4; if (do_lock5) mutexes_[size_++] = &mutex5; if (do_lock6) mutexes_[size_++] = &mutex6; this->sort_lock(); } /// Constructor SyncMultiLockUniType(Mutex& mutex1, Mutex& mutex2, Mutex& mutex3, Mutex& mutex4, Mutex& mutex5, Mutex& mutex6, Mutex& mutex7, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true, bool do_lock4 = true, bool do_lock5 = true, bool do_lock6 = true, bool do_lock7 = true) : size_(0) { mutexes_ = new Mutex*[7]; if (do_lock1) mutexes_[size_++] = &mutex1; if (do_lock2) mutexes_[size_++] = &mutex2; if (do_lock3) mutexes_[size_++] = &mutex3; if (do_lock4) mutexes_[size_++] = &mutex4; if (do_lock5) mutexes_[size_++] = &mutex5; if (do_lock6) mutexes_[size_++] = &mutex6; if (do_lock7) mutexes_[size_++] = &mutex7; this->sort_lock(); } /// Constructor SyncMultiLockUniType(Mutex& mutex1, Mutex& mutex2, Mutex& mutex3, Mutex& mutex4, Mutex& mutex5, Mutex& mutex6, Mutex& mutex7, Mutex& mutex8, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true, bool do_lock4 = true, bool do_lock5 = true, bool do_lock6 = true, bool do_lock7 = true, bool do_lock8 = true) : size_(0) { mutexes_ = new Mutex*[8]; if (do_lock1) mutexes_[size_++] = &mutex1; if (do_lock2) mutexes_[size_++] = &mutex2; if (do_lock3) mutexes_[size_++] = &mutex3; if (do_lock4) mutexes_[size_++] = &mutex4; if (do_lock5) mutexes_[size_++] = &mutex5; if (do_lock6) mutexes_[size_++] = &mutex6; if (do_lock7) mutexes_[size_++] = &mutex7; if (do_lock8) mutexes_[size_++] = &mutex8; this->sort_lock(); } /// Constructor SyncMultiLockUniType(Mutex& mutex1, Mutex& mutex2, Mutex& mutex3, Mutex& mutex4, Mutex& mutex5, Mutex& mutex6, Mutex& mutex7, Mutex& mutex8, Mutex& mutex9, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true, bool do_lock4 = true, bool do_lock5 = true, bool do_lock6 = true, bool do_lock7 = true, bool do_lock8 = true, bool do_lock9 = true) : size_(0) { mutexes_ = new Mutex*[9]; if (do_lock1) mutexes_[size_++] = &mutex1; if (do_lock2) mutexes_[size_++] = &mutex2; if (do_lock3) mutexes_[size_++] = &mutex3; if (do_lock4) mutexes_[size_++] = &mutex4; if (do_lock5) mutexes_[size_++] = &mutex5; if (do_lock6) mutexes_[size_++] = &mutex6; if (do_lock7) mutexes_[size_++] = &mutex7; if (do_lock8) mutexes_[size_++] = &mutex8; if (do_lock9) mutexes_[size_++] = &mutex9; this->sort_lock(); } /// Constructor SyncMultiLockUniType(Mutex& mutex1, Mutex& mutex2, Mutex& mutex3, Mutex& mutex4, Mutex& mutex5, Mutex& mutex6, Mutex& mutex7, Mutex& mutex8, Mutex& mutex9, Mutex& mutex10, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true, bool do_lock4 = true, bool do_lock5 = true, bool do_lock6 = true, bool do_lock7 = true, bool do_lock8 = true, bool do_lock9 = true, bool do_lock10 = true) : size_(0) { mutexes_ = new Mutex*[10]; if (do_lock1) mutexes_[size_++] = &mutex1; if (do_lock2) mutexes_[size_++] = &mutex2; if (do_lock3) mutexes_[size_++] = &mutex3; if (do_lock4) mutexes_[size_++] = &mutex4; if (do_lock5) mutexes_[size_++] = &mutex5; if (do_lock6) mutexes_[size_++] = &mutex6; if (do_lock7) mutexes_[size_++] = &mutex7; if (do_lock8) mutexes_[size_++] = &mutex8; if (do_lock9) mutexes_[size_++] = &mutex9; if (do_lock10) mutexes_[size_++] = &mutex10; this->sort_lock(); } /// Destructor, unlocks the mutexes (in reverse order) ~SyncMultiLockUniType() { for (std::size_t i = size_; i > 0; --i) { // unlock in reverse order LockPolicy::unlock(*(mutexes_[i-1])); } delete[] mutexes_; } /// Sort and lock the mutexes void sort_lock() { shell_sort(mutexes_, mutexes_ + size_); for (std::size_t i = 0; i < size_; ++i) { LockPolicy::lock(*(mutexes_[i])); } } private: Mutex** mutexes_; ///< array of Mutex* std::size_t size_; ///< Size of mutexes_ #if defined(__INTEL_COMPILER) || (!defined __GNUC__) || HZ_GCC_CHECK_VERSION(4, 2, 0) /// Deny copying, resolve ambiguity with earlier gcc versions SyncMultiLockUniType(const SyncMultiLockUniType& from); #endif /// Deny copying SyncMultiLockUniType& operator= (const SyncMultiLockUniType& from); }; /// A multi-lock which does nothing. Useful for None policy. template class SyncMultiLockEmpty { public: // -------------------------------- passed as an array /// Constructor template SyncMultiLockEmpty(Mutex* (&mutexes)[size], bool do_lock = true) { } /// Constructor for various containers template class Container> SyncMultiLockEmpty(const Container& mutexes, bool do_lock = true) { } /// Constructor for STL-like containers template class Container> SyncMultiLockEmpty(const Container& mutexes, bool do_lock = true) { } /// Constructor template class Container> SyncMultiLockEmpty(const Container& mutexes, bool do_lock = true) { } // -------------------------------- passed directly /// Constructor SyncMultiLockEmpty(Mutex& mutex1, bool do_lock1 = true) { } /// Constructor SyncMultiLockEmpty(Mutex& mutex1, Mutex& mutex2, bool do_lock1 = true, bool do_lock2 = true) { } /// Constructor SyncMultiLockEmpty(Mutex& mutex1, Mutex& mutex2, Mutex& mutex3, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true) { } /// Constructor SyncMultiLockEmpty(Mutex& mutex1, Mutex& mutex2, Mutex& mutex3, Mutex& mutex4, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true, bool do_lock4 = true) { } /// Constructor SyncMultiLockEmpty(Mutex& mutex1, Mutex& mutex2, Mutex& mutex3, Mutex& mutex4, Mutex& mutex5, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true, bool do_lock4 = true, bool do_lock5 = true) { } /// Constructor SyncMultiLockEmpty(Mutex& mutex1, Mutex& mutex2, Mutex& mutex3, Mutex& mutex4, Mutex& mutex5, Mutex& mutex6, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true, bool do_lock4 = true, bool do_lock5 = true, bool do_lock6 = true) { } /// Constructor SyncMultiLockEmpty(Mutex& mutex1, Mutex& mutex2, Mutex& mutex3, Mutex& mutex4, Mutex& mutex5, Mutex& mutex6, Mutex& mutex7, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true, bool do_lock4 = true, bool do_lock5 = true, bool do_lock6 = true, bool do_lock7 = true) { } /// Constructor SyncMultiLockEmpty(Mutex& mutex1, Mutex& mutex2, Mutex& mutex3, Mutex& mutex4, Mutex& mutex5, Mutex& mutex6, Mutex& mutex7, Mutex& mutex8, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true, bool do_lock4 = true, bool do_lock5 = true, bool do_lock6 = true, bool do_lock7 = true, bool do_lock8 = true) { } /// Constructor SyncMultiLockEmpty(Mutex& mutex1, Mutex& mutex2, Mutex& mutex3, Mutex& mutex4, Mutex& mutex5, Mutex& mutex6, Mutex& mutex7, Mutex& mutex8, Mutex& mutex9, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true, bool do_lock4 = true, bool do_lock5 = true, bool do_lock6 = true, bool do_lock7 = true, bool do_lock8 = true, bool do_lock9 = true) { } /// Constructor SyncMultiLockEmpty(Mutex& mutex1, Mutex& mutex2, Mutex& mutex3, Mutex& mutex4, Mutex& mutex5, Mutex& mutex6, Mutex& mutex7, Mutex& mutex8, Mutex& mutex9, Mutex& mutex10, bool do_lock1 = true, bool do_lock2 = true, bool do_lock3 = true, bool do_lock4 = true, bool do_lock5 = true, bool do_lock6 = true, bool do_lock7 = true, bool do_lock8 = true, bool do_lock9 = true, bool do_lock10 = true) { } private: /// Disallow copying SyncMultiLockEmpty(const SyncMultiLockEmpty& from); /// Disallow copying SyncMultiLockEmpty& operator= (const SyncMultiLockEmpty& from); }; } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/sync_multilock_test.cpp000066400000000000000000000046761417717230600224520ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_unlicense.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz_tests /// \weakgroup hz_tests /// @{ // disable libdebug, we don't link to it #undef HZ_USE_LIBDEBUG #define HZ_USE_LIBDEBUG 0 // enable libdebug emulation through std::cerr #undef HZ_EMULATE_LIBDEBUG #define HZ_EMULATE_LIBDEBUG 1 // If none are defined and there are no undefs below, the default // policy is used (see global_macros.h). /* #undef HZ_SYNC_DEFAULT_POLICY_GLIBMM #undef HZ_SYNC_DEFAULT_POLICY_GLIB #undef HZ_SYNC_DEFAULT_POLICY_BOOST #undef HZ_SYNC_DEFAULT_POLICY_POCO #undef HZ_SYNC_DEFAULT_POLICY_PTHREAD #undef HZ_SYNC_DEFAULT_POLICY_WIN32 // #define HZ_SYNC_DEFAULT_POLICY_GLIBMM // #define HZ_SYNC_DEFAULT_POLICY_GLIB // #define HZ_SYNC_DEFAULT_POLICY_BOOST // #define HZ_SYNC_DEFAULT_POLICY_POCO #define HZ_SYNC_DEFAULT_POLICY_PTHREAD // #define HZ_SYNC_DEFAULT_POLICY_WIN32 */ // The first header should be then one we're testing, to avoid missing // header pitfalls. #include "sync_multilock.h" #include "sync.h" // policies need this // #include "sync_policy_glib.h" // #include "sync_policy_glibmm.h" // #include "sync_policy_pthread.h" #include #include /// Main function for the test int main() { using namespace hz; // g_thread_init(NULL); { SyncPolicyMtDefault::Mutex m1, m2, m3, m4; { SyncMultiLockUniType lock(m1, m2, m3, m4, true, true, false, true); } { SyncPolicyMtDefault::Mutex* ms[] = {&m1, &m2, &m3, &m4}; SyncMultiLockUniType lock(ms); } { std::vector ms; ms.push_back(&m1); ms.push_back(&m2); SyncMultiLockUniType lock(ms); } } /* { SyncPolicyGlib::Mutex m1; SyncPolicyGlibmm::RecMutex m2; // SyncPolicyPthread::Mutex m3; SyncPolicyGlib::Mutex m3; // allow compilation of tests on non-pthread platforms for now. { SyncMultiLock lock(m1, m2, m3, true, true, false); // SyncMultiLock lock(m1, m2, m3, true, true, false); // error } } */ std::cerr << "All OK\n"; return 0; } /// @} gsmartcontrol-1.1.4/src/hz/sync_part_get_policy.h000066400000000000000000000013451417717230600222270ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_SYNC_PART_GET_POLICY_H #define HZ_SYNC_PART_GET_POLICY_H #include "hz_config.h" // feature macros /** \file Internal header, do not include manually. */ namespace hz { /// Get policy class by Mutex type. /// Policies should specialize this struct. template struct SyncGetPolicy; // no definition, only specializations. } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/sync_policy_boost.h000066400000000000000000000204041417717230600215450ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ #ifndef HZ_SYNC_POLICY_BOOST_H #define HZ_SYNC_POLICY_BOOST_H #include "hz_config.h" // feature macros #include #include "sync.h" /** \file Boost.Thread-based policy. */ // ----------------------------- Boost 1.35 version #include #include #include namespace hz { /// Native type notes: /// Boost scoped_lock doesn't provide the do_lock parameter anymore. /// It has been replaced by boost::defer_lock_type argument. /// There is no single lock for shared_mutex - it has shared_lock and unique_lock. /// We use native types for mutex types here, because they meet the requirements. /// Thus no need to specify locks and other stuff for these separately. struct SyncPolicyBoost : public SyncScopedLockProvider { // Types: typedef boost::mutex Mutex; typedef Mutex NativeMutex; ///< supports lock(), try_lock(), unlock() typedef boost::recursive_mutex RecMutex; typedef RecMutex NativeRecMutex; ///< supports lock(), try_lock(), unlock() typedef boost::shared_mutex RWMutex; typedef RWMutex NativeRWMutex; ///< lock(), try_lock(), unlock() - exclusive. *_shared() - shared. typedef GenericScopedLock ScopedLock; typedef GenericScopedTryLock ScopedTryLock; typedef GenericScopedLock ScopedNativeLock; typedef GenericScopedTryLock ScopedNativeTryLock; typedef GenericScopedLock ScopedRecLock; typedef GenericScopedTryLock ScopedRecTryLock; typedef GenericScopedLock ScopedNativeRecLock; typedef GenericScopedTryLock ScopedNativeRecTryLock; typedef GenericScopedRWLock ScopedRWLock; typedef GenericScopedRWTryLock ScopedRWTryLock; typedef GenericScopedRWLock ScopedNativeRWLock; typedef GenericScopedRWTryLock ScopedNativeRWTryLock; // Static methods static bool init() { return true; } static void lock(Mutex& m) { try { m.lock(); } catch (boost::thread_resource_error& ex) { throw_boost_to_sync_exception(ex, "SyncPolicyBoost::lock(Mutex): Error while locking mutex."); } } static bool trylock(Mutex& m) { bool status = false; try { status = m.try_lock(); } catch (boost::thread_resource_error& ex) { throw_boost_to_sync_exception(ex, "SyncPolicyBoost::trylock(Mutex): Error while trying to lock mutex."); } return status; } static void unlock(Mutex& m) { m.unlock(); // this doesn't throw anything } static void lock(RecMutex& m) { try { m.lock(); } catch (boost::thread_resource_error& ex) { throw_boost_to_sync_exception(ex, "SyncPolicyBoost::lock(RecMutex): Error while locking mutex."); } } static bool trylock(RecMutex& m) { bool status = false; try { status = m.try_lock(); } catch (boost::thread_resource_error& ex) { throw_boost_to_sync_exception(ex, "SyncPolicyBoost::trylock(RecMutex): Error while trying to lock mutex."); } return status; } static void unlock(RecMutex& m) { m.unlock(); // this doesn't throw anything } static void lock(RWMutex& m, bool for_write = false) { try { if (for_write) { m.lock(); } else { m.lock_shared(); } } catch (boost::thread_resource_error& ex) { throw_boost_to_sync_exception(ex, std::string("SyncPolicyBoost::trylock(RWMutex, ") + (for_write ? "true" : "false") + "): Error while locking mutex."); } } static bool trylock(RWMutex& m, bool for_write = false) { bool status = false; try { status = (for_write ? m.try_lock() : m.try_lock_shared()); } catch (boost::thread_resource_error& ex) { throw_boost_to_sync_exception(ex, std::string("SyncPolicyBoost::trylock(RWMutex, ") + (for_write ? "true" : "false") + "): Error while trying to lock mutex."); } return status; } static void unlock(RWMutex& m, bool for_write = false) { if (for_write) { m.unlock(); // doesn't throw } else { m.unlock_shared(); // doesn't throw } } private: static void throw_boost_to_sync_exception(const boost::thread_resource_error& ex, const std::string& smsg) { const char* msg = ex.what(); if (msg) { THROW_FATAL(sync_resource_error(smsg + " Original boost::thread_resource_error exception message: \"" + msg + "\".")); } else { THROW_FATAL(sync_resource_error(smsg + " This exception was thrown after boost::thread_resource_error was caught (it didn't contain any message).")); } } }; // mutex -> policy template<> struct SyncGetPolicy { typedef SyncPolicyBoost type; }; template<> struct SyncGetPolicy { typedef SyncPolicyBoost type; }; template<> struct SyncGetPolicy { typedef SyncPolicyBoost type; }; } // ns // ----------------------------- Boost 1.34 version (outdated!) /* #include #include // #include namespace hz { // Provide custom ScopedRWLock class. // class ScopedRWLockBoost : public boost::read_write_mutex::scoped_read_write_lock { // public: // ScopedRWLockBoost(boost::read_write_mutex& mutex, bool for_write = false, bool do_lock = true) // : boost::read_write_mutex::scoped_read_write_lock(mutex, do_lock ? // (for_write ? boost::read_write_lock_state::write_locked : boost::read_write_lock_state::read_locked) // : boost::read_write_lock_state::unlocked) // { } // }; // Native type notes: // Boost mutex types support locking through [mutex_type]::scoped_*lock objects only. // We use somewhat undocumented static functions of boost::detail::thread::*lock_ops. // Note: Boost Read/Write Mutex is documented, but is unavailable in source form // as of boost 1.34.1. That is, there were some implementation problems in boost, so // it was removed. The policy uses the documented API, but it's disabled until boost // provides a working implementation. // Classes may use this in single-threaded or non-locking environments struct SyncPolicyBoost { typedef boost::try_mutex Mutex; // doesn't have any usable methods typedef boost::try_mutex::scoped_lock ScopedLock; // works well typedef boost::recursive_try_mutex RecMutex; // doesn't have any usable methods typedef boost::recursive_try_mutex::scoped_lock ScopedRecLock; // works well // typedef boost::try_read_write_mutex RWMutex; // doesn't have any usable methods // typedef ScopedRWLockBoost ScopedRWLock; // needs a wrapper static void lock(Mutex& m) { boost::detail::thread::lock_ops::lock(m); } static bool trylock(Mutex& m) { return boost::detail::thread::lock_ops::trylock(m); } static void unlock(Mutex& m) { boost::detail::thread::lock_ops::unlock(m); } static void lock(RecMutex& m) { boost::detail::thread::lock_ops::lock(m); } static bool trylock(RecMutex& m) { return boost::detail::thread::lock_ops::trylock(m); } static void unlock(RecMutex& m) { boost::detail::thread::lock_ops::unlock(m); } // static void lock(RWMutex& m, bool for_write = false) // { // if (for_write) { // boost::detail::thread::read_write_lock_ops::write_lock(m); // } else { // boost::detail::thread::read_write_lock_ops::read_lock(m); // } // } // static bool trylock(RWMutex& m, bool for_write = false) // { // if (for_write) // return boost::detail::thread::read_write_lock_ops::try_write_lock(m); // } // static void unlock(RWMutex& m, bool for_write = false) // { // if (for_write) { // boost::detail::thread::read_write_lock_ops::write_unlock(m); // } else { // boost::detail::thread::read_write_lock_ops::read_unlock(m); // } // } }; // mutex -> policy template<> struct SyncGetPolicy { typedef SyncPolicyBoost type; }; template<> struct SyncGetPolicy { typedef SyncPolicyBoost type; }; // template<> struct SyncGetPolicy { // typedef SyncPolicyNone type; // }; } // ns */ #endif gsmartcontrol-1.1.4/src/hz/sync_policy_glib.h000066400000000000000000000231161417717230600213370ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ #ifndef HZ_SYNC_POLICY_GLIB_H #define HZ_SYNC_POLICY_GLIB_H #include "hz_config.h" // feature macros #include #include "sync.h" /** \file Glib-based policy. */ namespace hz { #if GLIB_CHECK_VERSION(2, 32, 0) /// C++ Wrapper for Glib mutex class MutexGlib : public hz::noncopyable { public: typedef GMutex native_type; static void native_lock(native_type& mutex) { g_mutex_lock(&mutex); } static bool native_trylock(native_type& mutex) { return g_mutex_trylock(&mutex); } static void native_unlock(native_type& mutex) { g_mutex_unlock(&mutex); } MutexGlib() { g_mutex_init(&mutex_); } ~MutexGlib() { g_mutex_clear(&mutex_); } void lock() { native_lock(mutex_); } bool trylock() { return native_trylock(mutex_); } void unlock() { native_unlock(mutex_); } private: GMutex mutex_; }; /// C++ Wrapper for Glib recursive mutex class RecMutexGlib : public hz::noncopyable { public: typedef GRecMutex native_type; static void native_lock(native_type& mutex) { g_rec_mutex_lock(&mutex); } static bool native_trylock(native_type& mutex) { return g_rec_mutex_trylock(&mutex); } static void native_unlock(native_type& mutex) { g_rec_mutex_unlock(&mutex); } RecMutexGlib() { g_rec_mutex_init(&mutex_); } ~RecMutexGlib() { g_rec_mutex_clear(&mutex_); } void lock() { native_lock(mutex_); } bool trylock() { return native_trylock(mutex_); } void unlock() { native_unlock(mutex_); } private: GRecMutex mutex_; }; /// C++ Wrapper for Glib RW lock class RWMutexGlib : public hz::noncopyable { public: typedef GRWLock native_type; static void native_lock(native_type& mutex, bool for_write = false) { if (for_write) { g_rw_lock_writer_lock(&mutex); } else { g_rw_lock_reader_lock(&mutex); } } static bool native_trylock(native_type& mutex, bool for_write = false) { return (for_write ? g_rw_lock_writer_trylock(&mutex) : g_rw_lock_reader_trylock(&mutex)); } static void native_unlock(native_type& mutex, bool for_write = false) { if (for_write) { g_rw_lock_writer_unlock(&mutex); } else { g_rw_lock_reader_unlock(&mutex); } } RWMutexGlib() { g_rw_lock_init(&mutex_); } ~RWMutexGlib() { g_rw_lock_clear(&mutex_); } void lock(bool for_write = false) { native_lock(mutex_, for_write); } bool trylock(bool for_write = false) { return native_trylock(mutex_, for_write); } void unlock(bool for_write = false) { native_unlock(mutex_, for_write); } private: GRWLock mutex_; }; #else // older glib /// Lock GStaticMutex #define hz_glib_static_mutex_lock(mutex) \ g_mutex_lock(g_static_mutex_get_mutex(mutex)) /// Try locking GStaticMutex #define hz_glib_static_mutex_trylock(mutex) \ g_mutex_trylock(g_static_mutex_get_mutex(mutex)) /// Unlock GStaticMutex #define hz_glib_static_mutex_unlock(mutex) \ g_mutex_unlock(g_static_mutex_get_mutex(mutex)) /// C++ Wrapper for Glib mutex class MutexGlib : public hz::noncopyable { public: typedef GStaticMutex native_type; static void native_lock(native_type& mutex) { hz_glib_static_mutex_lock(&mutex); } static bool native_trylock(native_type& mutex) { return hz_glib_static_mutex_trylock(&mutex); } static void native_unlock(native_type& mutex) { hz_glib_static_mutex_unlock(&mutex); } MutexGlib() { g_static_mutex_init(&mutex_); } ~MutexGlib() { g_static_mutex_free(&mutex_); } void lock() { native_lock(mutex_); } bool trylock() { return native_trylock(mutex_); } void unlock() { native_unlock(mutex_); } private: GStaticMutex mutex_; // use StaticMutex, I think it uses less heap memory }; /// C++ Wrapper for Glib recursive mutex class RecMutexGlib : public hz::noncopyable { public: typedef GStaticRecMutex native_type; static void native_lock(native_type& mutex) { g_static_rec_mutex_lock(&mutex); } static bool native_trylock(native_type& mutex) { return g_static_rec_mutex_trylock(&mutex); } static void native_unlock(native_type& mutex) { g_static_rec_mutex_unlock(&mutex); } RecMutexGlib() { g_static_rec_mutex_init(&mutex_); } ~RecMutexGlib() { g_static_rec_mutex_free(&mutex_); } void lock() { native_lock(mutex_); } bool trylock() { return native_trylock(mutex_); } void unlock() { native_unlock(mutex_); } private: GStaticRecMutex mutex_; }; /// C++ Wrapper for Glib RW lock class RWMutexGlib : public hz::noncopyable { public: typedef GStaticRWLock native_type; static void native_lock(native_type& mutex, bool for_write = false) { if (for_write) { g_static_rw_lock_writer_lock(&mutex); } else { g_static_rw_lock_reader_lock(&mutex); } } static bool native_trylock(native_type& mutex, bool for_write = false) { return (for_write ? g_static_rw_lock_writer_trylock(&mutex) : g_static_rw_lock_reader_trylock(&mutex)); } static void native_unlock(native_type& mutex, bool for_write = false) { if (for_write) { g_static_rw_lock_writer_unlock(&mutex); } else { g_static_rw_lock_reader_unlock(&mutex); } } RWMutexGlib() { g_static_rw_lock_init(&mutex_); } ~RWMutexGlib() { g_static_rw_lock_free(&mutex_); } void lock(bool for_write = false) { native_lock(mutex_, for_write); } bool trylock(bool for_write = false) { return native_trylock(mutex_, for_write); } void unlock(bool for_write = false) { native_unlock(mutex_, for_write); } private: GStaticRWLock mutex_; }; #endif /// Native type notes: /// MutexGlib's underlying structure is neither guaranteed to be /// recursive nor to be non-recursive. /// All these mutexes won't do anything unless gthread is initialized. /// /// Note: For this policy to work, you MUST initialize GThread system /// by calling one of the following (doesn't matter which exactly) beforehand: /// Glib::thread_init(); // glibmm /// or /// g_thread_init(NULL); // glib /// or /// SyncPolicyGlib::init(); // sync's wrapper /// /// If you don't do it, the operations will silently do nothing. /// /// Glib doesn't provide any means to detect errors, so no exceptions here. struct SyncPolicyGlib : public SyncScopedLockProvider { // Types: typedef MutexGlib Mutex; typedef Mutex::native_type NativeMutex; typedef RecMutexGlib RecMutex; typedef RecMutex::native_type NativeRecMutex; typedef RWMutexGlib RWMutex; typedef RWMutex::native_type NativeRWMutex; typedef GenericScopedLock ScopedLock; typedef GenericScopedTryLock ScopedTryLock; typedef GenericScopedLock ScopedNativeLock; typedef GenericScopedTryLock ScopedNativeTryLock; typedef GenericScopedLock ScopedRecLock; typedef GenericScopedTryLock ScopedRecTryLock; typedef GenericScopedLock ScopedNativeRecLock; typedef GenericScopedTryLock ScopedNativeRecTryLock; typedef GenericScopedRWLock ScopedRWLock; typedef GenericScopedRWTryLock ScopedRWTryLock; typedef GenericScopedRWLock ScopedNativeRWLock; typedef GenericScopedRWTryLock ScopedNativeRWTryLock; // Static methods /// If glib threads are unavailable, this will abort. #if GLIB_CHECK_VERSION(2, 32, 0) static bool init() { return true; } // g_thread_init() does nothing and is deprecated since 2.32. #else static bool init() { if (!g_thread_supported()) g_thread_init(NULL); return true; } #endif static void lock(Mutex& m) { m.lock(); } static bool trylock(Mutex& m) { return m.trylock(); } static void unlock(Mutex& m) { m.unlock(); } static void lock(NativeMutex& m) { Mutex::native_lock(m); } static bool trylock(NativeMutex& m) { return Mutex::native_trylock(m); } static void unlock(NativeMutex& m) { Mutex::native_unlock(m); } static void lock(RecMutex& m) { m.lock(); } static bool trylock(RecMutex& m) { return m.trylock(); } static void unlock(RecMutex& m) { m.unlock(); } static void lock(NativeRecMutex& m) { RecMutex::native_lock(m); } static bool trylock(NativeRecMutex& m) { return RecMutex::native_trylock(m); } static void unlock(NativeRecMutex& m) { RecMutex::native_unlock(m); } static void lock(RWMutex& m, bool for_write = false) { m.lock(for_write); } static bool trylock(RWMutex& m, bool for_write = false) { return m.trylock(for_write); } static void unlock(RWMutex& m, bool for_write = false) { m.unlock(for_write); } static void lock(NativeRWMutex& m, bool for_write = false) { RWMutex::native_lock(m, for_write); } static bool trylock(NativeRWMutex& m, bool for_write = false) { return RWMutex::native_trylock(m, for_write); } static void unlock(NativeRWMutex& m, bool for_write = false) { RWMutex::native_unlock(m, for_write); } }; // mutex -> policy template<> struct SyncGetPolicy { typedef SyncPolicyGlib type; }; template<> struct SyncGetPolicy { typedef SyncPolicyGlib type; }; template<> struct SyncGetPolicy { typedef SyncPolicyGlib type; }; template<> struct SyncGetPolicy { typedef SyncPolicyGlib type; }; template<> struct SyncGetPolicy { typedef SyncPolicyGlib type; }; template<> struct SyncGetPolicy { typedef SyncPolicyGlib type; }; } // ns #endif gsmartcontrol-1.1.4/src/hz/sync_policy_glibmm.h000066400000000000000000000074341417717230600216760ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ #ifndef HZ_SYNC_POLICY_GLIBMM_H #define HZ_SYNC_POLICY_GLIBMM_H #include "hz_config.h" // feature macros #include #include "sync.h" /** \file Glibmm-based policy. */ namespace hz { /// Note: For this policy to work, you MUST initialize GThread system /// by calling one of the following (doesn't matter which exactly) beforehand: /// Glib::thread_init(); // glibmm /// or /// g_thread_init(NULL); // glib /// or /// SyncPolicyGlibmm::init(); // sync's wrapper /// /// Note: Unlike Glib::RecMutex (and the types in Glib policy), Glib::Mutex will give /// errors if GThread is not initialized (as described above). Glib::RecMutex and /// Glib::RWLock will just silently do nothing. /// /// Native type notes: /// Glib::Mutex::Lock has acquire() / release(). /// Glib::RecMutex::Lock has acquire() / release(). Different class than Glib::Mutex::Lock. /// Glib::RWLock has reader_lock() / writer_lock() / reader_unlock() / writer_unlock(). /// Glib::RWLock::ReaderLock and Glib::RWLock::WriterLock are _two_ classes. /// /// Glibmm doesn't throw any exceptions for these types, nor does this policy. /// /// We use native types for mutex types here, because they meet the requirements. /// Thus no need to specify locks and other stuff for these separately. struct SyncPolicyGlibmm : public SyncScopedLockProvider { // Types: typedef Glib::Mutex Mutex; typedef Mutex NativeMutex; ///< supports lock(), unlock() typedef Glib::RecMutex RecMutex; typedef RecMutex NativeRecMutex; ///< supports lock(), unlock() typedef Glib::RWLock RWMutex; typedef RWMutex NativeRWMutex; ///< reader_(|try|un)lock(), writer_(|try|un)lock() typedef GenericScopedLock ScopedLock; typedef GenericScopedTryLock ScopedTryLock; typedef GenericScopedLock ScopedNativeLock; typedef GenericScopedTryLock ScopedNativeTryLock; typedef GenericScopedLock ScopedRecLock; typedef GenericScopedTryLock ScopedRecTryLock; typedef GenericScopedLock ScopedNativeRecLock; typedef GenericScopedTryLock ScopedNativeRecTryLock; typedef GenericScopedRWLock ScopedRWLock; typedef GenericScopedRWTryLock ScopedRWTryLock; typedef GenericScopedRWLock ScopedNativeRWLock; typedef GenericScopedRWTryLock ScopedNativeRWTryLock; // Static methods /// If glibmm threads are unavailable, this will abort. static bool init() { if (!Glib::thread_supported()) Glib::thread_init(); return true; } static void lock(Mutex& m) { m.lock(); } static bool trylock(Mutex& m) { return m.trylock(); } static void unlock(Mutex& m) { m.unlock(); } static void lock(RecMutex& m) { m.lock(); } static bool trylock(RecMutex& m) { return m.trylock(); } static void unlock(RecMutex& m) { m.unlock(); } static void lock(RWMutex& m, bool for_write = false) { if (for_write) { m.writer_lock(); } else { m.reader_lock(); } } static bool trylock(RWMutex& m, bool for_write = false) { return (for_write ? m.writer_trylock() : m.reader_trylock()); } static void unlock(RWMutex& m, bool for_write = false) { if (for_write) { m.writer_unlock(); } else { m.reader_unlock(); } } }; // mutex -> policy template<> struct SyncGetPolicy { typedef SyncPolicyGlibmm type; }; template<> struct SyncGetPolicy { typedef SyncPolicyGlibmm type; }; template<> struct SyncGetPolicy { typedef SyncPolicyGlibmm type; }; } // ns #endif gsmartcontrol-1.1.4/src/hz/sync_policy_poco.h000066400000000000000000000057471417717230600213740ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ #ifndef HZ_SYNC_POLICY_POCO_H #define HZ_SYNC_POLICY_POCO_H #include "hz_config.h" // feature macros #include #include #include "sync.h" /** \file Poco-based policy. */ namespace hz { /// Native type notes: /// Poco::Mutex is recursive, while Poco::FastMutex is not. /// Poco::ScopedLock doesn't have "bool do_lock", or anything else for that matter. /// Poco doesn't throw any exceptions for these types, nor does this policy. /// We use native types for mutex types here, because they meet the requirements. /// Thus no need to specify locks and other stuff for these separately. struct SyncPolicyPoco : public SyncScopedLockProvider { // Types: typedef Poco::FastMutex Mutex; typedef Mutex NativeMutex; ///< supports lock(), tryLock(), unlock() typedef Poco::Mutex RecMutex; typedef RecMutex NativeRecMutex; ///< supports lock(), tryLock(), unlock() typedef Poco::RWLock RWMutex; typedef RWMutex NativeRWMutex; ///< readLock(), tryReadLock(), writeLock(), tryWriteLock(), unlock(). typedef GenericScopedLock ScopedLock; typedef GenericScopedTryLock ScopedTryLock; typedef GenericScopedLock ScopedNativeLock; typedef GenericScopedTryLock ScopedNativeTryLock; typedef GenericScopedLock ScopedRecLock; typedef GenericScopedTryLock ScopedRecTryLock; typedef GenericScopedLock ScopedNativeRecLock; typedef GenericScopedTryLock ScopedNativeRecTryLock; typedef GenericScopedRWLock ScopedRWLock; typedef GenericScopedRWTryLock ScopedRWTryLock; typedef GenericScopedRWLock ScopedNativeRWLock; typedef GenericScopedRWTryLock ScopedNativeRWTryLock; // Static methods static bool init() { return true; } static void lock(Mutex& m) { m.lock(); } static bool trylock(Mutex& m) { return m.tryLock(); } static void unlock(Mutex& m) { m.unlock(); } static void lock(RecMutex& m) { m.lock(); } static bool trylock(RecMutex& m) { return m.tryLock(); } static void unlock(RecMutex& m) { m.unlock(); } static void lock(RWMutex& m, bool for_write = false) { if (for_write) { m.writeLock(); } else { m.readLock(); } } static bool trylock(RWMutex& m, bool for_write = false) { return (for_write ? m.tryWriteLock() : m.tryReadLock()); } static void unlock(RWMutex& m, bool for_write = false) { m.unlock(); } }; // mutex -> policy template<> struct SyncGetPolicy { typedef SyncPolicyPoco type; }; template<> struct SyncGetPolicy { typedef SyncPolicyPoco type; }; template<> struct SyncGetPolicy { typedef SyncPolicyPoco type; }; } // ns #endif gsmartcontrol-1.1.4/src/hz/sync_policy_pthread.h000066400000000000000000000306331417717230600220530ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ #ifndef HZ_SYNC_POLICY_PTHREAD_H #define HZ_SYNC_POLICY_PTHREAD_H #include "hz_config.h" // feature macros #include #include // EBUSY #include // Don't use DBG_ASSERT() here, it's using this library and therefore // too error-prone to use in this context. #include #ifndef ASSERT // assert() is undefined if NDEBUG is defined. #define ASSERT(a) assert(a) #endif #include "errno_string.h" // errno_string #include "sync.h" /** \file Posix Threads-based policy. Note: RecMutex assumes that pthread_mutexattr_settype and PTHREAD_MUTEX_RECURSIVE work on current platform. NOTE: This file requires UNIX98 support, enabled via _XOPEN_SOURCE >= 500 in glibc. Solaris doesn't need any additional feature macros. Configuration macros: This enables PTHREAD_MUTEX_ERRORCHECK flag on non-recursive mutexes, which may perform some additional checking on some systems. */ #ifndef HZ_SYNC_PTHREAD_ERROR_CHECKS #define HZ_SYNC_PTHREAD_ERROR_CHECKS 1 #endif namespace hz { namespace internal { /// Throw exception on pthread error inline void sync_pthread_throw_exception(const std::string& msg, int errno_value = 0) { if (errno_value == 0) { THROW_FATAL(sync_resource_error(msg)); } else { THROW_FATAL(sync_resource_error(msg + " Errno: " + hz::errno_string(errno_value))); } } } /// C++ wrapper for pthreads mutex. /// Attempting to destroy a locked mutex results in undefined behavior. class MutexPthread : public hz::noncopyable { public: typedef pthread_mutex_t native_type; static void native_lock(native_type& mutex) { int res = pthread_mutex_lock(&mutex); if (res != 0) { internal::sync_pthread_throw_exception("MutexPthread::native_lock(): Error locking mutex.", res); } } static bool native_trylock(native_type& mutex) { int res = pthread_mutex_trylock(&mutex); if (res == EBUSY) { // the only valid non-zero result return false; } if (res != 0) { internal::sync_pthread_throw_exception("MutexPthread::native_trylock(): Error while trying to lock mutex.", res); } return true; } static void native_unlock(native_type& mutex) { int res = pthread_mutex_unlock(&mutex); if (res != 0) { internal::sync_pthread_throw_exception("MutexPthread::native_unlock(): Error unlocking mutex.", res); } } MutexPthread() { #if defined HZ_SYNC_PTHREAD_ERROR_CHECKS && HZ_SYNC_PTHREAD_ERROR_CHECKS pthread_mutexattr_t attr; int res = pthread_mutexattr_init(&attr); if (res != 0) { internal::sync_pthread_throw_exception("MutexPthread::MutexPthread(): Error creating mutex attributes.", res); } res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); if (res != 0) { internal::sync_pthread_throw_exception("MutexPthread::MutexPthread(): Error setting mutex attributes.", res); pthread_mutexattr_destroy(&attr); // cleanup } res = pthread_mutex_init(&mutex_, &attr); if (res != 0) { internal::sync_pthread_throw_exception("MutexPthread::MutexPthread(): Error initializing mutex.", res); pthread_mutexattr_destroy(&attr); // cleanup } res = pthread_mutexattr_destroy(&attr); if (res != 0) { internal::sync_pthread_throw_exception("MutexPthread::MutexPthread(): Error destroying mutex attributes.", res); pthread_mutex_destroy(&mutex_); // avoid undetermined state } #else int res = pthread_mutex_init(&mutex_, NULL); if (res != 0) { internal::sync_pthread_throw_exception("MutexPthread::MutexPthread(): Error initializing mutex.", res); } #endif } ~MutexPthread() { int res = pthread_mutex_destroy(&mutex_); if (res != 0) { internal::sync_pthread_throw_exception("MutexPthread::~MutexPthread(): Error destroying mutex.", res); } } void lock() { native_lock(mutex_); } bool trylock() { return native_trylock(mutex_); } void unlock() { native_unlock(mutex_); } private: pthread_mutex_t mutex_; }; /// C++ wrapper for pthreads recursive mutex. class RecMutexPthread : public hz::noncopyable { public: typedef pthread_mutex_t native_type; static void native_lock(native_type& mutex) { int res = pthread_mutex_lock(&mutex); if (res != 0) { internal::sync_pthread_throw_exception("RecMutexPthread::native_lock(): Error locking mutex.", res); } } static bool native_trylock(native_type& mutex) { int res = pthread_mutex_trylock(&mutex); if (res == EBUSY) { // the only valid non-zero result return false; } if (res != 0) { internal::sync_pthread_throw_exception("RecMutexPthread::native_trylock(): Error while trying to lock mutex.", res); } return true; } static void native_unlock(native_type& mutex) { int res = pthread_mutex_unlock(&mutex); if (res != 0) { internal::sync_pthread_throw_exception("RecMutexPthread::native_unlock(): Error unlocking mutex.", res); } } RecMutexPthread() : count_(0) { pthread_mutexattr_t attr; int res = pthread_mutexattr_init(&attr); if (res != 0) { internal::sync_pthread_throw_exception("RecMutexPthread::RecMutexPthread(): Error creating mutex attributes.", res); } // may need -D__USE_UNIX98 on non-gcc (sun, pgi) res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); if (res != 0) { internal::sync_pthread_throw_exception("RecMutexPthread::RecMutexPthread(): Error setting mutex attributes.", res); pthread_mutexattr_destroy(&attr); // cleanup } res = pthread_mutex_init(&mutex_, &attr); if (res != 0) { internal::sync_pthread_throw_exception("RecMutexPthread::RecMutexPthread(): Error initializing mutex.", res); pthread_mutexattr_destroy(&attr); // cleanup } res = pthread_mutexattr_destroy(&attr); if (res != 0) { internal::sync_pthread_throw_exception("RecMutexPthread::RecMutexPthread(): Error destroying mutex attributes.", res); pthread_mutex_destroy(&mutex_); // avoid undetermined state } } ~RecMutexPthread() { int res = pthread_mutex_destroy(&mutex_); if (res != 0) { internal::sync_pthread_throw_exception("RecMutexPthread::~RecMutexPthread(): Error destroying mutex.", res); } } // Note: While pthread_mutex_t is recursively-lockable by its own, // these functions provide the benefit of avoiding a lock depth of // more than two, thus working around some system limits. // There's an additional checking as a bonus. void lock() { native_lock(mutex_); if (++count_ > 1) native_unlock(mutex_); } bool trylock() { if (native_trylock(mutex_) == false) return false; if (++count_ > 1) native_unlock(mutex_); return true; } void unlock() { if (count_ <= 0) internal::sync_pthread_throw_exception("RecMutexPthread::unlock(): Count underflow while trying to unlock a mutex."); if (--count_ == 0) native_unlock(mutex_); } private: pthread_mutex_t mutex_; unsigned count_; }; /// C++ wrapper for pthreads RW lock. class RWMutexPthread : public hz::noncopyable { public: typedef pthread_rwlock_t native_type; static void native_lock(native_type& mutex, bool for_write) { if (for_write) { int res = pthread_rwlock_wrlock(&mutex); if (res != 0) { internal::sync_pthread_throw_exception("RWMutexPthread::native_unlock(): Error write-locking a read/write lock.", res); } } else { int res = pthread_rwlock_rdlock(&mutex); if (res != 0) { internal::sync_pthread_throw_exception("RWMutexPthread::native_unlock(): Error read-locking a read/write lock.", res); } } } static bool native_trylock(native_type& mutex, bool for_write) { if (for_write) { int res = pthread_rwlock_trywrlock(&mutex); if (res == 0) return true; if (res != EBUSY) internal::sync_pthread_throw_exception("RWMutexPthread::native_trylock(): Error trying to write-lock a read/write lock.", res); } else { // read int res = pthread_rwlock_tryrdlock(&mutex); if (res == 0) return true; if (res != EBUSY) internal::sync_pthread_throw_exception("RWMutexPthread::native_trylock(): Error trying to read-lock a read/write lock.", res); } return false; } static void native_unlock(native_type& mutex, bool for_write) { int res = pthread_rwlock_unlock(&mutex); if (res != 0) internal::sync_pthread_throw_exception("RWMutexPthread::native_unlock(): Error while unlocking a read/write lock.", res); } RWMutexPthread() { int res = pthread_rwlock_init(&rwl_, NULL); if (res != 0) internal::sync_pthread_throw_exception("RWMutexPthread::RWMutexPthread(): Error while creating a read/write lock.", res); } ~RWMutexPthread() { int res = pthread_rwlock_destroy(&rwl_); if (res != 0) internal::sync_pthread_throw_exception("RWMutexPthread::RWMutexPthread(): Error while destroying a read/write lock.", res); } void lock(bool for_write = false) { native_lock(rwl_, for_write); } bool trylock(bool for_write = false) { return native_trylock(rwl_, for_write); } void unlock(bool for_write = false) { native_unlock(rwl_, for_write); } private: pthread_rwlock_t rwl_; }; /// Pthreads policy. /// Native type notes: none. struct SyncPolicyPthread : public SyncScopedLockProvider { // Types: typedef MutexPthread Mutex; typedef Mutex::native_type NativeMutex; typedef RecMutexPthread RecMutex; typedef RecMutex::native_type NativeRecMutex; ///< same type as NativeMutex typedef RWMutexPthread RWMutex; typedef RWMutex::native_type NativeRWMutex; typedef GenericScopedLock ScopedLock; typedef GenericScopedTryLock ScopedTryLock; typedef GenericScopedLock ScopedNativeLock; typedef GenericScopedTryLock ScopedNativeTryLock; typedef GenericScopedLock ScopedRecLock; typedef GenericScopedTryLock ScopedRecTryLock; typedef GenericScopedLock ScopedNativeRecLock; typedef GenericScopedTryLock ScopedNativeRecTryLock; typedef GenericScopedRWLock ScopedRWLock; typedef GenericScopedRWTryLock ScopedRWTryLock; typedef GenericScopedRWLock ScopedNativeRWLock; typedef GenericScopedRWTryLock ScopedNativeRWTryLock; // Static methods static bool init() { #ifdef PTW32_STATIC_LIB ptw32_processInitialize(); // needed only when linking statically to pthreads-win32 #endif return true; } static void lock(Mutex& m) { m.lock(); } static bool trylock(Mutex& m) { return m.trylock(); } static void unlock(Mutex& m) { m.unlock(); } static void lock(NativeMutex& m) { Mutex::native_lock(m); } static bool trylock(NativeMutex& m) { return Mutex::native_trylock(m); } static void unlock(NativeMutex& m) { Mutex::native_unlock(m); } static void lock(RecMutex& m) { m.lock(); } static bool trylock(RecMutex& m) { return m.trylock(); } static void unlock(RecMutex& m) { m.unlock(); } // static void lock(NativeRecMutex& m) { RecMutex::native_lock(m); } // static bool trylock(NativeRecMutex& m) { return RecMutex::native_trylock(m); } // static void unlock(NativeRecMutex& m) { RecMutex::native_unlock(m); } static void lock(RWMutex& m, bool for_write = false) { m.lock(for_write); } static bool trylock(RWMutex& m, bool for_write = false) { return m.trylock(for_write); } static void unlock(RWMutex& m, bool for_write = false) { m.unlock(for_write); } static void lock(NativeRWMutex& m, bool for_write = false) { RWMutex::native_lock(m, for_write); } static bool trylock(NativeRWMutex& m, bool for_write = false) { return RWMutex::native_trylock(m, for_write); } static void unlock(NativeRWMutex& m, bool for_write = false) { RWMutex::native_unlock(m, for_write); } }; // mutex -> policy template<> struct SyncGetPolicy { typedef SyncPolicyPthread type; }; template<> struct SyncGetPolicy { typedef SyncPolicyPthread type; }; template<> struct SyncGetPolicy { typedef SyncPolicyPthread type; }; // template<> struct SyncGetPolicy { typedef SyncPolicyPthread type; }; template<> struct SyncGetPolicy { typedef SyncPolicyPthread type; }; template<> struct SyncGetPolicy { typedef SyncPolicyPthread type; }; } // ns #endif gsmartcontrol-1.1.4/src/hz/sync_policy_win32.h000066400000000000000000000225641417717230600213720ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ #ifndef HZ_SYNC_POLICY_WIN32_H #define HZ_SYNC_POLICY_WIN32_H #include "hz_config.h" // feature macros #ifndef _WIN32 #error Cannot compile a win32-only file under non-win32 system #endif #include // Don't use DBG_ASSERT() here, it's using this library and therefore // too error-prone to use in this context. #include #ifndef ASSERT // assert() is undefined if NDEBUG is defined. #define ASSERT(a) assert(a) #endif #include "sync.h" /** \file Win32-Threads-based policy. */ namespace hz { /// C++ wrapper for Win32 CRITICAL_SECTION. /// Note: Win32 CRITICAL_SECTION is always recursive. template class MutexWin32 : public hz::noncopyable { public: typedef CRITICAL_SECTION native_type; static void native_lock(native_type& mutex) { try { EnterCriticalSection(&mutex); } catch (...) { THROW_FATAL(sync_resource_error("MutexWin32::native_lock(): Error locking mutex.")); } } static bool native_trylock(native_type& mutex) { bool res = 0; try { res = TryEnterCriticalSection(&mutex); } catch (...) { THROW_FATAL(sync_resource_error("MutexWin32::native_trylock(): Error trying to lock mutex.")); } return res; } static void native_unlock(native_type& mutex) { try { LeaveCriticalSection(&mutex); } catch (...) { THROW_FATAL(sync_resource_error("MutexWin32::native_unlock(): Error unlocking mutex.")); } } MutexWin32() { try { // MSDN says 4000 is a good value. has effect only for SMP. if (!InitializeCriticalSectionAndSpinCount(&cs_, 4000)) THROW_FATAL(sync_resource_error("MutexWin32::MutexWin32(): Error creating mutex.")); } catch (...) { THROW_FATAL(sync_resource_error("MutexWin32::MutexWin32(): Error creating mutex.")); } } ~MutexWin32() { try { DeleteCriticalSection(&cs_); } catch (...) { THROW_FATAL(sync_resource_error("MutexWin32::MutexWin32(): Error destroying mutex.")); } } void lock() { native_lock(cs_); } bool trylock() { return native_trylock(cs_); } void unlock() { native_unlock(cs_); } private: CRITICAL_SECTION cs_; }; /// Implementation of RW lock for Win32. class RWMutexWin32 : public hz::noncopyable { public: RWMutexWin32() : readers_(0), writers_(0) { if ((mutex_ = CreateMutexW(NULL, false, NULL)) != NULL) { if ((read_event_ = CreateEventW(NULL, true, true, NULL)) != NULL) { if ((write_event_ = CreateEventW(NULL, true, true, NULL)) != NULL) { return; } } } THROW_FATAL(sync_resource_error("RWMutexWin32::RWMutexWin32(): Error creating read/write lock.")); } ~RWMutexWin32() { try { if (mutex_) CloseHandle(mutex_); if (read_event_) CloseHandle(read_event_); if (write_event_) CloseHandle(write_event_); } catch (...) { THROW_FATAL(sync_resource_error("RWMutexWin32::~RWMutexWin32(): Error destroying read/write lock.")); } } void lock(bool for_write = false) { if (for_write) { add_writer(); HANDLE h[2] = {mutex_, write_event_}; DWORD res = WaitForMultipleObjects(2, h, TRUE, INFINITE); if (res == WAIT_OBJECT_0 || res == (WAIT_OBJECT_0 + 1)) { --writers_; ++readers_; ResetEvent(read_event_); ResetEvent(write_event_); ReleaseMutex(mutex_); } else { remove_writer(); THROW_FATAL(sync_resource_error("RWMutexWin32::lock(): Error write-locking a read/write lock.")); } } else { // read: HANDLE h[2] = {mutex_, read_event_}; DWORD res = WaitForMultipleObjects(2, h, TRUE, INFINITE); if (res == WAIT_OBJECT_0 || res == (WAIT_OBJECT_0 + 1)) { ++readers_; ResetEvent(write_event_); ReleaseMutex(mutex_); } else { THROW_FATAL(sync_resource_error("RWMutexWin32::lock(): Error read-locking a read/write lock.")); } } } bool trylock(bool for_write = false) { if (for_write) { add_writer(); HANDLE h[2] = {mutex_, write_event_}; DWORD res = WaitForMultipleObjects(2, h, TRUE, 1); if (res == WAIT_OBJECT_0 || res == (WAIT_OBJECT_0 + 1)) { --writers_; ++readers_; ResetEvent(read_event_); ResetEvent(write_event_); ReleaseMutex(mutex_); return true; } else { remove_writer(); if (res != WAIT_TIMEOUT) THROW_FATAL(sync_resource_error("RWMutexWin32::trylock(): Error while trying to write-lock a read/write lock.")); return false; } } else { // read: HANDLE h[2] = {mutex_, read_event_}; DWORD res = WaitForMultipleObjects(2, h, TRUE, 1); if (res == WAIT_OBJECT_0 || res == (WAIT_OBJECT_0 + 1)) { ++readers_; ResetEvent(write_event_); ReleaseMutex(mutex_); return true; } else { if (res != WAIT_TIMEOUT) THROW_FATAL(sync_resource_error("RWMutexWin32::trylock(): Error while trying to read-lock a read/write lock.")); return false; } } } void unlock(bool for_write = false) { if (WaitForSingleObject(mutex_, INFINITE) == WAIT_OBJECT_0) { if (writers_ == 0) SetEvent(read_event_); if (--readers_ == 0) SetEvent(write_event_); ReleaseMutex(mutex_); return; } THROW_FATAL(sync_resource_error("RWMutexWin32::unlock(): Error while unlocking a read/write lock.")); } private: void add_writer() { if (WaitForSingleObject(mutex_, INFINITE) == WAIT_OBJECT_0) { if (++writers_ == 1) ResetEvent(read_event_); ReleaseMutex(mutex_); return; } THROW_FATAL(sync_resource_error("RWMutexWin32::add_writer(): Error while locking a read/write lock.")); } void remove_writer() { if (WaitForSingleObject(mutex_, INFINITE) == WAIT_OBJECT_0) { if (--writers_ == 0) SetEvent(read_event_); ReleaseMutex(mutex_); return; } THROW_FATAL(sync_resource_error("RWMutexWin32::remove_writer(): Error while locking a read/write lock.")); } HANDLE mutex_; HANDLE read_event_; HANDLE write_event_; unsigned int readers_; unsigned int writers_; }; /// Win32-threads policy. /// Native type notes: none. /// Note: We don't know if we trap all the error conditions - one can never be sure with win32. struct SyncPolicyWin32 : public SyncScopedLockProvider { // Types: typedef MutexWin32<1> Mutex; typedef Mutex::native_type NativeMutex; typedef MutexWin32<2> RecMutex; typedef RecMutex::native_type NativeRecMutex; ///< same type as NativeMutex typedef RWMutexWin32 RWMutex; typedef RWMutex NativeRWMutex; ///< win32 doesn't have a native RWMutex-like type, so use the same type. typedef GenericScopedLock ScopedLock; typedef GenericScopedTryLock ScopedTryLock; typedef GenericScopedLock ScopedNativeLock; typedef GenericScopedTryLock ScopedNativeTryLock; typedef GenericScopedLock ScopedRecLock; typedef GenericScopedTryLock ScopedRecTryLock; typedef GenericScopedLock ScopedNativeRecLock; typedef GenericScopedTryLock ScopedNativeRecTryLock; typedef GenericScopedRWLock ScopedRWLock; typedef GenericScopedRWTryLock ScopedRWTryLock; typedef GenericScopedRWLock ScopedNativeRWLock; typedef GenericScopedRWTryLock ScopedNativeRWTryLock; // Static methods static bool init() { return true; } static void lock(Mutex& m) { m.lock(); } static bool trylock(Mutex& m) { return m.trylock(); } static void unlock(Mutex& m) { m.unlock(); } static void lock(NativeMutex& m) { Mutex::native_lock(m); } static bool trylock(NativeMutex& m) { return Mutex::native_trylock(m); } static void unlock(NativeMutex& m) { Mutex::native_unlock(m); } static void lock(RecMutex& m) { m.lock(); } static bool trylock(RecMutex& m) { return m.trylock(); } static void unlock(RecMutex& m) { m.unlock(); } // static void lock(NativeRecMutex& m) { RecMutex::native_lock(m); } // static bool trylock(NativeRecMutex& m) { return RecMutex::native_trylock(m); } // static void unlock(NativeRecMutex& m) { RecMutex::native_unlock(m); } static void lock(RWMutex& m, bool for_write = false) { m.lock(for_write); } static bool trylock(RWMutex& m, bool for_write = false) { return m.trylock(for_write); } static void unlock(RWMutex& m, bool for_write = false) { m.unlock(for_write); } // static void lock(NativeRWMutex& m, bool for_write = false) { RWMutex::native_lock(m, for_write); } // static bool trylock(NativeRWMutex& m, bool for_write = false) { return RWMutex::native_trylock(m, for_write); } // static void unlock(NativeRWMutex& m, bool for_write = false) { RWMutex::native_unlock(m, for_write); } }; // mutex -> policy template<> struct SyncGetPolicy { typedef SyncPolicyWin32 type; }; template<> struct SyncGetPolicy { typedef SyncPolicyWin32 type; }; template<> struct SyncGetPolicy { typedef SyncPolicyWin32 type; }; // template<> struct SyncGetPolicy { typedef SyncPolicyWin32 type; }; template<> struct SyncGetPolicy { typedef SyncPolicyWin32 type; }; // template<> struct SyncGetPolicy { typedef SyncPolicyWin32 type; }; } // ns #endif gsmartcontrol-1.1.4/src/hz/sync_test.cpp000066400000000000000000000042071417717230600203550ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_unlicense.txt ***************************************************************************/ // disable libdebug, we don't link to it #undef HZ_USE_LIBDEBUG #define HZ_USE_LIBDEBUG 0 // enable libdebug emulation through std::cerr #undef HZ_EMULATE_LIBDEBUG #define HZ_EMULATE_LIBDEBUG 1 // If none are defined and there are no undefs below, the default // policy is used (see global_macros.h). /* #undef HZ_SYNC_DEFAULT_POLICY_GLIBMM #undef HZ_SYNC_DEFAULT_POLICY_GLIB #undef HZ_SYNC_DEFAULT_POLICY_BOOST #undef HZ_SYNC_DEFAULT_POLICY_POCO #undef HZ_SYNC_DEFAULT_POLICY_PTHREAD #undef HZ_SYNC_DEFAULT_POLICY_WIN32 // #define HZ_SYNC_DEFAULT_POLICY_GLIBMM // #define HZ_SYNC_DEFAULT_POLICY_GLIB // #define HZ_SYNC_DEFAULT_POLICY_BOOST // #define HZ_SYNC_DEFAULT_POLICY_POCO // #define HZ_SYNC_DEFAULT_POLICY_PTHREAD // #define HZ_SYNC_DEFAULT_POLICY_WIN32 */ // The first header should be then one we're testing, to avoid missing // header pitfalls. #include "sync.h" #include int main() { using namespace hz; SyncPolicyMtDefault::init(); { SyncPolicyMtDefault::Mutex m; SyncPolicyMtDefault::lock(m); SyncPolicyMtDefault::unlock(m); SyncPolicyMtDefault::trylock(m); SyncPolicyMtDefault::unlock(m); SyncPolicyMtDefault::ScopedLock lock(m); } { SyncPolicyMtDefault::RecMutex m; SyncPolicyMtDefault::lock(m); SyncPolicyMtDefault::trylock(m); SyncPolicyMtDefault::unlock(m); SyncPolicyMtDefault::unlock(m); SyncPolicyMtDefault::ScopedRecLock lock(m); } { SyncPolicyMtDefault::RWMutex m; SyncPolicyMtDefault::lock(m); SyncPolicyMtDefault::unlock(m); SyncPolicyMtDefault::trylock(m); SyncPolicyMtDefault::unlock(m); SyncPolicyMtDefault::lock(m, true); SyncPolicyMtDefault::unlock(m, true); SyncPolicyMtDefault::trylock(m, true); SyncPolicyMtDefault::unlock(m, true); { SyncPolicyMtDefault::ScopedRWLock lock(m); } { SyncPolicyMtDefault::ScopedRWLock lock(m, true); } } std::cerr << "All OK\n"; return 0; } gsmartcontrol-1.1.4/src/hz/system_specific.h000066400000000000000000000063371417717230600212060ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2013 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_SYSTEM_SPECIFIC_H #define HZ_SYSTEM_SPECIFIC_H #include "hz_config.h" // feature macros /** \file System/compiler-specific stuff goes here... */ /// \def HZ_GCC_CHECK_VERSION(major, minor, micro) /// returns true if gcc version is greater or equal to specified. #ifndef HZ_GCC_CHECK_VERSION #define HZ_GCC_CHECK_VERSION(major, minor, micro) \ ( defined (__GNUC__) && ( \ ( (__GNUC__) > (major) ) \ || ( ((__GNUC__) == (major)) && ((__GNUC_MINOR__) > (minor)) ) \ || ( ((__GNUC__) == (major)) && ((__GNUC_MINOR__) == (minor)) && ((__GNUC_PATCHLEVEL__) >= (micro)) ) \ ) ) #endif /// \def HZ_GCC_ATTR(a) /// Wrap gcc's attributes. Evaluates to nothing in non-gcc-compatible compilers. #ifndef HZ_GCC_ATTR #ifdef __GNUC__ #define HZ_GCC_ATTR(a) __attribute__((a)) #else #define HZ_GCC_ATTR(a) #endif #endif /// \def HZ_FUNC_PRINTF_ISO_CHECK(format_idx, check_idx) /// Easy compile-time printf format checks. /// See http://gcc.gnu.org/onlinedocs/gcc-4.4.1/gcc/Function-Attributes.html /// \def HZ_FUNC_PRINTF_MS_CHECK(format_idx, check_idx) /// Easy compile-time printf format checks (windows version of format specifiers). /// ms_printf is available since gcc 4.4. /// Note: When using __USE_MINGW_ANSI_STDIO, mingw uses /// its own *printf() implementation (rather than msvcrt), which accepts /// both MS-style and standard format specifiers. /// ms_printf gives warnings on standard specifiers, but we still keep /// it so that the code will be portable to other win32 environments. /// TODO: Check if simply specifying "printf" selects the correct /// version for mingw. #ifndef HZ_FUNC_PRINTF_ISO_CHECK #define HZ_FUNC_PRINTF_ISO_CHECK(format_idx, check_idx) HZ_GCC_ATTR(format(printf, format_idx, check_idx)) #endif #ifndef HZ_FUNC_PRINTF_MS_CHECK #if defined _WIN32 && HZ_GCC_CHECK_VERSION(4, 4, 0) #define HZ_FUNC_PRINTF_MS_CHECK(format_idx, check_idx) HZ_GCC_ATTR(format(ms_printf, format_idx, check_idx)) #else #define HZ_FUNC_PRINTF_MS_CHECK(format_idx, check_idx) #endif #endif #include /** \fn std::string hz::type_name_demangle(const std::string& name) Demangle a C/C++ type name, as returned by std::type_info.name(). Similar to c++filt command. Supported under gcc only for now. */ #if defined HAVE_GCC_ABI_DEMANGLE && HAVE_GCC_ABI_DEMANGLE #include #include // std::free(). #include // __cxa_demangle namespace hz { inline std::string type_name_demangle(const std::string& name) { int status = 0; char* demangled = ::abi::__cxa_demangle(name.c_str(), NULL, NULL, &status); std::string ret; if (demangled) { if (status == 0) ret = demangled; std::free(demangled); } return ret; } } // ns hz #else // non-gcc-compatible namespace hz { inline std::string type_name_demangle(const std::string& name) { return name; // can't do anything here } } // ns hz #endif #endif /// @} gsmartcontrol-1.1.4/src/hz/tls.h000066400000000000000000000246411417717230600166150ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_TLS_H #define HZ_TLS_H #include "hz_config.h" // feature macros #include // std::free() // Don't use DBG_ASSERT() here, it's using this library and therefore // too error-prone to use in this context. #include #ifndef ASSERT // assert() is undefined if NDEBUG is defined. #define ASSERT(a) assert(a) #endif /** \file Thread-local storage. Notes on various policies: Poco-based policy cannot be implemented because Poco's ThreadLocal lacks some vital methods, e.g. setting it to an existing pointer (it creates the object itself), or specifying a destroy function (it just deletes the pointer). Glibmm-based policy has been implemented but it had problems with calling the destructor function (not calling it at all), plus it's completely redundant with availability of Glib policy. Win32-based policy doesn't have on-thread-exit cleanup function support due to technical limitations of MS design. Cleanup functions are called on object destruction and reset(), however. If this is a major problem, use Glib, pthread-win32 or Boost policies instead (they have some dll-specific workarounds implemented). See win32 policy file for details. */ namespace hz { /// TLS policy suitable for single-threaded model. struct TlsPolicyNone { typedef void (*native_cleanup_func_t)(void*); TlsPolicyNone(native_cleanup_func_t native_cleanup) : native_cleanup_(native_cleanup), inited_(false), p_(0) { } ~TlsPolicyNone() { if (inited_ && native_cleanup_) native_cleanup_(get()); } void* get() const { return p_; } void reset(void* p) { // call the cleanup function for the previous pointer. if (inited_ && native_cleanup_) native_cleanup_(p_); if (p) inited_ = true; p_ = p; } private: native_cleanup_func_t native_cleanup_; ///< may be NULL bool inited_; void* p_; TlsPolicyNone(const TlsPolicyNone&); TlsPolicyNone& operator= (const TlsPolicyNone& from); }; } /// \class TlsPolicyMtDefault /// Default TLS policy for thread_local_ptr. #if defined HZ_TLS_DEFAULT_POLICY_GLIB #include "tls_policy_glib.h" namespace hz { typedef TlsPolicyGlib TlsPolicyMtDefault; } #elif defined HZ_TLS_DEFAULT_POLICY_BOOST // No header here. The header contains thread_local_ptr specialization, // therefore it must be after thread_local_ptr (see below). namespace hz { // Note: TlsPolicyMtDefault should be before thread_local_ptr - it's // used as its default template argument. struct TlsPolicyBoost { }; // dummy, only to help specialization typedef TlsPolicyBoost TlsPolicyMtDefault; } #elif defined HZ_TLS_DEFAULT_POLICY_PTHREAD #include "tls_policy_pthread.h" namespace hz { typedef TlsPolicyPthread TlsPolicyMtDefault; } #elif defined HZ_TLS_DEFAULT_POLICY_WIN32 #include "tls_policy_win32.h" namespace hz { typedef TlsPolicyWin32 TlsPolicyMtDefault; } #else // default: NONE // #elif defined HZ_TLS_DEFAULT_POLICY_NONE // default: namespace hz { typedef TlsPolicyNone TlsPolicyMtDefault; } #endif namespace hz { namespace internal { /// Base class, needed to distinguish between actual and void* specialization template class thread_local_ptr_base { protected: /// Constructor, takes policy object template thread_local_ptr_base(PolicyArg cleanup) : policy_(cleanup) { } public: /// Destructor, deletes the pointer. ~thread_local_ptr_base() { reset(); } /// Get the wrapped pointer T* get() const { return static_cast(policy_.get()); } /// Get the wrapped pointer and call reset(). /// FIXME Is this correct (returning T* after reset())? T* release() { T* tmp = get(); reset(); return tmp; } /// Reset the pointer (delete it), taking ownership of the new one. void reset(T* p = 0) { if (get() == p) return; // this will call the cleanup function for the previous pointer policy_.reset(static_cast(p)); } /// Check policy capabilities - whether cleanup is supported bool cleanup_supported() { return TlsPolicy::cleanup_supported; } private: TlsPolicy policy_; ///< The policy object }; } // ns // -------------------------------- C++-style cleanup function linkage /** Note: These functions have C++ linkage. It is impossible to make them extern "C", because they are templates. Moving to void* arguments is also invalid, because C++ operator delete cannot reliably delete a void* pointer (destructor may not get called, etc...). C linkage _may_ be needed if: 1. The compiler doesn't support calling C++ functions by pointers from C code, AND 2. The underlying TLS implementation is in C (e.g. GLib, pthread, etc...). Note that this may not affect implementations which do implement their own callback system (e.g. Boost). If you desperately need C linkage, you may define your own C-linked callback. However, note that you MUST NOT delete the resulting pointer with delete operator if it's void*. Note: There may be problems when passing extern "C" function as a template parameter to non-extern "C" class template (sun compiler). */ template struct tls_functions { /// This will be called by default if no cleanup function has been set. static void cleanup_delete(T* p) { delete p; } /// Use this for delete[] static void cleanup_delete_array(T* p) { delete[] p; } /// Use this for C's free() static void cleanup_free(T* p) { std::free(p); } /// Use this for no cleanup static void nothing(T* p) { } // Note: This will work with C linkage too, because it's not actually called. }; /// Thread-local pointer. template::cleanup_delete> class thread_local_ptr : public internal::thread_local_ptr_base { // This class cannot support DefaultType, because it may be specialized by TlsPolicy parameter. // typedef typename type_auto_select::type TlsPolicy; // support DefaultType typedef internal::thread_local_ptr_base base; ///< Base class typedef thread_local_ptr self_type; ///< Self type public: using base::get; using base::release; using base::reset; using base::cleanup_supported; /// Constructor thread_local_ptr() : base(cleanup_func == tls_functions::nothing ? NULL : &cleanup_proxy) { } /// Arrow operator T* operator->() const { return get(); } /// Dereference operator T& operator*() const { T* cur = get(); ASSERT(cur); return *cur; } private: /// Disallow copying thread_local_ptr(const self_type&); /// Disallow copying thread_local_ptr& operator= (const self_type& from); /// Internal proxy function. This is called by the underlying implementation. /// Note: This should have been a C linkage function, because its pointer may /// be passed to C functions. However, we can't do that because it's a template. /// So we rely on compiler to support passing C++ function pointers to C functions. static void cleanup_proxy(void* p) { cleanup_func(static_cast(p)); } }; /// void* specialization of thread_local_ptr. Disallows operator*. template class thread_local_ptr : public internal::thread_local_ptr_base { typedef internal::thread_local_ptr_base base; typedef thread_local_ptr self_type; public: using base::get; using base::release; using base::reset; using base::cleanup_supported; thread_local_ptr() : base(cleanup_func == tls_functions::nothing ? NULL : cleanup_func) { } void* operator->() const { return get(); } private: thread_local_ptr(const self_type&); thread_local_ptr& operator= (const self_type& from); }; // -------------------------------- C-style cleanup function linkage /// C functions for T=void. extern "C" { /// Cleanup function type for void*, with C linkage. typedef void (tls_cleanup_c_func_t)(void*); /// A function that calls std::free(). C-linkage. inline void tls_cleanup_c_free(void* p) { std::free(p); } /// A function that does nothing. C-linkage. inline void tls_cleanup_c_nothing(void* p) { } } /// Same as thread_local_ptr, but uses C-linkage functions for cleanup. template class thread_local_c_ptr : public internal::thread_local_ptr_base { typedef internal::thread_local_ptr_base base; ///< Base class typedef thread_local_ptr self_type; ///< Self type public: using base::get; using base::release; using base::reset; using base::cleanup_supported; /// Constructor thread_local_c_ptr() : base(cleanup_func == tls_cleanup_c_nothing ? NULL : cleanup_func) { } /// Arrow operator T* operator->() const { return get(); } /// Dereference operator T& operator*() const { T* cur = get(); ASSERT(cur); return *cur; } private: /// Disallow copying thread_local_c_ptr(const self_type&); /// Disallow copying thread_local_c_ptr& operator= (const self_type& from); }; /// void* specialization for thread_local_c_ptr. Disallows operator*. template class thread_local_c_ptr : public internal::thread_local_ptr_base { typedef internal::thread_local_ptr_base base; typedef thread_local_ptr self_type; public: using base::get; using base::release; using base::reset; using base::cleanup_supported; thread_local_c_ptr() : base(cleanup_func == tls_cleanup_c_nothing ? NULL : cleanup_func) { } void* operator->() const { return get(); } private: thread_local_c_ptr(const self_type&); thread_local_c_ptr& operator= (const self_type& from); }; } // ns // must be after thread_local_ptr #if defined HZ_TLS_DEFAULT_POLICY_BOOST #include "tls_policy_boost.h" #endif #endif /// @} gsmartcontrol-1.1.4/src/hz/tls_policy_boost.h000066400000000000000000000037551417717230600214050ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_TLS_BOOST_H #define HZ_TLS_BOOST_H #include "hz_config.h" // feature macros #include /** \file Boost-based thread-local storage. */ namespace hz { // TlsPolicyBoost is declared in tls.h. // Our class mimics boost::thread_specific_ptr<> in its API, // so we may just wrap it without any changes. template class thread_local_ptr : public boost::thread_specific_ptr { public: thread_local_ptr() : boost::thread_specific_ptr(cleanup_func) { } static const bool cleanup_supported = true; }; // boost doesn't support void*, but we still need to specialize to avoid // ambigous situations. template class thread_local_ptr { public: thread_local_ptr() { } // these should trigger the errors void get() { } void reset() { } }; // These two will work if the compiler supports passing C functions // to C++-linkage template arguments template class thread_local_c_ptr : public boost::thread_specific_ptr { public: thread_local_c_ptr() : boost::thread_specific_ptr(cleanup_func) { } static const bool cleanup_supported = true; }; // boost doesn't support void*, but we still need to specialize to avoid // ambigous situations. template class thread_local_c_ptr { public: thread_local_c_ptr() { } // these should trigger the errors void get() { } void reset() { } }; } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/tls_policy_glib.h000066400000000000000000000054711417717230600211710ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_TLS_POLICY_GLIB_H #define HZ_TLS_POLICY_GLIB_H #include "hz_config.h" // feature macros #include /** \file Glib-based thread-local storage. */ namespace hz { extern "C" { typedef void (*tls_policy_glib_cleanup_func_t)(void*); } // suncc needs this #if defined HAVE_CXX_EXTERN_C_OVERLOAD && HAVE_CXX_EXTERN_C_OVERLOAD typedef void (*tls_policy_glib_cleanup_func_cpp_t)(void*); #endif #if GLIB_CHECK_VERSION(2, 32, 0) /// Glib-based TLS policy class TlsPolicyGlib { public: TlsPolicyGlib(tls_policy_glib_cleanup_func_t native_cleanup) : key_((GPrivate)G_PRIVATE_INIT(native_cleanup)) { } #if defined HAVE_CXX_EXTERN_C_OVERLOAD && HAVE_CXX_EXTERN_C_OVERLOAD TlsPolicyGlib(tls_policy_glib_cleanup_func_cpp_t native_cleanup) : key_((GPrivate)G_PRIVATE_INIT(native_cleanup)) { } #endif ~TlsPolicyGlib() { // Nothing } void* get() const { return g_private_get(&key_); } void reset(void* p) { // this will call the cleanup function for the previous pointer g_private_replace(&key_, static_cast(p)); } static const bool cleanup_supported = true; private: mutable GPrivate key_; TlsPolicyGlib(const TlsPolicyGlib&); TlsPolicyGlib& operator= (const TlsPolicyGlib& from); }; #else // old glib /// Glib-based TLS policy class TlsPolicyGlib { public: TlsPolicyGlib(tls_policy_glib_cleanup_func_t native_cleanup) : native_cleanup_(native_cleanup) { g_static_private_init(&key_); } #if defined HAVE_CXX_EXTERN_C_OVERLOAD && HAVE_CXX_EXTERN_C_OVERLOAD TlsPolicyGlib(tls_policy_glib_cleanup_func_cpp_t native_cleanup) : native_cleanup_(native_cleanup) { g_static_private_init(&key_); } #endif ~TlsPolicyGlib() { // if this object is not static, then it will die before the thread. // g_static_private_free() will call the destructor function if the key was associated // with a non-NULL value at least once. g_static_private_free(&key_); } void* get() const { return g_static_private_get(&key_); } void reset(void* p) { // this will call the cleanup function for the previous pointer g_static_private_set(&key_, static_cast(p), native_cleanup_); } static const bool cleanup_supported = true; private: mutable GStaticPrivate key_; tls_policy_glib_cleanup_func_t native_cleanup_; ///< may be NULL TlsPolicyGlib(const TlsPolicyGlib&); TlsPolicyGlib& operator= (const TlsPolicyGlib& from); }; #endif } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/tls_policy_pthread.h000066400000000000000000000051161417717230600216770ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_TLS_POLICY_PTHREAD_H #define HZ_TLS_POLICY_PTHREAD_H #include "hz_config.h" // feature macros #include // Don't use DBG_ASSERT() here, it's using this library and therefore // too error-prone to use in this context. #include #ifndef ASSERT // assert() is undefined if NDEBUG is defined. #define ASSERT(a) assert(a) #endif /** \file Pthreads-based thread-local storage. Note that cleanup-function calling policy emulates the Glib one. */ namespace hz { extern "C" { typedef void (*tls_policy_pthread_cleanup_func_t)(void*); } // suncc needs this #if defined HAVE_CXX_EXTERN_C_OVERLOAD && HAVE_CXX_EXTERN_C_OVERLOAD typedef void (*tls_policy_pthread_cleanup_cpp_func_t)(void*); #endif /// Pthreads-based TLS policy class TlsPolicyPthread { public: TlsPolicyPthread(tls_policy_pthread_cleanup_func_t native_cleanup) : native_cleanup_(native_cleanup), inited_(false) { int res = pthread_key_create(&key_, native_cleanup); ASSERT(res == 0); } #if defined HAVE_CXX_EXTERN_C_OVERLOAD && HAVE_CXX_EXTERN_C_OVERLOAD TlsPolicyPthread(tls_policy_pthread_cleanup_cpp_func_t native_cleanup) : native_cleanup_(native_cleanup) { int res = pthread_key_create(&key_, native_cleanup); ASSERT(res == 0); } #endif ~TlsPolicyPthread() { // if this object is not static, then it will die before the thread. // pthread_key_delete() won't call the destructor function, so call it manually. if (inited_ && native_cleanup_) native_cleanup_(get()); int res = pthread_key_delete(key_); ASSERT(res == 0); } void* get() const { return pthread_getspecific(key_); } void reset(void* p) { // call the cleanup function for the previous pointer. if (inited_ && native_cleanup_) native_cleanup_(get()); if (p) inited_ = true; int res = pthread_setspecific(key_, p); ASSERT(res == 0); } static const bool cleanup_supported = true; private: pthread_key_t key_; tls_policy_pthread_cleanup_func_t native_cleanup_; // may be NULL bool inited_; // if the key has been associated with the non-NULL value at least once. TlsPolicyPthread(const TlsPolicyPthread&); TlsPolicyPthread& operator= (const TlsPolicyPthread& from); }; } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/tls_policy_win32.h000066400000000000000000000046451417717230600212200ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_TLS_POLICY_WIN32_H #define HZ_TLS_POLICY_WIN32_H #include "hz_config.h" // feature macros #ifndef _WIN32 #error Cannot compile a win32-only file under non-win32 system #endif #include // Don't use DBG_ASSERT() here, it's using this library and therefore // too error-prone to use in this context. #include #ifndef ASSERT // assert() is undefined if NDEBUG is defined. #define ASSERT(a) assert(a) #endif /** \file Win32-threads-based thread-local storage. NOTE On-thread-exit cleanup functions ARE NOT implemented. They are called on object destruction and reset(), however. Implementing on-thread-exit callbacks requires some really ugly hacks, introducing some really ugly problems and limitations. See http://lists.boost.org/Archives/boost/2003/02/44905.php */ namespace hz { /// Win32-threads-based TLS policy class TlsPolicyWin32 { public: typedef void (*native_cleanup_func_t)(void*); TlsPolicyWin32(native_cleanup_func_t native_cleanup) : native_cleanup_(native_cleanup), inited_(false) { key_ = TlsAlloc(); // sets data to 0. ASSERT(key_ != TLS_OUT_OF_INDEXES); } ~TlsPolicyWin32() { // call cleanup manually if (inited_ && native_cleanup_) native_cleanup_(get()); if (key_ != TLS_OUT_OF_INDEXES) { BOOL res = TlsFree(key_); ASSERT(res != 0); } } void* get() const { void* p = TlsGetValue(key_); ASSERT(p || GetLastError() == ERROR_SUCCESS); // if p is 0, check last error return p; } void reset(void* p) { // call the cleanup function for the previous pointer. if (inited_ && native_cleanup_) native_cleanup_(get()); if (p) inited_ = true; BOOL res = TlsSetValue(key_, p); ASSERT(res != 0); } static const bool cleanup_supported = false; private: DWORD key_; native_cleanup_func_t native_cleanup_; ///< may be NULL bool inited_; ///< True if the key has been associated with the non-NULL value at least once. TlsPolicyWin32(const TlsPolicyWin32&); TlsPolicyWin32& operator= (const TlsPolicyWin32& from); }; } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/tls_test.cpp000066400000000000000000000044511417717230600202040ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_unlicense.txt ***************************************************************************/ // disable libdebug, we don't link to it #undef HZ_USE_LIBDEBUG #define HZ_USE_LIBDEBUG 0 // enable libdebug emulation through std::cerr #undef HZ_EMULATE_LIBDEBUG #define HZ_EMULATE_LIBDEBUG 1 // If none are defined and there are no undefs below, the default // policy is used (see global_macros.h). /* #undef HZ_TLS_DEFAULT_POLICY_GLIB #undef HZ_TLS_DEFAULT_POLICY_BOOST #undef HZ_TLS_DEFAULT_POLICY_PTHREAD #undef HZ_TLS_DEFAULT_POLICY_WIN32 // #define HZ_TLS_DEFAULT_POLICY_GLIB // #define HZ_TLS_DEFAULT_POLICY_BOOST #define HZ_TLS_DEFAULT_POLICY_PTHREAD // #define HZ_TLS_DEFAULT_POLICY_WIN32 */ // The first header should be then one we're testing, to avoid missing // header pitfalls. #include "tls.h" #include // For those compilers who don't support mixing C- and C++-style // linkage functions in function pointers, you should use a special // thread_local_c_ptr<> variant. // C++-style, for thread_local_ptr<> inline void custom_cleanup(int* p) { std::cerr << "Calling custom_cleanup with p=" << p << "\n"; delete p; } // C-style, for thread_local_c_ptr<> inline void custom_cleanup_c(void* p) { std::cerr << "Calling custom_cleanup_c with p=" << p << "\n"; free(p); } int main() { using namespace hz; // g_thread_init(NULL); { thread_local_ptr p; p.reset(new int(5)); std::cerr << *p << "\n"; p.reset(new int(6)); std::cerr << *p << "\n"; } { thread_local_ptr p; // static thread_local_ptr p; // void* pointers // thread_local_ptr p; // g++ yes, intel yes, sun no. // thread_local_c_ptr p; // all yes. p.reset(new int(7)); // should be NO cleanup here // std::cerr << *p << "\n"; p.reset(); // should call cleanup here std::cerr << p.get() << "\n"; // should call cleanup here if NOT static } std::cerr << "All OK\n"; // should call cleanup here if _static_ return 0; } gsmartcontrol-1.1.4/src/hz/type_categories.h000066400000000000000000000101631417717230600211730ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2003 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_TYPE_CATEGORIES_H #define HZ_TYPE_CATEGORIES_H #include "hz_config.h" // feature macros /** \file Type categories for easy specialization generation. This file provides some type facilities in absence of C++11's type_traits header. */ namespace hz { // split by integral / floating point struct type_arithm_unknown { }; struct type_arithm_integral { }; struct type_arithm_floating_point { }; template struct type_check_arithmetic { typedef type_arithm_unknown type; }; template<> struct type_check_arithmetic { typedef type_arithm_integral type; }; template<> struct type_check_arithmetic { typedef type_arithm_integral type; }; template<> struct type_check_arithmetic { typedef type_arithm_integral type; }; template<> struct type_check_arithmetic { typedef type_arithm_integral type; }; template<> struct type_check_arithmetic { typedef type_arithm_integral type; }; template<> struct type_check_arithmetic { typedef type_arithm_integral type; }; template<> struct type_check_arithmetic { typedef type_arithm_integral type; }; template<> struct type_check_arithmetic { typedef type_arithm_integral type; }; template<> struct type_check_arithmetic { typedef type_arithm_integral type; }; template<> struct type_check_arithmetic { typedef type_arithm_integral type; }; template<> struct type_check_arithmetic { typedef type_arithm_integral type; }; #if !(defined DISABLE_LL_INT && DISABLE_LL_INT) template<> struct type_check_arithmetic { typedef type_arithm_integral type; }; #endif #if !(defined DISABLE_ULL_INT && DISABLE_ULL_INT) template<> struct type_check_arithmetic { typedef type_arithm_integral type; }; #endif template<> struct type_check_arithmetic { typedef type_arithm_floating_point type; }; template<> struct type_check_arithmetic { typedef type_arithm_floating_point type; }; template<> struct type_check_arithmetic { typedef type_arithm_floating_point type; }; // split by category - bool, char, int, float struct type_cat_unknown { }; struct type_cat_bool { }; struct type_cat_char { }; struct type_cat_int { }; struct type_cat_float { }; template struct type_check_category { typedef type_cat_unknown type; }; template<> struct type_check_category { typedef type_cat_bool type; }; template<> struct type_check_category { typedef type_cat_char type; }; template<> struct type_check_category { typedef type_cat_char type; }; template<> struct type_check_category { typedef type_cat_char type; }; template<> struct type_check_category { typedef type_cat_char type; }; template<> struct type_check_category { typedef type_cat_int type; }; template<> struct type_check_category { typedef type_cat_int type; }; template<> struct type_check_category { typedef type_cat_int type; }; template<> struct type_check_category { typedef type_cat_int type; }; template<> struct type_check_category { typedef type_cat_int type; }; template<> struct type_check_category { typedef type_cat_int type; }; #if !(defined DISABLE_LL_INT && DISABLE_LL_INT) template<> struct type_check_category { typedef type_cat_int type; }; #endif #if !(defined DISABLE_ULL_INT && DISABLE_ULL_INT) template<> struct type_check_category { typedef type_cat_int type; }; #endif template<> struct type_check_category { typedef type_cat_float type; }; template<> struct type_check_category { typedef type_cat_float type; }; template<> struct type_check_category { typedef type_cat_float type; }; } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/type_properties.h000066400000000000000000000226711417717230600212510ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_TYPE_PROPERTIES_H #define HZ_TYPE_PROPERTIES_H #include "hz_config.h" // feature macros #include // std::size_t /** \file Type transformations, properties, etc... This file provides some type facilities in absence of C++11's type_traits header. */ namespace hz { // ---------------------- Type Examination #define TYPE_DEFINE_SPEC_0(Trait, Type, Value) \ template<> struct Trait : public type_integral_constant { }; \ template<> struct Trait : public type_integral_constant { }; \ template<> struct Trait : public type_integral_constant { }; \ template<> struct Trait : public type_integral_constant { } #define TYPE_DEFINE_SPEC_1(Trait, Type, Value) \ template struct Trait : public type_integral_constant { }; \ template struct Trait : public type_integral_constant { }; \ template struct Trait : public type_integral_constant { }; \ template struct Trait : public type_integral_constant { } template struct type_integral_constant { static const T value = v; typedef T value_type; typedef type_integral_constant type; }; template const T type_integral_constant::value; typedef type_integral_constant type_true_type; typedef type_integral_constant type_false_type; // needed for some other checks template struct type_is_void : public type_false_type { }; TYPE_DEFINE_SPEC_0(type_is_void, void, true); template struct type_is_integral : public type_false_type { }; TYPE_DEFINE_SPEC_0(type_is_integral, bool, true); TYPE_DEFINE_SPEC_0(type_is_integral, char, true); TYPE_DEFINE_SPEC_0(type_is_integral, signed char, true); TYPE_DEFINE_SPEC_0(type_is_integral, unsigned char, true); TYPE_DEFINE_SPEC_0(type_is_integral, wchar_t, true); TYPE_DEFINE_SPEC_0(type_is_integral, short, true); TYPE_DEFINE_SPEC_0(type_is_integral, unsigned short, true); TYPE_DEFINE_SPEC_0(type_is_integral, int, true); TYPE_DEFINE_SPEC_0(type_is_integral, unsigned int, true); TYPE_DEFINE_SPEC_0(type_is_integral, long, true); TYPE_DEFINE_SPEC_0(type_is_integral, unsigned long, true); #if !(defined DISABLE_LL_INT && DISABLE_LL_INT) TYPE_DEFINE_SPEC_0(type_is_integral, long long, true); #endif #if !(defined DISABLE_ULL_INT && DISABLE_ULL_INT) TYPE_DEFINE_SPEC_0(type_is_integral, unsigned long long, true); #endif template struct type_is_signed : public type_false_type { }; TYPE_DEFINE_SPEC_0(type_is_signed, signed char, true); TYPE_DEFINE_SPEC_0(type_is_signed, short, true); TYPE_DEFINE_SPEC_0(type_is_signed, int, true); TYPE_DEFINE_SPEC_0(type_is_signed, long, true); #if !(defined DISABLE_LL_INT && DISABLE_LL_INT) TYPE_DEFINE_SPEC_0(type_is_signed, long long, true); #endif template struct type_is_unsigned : public type_false_type { }; TYPE_DEFINE_SPEC_0(type_is_unsigned, unsigned char, true); TYPE_DEFINE_SPEC_0(type_is_unsigned, unsigned short, true); TYPE_DEFINE_SPEC_0(type_is_unsigned, unsigned int, true); TYPE_DEFINE_SPEC_0(type_is_unsigned, unsigned long, true); #if !(defined DISABLE_ULL_INT && DISABLE_ULL_INT) TYPE_DEFINE_SPEC_0(type_is_unsigned, unsigned long long, true); #endif template struct type_is_floating_point : public type_false_type { }; TYPE_DEFINE_SPEC_0(type_is_floating_point, float, true); TYPE_DEFINE_SPEC_0(type_is_floating_point, double, true); TYPE_DEFINE_SPEC_0(type_is_floating_point, long double, true); template struct type_is_arithmetic : public type_integral_constant::value || type_is_floating_point::value)> { }; template struct type_is_pointer : public type_false_type { }; TYPE_DEFINE_SPEC_1(type_is_pointer, T*, true); template struct type_is_reference : public type_false_type { }; template struct type_is_reference : public type_true_type { }; template struct type_is_array : public type_false_type { }; template struct type_is_array : public type_true_type { }; template struct type_is_array : public type_true_type { }; template struct type_is_const : public type_false_type { }; template struct type_is_const : public type_true_type { }; template struct type_is_volatile : public type_false_type { }; template struct type_is_volatile : public type_true_type { }; #undef TYPE_DEFINE_SPEC_0 #undef TYPE_DEFINE_SPEC_1 template struct type_is_same : public type_false_type { }; template struct type_is_same : public type_true_type { }; namespace internal { template struct type_is_polymorphic_helper { private: template struct first : public U { }; template struct second : public U { virtual void internal_dummy(); virtual ~second(); }; public: static const bool value = (sizeof(first) == sizeof(second)); }; } // ns template struct type_is_polymorphic : public type_integral_constant::value> { }; template struct type_is_convertible { protected: // instead of private, to avoid gcc warnings typedef char (&yes) [1]; typedef char (&no) [2]; static yes f(To*); static no f(...); public: static const bool value = (sizeof( f((From*)0) ) == sizeof(yes)); }; // ---------------------- Type Reference / Pointer / CV-Qualifier Modification template struct type_remove_reference { typedef T type; }; template struct type_remove_reference { typedef T type; }; template struct type_add_reference { typedef typename type_remove_reference::type& type; }; // there's no void&, and this may be needed for function return types (T&) template<> struct type_add_reference { typedef void type; }; template // int -> int struct type_remove_pointer { typedef T type; }; template // int* -> int struct type_remove_pointer { typedef T type; }; template // int* const -> int struct type_remove_pointer { typedef T type; }; template // int* volatile -> int struct type_remove_pointer { typedef T type; }; template // int* const volatile -> int struct type_remove_pointer { typedef T type; }; template struct type_add_pointer { typedef typename type_remove_reference::type* type; }; template struct type_remove_const { typedef T type; }; template struct type_remove_const { typedef T type; }; template struct type_remove_volatile { typedef T type; }; template struct type_remove_volatile { typedef T type; }; template struct type_remove_cv { typedef typename type_remove_const::type>::type type; }; template struct type_add_const { typedef T const type; }; template struct type_add_volatile { typedef T volatile type; }; template struct type_add_cv { typedef typename type_add_const::type>::type type; }; // All-in-one transformator template struct type_transform { typedef typename type_remove_const::type>::type clean_type; typedef typename type_add_reference::type reference_type; typedef typename type_add_const::type const_type; typedef typename type_add_const::type const_reference_type; }; // ---------------------- Type Sign Modification template struct type_make_signed { typedef T type; }; template struct type_make_unsigned { typedef T type; }; // list unsigned types template<> struct type_make_signed { typedef signed char type; }; template<> struct type_make_signed { typedef signed char type; }; template<> struct type_make_signed { typedef short int type; }; template<> struct type_make_signed { typedef int type; }; template<> struct type_make_signed { typedef long int type; }; #if !(defined DISABLE_ULL_INT && DISABLE_ULL_INT) template<> struct type_make_signed { typedef long long int type; }; #endif // list signed types template<> struct type_make_unsigned { typedef unsigned char type; }; template<> struct type_make_unsigned { typedef unsigned char type; }; template<> struct type_make_unsigned { typedef unsigned short int type; }; template<> struct type_make_unsigned { typedef unsigned int type; }; template<> struct type_make_unsigned { typedef unsigned long int type; }; #if !(defined DISABLE_LL_INT && DISABLE_LL_INT) template<> struct type_make_unsigned { typedef unsigned long long int type; }; #endif } // ns #endif /// @} gsmartcontrol-1.1.4/src/hz/win32_tools.h000066400000000000000000000531451417717230600201760ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2003 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup hz /// \weakgroup hz /// @{ #ifndef HZ_WIN32_TOOLS_H #define HZ_WIN32_TOOLS_H #include "hz_config.h" // feature macros #ifdef _WIN32 // Guards this header completely #include // lots of stuff #include // SH* #include // _open_osfhandle #include // _O_TEXT #include #include // for ctype.h #include // towupper #include // for stdio.h, std::f*, std::setvbuf, std::fprintf, std::FILE #include // _fdopen, _wfopen, _wremove #include // std::atexit #include // std::ios::sync_with_stdio /** \file \brief Win32-specific API functions. \note The public API works with UTF-8 strings, unless noted otherwise. */ namespace hz { /// Get a list of drives available for the user. The drives are in /// "C:\" format (uppercase, utf-8). template inline bool win32_get_drive_list(Container& put_here); /// Get windows "special" folder by CSIDL (CSIDL_APPDATA, for example). /// See http://msdn.microsoft.com/en-us/library/bb762494(VS.85).aspx /// for details. inline std::string win32_get_special_folder(int csidl, bool auto_create = true); /// Usually C:\\Windows or something like that. inline std::string win32_get_windows_directory(); /// Get registry value as a string. /// Base may be e.g. HKEY_CURRENT_USER. /// Note that this works only with REG_SZ (string) types, /// returning their value as utf-8. False is returned for all other types. inline bool win32_get_registry_value_string(HKEY base, const std::string& keydir, const std::string& key, std::string& put_here); /// Set registry value as a string. /// Note that this sets the type as REG_SZ (string). The accepted utf-8 /// value is converted to utf-16 for storage. inline bool win32_set_registry_value_string(HKEY base, const std::string& keydir, const std::string& key, const std::string& value); /// Redirect stdout and stderr to console window (if open). Requires winxp (at compile-time). /// \param create_if_none if true, create a new console if none was found and attach to it. /// \return false if failed or unsupported. inline bool win32_redirect_stdio_to_console(bool create_if_none = false); /// Redirect stdout and stderr to console window (if open). Requires winxp (at compile-time). /// \param create_if_none if true, create a new console if none was found and attach to it. /// \param console_created Set to true if a new console was created due to \c create_if_none. /// \return false if failed or unsupported. inline bool win32_redirect_stdio_to_console(bool create_if_none, bool& console_created); /// Redirect stdout to stdout.txt and stderr to stderr.txt. /// Call once. /// \return true on success, false on failure. inline bool win32_redirect_stdio_to_files(std::string stdout_file = "", std::string stderr_file = ""); /// Convert a string in user-specified encoding to utf-16 string (00-terminated array of wchar_t). /// wchar_t is 2 bytes on windows, thus holding utf-16 code unit. /// Caller must delete[] the returned value. /// returned_buf_size (if not 0) will be set to the size of returned buffer /// (in wchar_t; including terminating 00). The byte size will be (returned_buf_size*2). inline wchar_t* win32_user_to_utf16(UINT from_cp, const char* from_str, unsigned int* returned_buf_size = 0); /// Convert utf-16 string (represented as 0-terminated array of wchar_t, where /// wchar_t is 2 bytes (on windows)) to a string in user-specified encoding. /// Caller must delete[] the returned value. /// The size of the returned buffer is std::strlen(buf) + 1. /// returned_buf_size (if not 0) will be set to the size of returned buffer /// (in char; including terminating 0). inline char* win32_utf16_to_user(UINT to_cp, const wchar_t* utf16_str, unsigned int* returned_buf_size = 0); /// Convert utf-8 string to utf-16 string. See win32_user_to_utf16() for details. inline wchar_t* win32_utf8_to_utf16(const char* utf8_str, unsigned int* returned_buf_size = 0); /// Convert utf-16 string to utf-8 string. See win32_utf16_to_user() for details. inline char* win32_utf16_to_utf8(const wchar_t* utf16_str, unsigned int* returned_buf_size = 0); /// Same as win32_utf16_to_utf8(), but safer since it never returns 0 (it returns "" instead). /// Note that we don't provide non-utf8 versions of this function, because the other charsets /// may actually be multi-byte (std::string can't hold their terminating 0 properly). /// Also, we don't provide std::wstring versions for utf16, because they are not always /// supported by non-MS compilers (mingw, for example). inline std::string win32_utf16_to_utf8_string(const wchar_t* utf16_str); /// Convert current locale-encoded string to utf-16 string. See win32_user_to_utf16() for details. /// Use CP_ACP is system default windows ANSI codepage. /// Use CP_THREAD_ACP is for current thread. Think before you choose. :) inline wchar_t* win32_locale_to_utf16(const char* loc_str, unsigned int* returned_buf_size = 0, bool use_thread_locale = false); /// Convert utf-16 string to locale-encoded string. See win32_utf16_to_user() for details. inline char* win32_utf16_to_locale(const wchar_t* utf16_str, unsigned int* returned_buf_size = 0, bool use_thread_locale = false); /// Convert current locale-encoded string to utf-8 string. The returned value must be freed by caller. inline char* win32_locale_to_utf8(const char* loc_str, unsigned int* returned_buf_size = 0, bool use_thread_locale = false); /// Convert utf-8 string to locale-encoded string. See win32_utf8_to_user() for details. inline char* win32_utf8_to_locale(const char* utf8_str, unsigned int* returned_buf_size = 0, bool use_thread_locale = false); // ------------------------------------------- Implementation // Get a list of drives available for the user. The drives are in // "C:\" format (uppercase, utf-8). template inline bool win32_get_drive_list(Container& put_here) { DWORD buf_size = GetLogicalDriveStringsW(0, 0); // find out the required buffer size if (!buf_size) // error return false; wchar_t* buf = new wchar_t[buf_size]; // Returns a consecutive array of 00-terminated strings (each 00-terminated). if (!GetLogicalDriveStringsW(buf_size, buf)) { delete[] buf; return false; } wchar_t* text_ptr = buf; // add the drives to list while (*text_ptr != 0) { text_ptr[0] = static_cast(towupper(text_ptr[0])); std::string drive = win32_utf16_to_utf8_string(text_ptr); if (!drive.empty()) { put_here.push_back(drive + ":\\"); } while (*text_ptr != 0) ++text_ptr; ++text_ptr; // set to position after 0 } delete[] buf; return true; } // Get windows "special" folder by CSIDL (CSIDL_APPDATA, for example). // See http://msdn.microsoft.com/en-us/library/bb762494(VS.85).aspx // for details. inline std::string win32_get_special_folder(int csidl, bool auto_create) { wchar_t buf[MAX_PATH] = {0}; // SHGetSpecialFolderPath() is since ie4 #if (defined _WIN32_IE) && (_WIN32_IE >= 0x0400) if (SHGetSpecialFolderPathW(NULL, buf, csidl, auto_create)) { return win32_utf16_to_utf8_string(buf); } #else LPITEMIDLIST pidl = 0; if (SHGetSpecialFolderLocation(NULL, csidl, &pidl) == S_OK) { if (SHGetPathFromIDListW(pidl, buf)) { errno = 0; // 00 is F_OK (for msvc). ENOENT is "no such file or directory". // We can't really do anything if errors happen here, so ignore them. if (auto_create && _waccess(buf, 00) == -1 && errno == ENOENT) { _wmkdir(buf); } return win32_utf16_to_utf8_string(buf); } // We must free pidl. This can be done in two ways: // CoTaskMemFree() requires linking to ole32. // CoTaskMemFree(pidl); // SHGetMalloc() which is deprecated and may be removed in the future. IMalloc* allocator = 0; if (SHGetMalloc(&allocator) == S_OK && allocator) { allocator->Free(pidl); allocator->Release(); } } #endif return std::string(); } // Usually C:\Windows or something like that. inline std::string win32_get_windows_directory() { wchar_t buf[MAX_PATH] = {0}; UINT status = GetWindowsDirectoryW(buf, MAX_PATH); return ((status != 0 && buf[0]) ? win32_utf16_to_utf8_string(buf) : "C:\\"); } // base may be e.g. HKEY_CURRENT_USER. // Note that this works only with REG_SZ (string) types, // returning their value as utf-8. False is returned for all other types. inline bool win32_get_registry_value_string(HKEY base, const std::string& keydir, const std::string& key, std::string& put_here) { wchar_t* wkeydir = win32_utf8_to_utf16(keydir.c_str()); if (!wkeydir) return false; HKEY reg_key = NULL; bool open_status = (RegOpenKeyExW(base, wkeydir, 0, KEY_QUERY_VALUE, ®_key) == ERROR_SUCCESS); delete[] wkeydir; if (!open_status) return false; wchar_t* wkey = win32_utf8_to_utf16(key.c_str()); if (!wkey) { // conversion error. Note that an empty string is not an error. if (reg_key) RegCloseKey(reg_key); return false; } DWORD type = 0; DWORD nbytes = 0; // This returns the size, including the 0 character only if the data was stored with it. bool status = (RegQueryValueExW(reg_key, wkey, 0, &type, NULL, &nbytes) == ERROR_SUCCESS); if (status) { DWORD buf_len = ((nbytes % 2) ? (nbytes+1) : nbytes); // 00-terminate on 2-byte boundary, in case the value isn't. BYTE* result = new BYTE[buf_len]; result[buf_len-1] = result[buf_len-2] = result[buf_len-3] = 0; status = (RegQueryValueExW(reg_key, wkey, 0, &type, result, &nbytes) == ERROR_SUCCESS); if (status) { char* utf8 = win32_utf16_to_utf8(reinterpret_cast(result)); if (utf8) { put_here = utf8; delete[] utf8; } else { status = false; } } delete[] result; } if (reg_key) RegCloseKey(reg_key); delete[] wkey; return status; } // Note that this sets the type as REG_SZ (string). The accepted utf-8 // value is converted to utf-16 for storage. inline bool win32_set_registry_value_string(HKEY base, const std::string& keydir, const std::string& key, const std::string& value) { wchar_t* wkeydir = win32_utf8_to_utf16(keydir.c_str()); if (!wkeydir) return false; HKEY reg_key = NULL; if (RegOpenKeyExW(base, wkeydir, 0, KEY_QUERY_VALUE, ®_key) != ERROR_SUCCESS) return false; delete[] wkeydir; bool status = true; unsigned int value_size = 0; // including terminating 00 wchar_t* wvalue = win32_utf8_to_utf16(value.c_str(), &value_size); wchar_t* wkey = win32_utf8_to_utf16(key.c_str()); if (!wvalue || !wkey) { status = false; } if (status) { status = (RegSetValueExW(reg_key, wkey, 0, REG_SZ, reinterpret_cast(wvalue), value_size) == ERROR_SUCCESS); } delete[] wvalue; delete[] wkey; if (reg_key) RegCloseKey(reg_key); return status; } // Redirect stdout and stderr to console window (if open). inline bool win32_redirect_stdio_to_console(bool create_if_none) { bool console_created = false; return win32_redirect_stdio_to_console(create_if_none, console_created); } // Redirect stdout and stderr to console window (if open). inline bool win32_redirect_stdio_to_console(bool create_if_none, bool& console_created) { console_created = false; // AttachConsole is since winxp (note that mingw and MS headers say win2k, // which is incorrect; msdn is correct). #if defined WINVER && WINVER >= 0x0501 if (!AttachConsole(ATTACH_PARENT_PROCESS)) { if (create_if_none) { // Even though msdn says that stdio is redirected to the new console, // it isn't so and we must do it manually. if (!AllocConsole()) { return false; } else { console_created = true; } } else { return false; } } // redirect unbuffered STDOUT to the console HANDLE h_out = GetStdHandle(STD_OUTPUT_HANDLE); std::FILE* fp_out = _fdopen(_open_osfhandle((intptr_t)(h_out), _O_TEXT), "w"); *stdout = *fp_out; std::setvbuf(stdout, 0, _IONBF, 0); // redirect unbuffered STDERR to the console HANDLE h_err = GetStdHandle(STD_ERROR_HANDLE); std::FILE* fp_err = _fdopen(_open_osfhandle((intptr_t)(h_err), _O_TEXT), "w"); *stderr = *fp_err; std::setvbuf(stderr, 0, _IONBF, 0); std::ios::sync_with_stdio(); std::fprintf(stderr, "\n"); // so that we start with an empty line return true; #else // unsupported return false; #endif } namespace internal { template struct Win32RedirectStaticHolder { static const wchar_t* stdout_file; static const wchar_t* stderr_file; }; // definitions template const wchar_t* Win32RedirectStaticHolder::stdout_file = 0; template const wchar_t* Win32RedirectStaticHolder::stderr_file = 0; typedef Win32RedirectStaticHolder Win32RedirectHolder; // one (and only) specialization. /// Returns full path (in utf-16) to the output filename (in utf-8) inline wchar_t* win32_get_std_output_file(const char* base) { if (!base) return 0; // GetModuleFileName() is since win2k #if defined WINVER && WINVER >= 0x0500 wchar_t name[MAX_PATH] = {0}; // This function doesn't write terminating 0 if the buffer is insufficient, // so we reserve some space for it. if (GetModuleFileNameW(0, name, MAX_PATH - 1)) { std::string name_str = win32_utf16_to_utf8_string(name); if (!name_str.empty()) { std::string::size_type pos = name_str.find_last_of("."); name_str = name_str.substr(0, pos); // full path, including the executable without extension. if (!name_str.empty()) { return win32_utf8_to_utf16((name_str + "-" + base).c_str()); } } // std::string::size_type pos = name_str.find_last_of("\\/"); // if (pos != std::string::npos && name_str.size() > pos + 1) { // name_str.substr(pos + 1); // } } #endif return win32_utf8_to_utf16(base); } /// Remove the output files if there was no output written. /// This is an atexit() callback. extern "C" inline void win32_redirect_stdio_to_files_cleanup() { // Flush the output in case anything is queued std::fclose(stdout); std::fclose(stderr); // See if the files have any output in them if (Win32RedirectHolder::stdout_file && Win32RedirectHolder::stdout_file[0] != 0) { std::FILE* file = 0; #if defined HAVE_WIN_SE_FUNCS && HAVE_WIN_SE_FUNCS errno = _wfopen_s(&file, Win32RedirectHolder::stdout_file, L"rb"); #else file = _wfopen(Win32RedirectHolder::stdout_file, L"rb"); #endif if (file) { bool empty = (std::fgetc(file) == EOF); std::fclose(file); if (empty) { (void)_wremove(Win32RedirectHolder::stdout_file); // ignore possible failure } } } delete[] Win32RedirectHolder::stdout_file; Win32RedirectHolder::stdout_file = 0; if (Win32RedirectHolder::stdout_file && Win32RedirectHolder::stderr_file[0] != 0) { std::FILE* file = 0; #if defined HAVE_WIN_SE_FUNCS && HAVE_WIN_SE_FUNCS errno = _wfopen_s(&file, Win32RedirectHolder::stderr_file, L"rb"); #else file = _wfopen(Win32RedirectHolder::stderr_file, L"rb"); #endif if (file) { bool empty = (std::fgetc(file) == EOF); std::fclose(file); if (empty) { (void)_wremove(Win32RedirectHolder::stderr_file); // ignore possible failure } } } delete[] Win32RedirectHolder::stderr_file; Win32RedirectHolder::stderr_file = 0; } } // ns // Redirect stdout to stdout.txt and stderr to stderr.txt. // Call once. inline bool win32_redirect_stdio_to_files(std::string stdout_file, std::string stderr_file) { wchar_t* wstdout_file = 0; // freed on cleanup if (stdout_file.empty()) { wstdout_file = internal::win32_get_std_output_file("stdout.txt"); } else { wstdout_file = win32_utf8_to_utf16(stdout_file.c_str()); } std::FILE* fp_out = 0; if (wstdout_file) { #if defined HAVE_WIN_SE_FUNCS && HAVE_WIN_SE_FUNCS errno = _wfopen_s(&fp_out, wstdout_file, L"wb"); #else fp_out = _wfopen(wstdout_file, L"wb"); #endif } if (wstdout_file && fp_out) { *stdout = *fp_out; std::setvbuf(stdout, 0, _IOLBF, BUFSIZ); // Line buffered } else { delete[] wstdout_file; } internal::Win32RedirectHolder::stdout_file = wstdout_file; // so that it's freed properly wchar_t* wstderr_file = 0; // freed on cleanup if (stderr_file.empty()) { wstderr_file = internal::win32_get_std_output_file("stderr.txt"); } else { wstderr_file = win32_utf8_to_utf16(stderr_file.c_str()); } std::FILE* fp_err = 0; if (wstderr_file) { #if defined HAVE_WIN_SE_FUNCS && HAVE_WIN_SE_FUNCS errno = _wfopen_s(&fp_err, wstderr_file, L"wb"); #else fp_err = _wfopen(wstderr_file, L"wb"); #endif } if (wstderr_file && fp_out) { *stderr = *fp_err; std::setvbuf(stderr, 0, _IONBF, BUFSIZ); // No buffering } else { delete[] wstderr_file; } internal::Win32RedirectHolder::stderr_file = wstderr_file; // so that it's freed properly if (fp_out || fp_err) { // make sure cout, wcout, cin, wcin, wcerr, cerr, wclog and clog // write there as well. std::ios::sync_with_stdio(); std::atexit(internal::win32_redirect_stdio_to_files_cleanup); return true; } return false; } // Despite what MSDN says, these MultiByteToWideChar and // WideCharToMultiByte are even on win95. // Convert a string in user-specified encoding to utf-16 string (00-terminated array of wchar_t). // wchar_t is 2 bytes on windows, thus holding utf-16 code unit. // Caller must delete[] the returned value. // returned_buf_size (if not 0) will be set to the size of returned buffer // (in wchar_t; including terminating 00). The byte size will be (returned_buf_size*2). inline wchar_t* win32_user_to_utf16(UINT from_cp, const char* from_str, unsigned int* returned_buf_size) { if (!from_str) return 0; int wide_bufsize = MultiByteToWideChar(from_cp, 0, from_str, -1, 0, 0); // including terminating 0 if (wide_bufsize == ERROR_NO_UNICODE_TRANSLATION) { return 0; // invalid sequence } if (wide_bufsize == 0) { return 0; // error in conversion } wchar_t* res = new wchar_t[wide_bufsize]; int conv_size = MultiByteToWideChar(from_cp, 0, from_str, -1, res, wide_bufsize); if (conv_size != wide_bufsize) { delete[] res; return 0; // ? } if (returned_buf_size) { *returned_buf_size = wide_bufsize; } return res; } // Convert utf-16 string (represented as 0-terminated array of wchar_t, where // wchar_t is 2 bytes (on windows)) to a string in user-specified encoding. // Caller must delete[] the returned value. // The size of the returned buffer is std::strlen(buf) + 1. // returned_buf_size (if not 0) will be set to the size of returned buffer // (in char; including terminating 0). inline char* win32_utf16_to_user(UINT to_cp, const wchar_t* utf16_str, unsigned int* returned_buf_size) { if (!utf16_str) return 0; int buf_size = WideCharToMultiByte(to_cp, 0, utf16_str, -1, 0, 0, 0, 0); if (buf_size == 0) { return 0; } char* res = new char[buf_size]; int conv_size = WideCharToMultiByte(to_cp, 0, utf16_str, -1, res, buf_size, 0, 0); if (conv_size != buf_size) { delete[] res; return 0; // ? } if (returned_buf_size) { *returned_buf_size = buf_size; } return res; } // Convert utf-8 string to utf-16 string. See win32_user_to_utf16() for details. inline wchar_t* win32_utf8_to_utf16(const char* utf8_str, unsigned int* returned_buf_size) { return win32_user_to_utf16(CP_UTF8, utf8_str, returned_buf_size); } // Convert utf-16 string to utf-8 string. See win32_utf16_to_user() for details. inline char* win32_utf16_to_utf8(const wchar_t* utf16_str, unsigned int* returned_buf_size) { return win32_utf16_to_user(CP_UTF8, utf16_str, returned_buf_size); } // Same as win32_utf16_to_utf8(), but safer since it never returns 0 (it returns "" instead). // Note that we don't provide non-utf8 versions of this function, because the other charsets // may actually be multi-byte (std::string can't hold their terminating 0 properly). // Also, we don't provide std::wstring versions for utf16, because they are not always // supported by non-MS compilers (mingw, for example). inline std::string win32_utf16_to_utf8_string(const wchar_t* utf16_str) { char* buf = win32_utf16_to_utf8(utf16_str); std::string ret(buf ? buf : ""); delete[] buf; return ret; } // Convert current locale-encoded string to utf-16 string. See win32_user_to_utf16() for details. // Use CP_ACP is system default windows ANSI codepage. // Use CP_THREAD_ACP is for current thread. Think before you choose. :) inline wchar_t* win32_locale_to_utf16(const char* loc_str, unsigned int* returned_buf_size, bool use_thread_locale) { return win32_user_to_utf16(use_thread_locale ? CP_THREAD_ACP : CP_ACP, loc_str, returned_buf_size); } // Convert utf-16 string to locale-encoded string. See win32_utf16_to_user() for details. inline char* win32_utf16_to_locale(const wchar_t* utf16_str, unsigned int* returned_buf_size, bool use_thread_locale) { return win32_utf16_to_user(use_thread_locale ? CP_THREAD_ACP : CP_ACP, utf16_str, returned_buf_size); } // Convert current locale-encoded string to utf-8 string. The returned value must be freed by caller. inline char* win32_locale_to_utf8(const char* loc_str, unsigned int* returned_buf_size, bool use_thread_locale) { wchar_t* utf16_str = win32_locale_to_utf16(loc_str, 0, use_thread_locale); char* utf8_str = win32_utf16_to_utf8(utf16_str, returned_buf_size); delete[] utf16_str; return utf8_str; } // Convert utf-8 string to locale-encoded string. See win32_utf8_to_user() for details. inline char* win32_utf8_to_locale(const char* utf8_str, unsigned int* returned_buf_size, bool use_thread_locale) { wchar_t* utf16_str = win32_utf8_to_utf16(utf8_str); char* loc_str = win32_utf16_to_locale(utf16_str, returned_buf_size, use_thread_locale); delete[] utf16_str; return loc_str; } } // ns #endif // win32 #endif // hg /// @} gsmartcontrol-1.1.4/src/libdebug/000077500000000000000000000000001417717230600167675ustar00rootroot00000000000000gsmartcontrol-1.1.4/src/libdebug/Makefile.am000066400000000000000000000012721417717230600210250ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) METASOURCES = AUTO noinst_HEADERS = dchannel.h dcmdarg.h dexcept.h dflags.h dout.h dstate.h \ dstate_pub.h dstream.h libdebug.h libdebug_mini.h noinst_LIBRARIES = libdebug.a libdebug_a_SOURCES = dchannel.cpp dcmdarg.cpp dflags.cpp dout.cpp dstate.cpp \ dstream.cpp # don't use absolute path for the current dir's .a, because the make # dependency resolver won't get it (needed for parallel builds) libdebug_test_SOURCES = libdebug_test.cpp libdebug_test_LDADD = libdebug.a # we don't list them in a separate variable because otherwise kdevelop won't see them. noinst_PROGRAMS = if ENABLE_TESTS noinst_PROGRAMS += libdebug_test endif tests: $(noinst_PROGRAMS) gsmartcontrol-1.1.4/src/libdebug/dchannel.cpp000066400000000000000000000045141417717230600212530ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup libdebug /// \weakgroup libdebug /// @{ #include "hz/format_unit.h" // format_date() #include "dchannel.h" // #include // tmp std::string debug_format_message(debug_level::flag level, const std::string& domain, debug_format::type& format_flags, int indent_level, bool is_first_line, const std::string& msg) { if (msg.empty()) return msg; std::string ret; ret.reserve(msg.size() + 40); // indentation + domain/level // allow prefix only for first line and others when first_line_only is disabled if (is_first_line || !(format_flags.to_ulong() & debug_format::first_line_only)) { if (format_flags.to_ulong() & debug_format::datetime) { // print time ret += hz::format_date("%Y-%m-%d %H:%M:%S: ", false); } if (format_flags.to_ulong() & debug_format::level) { // print level name bool use_color = (format_flags.to_ulong() & debug_format::color); if (use_color) ret += debug_level::get_color_start(level); std::string level_name = std::string("<") + debug_level::get_name(level) + ">"; ret += level_name + std::string(8 - level_name.size(), ' '); if (use_color) ret += debug_level::get_color_stop(level); } if (format_flags.to_ulong() & debug_format::domain) { // print domain name ret += std::string("[") + domain + "] "; } } if (format_flags.to_ulong() & debug_format::indent) { std::string spaces(indent_level * 4, ' '); // indentation spaces // std::cerr << "MSG: " << msg; // replace all newlines with \n(indent-spaces) except for the last one. std::string::size_type oldpos = 0, pos = 0; while(pos != std::string::npos) { if (pos == 0) { ret.insert(0, spaces); } else { ret.append(spaces); } pos = msg.find('\n', oldpos); if (pos != std::string::npos) pos++; if (pos >= msg.size()) pos = std::string::npos; ret.append(msg, oldpos, pos - oldpos); oldpos = pos; // pos = std::string::npos; // ret.append(msg); } } else { ret += msg; } // std::cerr << "FINAL MSG: " << ret; return ret; } /// @} gsmartcontrol-1.1.4/src/libdebug/dchannel.h000066400000000000000000000067441417717230600207270ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup libdebug /// \weakgroup libdebug /// @{ #ifndef LIBDEBUG_DCHANNEL_H #define LIBDEBUG_DCHANNEL_H #include #include // std::ostream (iosfwd is not enough for win32 and suncc) #include "hz/intrusive_ptr.h" #include "hz/sync.h" #include "dflags.h" class DebugChannelBase; /// Strong reference-holding pointer typedef hz::intrusive_ptr debug_channel_base_ptr; /// Strong reference-holding pointer typedef hz::intrusive_ptr debug_channel_base_const_ptr; // TODO Per-channel flags (instead of per-debug-stream flags). /// All channels must inherit this. class DebugChannelBase : public hz::intrusive_ptr_referenced_locked { public: /// Virtual destructor virtual ~DebugChannelBase() { } // virtual DebugChannelBase* clone() = 0; /// Clone the channel and return a strong reference-holding pointer virtual debug_channel_base_ptr clone_ptr() = 0; /// Clone the channel and return a strong reference-holding pointer virtual debug_channel_base_const_ptr clone_ptr() const = 0; /// Send a message to channel virtual void send(debug_level::flag level, const std::string& domain, debug_format::type& format_flags, int indent_level, bool is_first_line, const std::string& msg) = 0; }; /// Helper function for DebugChannel objects, formats a message. std::string debug_format_message(debug_level::flag level, const std::string& domain, debug_format::type& format_flags, int indent_level, bool is_first_line, const std::string& msg); /// std::ostream wrapper as a DebugChannel. /// Note: Use the _same_ channel instance for the same ostreams, only /// this way you will get proper ostream locking. Other than for send(), /// the locking must be performed manually by the caller. /// The smartpointer's refcount mutex is reused for wrapped stream locking. class DebugChannelOStream : public DebugChannelBase { public: /// Constructor DebugChannelOStream(std::ostream& os) : os_(os) { } /// Virtual destructor virtual ~DebugChannelOStream() { } // Reimplemented virtual debug_channel_base_ptr clone_ptr() { // this will prevent the object from being copied, which may harm the wrapped stream. return debug_channel_base_ptr(this); // aka copy. } // Reimplemented virtual debug_channel_base_const_ptr clone_ptr() const { // this will prevent the object from being copied, which may harm the wrapped stream. return debug_channel_base_const_ptr(this); // aka copy. } /// Reimplemented from DebugChannelBase. This function locks the /// wrapped stream, as long as there's only one instance of this class. virtual void send(debug_level::flag level, const std::string& domain, debug_format::type& format_flags, int indent_level, bool is_first_line, const std::string& msg) { intrusive_ptr_scoped_lock_type locker(get_ref_mutex()); os_ << debug_format_message(level, domain, format_flags, indent_level, is_first_line, msg); } // Non-debug-API members: /// Get the ostream. Lock the channel for using the ostream. std::ostream& get_ostream() { return os_; } private: std::ostream& os_; ///< Wrapped ostream }; #endif /// @} gsmartcontrol-1.1.4/src/libdebug/dcmdarg.cpp000066400000000000000000000164571417717230600211110ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup libdebug /// \weakgroup libdebug /// @{ #include "hz/hz_config.h" // ENABLE_GLIB, DEBUG_BUILD #if defined ENABLE_GLIB && ENABLE_GLIB #include #include #include #include #include #include // std::boolalpha #include // std::find #include "hz/string_algo.h" // string_split() #include "dcmdarg.h" #include "dflags.h" #include "dstate.h" namespace debug_internal { /// Internal class, holds values of command line args. struct DebugCmdArgs { /// Constructor DebugCmdArgs() : // defaults #ifdef _WIN32 verbose(TRUE), #else verbose(FALSE), #endif quiet(FALSE), #ifdef DEBUG_BUILD verbosity_level(5), // all #else verbosity_level(3), // warn, error, fatal #endif // debug_levels(), #ifndef _WIN32 debug_colorize(TRUE) #else debug_colorize(FALSE) // disable colors on win32 #endif { } // use glib types here gboolean verbose; ///< Verbose output (enables higher verbosity level) gboolean quiet; ///< Less verbose output (enables lower verbosity level) gint verbosity_level; ///< Verbosity level override std::vector debug_levels; ///< Comma-separated names of levels to enable gboolean debug_colorize; ///< Colorize the output or not debug_level::type levels_enabled; ///< Final vector - not actually an argument, but filled after the parsing. }; /// Libdebug global command-line arguments static DebugCmdArgs s_debug_cmd_args; /// Get libdebug command-line arguments inline DebugCmdArgs* debug_get_args_holder() { return &s_debug_cmd_args; } } // ns debug_internal extern "C" { /// Glib callback for parsing libdebug levels argument static gboolean debug_internal_parse_levels(const gchar* option_name, const gchar* value, gpointer data, GError** error); /// Glib callback for performing post-parse phase (parse hook) static gboolean debug_internal_post_parse_func(GOptionContext* context, GOptionGroup *group, gpointer data, GError** error); } // GLib callback static gboolean debug_internal_parse_levels(const gchar* option_name, const gchar* value, gpointer data, GError** error) { if (!value) return false; debug_internal::DebugCmdArgs* args = static_cast(data); std::string levels = value; hz::string_split(levels, ',', args->debug_levels, true); // will filter out invalid ones later return true; } // GLib callback static gboolean debug_internal_post_parse_func(GOptionContext* context, GOptionGroup *group, gpointer data, GError** error) { debug_internal::DebugCmdArgs* args = static_cast(data); if (!args->debug_levels.empty()) { // no string levels on command line given args->levels_enabled = debug_level::none; // reset args->levels_enabled |= (std::find(args->debug_levels.begin(), args->debug_levels.end(), "dump") != args->debug_levels.end() ? debug_level::dump : debug_level::none); args->levels_enabled |= (std::find(args->debug_levels.begin(), args->debug_levels.end(), "info") != args->debug_levels.end() ? debug_level::info : debug_level::none); args->levels_enabled |= (std::find(args->debug_levels.begin(), args->debug_levels.end(), "warn") != args->debug_levels.end() ? debug_level::warn : debug_level::none); args->levels_enabled |= (std::find(args->debug_levels.begin(), args->debug_levels.end(), "error") != args->debug_levels.end() ? debug_level::error : debug_level::none); args->levels_enabled |= (std::find(args->debug_levels.begin(), args->debug_levels.end(), "fatal") != args->debug_levels.end() ? debug_level::fatal : debug_level::none); } else if (args->quiet) { args->levels_enabled = debug_level::none; } else if (args->verbose) { args->levels_enabled = debug_level::all; } else { args->levels_enabled = debug_level::none; if (args->verbosity_level > 0) args->levels_enabled |= debug_level::fatal; if (args->verbosity_level > 1) args->levels_enabled |= debug_level::error; if (args->verbosity_level > 2) args->levels_enabled |= debug_level::warn; if (args->verbosity_level > 3) args->levels_enabled |= debug_level::info; if (args->verbosity_level > 4) args->levels_enabled |= debug_level::dump; } bool color_enabled = args->debug_colorize; unsigned long levels_enabled_ulong = args->levels_enabled.to_ulong(); debug_internal::DebugState::domain_map_t& dm = debug_internal::get_debug_state().get_domain_map(); for (debug_internal::DebugState::domain_map_t::iterator iter = dm.begin(); iter != dm.end(); ++iter) { for (debug_internal::DebugState::level_map_t::iterator iter2 = iter->second.begin(); iter2 != iter->second.end(); ++iter2) { iter2->second->set_enabled(levels_enabled_ulong & iter2->first); debug_format::type format = iter2->second->get_format(); if (color_enabled) { format |= debug_format::color; } else { format &= ~(unsigned long)debug_format::color; } iter2->second->set_format(format); } } return true; } std::string debug_get_cmd_args_dump() { debug_internal::DebugCmdArgs* args = debug_internal::debug_get_args_holder(); std::ostringstream ss; // ss << "\tverbose: " << std::boolalpha << static_cast(args->verbose) << "\n"; // ss << "\tquiet: " << std::boolalpha << static_cast(args->quiet) << "\n"; // ss << "\tverbosity_level: " << args->verbosity_level << "\n"; // ss << "\tdebug_levels: " << args->debug_levels << "\n"; ss << "\tlevels_enabled: " << args->levels_enabled << "\n"; ss << "\tdebug_colorize: " << std::boolalpha << static_cast(args->debug_colorize) << "\n"; return ss.str(); } // If adding the returned value to context (which you must do), // no need to free the result. GOptionGroup* debug_get_option_group() { debug_internal::DebugCmdArgs* args = debug_internal::debug_get_args_holder(); GOptionGroup* group = g_option_group_new("debug", "Libdebug Logging Options", "Show libdebug options", args, NULL); static const GOptionEntry entries[] = { { "verbose", 'v', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE, &(args->verbose), "Enable verbose logging; same as --verbosity-level 5", NULL }, { "quiet", 'q', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE, &(args->quiet), "Disable logging; same as --verbosity-level 0", NULL }, { "verbosity-level", 'b', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_INT, &(args->verbosity_level), "Set verbosity level [0-5]", NULL }, { "debug-levels", '\0', 0, G_OPTION_ARG_CALLBACK, (gpointer)(&debug_internal_parse_levels), // reinterpret_cast<> doesn't work here "Enable only these logging levels; the argument is a comma-separated list of (dump, info, warn, error, fatal)", NULL }, { "debug-colorize", '\0', 0, G_OPTION_ARG_NONE, &(args->debug_colorize), "Enable colored output", NULL }, { "debug-no-colorize", '\0', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &(args->debug_colorize), "Disable colored output", NULL }, { NULL } }; g_option_group_add_entries(group, entries); g_option_group_set_parse_hooks(group, NULL, &debug_internal_post_parse_func); return group; } #endif // glib /// @} gsmartcontrol-1.1.4/src/libdebug/dcmdarg.h000066400000000000000000000021001417717230600205320ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup libdebug /// \weakgroup libdebug /// @{ #ifndef LIBDEBUG_DCMDARG_H #define LIBDEBUG_DCMDARG_H #include "hz/hz_config.h" // ENABLE_GLIB /** \file Glib option parsing support - public interface */ #if defined ENABLE_GLIB && ENABLE_GLIB #include /// Get Glib option group to use for handling libdebug command-line /// arguments using Glib. You must not delete the returned value if /// adding it to context (which is usually the case). /// This function will also automatically apply the passed (and default) /// options to all domains. GOptionGroup* debug_get_option_group(); /// Return a string-dump of all libdebug options (use to debug /// command-line option issues). std::string debug_get_cmd_args_dump(); #endif // glib #endif /// @} gsmartcontrol-1.1.4/src/libdebug/dexcept.h000066400000000000000000000034331417717230600205770ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup libdebug /// \weakgroup libdebug /// @{ #ifndef LIBDEBUG_DEXCEPT_H #define LIBDEBUG_DEXCEPT_H #include // std::size_t #include // std::strncpy / std::strlen #include // std::exception /// Exception thrown on internal libdebug errors struct debug_internal_error : virtual public std::exception { /// Constructor debug_internal_error(const char* msg) { std::size_t buf_len = std::strlen(msg) + 1; msg_ = std::strncpy(new char[buf_len], msg, buf_len); } /// Virtual destructor virtual ~debug_internal_error() throw() { delete[] msg_; msg_ = 0; // protect from double-deletion compiler bugs } /// Reimplemented. Note: messages in exceptions are not newline-terminated. virtual const char* what() const throw() { return msg_; } private: char* msg_; ///< The error message }; /// Exception thrown on libdebug API usage errors struct debug_usage_error : virtual public std::exception { // from /// Constructor debug_usage_error(const char* msg) { std::size_t buf_len = std::strlen(msg) + 1; msg_ = std::strncpy(new char[buf_len], msg, buf_len); } /// Virtual destructor virtual ~debug_usage_error() throw() { delete[] msg_; msg_ = 0; // protect from double-deletion compiler bugs } /// Reimplemented. Note: messages in exceptions are not newline-terminated. virtual const char* what() const throw() { return msg_; } private: char* msg_; ///< The error message }; #endif /// @} gsmartcontrol-1.1.4/src/libdebug/dflags.cpp000066400000000000000000000030421417717230600207320ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup libdebug /// \weakgroup libdebug /// @{ #include "dflags.h" #include /// Debug level flags implementation struct DebugFlags { /// Constructor (static) DebugFlags() { level_names[debug_level::fatal] = "fatal"; level_names[debug_level::error] = "error"; level_names[debug_level::warn] = "warn"; level_names[debug_level::info] = "info"; level_names[debug_level::dump] = "dump"; level_colors[debug_level::fatal] = "\033[1;4;31m"; // red underlined level_colors[debug_level::error] = "\033[1;31m"; // red level_colors[debug_level::warn] = "\033[1;35m"; // magenta level_colors[debug_level::info] = "\033[1;36m"; // cyan level_colors[debug_level::dump] = "\033[1;32m"; // green } /// Debug level names std::map level_names; /// Debug level color start sequences std::map level_colors; }; /// Global libdebug level flags static DebugFlags s_debug_flags; namespace debug_level { const char* get_name(flag level) { return s_debug_flags.level_names[level]; } const char* get_color_start(flag level) { return s_debug_flags.level_colors[level]; } const char* get_color_stop(flag level) { return "\033[0m"; } } /// @} gsmartcontrol-1.1.4/src/libdebug/dflags.h000066400000000000000000000065571417717230600204150ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup libdebug /// \weakgroup libdebug /// @{ #ifndef LIBDEBUG_DFLAGS_H #define LIBDEBUG_DFLAGS_H #include /// Debug level enum and related functions namespace debug_level { enum flag { ///< Debug level (seriousness). Some of these flags can be ORed for some functions. none = 0, ///< No flags dump = 1 << 0, ///< Dump level (structure dumps, additional verbosity, etc...) info = 1 << 1, ///< Information level (what the application is doing) warn = 1 << 2, ///< Warning level (simple warnings) error = 1 << 3, ///< Error level (recoverable errors) fatal = 1 << 4, ///< Fatal level (non-recoverable errors) all = dump | info | warn | error | fatal, ///< All flags bits = 5 ///< Number of bits for bitset }; typedef std::bitset type; ///< Combination of debug level flags /// Get debug level name const char* get_name(flag level); /// Get color start sequence for debug level (for colorizing the output) const char* get_color_start(flag level); /// Get color stop sequence for debug level (for colorizing the output) const char* get_color_stop(flag level); /// Convert ORed flags into a vector of flags template inline void get_matched_levels_array(const type& levels, Container& put_here) { unsigned long levels_ulong = levels.to_ulong(); if (levels_ulong & debug_level::dump) put_here.push_back(debug_level::dump); if (levels_ulong & debug_level::info) put_here.push_back(debug_level::info); if (levels_ulong & debug_level::warn) put_here.push_back(debug_level::warn); if (levels_ulong & debug_level::error) put_here.push_back(debug_level::error); if (levels_ulong & debug_level::fatal) put_here.push_back(debug_level::fatal); } } /// Debug formatting option (how to format the message) enum and related functions namespace debug_format { enum flag { ///< Format flag. Some of these flags can be ORed for some functions. none = 0, ///< No flags datetime = 1 << 0, ///< Show datetime level = 1 << 1, ///< Show debug level name domain = 1 << 2, ///< Show domain name color = 1 << 3, ///< Colorize output. Note: Colorization works only for supported shells / terminals (e.g. bash / xterm). indent = 1 << 4, ///< Enable indentation first_line_only = 1 << 5, ///< Internal flag, prefix first line only. all = datetime | level | domain | color | indent, ///< All user formatting flags enabled bits = 6 ///< Number of bits for bitset }; typedef std::bitset type; ///< Combination of format flags } /// Debug position output flags (how to format the current source line information) namespace debug_pos { enum flag { ///< Position output flags none = 0, ///< No flags func_name = 1 << 0, ///< Print function name (only) func = 1 << 1, ///< Print function name with namespaces, etc... (off by default). line = 1 << 2, ///< Print source code line file = 1 << 3, ///< Print file path and name def = func_name | line | file, ///< Default flags bits = 4 ///< Number of bits for bitset }; typedef std::bitset type; ///< Combination of flags } #endif /// @} gsmartcontrol-1.1.4/src/libdebug/dout.cpp000066400000000000000000000102411417717230600204440ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup libdebug /// \weakgroup libdebug /// @{ #include #include // std::ostream definition #include #include // std::va_start, va_list macro and friends #include "hz/tls.h" #include "hz/string_sprintf.h" // string_vsprintf() #include "hz/exceptions.h" // THROW_FATAL #include "dout.h" #include "dflags.h" #include "dstate.h" #include "dexcept.h" // This may throw for invalid domain or level. std::ostream& debug_out(debug_level::flag level, const std::string& domain) { debug_internal::DebugState::domain_map_t& dm = debug_internal::get_debug_state().get_domain_map(); debug_internal::DebugState::domain_map_t::iterator level_map = dm.find(domain); if (level_map == dm.end()) { // no such domain std::string msg = "debug_out(): Debug state doesn't contain the requested domain: \"" + domain + "\"."; if (domain != "default") { debug_out(debug_level::warn, "default") << msg << "\n"; debug_out(debug_level::info, "default") << "Auto-creating the missing domain.\n"; debug_register_domain(domain); debug_out(debug_level::warn, "default") << "The message follows:\n"; return debug_out(level, domain); // try again } // this is an internal error THROW_FATAL(debug_internal_error(msg.c_str())); } debug_internal::DebugState::level_map_t::iterator os = level_map->second.find(level); if (level_map == dm.end()) { std::string msg = std::string("debug_out(): Debug state doesn't contain the requested level ") + debug_level::get_name(level) + " in domain: \"" + domain + "\"."; // this is an internal error THROW_FATAL(debug_internal_error(msg.c_str())); } return *(os->second); } void debug_print(debug_level::flag level, const std::string& domain, const char* format, ...) { std::va_list ap; va_start(ap, format); std::string s = hz::string_vsprintf(format, ap); va_end(ap); debug_out(level, domain) << s; } // Start / stop prefix printing. Useful for large dumps void debug_begin() { // inside_begin is thread-local, no need to lock anything debug_internal::get_debug_state().push_inside_begin(); } void debug_end() { // inside_begin is thread-local, no need to lock anything debug_internal::get_debug_state().pop_inside_begin(); // this is needed because else the contents won't be written until next write. debug_internal::get_debug_state().force_output(); // this call is thread-safe in read-only context. } namespace debug_internal { std::string DebugSourcePos::str() const { std::ostringstream os; os << "("; if (enabled_types.to_ulong() & debug_pos::func_name) { os << "function: " << func_name; } else if (enabled_types.to_ulong() & debug_pos::func) { os << "function: " << func << "()"; } if (enabled_types.to_ulong() & debug_pos::file) { if (os.str() != "(") os << ", "; os << "file: " << file; } if (enabled_types.to_ulong() & debug_pos::line) { if (os.str() != "(") os << ", "; os << "line: " << line; } os << ")"; return os.str(); } } // ------------------ Indentation and manipulators // increase indentation level for all debug levels void debug_indent_inc(int by) { // indent level is thread-local, no need to lock anything int curr = debug_internal::get_debug_state().get_indent_level(); debug_internal::get_debug_state().set_indent_level(curr + by); } void debug_indent_dec(int by) { // indent level is thread-local, no need to lock anything int curr = debug_internal::get_debug_state().get_indent_level(); curr -= by; if (curr < 0) curr = 0; debug_internal::get_debug_state().set_indent_level(curr); } void debug_indent_reset() { // indent level is thread-local, no need to lock anything debug_internal::get_debug_state().set_indent_level(0); } namespace debug_internal { // manupulator objects DebugIndent debug_indent; DebugUnindent debug_unindent; DebugResetIndent debug_resindent; } /// @} gsmartcontrol-1.1.4/src/libdebug/dout.h000066400000000000000000000247631417717230600201270ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup libdebug /// \weakgroup libdebug /// @{ #ifndef LIBDEBUG_DOUT_H #define LIBDEBUG_DOUT_H #include // Note: Sun compiler refuses to compile without (iosfwd is not enough). // Since every useful operator << is defined in ostream, we include it here anyway. #include // std::ostream #include "hz/system_specific.h" #include "hz/hz_config.h" // HAVE_CXX_* #include "hz/string_sprintf_macros.h" // HZ_FUNC_STRING_SPRINTF_CHECK #include "dflags.h" /** \file These functions are thread-safe if the state doesn't change. That is, you may use the output functions in different threads, but if you modify the state (channels, default format flags, etc...), then you _must_ stop all libdebug activity in _all_ threads except the one you're modifying with. */ /// Get a libdebug-handled stream for \c level and \c domain. /// \throw debug_usage_error if invalid domain or level. std::ostream& debug_out(debug_level::flag level, const std::string& domain); // These are macros to be able to easily compile-out per-level output. /// Send an output to debug stream. For example: /// \code /// debug_out_error("app", DBG_FUNC_MSG << "Error in structure consistency.\n"); /// debug_out_dump("app", "Error value: " << value << ".\n"); /// \endcode #define debug_out_dump(domain, output) \ debug_out(debug_level::dump, domain) << output /// Send an output to debug stream. \see debug_out_dump(). #define debug_out_info(domain, output) \ debug_out(debug_level::info, domain) << output /// Send an output to debug stream. \see debug_out_dump(). #define debug_out_warn(domain, output) \ debug_out(debug_level::warn, domain) << output /// Send an output to debug stream. \see debug_out_dump(). #define debug_out_error(domain, output) \ debug_out(debug_level::error, domain) << output /// Send an output to debug stream. \see debug_out_dump(). #define debug_out_fatal(domain, output) \ debug_out(debug_level::fatal, domain) << output /// Send a printf-like-formatted string to libdebug stream. void debug_print(debug_level::flag level, const std::string& domain, const char* format, ...) HZ_FUNC_STRING_SPRINTF_CHECK(3, 4); /// Send a printf-like-formatted string to libdebug stream. For example: /// \code /// debug_print_error("app", "Error in %s while handling input parameters.\n", DBG_FUNC); /// debug_print_dump("app", "Parameter value: %d.\n", value); /// \endcode #define debug_print_dump(domain, ...) \ debug_print(debug_level::dump, domain, __VA_ARGS__) /// Send a printf-like-formatted string to libdebug stream. \see debug_print_dump(). #define debug_print_info(domain, ...) \ debug_print(debug_level::info, domain, __VA_ARGS__) /// Send a printf-like-formatted string to libdebug stream. \see debug_print_dump(). #define debug_print_warn(domain, ...) \ debug_print(debug_level::warn, domain, __VA_ARGS__) /// Send a printf-like-formatted string to libdebug stream. \see debug_print_dump(). #define debug_print_error(domain, ...) \ debug_print(debug_level::error, domain, __VA_ARGS__) /// Send a printf-like-formatted string to libdebug stream. \see debug_print_dump(). #define debug_print_fatal(domain, ...) \ debug_print(debug_level::fatal, domain, __VA_ARGS__) /// Start prefix printing. Useful for large dumps where you don't want prefixes to /// be printed on each debug_* call. void debug_begin(); /// Stop prefix printing. void debug_end(); // Source position tracking namespace debug_internal { /// Source position object for sending to libdebug streams, prints source position. struct DebugSourcePos { /// Constructor inline DebugSourcePos(const std::string& file_, unsigned int line_, const std::string& func_name_, const std::string& func_) : func_name(func_name_), func(func_), line(line_), file(file_), enabled_types(debug_pos::def) { } /// Formatted output string std::string str() const; std::string func_name; ///< Function name only std::string func; ///< Function name with namespaces and classes unsigned int line; ///< Source line std::string file; ///< Source file debug_pos::type enabled_types; ///< Enabled formatting types }; /// Output operator inline std::ostream& operator<< (std::ostream& os, const DebugSourcePos& pos) { return os << pos.str(); } /// Format the function name inline std::string format_function_msg(const std::string& func, bool add_suffix) { // if it's "bool::A::func(int)" or "bool test::A::func(int)", // remove the return type and parameters. std::string::size_type endpos = func.find('('); if (endpos == std::string::npos) endpos = func.size(); // search for first space (after the parameter), or "". std::string::size_type pos = func.find_first_of(" >"); if (pos != std::string::npos) { if (func[pos] == '>') pos += 2; // skip :: ++pos; // skip whatever character we're over // debug_out_info("default", "pos: " << pos << ", endpos: " << endpos << "\n"); return func.substr(pos >= endpos ? 0 : pos, endpos - pos) + (add_suffix ? "(): " : "()"); } return func.substr(0, endpos) + (add_suffix ? "(): " : "()"); } } // __BASE_FILE__, __FILE__, __LINE__, __func__, __FUNCTION__, __PRETTY_FUNCTION__ // These two may seem pointless, but they actually help to implement // zero-overhead (if you define them to something else when libdebug is disabled). /// Current file as const char*. #define DBG_FILE __FILE__ /// Current line as unsigned int. #define DBG_LINE __LINE__ /// \def DBG_FUNC_NAME /// Function name (without classes / namespaces) only, e.g. "main", as const char*. #if defined HAVE_CXX___func__ && HAVE_CXX___func__ #define DBG_FUNC_NAME __func__ #elif defined HAVE_CXX___FUNCTION__ && HAVE_CXX___FUNCTION__ #define DBG_FUNC_NAME __FUNCTION__ #else #define DBG_FUNC_NAME "unknown" #endif /// \def DBG_FUNC_PRNAME /// Function pretty name is the whole function prototype, /// including return type and classes / namespaces, as const char*. #ifdef __GNUC__ #define DBG_FUNC_PRNAME __PRETTY_FUNCTION__ #else #define DBG_FUNC_PRNAME DBG_FUNC_NAME #endif /// "class::function()", as const char*. #define DBG_FUNC (debug_internal::format_function_msg(DBG_FUNC_PRNAME, false).c_str()) /// "class::function(): ", as const char*. #define DBG_FUNC_MSG (debug_internal::format_function_msg(DBG_FUNC_PRNAME, true).c_str()) /// When sent into std::ostream, this object prints current source position. #define DBG_POS debug_internal::DebugSourcePos(DBG_FILE, DBG_LINE, DBG_FUNC_NAME, DBG_FUNC) /// A standalone function-like macro, prints "Trace point "a" reached at (source position)" /// (\c a is the macro parameter). #define DBG_TRACE_POINT_MSG(a) debug_out_dump("default", "TRACE point \"" << #a << "\" reached at " << DBG_POS << ".\n") /// A standalone function-like macro, prints "Trace point reached at (source position)". #define DBG_TRACE_POINT_AUTO debug_out_dump("default", "TRACE point reached at " << DBG_POS << ".\n") /// A standalone function-like macro, prints "ENTER: "function_name"". #define DBG_FUNCTION_ENTER_MSG debug_out_dump("default", "ENTER: \"" << DBG_FUNC << "\"\n") /// A standalone function-like macro, prints "EXIT: "function_name"". #define DBG_FUNCTION_EXIT_MSG debug_out_dump("default", "EXIT: \"" << DBG_FUNC << "\"\n") /// A standalone function-like macro, prints \c msg if \c cond evaluates to false. #define DBG_ASSERT_MSG(cond, msg) \ if (true) { \ if (!(cond)) \ debug_out_error("default", (msg) << "\n"); \ } else (void)0 /// A standalone function-like macro, prints generic message if \c cond evaluates to false. #define DBG_ASSERT(cond) \ if (true) { \ if (!(cond)) \ debug_out_error("default", "ASSERTION FAILED: " << #cond << " at " << DBG_POS << "\n"); \ } else (void)0 // ------------------ Indentation and manipulators /// Increase indentation level for all debug levels void debug_indent_inc(int by = 1); /// Decrease indentation level for all debug levels void debug_indent_dec(int by = 1); /// Reset indentation level to 0 for all debug levels void debug_indent_reset(); namespace debug_internal { struct DebugIndent; struct DebugUnindent; struct DebugResetIndent; /// A stream manipulator that increases the indentation level struct DebugIndent { /// Constructor DebugIndent(int indent_level = 1) : by(indent_level) { } /// Constructs a new DebugIndent object DebugIndent operator() (int indent_level = 1) { return DebugIndent(indent_level); } int by; ///< Number of indentation levels to increase with (may be negative) }; /// A stream manipulator that decreases the indentation level struct DebugUnindent { /// Constructor DebugUnindent(int unindent_level = 1) : by(unindent_level) { } /// Constructs a new DebugUnindent object DebugUnindent operator() (int unindent_level = 1) { return DebugUnindent(unindent_level); } int by; ///< Number of indentation levels to decrease with (may be negative) }; /// A stream manipulator that resets the indentation level to 0 struct DebugResetIndent { /// Constructs a new DebugResetIndent object DebugResetIndent operator() () // just for consistency { return *this; } }; // These operators need to be inside the same namespace that their // operands are for ADL to work inside _other_ namespaces. /// A stream manipulator operator inline std::ostream& operator<< (std::ostream& os, debug_internal::DebugIndent& m) { debug_indent_inc(m.by); return os; } /// A stream manipulator operator inline std::ostream& operator<< (std::ostream& os, debug_internal::DebugUnindent& m) { debug_indent_dec(m.by); return os; } /// A stream manipulator operator inline std::ostream& operator<< (std::ostream& os, debug_internal::DebugResetIndent& m) { debug_indent_reset(); return os; } // Manipulator objects: /// Send this to libdebug-backed stream to increase the indentation level by 1. extern DebugIndent debug_indent; /// Send this to libdebug-backed stream to decrease the indentation level by 1. extern DebugUnindent debug_unindent; /// Send this to libdebug-backed stream to reset the indentation level to 0. extern DebugResetIndent debug_resindent; } // ns // manupulator objects using debug_internal::debug_indent; using debug_internal::debug_unindent; using debug_internal::debug_resindent; #endif /// @} gsmartcontrol-1.1.4/src/libdebug/dstate.cpp000066400000000000000000000203511417717230600207600ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup libdebug /// \weakgroup libdebug /// @{ #include // std::cerr, the default output stream #include #include "hz/hz_config.h" // DEBUG_BUILD #include "hz/exceptions.h" // THROW_FATAL #include "dstate.h" #include "dflags.h" #include "dchannel.h" #include "dstream.h" namespace debug_internal { void DebugState::setup_default_state() { // defaults: debug_level::type levels_enabled = debug_level::warn | debug_level::error | debug_level::fatal; #ifdef DEBUG_BUILD levels_enabled = debug_level::all; #endif // default format debug_format::type format_flags = debug_format::level | debug_format::domain | debug_format::indent; #ifndef _WIN32 format_flags |= debug_format::color; #endif std::map levels; unsigned long levels_enabled_ulong = levels_enabled.to_ulong(); levels[debug_level::dump] = (levels_enabled_ulong & debug_level::dump); levels[debug_level::info] = (levels_enabled_ulong & debug_level::info); levels[debug_level::warn] = (levels_enabled_ulong & debug_level::warn); levels[debug_level::error] = (levels_enabled_ulong & debug_level::error); levels[debug_level::fatal] = (levels_enabled_ulong & debug_level::fatal); domain_map_t& dm = get_domain_map(); dm["default"] = level_map_t(); level_map_t& level_map = dm.find("default")->second; // we add the same copy to save memory and to ensure proper std::cerr locking. debug_channel_base_ptr channel(new DebugChannelOStream(std::cerr)); for (std::map::const_iterator iter = levels.begin(); iter != levels.end(); ++iter) { debug_level::flag level = iter->first; level_map[level] = out_stream_ptr(new DebugOutStream(level, "default", format_flags)); level_map[level]->add_channel(channel); // add by smartpointer level_map[level]->set_enabled(iter->second); } } /// Global libdebug state. /// This will initialize the default domain and channels automatically. static DebugState s_debug_state; DebugState& get_debug_state() { return s_debug_state; } } bool debug_register_domain(const std::string& domain) { using namespace debug_internal; DebugState::domain_map_t& dm = get_debug_state().get_domain_map(); if (dm.find(domain) != dm.end()) // already exists return false; // copy the "default" domain - use it as a template DebugState::domain_map_t::iterator def_iter = dm.find("default"); if (def_iter == dm.end()) { THROW_FATAL(debug_internal_error(("debug_register_domain(\"" + domain + "\"): Domain \"default\" doesn't exist.").c_str())); } DebugState::level_map_t& def_level_map = def_iter->second; dm[domain] = DebugState::level_map_t(); DebugState::level_map_t& level_map = dm.find(domain)->second; for (DebugState::level_map_t::const_iterator iter = def_level_map.begin(); iter != def_level_map.end(); ++iter) { level_map[iter->first] = DebugState::out_stream_ptr(new DebugOutStream(*(iter->second), domain)); } return true; } bool debug_unregister_domain(const std::string& domain) { using namespace debug_internal; DebugState::domain_map_t& dm = get_debug_state().get_domain_map(); DebugState::domain_map_t::iterator found = dm.find(domain); if (found == dm.end()) // doesn't exists return false; dm.erase(found); // this should clear everything - it's all smartpointers return true; } std::vector debug_get_registered_domains() { using namespace debug_internal; DebugState::domain_map_t& dm = get_debug_state().get_domain_map(); std::vector domains; domains.reserve(dm.size()); for (DebugState::domain_map_t::iterator iter = dm.begin(); iter != dm.end(); ++iter) domains.push_back(iter->first); return domains; } bool debug_set_enabled(const std::string& domain, const debug_level::type& levels, bool enabled) { using namespace debug_internal; DebugState::domain_map_t& dm = get_debug_state().get_domain_map(); if (domain == "all") { bool status = true; for (DebugState::domain_map_t::iterator iter = dm.begin(); iter != dm.end(); ++iter) status = status && debug_set_enabled(iter->first, levels, enabled); return status; } DebugState::domain_map_t::iterator found = dm.find(domain); if (found == dm.end()) // doesn't exists return false; std::vector matched_levels; debug_level::get_matched_levels_array(levels, matched_levels); for (unsigned int i = 0; i < matched_levels.size(); ++i) { found->second[matched_levels[i]]->set_enabled(enabled); } return true; } debug_level::type debug_get_enabled(const std::string& domain) { using namespace debug_internal; DebugState::domain_map_t& dm = get_debug_state().get_domain_map(); debug_level::type levels; DebugState::domain_map_t::iterator found = dm.find(domain); if (found == dm.end()) // doesn't exists return levels; DebugState::level_map_t& level_map = found->second; for (DebugState::level_map_t::const_iterator iter = level_map.begin(); iter != level_map.end(); ++iter) { if (iter->second->get_enabled()) levels |= iter->first; } return levels; } bool debug_set_format(const std::string& domain, const debug_level::type& levels, const debug_format::type& format) { using namespace debug_internal; DebugState::domain_map_t& dm = get_debug_state().get_domain_map(); if (domain == "all") { bool status = true; for (DebugState::domain_map_t::iterator iter = dm.begin(); iter != dm.end(); ++iter) status = status && debug_set_format(iter->first, levels, format); return status; } DebugState::domain_map_t::iterator found = dm.find(domain); if (found == dm.end()) // doesn't exists return false; std::vector matched_levels; debug_level::get_matched_levels_array(levels, matched_levels); for (unsigned int i = 0; i < matched_levels.size(); ++i) { found->second[matched_levels[i]]->set_format(format); } return true; } std::map debug_get_formats(const std::string& domain) { using namespace debug_internal; DebugState::domain_map_t& dm = get_debug_state().get_domain_map(); std::map formats; DebugState::domain_map_t::iterator found = dm.find(domain); if (found == dm.end()) // doesn't exists return formats; for (DebugState::level_map_t::const_iterator iter = found->second.begin(); iter != found->second.end(); ++iter) formats[iter->first] = iter->second->get_format(); return formats; } bool debug_add_channel(const std::string& domain, const debug_level::type& levels, debug_channel_base_ptr channel) { using namespace debug_internal; DebugState::domain_map_t& dm = get_debug_state().get_domain_map(); if (domain == "all") { bool status = true; for (DebugState::domain_map_t::iterator iter = dm.begin(); iter != dm.end(); ++iter) status = status && debug_add_channel(iter->first, levels, channel); return status; } DebugState::domain_map_t::iterator found = dm.find(domain); if (found == dm.end()) // doesn't exists return false; std::vector matched_levels; debug_level::get_matched_levels_array(levels, matched_levels); for (unsigned int i = 0; i < matched_levels.size(); ++i) { found->second[matched_levels[i]]->add_channel(channel); } return true; } bool debug_clear_channels(const std::string& domain, const debug_level::type& levels) { using namespace debug_internal; DebugState::domain_map_t& dm = get_debug_state().get_domain_map(); if (domain == "all") { bool status = true; for (DebugState::domain_map_t::iterator iter = dm.begin(); iter != dm.end(); ++iter) status = status && debug_clear_channels(iter->first, levels); return status; } DebugState::domain_map_t::iterator found = dm.find(domain); if (found == dm.end()) // doesn't exists return false; std::vector matched_levels; debug_level::get_matched_levels_array(levels, matched_levels); for (unsigned int i = 0; i < matched_levels.size(); ++i) { found->second[matched_levels[i]]->set_channels(channel_list_t()); } return true; } /// @} gsmartcontrol-1.1.4/src/libdebug/dstate.h000066400000000000000000000103351417717230600204260ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup libdebug /// \weakgroup libdebug /// @{ #ifndef LIBDEBUG_DSTATE_H #define LIBDEBUG_DSTATE_H #include #include #include #include "hz/intrusive_ptr.h" #include "hz/tls.h" #include "hz/exceptions.h" // THROW_FATAL #include "dflags.h" #include "dstream.h" #include "dexcept.h" #include "dstate_pub.h" // public interface part of this header namespace debug_internal { // domain name "default" - template for all new domains. // domain name "all" - used for manipulating all domains. /// Libdebug global state class DebugState { public: /// Debug output stream strong reference-holding pointer typedef hz::intrusive_ptr out_stream_ptr; /// A mapping of debug levels to respective streams typedef std::map level_map_t; /// A mapping of domains to debug level maps with streams typedef std::map domain_map_t; /// Constructor (statically called), calls setup_default_state(). DebugState() { setup_default_state(); } /// Initialize the "default" template domain, set the default enabled levels / format flags. /// This function is NOT thread-safe. Call it before using any /// other functions in MT environment. Automatically called by constructor. void setup_default_state(); /// Get the domain/level mapping. /// This is function thread-safe in read-only context. domain_map_t& get_domain_map() { return domain_map; } /// Get current indentation level. This is function thread-safe. int get_indent_level() const { if (!indent_level_.get()) indent_level_.reset(new int(0)); return *indent_level_; } /// Set current indentation level. This is function thread-safe. void set_indent_level(int indent_level) { if (!indent_level_.get()) { indent_level_.reset(new int(indent_level)); } else { *indent_level_ = indent_level; } } /// Open a debug_begin() context. This is function thread-safe. void push_inside_begin(bool value = true) { if (!inside_begin_.get()) inside_begin_.reset(new std::stack()); inside_begin_->push(value); } /// Close a debug_begin() context. This is function thread-safe. bool pop_inside_begin() { if (!inside_begin_.get()) inside_begin_.reset(new std::stack()); if (inside_begin_->empty()) THROW_FATAL(debug_usage_error("DebugState::pop_inside_begin(): Begin / End stack underflow! Mismatched begin()/end()?")); bool val = inside_begin_->top(); inside_begin_->pop(); return val; } /// Check if we're inside a debug_begin() context. This is function thread-safe. bool get_inside_begin() const { if (!inside_begin_.get()) inside_begin_.reset(new std::stack()); if (inside_begin_->empty()) return false; return inside_begin_->top(); } /// Flush all the stream buffers. This will write prefixes too. /// This is function thread-safe in read-only context. void force_output() { for(domain_map_t::iterator iter = domain_map.begin(); iter != domain_map.end(); ++iter) { for(level_map_t::iterator iter2 = iter->second.begin(); iter2 != iter->second.end(); ++iter2) iter2->second->force_output(); } } private: // Without mutable there's no on-demand allocation for these. // It's thread-local because it is not shared between different flows. // We can't provide any manual cleanup, because the only one we can do // is in main thread, and it's already being done with the destructor. mutable hz::thread_local_ptr indent_level_; ///< Current indentation level mutable hz::thread_local_ptr > inside_begin_; ///< True if inside debug_begin() / debug_end() block domain_map_t domain_map; ///< Domain / debug level mapping. }; /// Get global libdebug state DebugState& get_debug_state(); } // ns #endif /// @} gsmartcontrol-1.1.4/src/libdebug/dstate_pub.h000066400000000000000000000046071417717230600213010ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup libdebug /// \weakgroup libdebug /// @{ #ifndef LIBDEBUG_DSTATE_PUB_H #define LIBDEBUG_DSTATE_PUB_H #include #include #include #include "dflags.h" #include "dchannel.h" /** \file Public interface of dstate.h */ /// Register a libdebug output domain. Domains are like components /// of an application, and libdebug flags for each one can be manipulated /// separately. This will use the "default" domain as a template. /// \return false if the domain is registered already. bool debug_register_domain(const std::string& domain); /// Unregister a previously registered domain. /// \return false if no such domain bool debug_unregister_domain(const std::string& domain); /// Get a list of registered domain std::vector debug_get_registered_domains(); /// Enable/disable output streams. Set the domain to "all" for all domains. /// Multiple levels may be passed (OR'ed), as well as debug_level::all. bool debug_set_enabled(const std::string& domain, const debug_level::type& levels, bool enabled); /// See which levels are enabled for domain. debug_level::type debug_get_enabled(const std::string& domain); /// Set format flags for domain / level. Set the domain to "all" for all domains. /// Multiple levels may be passed (OR'ed), as well as debug_level::all. bool debug_set_format(const std::string& domain, const debug_level::type& levels, const debug_format::type& format); /// Get all enabled format flags for each level in a domain std::map debug_get_formats(const std::string& domain); /// Add a new output channel to domain. Set the domain to "all" for all domains. /// Multiple levels may be passed (OR'ed), as well as debug_level::all. bool debug_add_channel(const std::string& domain, const debug_level::type& levels, debug_channel_base_ptr channel); /// Remove all output channels from domain. Set the domain to "all" for all domains. /// Multiple levels may be passed (OR'ed), as well as debug_level::all. bool debug_clear_channels(const std::string& domain, const debug_level::type& levels); #endif /// @} gsmartcontrol-1.1.4/src/libdebug/dstream.cpp000066400000000000000000000037511417717230600211400ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup libdebug /// \weakgroup libdebug /// @{ #include // std::ostream definition #include "dstream.h" #include "dstate.h" #include "dchannel.h" // #include // tmp namespace debug_internal { /// Null stream buf - discards anything that is sent to it. class NullStreamBuf : public std::streambuf { protected: int overflow(int) { return 0; } int sync() { return 0; } }; /// Null streambuffer object static NullStreamBuf s_null_streambuf; /// Null ostream - discards anything that is sent to it. static std::ostream s_null_stream(&s_null_streambuf); /// Get null streambuf, see s_null_streambuf. std::streambuf& get_null_streambuf() { return s_null_streambuf; } /// Get null ostream, see s_null_stream. std::ostream& get_null_stream() { return s_null_stream; } void DebugStreamBuf::flush_to_channel() { if (!oss_.get()) // tls oss_.reset(new std::ostringstream()); debug_format::type flags = dos_->format_; bool is_first_line = false; if (get_debug_state().get_inside_begin()) { flags |= debug_format::first_line_only; if (dos_->get_is_first_line()) { dos_->set_is_first_line(false); // tls is_first_line = true; } } else { dos_->set_is_first_line(true); is_first_line = true; } // std::cerr << "SENDING: " << oss.str(); channel_list_t::iterator iter = dos_->channels_.begin(); for (; iter != dos_->channels_.end(); ++iter) { // send() locks the channel if needed (*iter)->send(dos_->level_, dos_->domain_, flags, get_debug_state().get_indent_level(), is_first_line, oss_->str()); } oss_->str(""); // clear the buffer oss_->clear(); // clear the flags } } /// @} gsmartcontrol-1.1.4/src/libdebug/dstream.h000066400000000000000000000176371417717230600206150ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup libdebug /// \weakgroup libdebug /// @{ #ifndef LIBDEBUG_DSTREAM_H #define LIBDEBUG_DSTREAM_H #include // std::ostream definition #include // std::streambuf definition #include #include #include #include "hz/intrusive_ptr.h" #include "hz/tls.h" #include "dflags.h" #include "dchannel.h" namespace debug_internal { /// Get null streambuf - a streambuf which does nothing. std::streambuf& get_null_streambuf(); /// Get null ostream - an ostream which does nothing. std::ostream& get_null_stream(); // state.h includes us, so we need these forward declarations // class DebugState; // DebugState& get_debug_state(); class DebugOutStream; /// Streambuf for libdebug, used in DebugOutStream implementation. class DebugStreamBuf : public std::streambuf { public: /// Constructor DebugStreamBuf(DebugOutStream* dos) : dos_(dos) { // in case of overflow for output, overflow() will be called to _output_ the data. // no buffers here - we process it char-by-char. Apart from practical reasons, // this is also needed to ensure that the state of the object (buffer) doesn't // change during calls to methods - to ensure read-only thread-safety. int buf_size = 0; if (buf_size) { char* buf = new char[buf_size]; // further accessible through pbase(). setp(buf, buf + buf_size); // Set output sequence pointers, aka output buffer } else { setp(NULL, NULL); } setg(NULL, NULL, NULL); // Set input sequence pointers; not relevant in this class. } /// Virtual destructor virtual ~DebugStreamBuf() { sync(); delete[] pbase(); // delete the buffer } /// Force output of the stringstream's contents to the channels. /// This is function thread-safe as long as state is not modified. void force_output() { flush_to_channel(); } protected: /// Overflow happens when a new character is to be written at the put /// pointer pptr position, but this has reached the end pointer epptr. /// Reimplemented. virtual int overflow(int c) { sync(); // write the buffer contents if available if (c != traits_type::eof()) { if (pbase() == epptr()) { // no buffer, write it char-by-char (epptr() - buffer end pointer) // std::string tmp; // tmp += char(c); // write_out(tmp); write_char(char(c)); } else { // we have a buffer // put c into buffer (the overflowed char); the rest is written in sync() earlier. sputc(static_cast(c)); } } return 0; } /// Sort-of flush the buffer. Only makes sense if there is a buffer. /// Reimplemented. virtual int sync() { if (pbase() != pptr()) { // pptr() - current position; condition is true only if there is something in the buffer. // write_out(std::string(pbase(), pptr() - pbase())); for (char* pos = pbase(); pos != pptr(); ++pos) write_char(*pos); setp(pbase(), epptr()); // reset the buffer's current pointer (?) } return 0; } /// Write contents if necessary. void write_char(char c) { if (!oss_.get()) oss_.reset(new std::ostringstream()); *oss_ << c; if (c == '\n') // send to channels on newline flush_to_channel(); } /// Flush contents to debug channel. /// This is function thread-safe as long as state is not modified. void flush_to_channel(); private: DebugOutStream* dos_; ///< Debug output stream // It's thread-local because it is not shared between different flows. // we can't provide any manual cleanup, because the only one we can do it // is in main thread, and it's already being done with the destructor. hz::thread_local_ptr oss_; ///< A buffer for output storage. /// Disallow copying DebugStreamBuf(const DebugStreamBuf& from); }; /// Debug channel list typedef std::vector channel_list_t; /// Debug output stream (inherits std::ostream). /// This is returned by debug_out(). class DebugOutStream : public std::ostream, public hz::intrusive_ptr_referenced { public: friend class DebugStreamBuf; /// Constructor DebugOutStream(debug_level::flag level, const std::string& domain, const debug_format::type& format_flags) : std::ostream(NULL), level_(level), domain_(domain), format_(format_flags), buf_(this) { set_enabled(true); // sets ostream's rdbuf } // DebugOutStream() : std::ostream(NULL), buf_(this) // { // set_enabled(false); // } /// Construct with settings from another DebugOutStream. DebugOutStream(const DebugOutStream& other, const std::string& domain) : std::ostream(NULL), level_(other.level_), domain_(domain), format_(other.format_), buf_(this) { set_enabled(other.get_enabled()); // sets ostream's rdbuf for (channel_list_t::const_iterator iter = other.channels_.begin(); iter != other.channels_.end(); ++iter) { // we let the object dictate the copy rules because really copying it // may harm the underlying locking mechanism channels_.push_back((*iter)->clone_ptr()); } } /* void set_level(debug_level::flag level) { level_ = level; } void set_domain(const std::string& domain) { domain_ = domain; } */ /// Set format flags void set_format(const debug_format::type& format_flags) { format_ = format_flags; } /// Get format flags debug_format::type get_format() const { return format_; } /// Enable or disable output. If disabled, any data sent to this /// stream is discarded. void set_enabled(bool enabled) { if (enabled) rdbuf(&buf_); else rdbuf(&get_null_streambuf()); } /// Check whether the stream is enabled or not. bool get_enabled() const { return (rdbuf() == &buf_); } /// Set channel list to send the data to. void set_channels(const channel_list_t& channels) { channels_ = channels; } /// Get channel list channel_list_t& get_channels() { return channels_; } /// Add a channel to channel list. /// This will claim the ownership of the passed parameter. void add_channel(debug_channel_base_ptr channel) { channels_.push_back(channel); } /// Check if the last sent output is still on the same line /// as the first one. bool get_is_first_line() { if (!is_first_line_.get()) is_first_line_.reset(new bool(true)); return *is_first_line_; } /// Set whether we're on the first line of the output or not. void set_is_first_line(bool b) { if (!is_first_line_.get()) { is_first_line_.reset(new bool(b)); } else { *is_first_line_ = b; } } /// Force output of buf_'s contents to the channels. /// This also outputs a prefix if needed. /// This is function thread-safe in read-only context. std::ostream& force_output() { buf_.force_output(); return *this; } private: debug_level::flag level_; ///< Debug level of this stream std::string domain_; ///< Domain of this stream debug_format::type format_; ///< Format flags // It's thread-local because it is not shared between different flows. // we can't provide any manual cleanup, because the only one we can do // is in main thread, and it's already being done with the destructor. hz::thread_local_ptr is_first_line_; ///< Whether it's the first line of output or not channel_list_t channels_; ///< Channels that the output is sent to DebugStreamBuf buf_; /// Streambuf for implementation. Not thread-local, but its internal buffer is. }; } // ns debug_internal #endif /// @} gsmartcontrol-1.1.4/src/libdebug/libdebug.h000066400000000000000000000020241417717230600207130ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup libdebug /// \weakgroup libdebug /// @{ #ifndef LIBDEBUG_LIBDEBUG_H #define LIBDEBUG_LIBDEBUG_H /** \file Main libdebug include file, includes complete libdebug functionality. \see libdebug_mini.h */ // These macros may be used to control how libdebug is built: /// \def ENABLE_GLIB /// Define to 1 to enable Glib option parsing support. /// \def DEBUG_BUILD /// Define to 1 to enable all levels by default. /// \namespace debug_internal /// Libdebug internal implementation details // all libdebug headers: #include "dchannel.h" #include "dcmdarg.h" #include "dexcept.h" #include "dflags.h" #include "dout.h" #include "dstate_pub.h" // #include "dstream.h" // no dstream - it's internal only #endif /// @} gsmartcontrol-1.1.4/src/libdebug/libdebug_mini.h000066400000000000000000000013301417717230600217260ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup libdebug /// \weakgroup libdebug /// @{ #ifndef LIBDEBUG_LIBDEBUG_MINI_H #define LIBDEBUG_LIBDEBUG_MINI_H /** \file You may include this instead of libdebug.h if you only need the output functions and not control functions. This may speed up compilation times because it avoids many dependencies. \see libdebug.h */ // output-related files only #include "dflags.h" #include "dout.h" #endif /// @} gsmartcontrol-1.1.4/src/libdebug/libdebug_test.cpp000066400000000000000000000115251417717230600223130ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_unlicense.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup libdebug_tests /// \weakgroup libdebug_tests /// @{ // The first header should be then one we're testing, to avoid missing // header pitfalls. #include "libdebug.h" #include /// libdebug namespace for tests namespace libdebug_test { /// Libdebug test class struct TestClassA { bool func(int a = 2) { // this prints "func" debug_out_info("default", DBG_FUNC_NAME << "\n"); // this prints "bool::TestClassA::func()" if in anonymous namespace, // else "bool test::TestClass::func()". debug_out_info("default", DBG_FUNC_PRNAME << "\n"); // prints "test::TestClassA::func(): function called." debug_out_info("default", DBG_FUNC_MSG << "function called.\n"); return true; } }; } namespace { /// Libdebug test struct template struct TestClassB { template U func2(V v, int) { debug_out_info("default", DBG_FUNC_PRNAME << "\n"); debug_out_info("default", DBG_FUNC_MSG << "function called.\n"); return 0; } }; /// Libdebug test struct template struct TestClassC { }; } /// Main function for the test int main(int argc, char *argv[]) { debug_register_domain("dom"); debug_set_enabled("dom", debug_level::dump, false); debug_set_format("dom", debug_level::info, (debug_get_formats("dom")[debug_level::info].to_ulong() & !debug_format::color) | debug_format::datetime); std::string something = "some thing"; const char* obj = "obj"; int op = 5; debug_print_dump("dom", "Dumping something: %s\n", something.c_str()); debug_print_info("dom", "Doing something: %s\n", something.c_str()); debug_print_error("dom", "Error while doing something\n"); debug_out_info("dom", "Doing something with " << obj << " object\n"); debug_out_fatal("dom", "Fatal error while performing operation " << op << "\n"); DBG_ASSERT_MSG(1 == 0, "One does not equal 0"); DBG_ASSERT(1 == 0); // debug::out(debug::dump) << debug::libdebug_info; // or // printd(debug::dump, "%s", debug::libdebug_info_str().c_str()); // debug::out() << "info1\n"; // info level, default domain // or // debug::print("info1\n"); // debug::out(debug::error) << "error1\n"; // or // debug::out_error() << "error1\n"; // or // debug::print(debug::error, "error1\n"); // or // debug::print_error("error1\n"); // debug::out(debug::warn, "dom") << debug::indent << "\nwarning1\nwarning2" << debug::unindent << "\n"; // or // debug::indent++; // debug::printd_warn("dom", "\nwarning1\nwarning2"); // debug::indent--; // reseting indentation is done by // debug::out("dom", debug::prnone) << debug::indent(0); // or // indent_reset(); // debug::out("dom", debug::prnone) << "info2\n"; // or // debug::out(debug::info, "dom") << "info2\n"; // or // debug::printd("dom", "info2\n"); // debug::out(debug::error, "dom") << "error2\n"; // or // debug::printd_error("dom", "error2\n"); // debug::out(debug::dump, "default", debug::prnone) << "dump1, no prefixes here\n"; // print out current function name // debug::out(debug::dump) << "dump2, " << DBG_POS(debug::posfunc) << "\n"; // or // debug::print_dump("%s", DBG_POS(debug::posfunc).get_text().c_str()); // debug::out(debug::dump, "default", debug::prdate) << "date prefix here\n"; debug_out_dump("default", DBG_POS << "\n"); debug_out_dump("default", DBG_POS.func << "\n"); // or debug_print_info("default", "%s\n", DBG_POS.str().c_str()); DBG_TRACE_POINT_MSG(1); DBG_TRACE_POINT_MSG(666 a); DBG_TRACE_POINT_AUTO; DBG_TRACE_POINT_AUTO; // Str s; // s.somefunc(1, 1); // these begin()/end() lock the streams so no other threads can write, and turn off prefix printing debug_begin(); // don't use different levels inside, or they might get merged (the order will be different). debug_out_info("default", "The following lines should have no prefixes\n"); debug_out_info("default", "1st line\n" << "2nd line\n"); debug_out_error("default", "3rd line, error, prefixed\n"); debug_out_info("default", debug_indent << "4th line, not prefixed\n"); debug_out_warn("default", "5th line, warning, prefixed\n"); debug_out_warn("default", "6th line, warning, not prefixed\n"); debug_indent_dec(); // or use << debug_unindent debug_end(); debug_out_info("default", "prefixed\n"); std::ostream& os = debug_out_dump("default", ""); // get the ostream os << ""; libdebug_test::TestClassA().func(); TestClassB().func2(TestClassC(), 0); // debug_out_warn("default", DBG_FUNC_MSG << "Doing something.\n"); // debug_out_warn("default", DBG_FUNC_MSG << "Doing something.\n"); return EXIT_SUCCESS; } /// @} gsmartcontrol-1.1.4/src/rconfig/000077500000000000000000000000001417717230600166415ustar00rootroot00000000000000gsmartcontrol-1.1.4/src/rconfig/Makefile.am000066400000000000000000000005611417717230600206770ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) METASOURCES = AUTO noinst_HEADERS = rcautosave.h rcdump.h rcloadsave.h rcmain.h rconfig.h \ rconfig_mini.h rconfig_test_SOURCES = rconfig_test.cpp # we don't list them in a separate variable because otherwise kdevelop won't see them. noinst_PROGRAMS = if ENABLE_TESTS noinst_PROGRAMS += rconfig_test endif tests: $(noinst_PROGRAMS) gsmartcontrol-1.1.4/src/rconfig/rcautosave.h000066400000000000000000000112601417717230600211660ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup rconfig /// \weakgroup rconfig /// @{ #ifndef RCONFIG_RCAUTOSAVE_H #define RCONFIG_RCAUTOSAVE_H #include "hz/hz_config.h" // ENABLE_GLIB // Autosave functions are only available if GLib is enabled. #if defined ENABLE_GLIB && ENABLE_GLIB #include #include #include "hz/debug.h" #include "hz/sync.h" #include "hz/sync_policy_glib.h" #include "hz/fs_path.h" #include "rcloadsave.h" namespace rconfig { /// Auto-save sync policy, to avoid race conditions while saving configuration. typedef hz::SyncPolicyMtDefault AutoSaveLockPolicy; /// Holder for static variables template struct AutoSaveStaticHolder { static std::string config_file; ///< Config file to autosave to. static bool autosave_enabled; ///< Autosave enabled or not. This acts as a stopper flag for autosave callback. static AutoSaveLockPolicy::Mutex mutex; // Mutex for the static variables. }; // definitions template std::string AutoSaveStaticHolder::config_file; template bool AutoSaveStaticHolder::autosave_enabled = false; template AutoSaveLockPolicy::Mutex AutoSaveStaticHolder::mutex; /// Specify the same template parameter to get the same set of variables. typedef AutoSaveStaticHolder AutoSaveHolder; // one (and only) specialization. extern "C" { /// Autosave timeout callback for Glib. internal. inline gboolean autosave_timeout_callback(gpointer data) { bool force = (bool)data; // If previous call is active, return (callback registered multiple times?). // Don't do this for forced call, because it may expect the file to be updated // once this function exits. if (!force) { if (!AutoSaveLockPolicy::trylock(AutoSaveHolder::mutex)) // can't lock, another save active, exit. return true; // automatically try next time AutoSaveLockPolicy::unlock(AutoSaveHolder::mutex); // unlock so we can use the scoped lock. } // manually called or no parallel threads saving: // lock all static vars of this file. AutoSaveLockPolicy::ScopedLock locker(AutoSaveHolder::mutex); if (!force && !AutoSaveHolder::autosave_enabled) // no more autosaves return false; // remove timeout, disable autosave for real. debug_print_info("rconfig", "Autosaving config to \"%s\".\n", AutoSaveHolder::config_file.c_str()); hz::FsPath p(AutoSaveHolder::config_file); if ((p.exists() && !p.is_regular()) || !p.is_writable()) { debug_out_error("rconfig", "Autosave failed: Cannot write to file: " << p.get_error_locale() << "\n"); return (force ? false : true); // if manual, return failure. else, don't stop the timeout. } bool status = rconfig::save_to_file(AutoSaveHolder::config_file); if (force) return status; // return status to caller return true; // continue timeouts } } /// Set config file to autosave to. inline bool autosave_set_config_file(const std::string& file) { if (file.empty()) { debug_print_error("rconfig", "autosave_set_config_file(): Error: Filename is empty.\n"); return false; } AutoSaveLockPolicy::ScopedLock locker(AutoSaveHolder::mutex); AutoSaveHolder::config_file = file; debug_print_info("rconfig", "Setting autosave config file to \"%s\"\n", file.c_str()); return true; } /// Enable autosave every \c sec_interval seconds. inline bool autosave_start(unsigned int sec_interval) { AutoSaveLockPolicy::ScopedLock locker(AutoSaveHolder::mutex); if (AutoSaveHolder::autosave_enabled) { // already autosaving, you should stop it first. debug_print_warn("rconfig", "Error while starting config autosave: Autosave is active already.\n"); return false; } AutoSaveHolder::autosave_enabled = true; debug_print_info("rconfig", "Starting config autosave with %u sec. interval.\n", sec_interval); g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, sec_interval*1000, &autosave_timeout_callback, NULL, NULL); return true; } /// Disable autosave inline void autosave_stop() { debug_print_info("rconfig", "Stopping config autosave.\n"); AutoSaveLockPolicy::ScopedLock locker(AutoSaveHolder::mutex); // set the stop flag. it will make autosave stop on next timeout callback call. AutoSaveHolder::autosave_enabled = false; } /// Forcibly save the config now. inline bool autosave_force_now() { return autosave_timeout_callback((void*)true); // anyone tell me what is the C++ variant of this? } } // ns #endif // glib #endif /// @} gsmartcontrol-1.1.4/src/rconfig/rcdump.h000066400000000000000000000030051417717230600203020ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup rconfig /// \weakgroup rconfig /// @{ #ifndef RCONFIG_RCDUMP_H #define RCONFIG_RCDUMP_H #include #include // std::ostream #include "hz/debug.h" // debug_* #include "rmn/resource_node_dump.h" // node dumper functions #include "rcmain.h" namespace rconfig { /// Dump a config tree to libdebug stream (in displayable format). This function is thread-safe. inline void dump_tree() { ConfigLockPolicy::ScopedLock locker(RootHolder::mutex); debug_begin(); // includes the trailing newline debug_out_dump("rconfig", rmn::resource_node_dump_recursive(get_root())); debug_end(); } /// Dump a config tree to std::ostream (in displayable format). This function is thread-safe. inline void dump_tree_to_stream(std::ostream& os) { ConfigLockPolicy::ScopedLock locker(RootHolder::mutex); debug_begin(); // includes the trailing newline os << rmn::resource_node_dump_recursive(get_root()); debug_end(); } /// Dump a config tree to a string (in displayable format). This function is thread-safe. inline std::string dump_tree_to_string() { ConfigLockPolicy::ScopedLock locker(RootHolder::mutex); return rmn::resource_node_dump_recursive(get_root()); } } // ns #endif /// @} gsmartcontrol-1.1.4/src/rconfig/rcloadsave.h000066400000000000000000000041131417717230600211340ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup rconfig /// \weakgroup rconfig /// @{ #ifndef RCONFIG_RCLOADSAVE_H #define RCONFIG_RCLOADSAVE_H #include #include "rmn/resource_serialization.h" // serialize / unserialize nodes #include "rcmain.h" namespace rconfig { /// Load the "/config" branch from file. This function is thread-safe. inline bool load_from_file(const std::string& file) { ConfigLockPolicy::ScopedLock locker(RootHolder::mutex); return rmn::unserialize_nodes_from_file(get_config_branch(), file); } /// Load the "/config" branch from string. This function is thread-safe. inline bool load_from_string(const std::string& str) { ConfigLockPolicy::ScopedLock locker(RootHolder::mutex); return rmn::unserialize_nodes_from_string(get_config_branch(), str); } /// \fn bool save_to_file(const std::string& file) /// Save the "/config" branch to a file. This function is thread-safe. /// This function is available only if \c RMN_SERIALIZE_AVAILABLE is 1. /// \fn bool save_to_string(std::string& put_here) /// Save the "/config" branch to a string. This function is thread-safe. /// This function is available only if \c RMN_SERIALIZE_AVAILABLE is 1. #if defined RMN_SERIALIZE_AVAILABLE && RMN_SERIALIZE_AVAILABLE inline bool save_to_file(const std::string& file) { ConfigLockPolicy::ScopedLock locker(RootHolder::mutex); return rmn::serialize_node_to_file_recursive(get_config_branch(), file); } inline bool save_to_string(std::string& put_here) { ConfigLockPolicy::ScopedLock locker(RootHolder::mutex); return rmn::serialize_node_to_string_recursive(get_config_branch(), put_here); } #else // no save function inline bool save_to_file(const std::string& file) { return false; } inline bool save_to_string(std::string& put_here) { return false; } #endif } // ns #endif /// @} gsmartcontrol-1.1.4/src/rconfig/rcmain.h000066400000000000000000000332571417717230600202750ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup rconfig /// \weakgroup rconfig /// @{ #ifndef RCONFIG_RCMAIN_H #define RCONFIG_RCMAIN_H #include #include "hz/hz_config.h" // DISABLE_RTTI, RMN_TYPE_TRACKING // if there's RTTI or type tracking enabled #if !(defined DISABLE_RTTI && DISABLE_RTTI) \ || (defined RMN_TYPE_TRACKING && RMN_TYPE_TRACKING) #include // std::runtime_error #endif #include "hz/sync.h" #include "hz/exceptions.h" // THROW_FATAL #include "rmn/resource_node.h" // resource_node type #include "rmn/resource_data_any.h" // any_type data provider #include "rmn/resource_data_locking.h" // locking policies for data #include "rmn/resource_exception.h" // rmn::no_such_node namespace rconfig { /// Rconfig node type typedef rmn::resource_node< rmn::ResourceDataAny > node_t; /// Rconfig strong reference-holding node pointer typedef node_t::node_ptr node_ptr; /// Locking policy for rconfig (thread-safe) typedef hz::SyncPolicyMtDefault ConfigLockPolicy; /// Config branch for serializable values ("/config") static const char* const s_config_name = "config"; /// Config branch for default config values ("/default") static const char* const s_default_name = "default"; // Note: C++ standard allows multiple _definitions_ of static class // template member variables. This means that they may be defined // in headers, as opposed to cpp files (as with static non-template class // members and other static variables). // This gives us opportunity to get rid of the cpp file. /// Static variable holder template struct NodeStaticHolder { static node_ptr root_node; ///< Node for "/" static node_ptr config_node; ///< Node for "/config" static node_ptr default_node; ///< Node for "/default" static ConfigLockPolicy::Mutex mutex; ///< Mutex for static variables }; // definitions template node_ptr NodeStaticHolder::root_node = 0; template node_ptr NodeStaticHolder::config_node = 0; template node_ptr NodeStaticHolder::default_node = 0; template ConfigLockPolicy::Mutex NodeStaticHolder::mutex; // Specify the same template parameter to get the same set of variables. typedef NodeStaticHolder RootHolder; ///< Holder for static variables (one (and only) instantiation). /// Initialize the root node. This is called automatically. /// This function is thread-safe. inline bool init_root(bool do_lock = true) { ConfigLockPolicy::ScopedLock locker(RootHolder::mutex, do_lock); // Note: Do NOT check every node individually. This will break handling // of absolute paths, e.g. get_data("/default/...") will check for existance of "/config". if (RootHolder::root_node) // already inited return false; // "/" RootHolder::root_node = node_ptr(new node_t); RootHolder::root_node->set_name("/"); // "/config" RootHolder::config_node = node_ptr(new node_t); RootHolder::config_node->set_name(s_config_name); RootHolder::root_node->add_child(RootHolder::config_node); // "/default" RootHolder::default_node = node_ptr(new node_t); RootHolder::default_node->set_name(s_default_name); RootHolder::root_node->add_child(RootHolder::default_node); return true; } // --------------------------------- Get root, config, default branches of rconfig tree. // Note: These functions are NOT thread-safe, because they return non-thread-safe // node structure. (That is, the user may access node_ptr in non-locked environment). // To use them in a thread-safe environment, you have to lock their access manually. /// Get the root node. inline node_ptr get_root() { if (!RootHolder::root_node) init_root(false); return RootHolder::root_node; } /// Get the config branch node inline node_ptr get_config_branch() { if (!RootHolder::root_node) init_root(false); return RootHolder::config_node; } /// Get the default branch node inline node_ptr get_default_branch() { if (!RootHolder::root_node) init_root(false); return RootHolder::default_node; } // --------------------------------- Getting nodes by config path. // Note: These functions are NOT thread-safe, because they return non-thread-safe // node structure. (That is, the user may access node_ptr in non-locked environment). // To use them in a thread-safe environment, you have to lock their access manually. /// Get a node by path (relative or absolute). If relative, look in /config, then /default. /// Useful for searching values (for reading). inline node_ptr get_node(const std::string& path) { if (node_t::is_abs_path(path)) { // absolute path, start from root node_ptr node = get_root()->find_node(path); if (node) return node; return node_ptr(0); // not found } // search by relative path in /config node_ptr cnode = get_config_branch()->find_node(path); if (cnode) return cnode; // search by relative path in /default node_ptr dnode = get_default_branch()->find_node(path); if (dnode) return dnode; return node_ptr(0); // not found } /// Get a node by path (relative or absolute). If relative, look in /config. /// If the path doesn't exist, it can be created. inline node_ptr get_config_node(std::string path, bool create_if_not_exists = false) { if (node_t::is_abs_path(path)) { // absolute path, start from root node_ptr node = get_root()->find_node(path); if (node) return node; if (create_if_not_exists) { if (get_root()->build_nodes(path)) return get_root()->find_node(path); } return node_ptr(0); // not found } // start from /config/ // return get_config_node(std::string(rmn::PATH_DELIMITER_S) // + s_config_name + rmn::PATH_DELIMITER_S + path); node_ptr node = get_config_branch()->find_node(path); if (node) return node; if (create_if_not_exists) { if (get_config_branch()->build_nodes(path)) return get_config_branch()->find_node(path); } return node_ptr(0); // not found } /// Get a node by path (relative or absolute). If relative, look in /default. /// If the path doesn't exist, it can be created. inline node_ptr get_default_node(std::string path, bool create_if_not_exists = false) { if (node_t::is_abs_path(path)) { // absolute path, start from root node_ptr node = get_root()->find_node(path); if (node) return node; if (create_if_not_exists) { if (get_root()->build_nodes(path)) return get_root()->find_node(path); } return node_ptr(0); // not found } // start from /default/ // return get_default_node(std::string(rmn::PATH_DELIMITER_S) // + s_default_name + rmn::PATH_DELIMITER_S + path); node_ptr node = get_default_branch()->find_node(path); if (node) return node; if (create_if_not_exists) { if (get_default_branch()->build_nodes(path)) return get_default_branch()->find_node(path); } return node_ptr(0); // not found } // --------------------------------- (Thread-safe) Root node manipulation /// Clear everything, including /config and /default. inline void clear_root_all() { ConfigLockPolicy::ScopedLock locker(RootHolder::mutex); RootHolder::root_node = 0; // this will delete everything. } /// Clear /config inline void clear_config_all() { ConfigLockPolicy::ScopedLock locker(RootHolder::mutex); if (RootHolder::config_node) RootHolder::config_node->clear_children(); } /// Clear /default inline void clear_default_all() { ConfigLockPolicy::ScopedLock locker(RootHolder::mutex); if (RootHolder::default_node) RootHolder::default_node->clear_children(); } // --------------------------------- (Thread-safe) Data manipulation // Note: if path is absolute (starts with "/"), then it's looked up in root ("/"). /// Clear the data in path (the node becomes empty), or "/config" if the path is relative. inline void clear_data(const std::string& path) { ConfigLockPolicy::ScopedLock locker(RootHolder::mutex); node_ptr cnode = get_config_node(path); if (cnode) cnode->clear_data(); } /// Clear the data in path (the node becomes empty), or "/default" if the path is relative. inline void clear_default_data(const std::string& path) { ConfigLockPolicy::ScopedLock locker(RootHolder::mutex); node_ptr cnode = get_default_node(path); if (cnode) cnode->clear_data(); } /// Set the data in path, or "/config" if the path is relative. /// Note: This will convert "const char*" data to std::string. template inline bool set_data(const std::string& path, T data) { ConfigLockPolicy::ScopedLock locker(RootHolder::mutex); // Verify that the default's type matches T, // if there's RTTI or type tracking enabled. #if !(defined DISABLE_RTTI && DISABLE_RTTI) \ || (defined RMN_TYPE_TRACKING && RMN_TYPE_TRACKING) if (!node_t::is_abs_path(path)) { node_ptr def_node = get_default_node(path); if (def_node && !def_node->data_is_empty()) { // if exists and not empty // template is needed for gcc3.3 if (!def_node->template data_is_type()) THROW_FATAL(std::runtime_error(std::string( "rconfig::set_data(): Error: Type mismatch between default and config value for \"") + path + "\"!")); } } #endif node_ptr cnode = get_config_node(path, true); // auto-create. note that it still may fail. if (cnode) return cnode->set_data(data); return false; } /// Set the data in path, or "/default" if the path is relative. /// Note: This will convert "const char*" data to std::string. template inline bool set_default_data(const std::string& path, T data) { ConfigLockPolicy::ScopedLock locker(RootHolder::mutex); node_ptr cnode = get_default_node(path, true); // auto-create. note that it still may fail. if (cnode) return cnode->set_data(data); return false; } // --------------------------------- (Thread-safe) Data Reading // These functions search in "/config" _or_ "/default" only. // Note: if path is absolute (starts with "/"), then it's looked up in root ("/"). /// Get the data in path, or "/config" if the path is relative. /// \return false if cast failed or no such node. template inline bool get_config_data(const std::string& path, T& put_it_here) { ConfigLockPolicy::ScopedLock locker(RootHolder::mutex); node_ptr node = get_config_node(path); if (!node) return false; // no such node return node->get_data(put_it_here); } /// Get the data in path, or "/default" if the path is relative. /// \return false if cast failed or no such node. template inline bool get_default_data(const std::string& path, T& put_it_here) { ConfigLockPolicy::ScopedLock locker(RootHolder::mutex); node_ptr node = get_default_node(path); if (!node) return false; // no such node return node->get_data(put_it_here); } // --------------------------------- (Thread-safe) Merged Data Reading // These functions search in "/config", _then_ in "/default". // Note: if path is absolute (starts with "/"), then it's looked up in root ("/"). /// Check if data at path is empty. If the path is relative, look in /config, then /default. inline bool data_is_empty(const std::string& path) { ConfigLockPolicy::ScopedLock locker(RootHolder::mutex); node_ptr node = get_node(path); if (!node) return false; // no such node return node->data_is_empty(); } #if !(defined DISABLE_RTTI && DISABLE_RTTI) \ || (defined RMN_TYPE_TRACKING && RMN_TYPE_TRACKING) /// Check if data at path is of type \c T. If the path is relative, look in /config, then /default. /// This function works only if either RTTI or type tracking is enabled template inline bool data_is_type(const std::string& path) { ConfigLockPolicy::ScopedLock locker(RootHolder::mutex); node_ptr node = get_node(path); if (!node) return false; // no such node return node->data_is_type(); } #endif /// Get data at path. If the path is relative, look in /config, then /default. /// \return false if cast failed or no such node. template inline bool get_data(const std::string& path, T& put_it_here) { ConfigLockPolicy::ScopedLock locker(RootHolder::mutex); node_ptr node = get_node(path); if (!node) return false; // no such node return node->get_data(put_it_here); } /// Get data at path. If the path is relative, look in /config, then /default. /// \throw rmn::no_such_node No such node /// \throw rmn::empty_data_retrieval Data is empty /// \throw rmn::type_mismatch Type mismatch template inline T get_data(const std::string& path) { ConfigLockPolicy::ScopedLock locker(RootHolder::mutex); node_ptr node = get_node(path); if (!node) THROW_FATAL(rmn::no_such_node(path)); // no such node return node->get_data(); } /// Similar to get_data(), but with looser conversion - can convert between /// C++ built-in types and std::string. Uses hz::any_convert<>. /// \return false if casting failed, or empty or invalid type. template inline bool convert_data(const std::string& path, T& put_it_here) // returns false if cast failed { ConfigLockPolicy::ScopedLock locker(RootHolder::mutex); node_ptr node = get_node(path); if (!node) return false; // no such node return node->convert_data(put_it_here); } /// Similar to get_data(), but with looser conversion - can convert between /// C++ built-in types and std::string. Uses hz::any_convert<>. /// \throw rmn::no_such_node No such node /// \throw rmn::empty_data_retrieval Data is empty /// \throw rmn::type_mismatch Type mismatch template inline T convert_data(const std::string& path) { ConfigLockPolicy::ScopedLock locker(RootHolder::mutex); node_ptr node = get_node(path); if (!node) THROW_FATAL(rmn::no_such_node(path)); return node->convert_data(); } } // ns #endif /// @} gsmartcontrol-1.1.4/src/rconfig/rconfig.h000066400000000000000000000016231417717230600204430ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup rconfig /// \weakgroup rconfig /// @{ #ifndef RCONFIG_RCONFIG_H #define RCONFIG_RCONFIG_H /** \file Full rconfig. You may include the individual headers directly to decrease the number of dependencies. */ /** \namespace rconfig Rconfig is rmn-based configuration management system. It provides default config values, config file save / load, etc... */ // Full rconfig: #include "rcmain.h" // data / node functions. #include "rcdump.h" // dumping functions #include "rcloadsave.h" // load/save to file/string #include "rcautosave.h" // config autosave with timeout #endif /// @} gsmartcontrol-1.1.4/src/rconfig/rconfig_mini.h000066400000000000000000000012621417717230600214560ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_zlib.txt file ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup rconfig /// \weakgroup rconfig /// @{ #ifndef RCONFIG_RCONFIG_MINI_H #define RCONFIG_RCONFIG_MINI_H /** \file You may include this instead of rconfig.h if you need only data set / get functions and not control functions. This may speed up compile times because it avoids using many dependencies. */ #include "rcmain.h" // data / node functions. #endif /// @} gsmartcontrol-1.1.4/src/rconfig/rconfig_test.cpp000066400000000000000000000056611417717230600220430ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_unlicense.txt ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup rconfig_tests /// \weakgroup rconfig_tests /// @{ // disable libdebug, we don't link to it #undef HZ_USE_LIBDEBUG #define HZ_USE_LIBDEBUG 0 // enable libdebug emulation through std::cerr #undef HZ_EMULATE_LIBDEBUG #define HZ_EMULATE_LIBDEBUG 1 // The first header should be then one we're testing, to avoid missing // header pitfalls. #include "rconfig.h" // #include "rconfig_mini.h" #include #include "hz/hz_config.h" // ENABLE_GLIB #if defined ENABLE_GLIB && ENABLE_GLIB #include #endif /// Main function for the test int main() { rconfig::load_from_file("test.config"); // populate /default: rconfig::set_default_data("app/use_stuff", true); rconfig::set_default_data("app/some_string1", std::string("some_string1_data")); rconfig::set_default_data("app/some_string2", "some_string2_data"); // this will store it as std::string! rconfig::set_default_data("app/int_var", uint32_t(5)); rconfig::set_default_data("app/int_var2", int64_t(10)); rconfig::set_default_data("app/huh", 6.7f); // float rconfig::set_data("app/int_var2", int64_t(11)); // override default. // rconfig::set_data("app/int_var2", uint16_t(12)); // override default. this will error out because it has different type rconfig::set_data("/this/is/absolute", 2); // This will go to root ("/"), not /config or /default. // strict typing: int64_t int_var = 0; rconfig::get_data("app/int_var", int_var); // this should leave it as 0, because of different types. std::cerr << "app/int_var: " << int_var << "\n"; // we inserted const char*, but it was converted to std::string for storage, so this will work. std::cerr << "app/some_string2: " << rconfig::get_data("app/some_string2") << "\n"; // loose typing: int int_var2 = 0; rconfig::convert_data("app/int_var2", int_var2); // this will do the type conversion std::cerr << "app/int_var2: " << int_var2 << "\n"; // this will get out 11, not 10 (default) std::cerr << "app/some_string1: " << rconfig::convert_data("app/some_string1") << "\n"; std::string huh; rconfig::convert_data("app/huh", huh); // float -> string conversion, should work. std::cerr << "app/huh: " << huh << "\n"; std::cerr << "\"app/empty\" exists: " << std::boolalpha << rconfig::data_is_empty("app/empty") << "\n"; rconfig::dump_tree(); rconfig::save_to_file("test.config"); #if defined ENABLE_GLIB && ENABLE_GLIB rconfig::autosave_set_config_file("test2.config"); rconfig::autosave_start(2); // every 2 seconds while(true) { // without this the timeout function won't be called. g_main_context_iteration(NULL, false); } #endif return 0; } /// @} gsmartcontrol-1.1.4/src/res/000077500000000000000000000000001417717230600160035ustar00rootroot00000000000000gsmartcontrol-1.1.4/src/res/Makefile.am000066400000000000000000000024421417717230600200410ustar00rootroot00000000000000 # Disable implicit suffix rules for these extensions. # The problem is that make sees the .ui.cpp files # with their .ui prerequisites, and decides to rebuild the # (already existing) .ui files by using .ui.cpp files. # Implicit rules are to blame, so disable them for .ui. SUFFIXES = .ui .txt AM_CPPFLAGS = METASOURCES = AUTO noinst_LIBRARIES = libres.a libres_a_SOURCES = # List _all_ sources that may be compiled. This is basically the same list as # in configure.ac, but with .cpp extensions and without paths. EXTRA_libres_a_SOURCES = gsc_about_dialog.ui.cpp \ gsc_add_device_window.ui.cpp \ gsc_executor_log_window.ui.cpp \ gsc_info_window.ui.cpp \ gsc_main_window.ui.cpp \ gsc_preferences_window.ui.cpp \ gsc_text_window.ui.cpp \ AUTHORS.txt.cpp LICENSE_gsmartcontrol.txt.cpp # list all intermediate files here (.tmp_ui files are cleaned automatically by make, but it won't hurt) mostlyclean-local: rm -f *.ui.cpp *.tmp_ui *.txt.cpp # This lists all objects that are actually built. libres_a_LIBADD = $(RES_LIBADD) libres_a_DEPENDENCIES = $(RES_LIBADD) # This lists all resources in this directory (otherwise they don't get bundled, # noinst_DATA doesn't put them into distribution (why?)). EXTRA_DIST = $(RES_DIST) # This lists the actual makefile rules for each target. @RES_TARGETS@ gsmartcontrol-1.1.4/src/res/gsc_about_dialog.ui000066400000000000000000000046611417717230600216360ustar00rootroot00000000000000 False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 6 About GSmartControl False center-on-parent True dialog here be copyrights Control and monitor hard disk SMART data here be license here be authors here be documentors translator-credits True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK vertical 6 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK end False True end 0 gsmartcontrol-1.1.4/src/res/gsc_add_device_window.ui000066400000000000000000000254311417717230600226410ustar00rootroot00000000000000 False Add Device - GSmartControl True True False 12 vertical 12 True False vertical True False start The Smartctl Options section in the All Help Topics menu contains information on what you can enter here. True True 6 0 True False 6 12 True False 6 True True â— True False False True True 0 _Browse... True True True True False True 1 1 0 True False start Device _type: True 0 1 True False Additional smartctl parameters start Smartctl parameters: 0 2 True False True True 1 1 True True Additional smartctl parameters â— True False False 1 2 True False start Device _name: True 0 0 False True 6 1 True False start Note: To permanently add a device, you'll have to use the gsmartcontrol --add-device command line option. True True 6 2 False True 0 True False 6 end gtk-cancel True True True True False False 0 gtk-ok True True True True True True False False 1 False True end 1 gsmartcontrol-1.1.4/src/res/gsc_executor_log_window.ui000066400000000000000000000324011417717230600232640ustar00rootroot00000000000000 False Execution Log - GSmartControl 900 650 True True False 12 vertical 12 True True 180 True False vertical 6 True True in True True Commands executed True True True 0 True False True True False True True 0 gtk-clear True True True Clear command list True True True 1 False True 1 False True True False 2 True False vertical 6 True False 6 True False start Command: False True 0 True True Command with parameters False True True False False True True 1 False True 3 0 True False start Output: False True 1 True True in True True Complete output of the command False 5 5 True True 2 True True True True 0 True False 6 True _Save Current True True True Save currently shown output True True True 0 Save _All True True True Save complete log of command execution (useful when reporting bugs) True True True 1 True False True True 2 gtk-close True True True Close this window True True True 3 False True 1 gsmartcontrol-1.1.4/src/res/gsc_info_window.ui000066400000000000000000001744071417717230600215350ustar00rootroot00000000000000 These are dummy entries Immediate Offline Short Extended Conveyance False Device Information - GSmartControl center-on-parent 850 600 True True False 12 vertical 12 True False vertical 6 True False 6 6 True False False False 0 True True True True False vertical True True in True False 6 True False center center True 12 6 12 True True 0 True True True True Drive identity and general health information 3 3 True _General True True False True False 6 vertical 6 True False vertical 3 False True 0 True True in True True True both multiple True True 1 1 True True SMART attributes True Attributes True True 1 False True False 6 vertical 6 True False vertical 3 False True 0 True True in True True False True both multiple True True 1 2 True True Drive statistics True Statistics True True 2 False True False 12 vertical 6 True False vertical 15 True False start Self-tests are built-in tests within the drive designed to recognize drive fault conditions. All self-tests are safe to user data. The tests can be performed during normal system operation, but will take longer to complete if the drive is not idle. You will not be able to access the drive's SMART data while a test is in progress. True 70 False False 0 True False 6 6 False 6 True False dialog-information 3 False True 0 True False Test result text True 70 True True 1 0 3 2 True False Test progress True Test completion: 90%; ETA: 12 minutes True end 0 2 gtk-stop True True True Abort the test True 1 2 85 True True in 80 True True Test description False word 5 5 False 0 1 2 gtk-execute True True True Perform selected test True 1 0 True False True 6 True False 6 True False start <b>Test type:</b> True False True 0 True False Choose a test type model1 False True 1 False True 0 True False 6 True False Estimated test duration on idle drive <b>Estimated duration:</b> True False True 0 True False Minimum test duration on idle drive N/A False True 1 True True 10 1 0 0 True True 1 False True 0 True False 6 0 none True False True False vertical True False vertical 3 False True 4 0 True True in True True True both multiple True True 1 True False Self-Test Log True True 1 3 True True Perform self-tests on the hard drive. Self-test log contains information about the most recent manually performed SMART self-tests. True Self-Tests True True 3 False True False 6 vertical True False vertical 6 True False vertical 3 False True 0 True False vertical True True vertical 170 True True True True in True True both False True True True in True True Complete error log information False 5 5 True True True True True 0 True True 1 True True 0 4 True True SMART error log contains most recent non-trivial errors the hard drive has encountered True Error Log True True 4 False True False 6 vertical 6 True False vertical False True 0 True True in True True False 5 5 True True True 1 5 True False Current temperature and history True Temperature Log 5 False True False vertical 6 True True 6 True True in True True both True False 6 6 3 3 Capabilities False True True in True True False 5 5 True 1 True False 6 6 Error Recovery 1 False True True in True True False 5 5 True 2 True False 6 6 Selective Self-Test Log 2 False True True in True True False 5 5 True 3 True False 6 6 Physical 3 False True True in True True False 5 5 True 4 True False 6 6 Directory 4 False True True 0 6 False True True Advanced parameters and logs True Advanced True True 6 False True True 1 True True 0 True False 6 True gtk-refresh True True True Re-read all the information True True True 0 _View Output True True True View smartctl output True True True 1 gtk-save-as True True True Save the SMART information (smartctl output) to a text file True True True 2 True False True True 3 gtk-close True True True Close this window True True True 4 False False 1 gsmartcontrol-1.1.4/src/res/gsc_main_window.ui000066400000000000000000000212661417717230600215200ustar00rootroot00000000000000 False GSmartControl 560 450 True False vertical True False False False 0 True False 6 6 6 6 6 6 True False 1 0 True False 1 1 True False start <b>Drive information:</b> True 0 0 True False start <b>Basic health check:</b> True 0 1 True False start <b>Model family:</b> True True 0 2 True False 1 2 False True 1 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK in True True True True GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 10 250 3 20 5 True True 2 True False 6 6 6 6 vertical 6 enable smart (action-replaced text) True True False True True True 0 enable aodc (action-replaced text) True True False True True True 1 False True 3 gsmartcontrol-1.1.4/src/res/gsc_preferences_window.ui000066400000000000000000001453171417717230600231010ustar00rootroot00000000000000 False True False 12 vertical 12 True False vertical True True True True False 5 vertical 6 True False 0 none True False 12 True False vertical True False vertical 6 _Scan system for drives on startup True True False True True True True 0 Show SMART-capable drives only True True False True True True True 1 Show device name under drive icon True True False True True True True 2 Show serial number under drive icon True True False True True True True 3 True True 10 0 True False <b>Program Settings</b> True False True 5 0 True False 0 none True False 12 True False vertical True False vertical 7 Loo_k for smartctl in smartmontools installation directory first True True False If smartmontools is installed, use its smartctl by default True True True True 0 True False 6 12 True False 6 True True True False False True True 0 _Browse... True True True True True True 1 1 0 True True Global parameters for smartctl. These parameters will be used every time the program invokes smartctl. Must be shell-escaped. True False False 1 1 True False Global parameters for smartctl. These parameters will be used every time the program invokes smartctl. Must be shell-escaped. start Smartctl parameters: True 0 1 True False start S_martctl binary: True 0 0 True True 1 True True 10 0 True False <b>Smartctl Invocation</b> True False True 5 1 True False 3 3 True _General True False True False 5 vertical 6 True False 0 none True False 12 True False vertical True False 6 True False Semicolon-separated blacklist PCRE (perl-like) patterns for device search. For example, to blacklist all "/dev/sd*" devices, use "sd.{1}$". Simply listing them as "sda;sdb" will also work. start Device blacklist patterns: True False True 0 True True Semicolon-separated blacklist PCRE patterns for device search. For example, to blacklist all "/dev/sd*" devices, use "sd.$". Simply listing them as "sda;sdb" or "sd[ab]" will also work. True False False True True 1 True True 12 0 True False <b>Drive Search</b> True False True 6 0 True False 0 none True False 12 True False vertical True False 12 True False start Adding a drive here will make it use the specified parameters. True True 6 0 True False 12 True False vertical 6 True True in True True Device list False True True 0 True False 6 True gtk-remove True True True Remove selected entry from device list True True True 0 gtk-add True True True Add a new entry to device list True True True 1 False True 1 True True 0 True False vertical True False 6 12 True False Smartctl parameters to add (for example, "-T permissive" or "-d usbsunplus") start _Parameters: True 0 3 True False Match only this type of device (as used by the -d smartctl parameter). Leave empty for all types. This can be used to match a drive behind a RAID device, e.g. "areca,2". start _Type: True 0 2 True False start De_vice: True 0 1 True True Smartctl parameters to add (for example, "-T permissive" or "-d usbsunplus") False False 1 3 True True Match only this type of device (as used by the -d smartctl parameter). Leave empty for all types. This can be used to match a drive behind a RAID device, e.g. "areca,2". False False 1 2 True True False False 1 1 True False start <b>Drive Properties:</b> True 0 0 2 False True 0 True True 1 True True 6 1 True False <b>Per-drive Smartctl Parameters</b> True False True 6 1 1 True False 6 6 True _Drives True 10 1 False True True 0 True True 0 True False 6 True Reset a_ll True True True Reset all program settings to their defaults True True True 0 True False True True 1 gtk-cancel True True True True True True 2 gtk-ok True True True True True True True True True 3 False True 1 gsmartcontrol-1.1.4/src/res/gsc_text_window.ui000066400000000000000000000126601417717230600215560ustar00rootroot00000000000000 False GSmartControl 800 600 True True False 12 vertical 12 True False vertical True True in True True True True False 5 5 False True True 0 True True 0 True False 6 True gtk-save-as True True True Saved displayed text as... True True True 0 True False True True 1 True False True True 2 gtk-close True True True True True Close this window True True True 3 False True 1 gsmartcontrol-1.1.4/src/rmn/000077500000000000000000000000001417717230600160065ustar00rootroot00000000000000gsmartcontrol-1.1.4/src/rmn/Makefile.am000066400000000000000000000010431417717230600200400ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) METASOURCES = AUTO noinst_HEADERS = resource_base.h resource_data_any.h resource_data_locking.h \ resource_data_one.h resource_data_types.h resource_exception.h resource_node.h \ resource_node_dump.h resource_serialization.h rmn.h rmn_test_SOURCES = rmn_test.cpp # Not working yet # EXTRA_DIST = resource_data_multi.h.notworing # we don't list them in a separate variable because otherwise kdevelop won't see them. noinst_PROGRAMS = if ENABLE_TESTS noinst_PROGRAMS += rmn_test endif tests: $(noinst_PROGRAMS) gsmartcontrol-1.1.4/src/rmn/resource_base.h000066400000000000000000000047631417717230600210120ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2003 - 2010 Irakli Elizbarashvili (C) 2008 - 2012 Alexander Shaduri License: This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ***************************************************************************/ /// \file /// \author Irakli Elizbarashvili /// \author Alexander Shaduri /// \ingroup rmn /// \weakgroup rmn /// @{ #ifndef RMN_RESOURCE_BASE_H #define RMN_RESOURCE_BASE_H #include // names are strings #include "hz/intrusive_ptr.h" // intrusive_ptr_referenced class namespace rmn { /// This class provides: /// 1) reference counting (through its base class). /// 2) naming objects. class resource_base : public hz::intrusive_ptr_referenced { public: /// Name comparator struct compare_name; /// Constructor resource_base() { } /// Constructor resource_base(const std::string& name) : name_(name) { } /// Set resource name void set_name(const std::string& name) { name_ = name; } /// Get resource name std::string get_name() const { return name_; } private: std::string name_; ///< Resource name }; // smart pointer to resource_base // typedef intrusive_ptr resource_base_ptr; // typedef intrusive_ptr resource_base_const_ptr; struct resource_base::compare_name { /// Constructor compare_name(const std::string& n) { name = n; } /// Check two names for equality template bool operator() (const T& p) const { if (p->get_name() == name) return true; return false; } std::string name; ///< Name given in constructor }; } // ns #endif /// @} gsmartcontrol-1.1.4/src/rmn/resource_data_any.h000066400000000000000000000160011417717230600216440ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup rmn /// \weakgroup rmn /// @{ #ifndef RMN_RESOURCE_DATA_ANY_H #define RMN_RESOURCE_DATA_ANY_H #include #include // std::ostream #include "hz/exceptions.h" // THROW_FATAL #include "hz/any_type.h" #include "hz/hz_config.h" // DISABLE_RTTI, RMN_TYPE_TRACKING (global_macros.h) #include "resource_data_types.h" #include "resource_exception.h" /** \file Any-type data for resource_node */ namespace rmn { /// Helper class for operator\<\< with std::ostream. template struct ResourceDataAnyDumper { ResourceDataAnyDumper(T* o) : obj(o) { } /// Dump resource data into \c os void dump(std::ostream& os) const { os << obj->data_.to_stream(); } T* obj; ///< ResourceDataAny object }; /// Output the data into \c os template inline std::ostream& operator<<(std::ostream& os, const ResourceDataAnyDumper& dumper) { dumper.dump(os); return os; } /// Resource data which can hold variables of any type. template class ResourceDataAny { typedef ResourceDataAny self_type; ///< Self type public: /// Constructor ResourceDataAny() #if defined RMN_TYPE_TRACKING && RMN_TYPE_TRACKING : type_(T_EMPTY) #endif { } /// Copy data from \c src node template bool copy_data_from(const T& src) { if (!src) return false; data_ = src->data_; #if defined RMN_TYPE_TRACKING && RMN_TYPE_TRACKING type_ = src->get_type(); #endif return true; } /// Check whether data is empty bool data_is_empty() const { return data_.empty(); } /// Clear the data, making it empty void clear_data() { data_.clear(); #if defined RMN_TYPE_TRACKING && RMN_TYPE_TRACKING type_ = T_EMPTY; #endif } /// Set data of any type template inline bool set_data(T data) { data_ = data; #if defined RMN_TYPE_TRACKING && RMN_TYPE_TRACKING type_ = node_data_type_by_real::type; #endif return true; } /// const char* -\> std::string specialization inline bool set_data(const char* data) { data_ = std::string(data); #if defined RMN_TYPE_TRACKING && RMN_TYPE_TRACKING type_ = node_data_type_by_real::type; #endif return true; } /// \fn bool data_is_type() const /// Check whether data is of type \c T. /// This function is available only if either RTTI or type tracking is enabled. #if !(defined DISABLE_RTTI && DISABLE_RTTI) template inline bool data_is_type() const { // template is needed for gcc 3.3 return data_.template is_type(); // won't work without RTTI! If empty, reacts to void only. } #elif defined RMN_TYPE_TRACKING && RMN_TYPE_TRACKING template inline bool data_is_type() const { return node_data_type_by_real::type == type_; } #endif #if defined RMN_TYPE_TRACKING && RMN_TYPE_TRACKING /// Check whether data is of type \c type. /// This function is available only if type tracking is enabled. inline bool data_is_type(node_data_type type) const { return type == type_; } /// Get data type. /// This function is available only if type tracking is enabled. inline node_data_type get_type() const { return type_; } #endif /// Get data of type \c T. /// \return false if casting failed, or if it's empty or invalid type. template inline bool get_data(T& put_it_here) const { #if defined RMN_TYPE_TRACKING && RMN_TYPE_TRACKING if (node_data_type_by_real::type != type_) return false; #endif return data_.get(put_it_here); // returns false if empty or invalid type } /// Return a copy of data of type \c T. /// \throw rmn::empty_data_retrieval Data is empty /// \throw rmn::type_mismatch Type mismatch template T get_data() const { if (data_.empty()) THROW_FATAL(empty_data_retrieval()); #if defined DISABLE_RTTI && defined RMN_TYPE_TRACKING if (node_data_type_by_real::type != type_) THROW_FATAL(type_mismatch()); #endif try { // template is needed for gcc 3.3 return data_.template get(); // won't work if empty or invalid type } catch (hz::bad_any_cast& e) { // convert any_type exception to rmn exception. THROW_CUSTOM_BAD_CAST(type_mismatch, data_.type(), typeid(T)); } } /// Similar to get_data(), but with looser conversion - can convert between /// C++ built-in types and std::string. Uses hz::any_convert<>. /// \return false if casting failed, or empty or invalid type. template inline bool convert_data(T& put_it_here) const { return data_.convert(put_it_here); } /// Similar to get_data(), but with looser conversion - can convert between /// C++ built-in types and std::string. Uses hz::any_convert<>. /// \throw rmn::empty_data_retrieval Data is empty /// \throw rmn::type_mismatch Type mismatch template T convert_data() const { if (data_.empty()) THROW_FATAL(empty_data_retrieval()); #if defined DISABLE_RTTI && defined RMN_TYPE_TRACKING node_data_type to = node_data_type_by_real::type; // any T_ type is ok except these: if (type_ == T_VOIDPTR || type_ == T_UNKNOWN || to == T_VOIDPTR || to == T_UNKNOWN) { THROW_FATAL(type_convert_error()); } #endif try { // Note: This throws only if RTTI is enabled. // template is needed for gcc 3.3 return data_.template convert(); // won't work if empty or invalid type } catch (hz::bad_any_cast& e) { THROW_CUSTOM_BAD_CAST(type_convert_error, data_.type(), typeid(T)); } } template friend struct ResourceDataAnyDumper; /// Return a helper object that can be dumped into ostream. inline ResourceDataAnyDumper dump_data_to_stream() const { return ResourceDataAnyDumper(this); } private: hz::any_type data_; ///< The data #if defined RMN_TYPE_TRACKING && RMN_TYPE_TRACKING node_data_type type_; ///< Type of the data #endif }; } // namespace #endif /// @} gsmartcontrol-1.1.4/src/rmn/resource_data_locking.h000066400000000000000000000025701417717230600225110ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup rmn /// \weakgroup rmn /// @{ #ifndef RMN_RESOURCE_DATA_LOCKING_H #define RMN_RESOURCE_DATA_LOCKING_H namespace rmn { /// Rmn sync policy of no synchronization (for single-threaded access) struct ResourceSyncPolicyNone { }; } // namespace #endif /// @} gsmartcontrol-1.1.4/src/rmn/resource_data_one.h000066400000000000000000000117721417717230600216500ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup rmn /// \weakgroup rmn /// @{ #ifndef RMN_RESOURCE_DATA_ONE_H #define RMN_RESOURCE_DATA_ONE_H #include #include // std::ostream #include "hz/type_properties.h" // type_is_same<> #include "hz/exceptions.h" // THROW_FATAL #include "hz/static_assert.h" // HZ_STATIC_ASSERT #include "resource_data_types.h" #include "resource_exception.h" namespace rmn { // Internal Note: Don't use static type checks here - they won't allow // compiling invalid unused code (like in serializer). /// Helper class for operator\<\< with std::ostream. template struct ResourceDataOneDumper { ResourceDataOneDumper(T* o) : obj(o) { } /// Dump resource data into \c os void dump(std::ostream& os) const { if (!obj->data_is_empty()) os << obj->data_; } T* obj; ///< ResourceDataOne object }; /// Output the data into \c os template inline std::ostream& operator<<(std::ostream& os, const ResourceDataOneDumper& dumper) { dumper.dump(os); return os; } /// Resource data which can handle data of \c DataType only. template class ResourceDataOne { typedef ResourceDataOne self_type; ///< Self type typedef DataType value_type; ///< DataType public: /// Constructor ResourceDataOne() : empty_(true) { } /// Copy data from \c src node template bool copy_data_from(const T& src) { return set_data(src->template get_data()); } /// Check whether data is empty bool data_is_empty() const { return empty_; } /// Clear the data, making it empty void clear_data() { empty_ = true; } /// Set data of any type. This operation will fail for all types except DataType. template inline bool set_data(T data) { return false; // invalid type } /// Set data of type DataType inline bool set_data(DataType data) { data_ = data; empty_ = false; return true; } /// Check whether data is of type \c T. template inline bool data_is_type() const { return !empty_ && hz::type_is_same::value; } /// Check whether data is of type \c type. inline bool data_is_type(node_data_type type) const { return !empty_ && type == node_data_type_by_real::type; } /// Get data type inline node_data_type get_type() const { if (empty_) return node_data_type_by_real::type; return node_data_type_by_real::type; } /// Get data. /// \return false on error. inline bool get_data(DataType& put_it_here) const { if (empty_) return false; put_it_here = data_; return true; } /// Get a copy of data. /// \throw rmn::empty_data_retrieval Data is empty template T get_data() const { // use static assertion - early compile-time error is better than runtime error. HZ_STATIC_ASSERT((hz::type_is_same::value), rmn_type_mismatch); if (empty_) THROW_FATAL(empty_data_retrieval()); return data_; } /// Get data (use static_cast conversion). template inline bool convert_data(T& put_it_here) const // returns false if cast failed { put_it_here = static_cast(data_); return true; // static_cast will check it at compile-time. } /// Get a copy of data (use static_cast conversion). /// \throw rmn::empty_data_retrieval if data is empty template T convert_data() const { if (empty_) THROW_FATAL(empty_data_retrieval()); return static_cast(data_); } template friend struct ResourceDataOneDumper; /// Return a helper object that can be dumped into ostream. inline ResourceDataOneDumper dump_data_to_stream() const { return ResourceDataOneDumper(this); } private: DataType data_; ///< The data bool empty_; ///< Whether the data is empty or not. }; } // namespace #endif /// @} gsmartcontrol-1.1.4/src/rmn/resource_data_types.h000066400000000000000000000125421417717230600222270ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup rmn /// \weakgroup rmn /// @{ #ifndef RMN_RESOURCE_DATA_TYPES_H #define RMN_RESOURCE_DATA_TYPES_H #include #include "hz/hz_config.h" // DISABLE_RTTI, RMN_TYPE_TRACKING (global_macros.h) #include "hz/cstdint.h" namespace rmn { /// Node data type, used mainly in \c RMN_TYPE_TRACKING. /// Only serializable and some additional types are here. enum node_data_type { T_EMPTY, ///< Not really a type, but may be handy T_BOOL, ///< bool T_INT32, ///< int32_t. NOTE: when writing constants, either \c T_INT32 or \c T_INT64 will be the default. T_UINT32, ///< uint32_t T_INT64, ///< int64_t T_UINT64, ///< uint64_t T_DOUBLE, ///< double. Default when writing floating point constants. T_FLOAT, ///< float. T_LDOUBLE, ///< long double. Not recommended if using windows-based compilers. T_STRING, ///< std::string T_VOIDPTR, ///< void* T_UNKNOWN ///< Unknown type }; /// Get node_data_type by type \c T. This struct is specialized for different types. template struct node_data_type_by_real { /// Node data type static const node_data_type type = T_UNKNOWN; }; // specializations template<> struct node_data_type_by_real { static const node_data_type type = T_EMPTY; }; template<> struct node_data_type_by_real { static const node_data_type type = T_BOOL; }; template<> struct node_data_type_by_real { static const node_data_type type = T_INT32; }; template<> struct node_data_type_by_real { static const node_data_type type = T_UINT32; }; template<> struct node_data_type_by_real { static const node_data_type type = T_INT64; }; template<> struct node_data_type_by_real { static const node_data_type type = T_UINT64; }; template<> struct node_data_type_by_real { static const node_data_type type = T_DOUBLE; }; template<> struct node_data_type_by_real { static const node_data_type type = T_FLOAT; }; template<> struct node_data_type_by_real { static const node_data_type type = T_LDOUBLE; }; template<> struct node_data_type_by_real { static const node_data_type type = T_STRING; }; template<> struct node_data_type_by_real { static const node_data_type type = T_VOIDPTR; }; /// \fn node_data_type resource_node_get_type(intrusive_ptr > node) /// Get node_data_type from rmn node. Const pointer overload. /// \fn node_data_type resource_node_get_type(intrusive_ptr > node) /// Get node_data_type from rmn node. #if defined RMN_TYPE_TRACKING && RMN_TYPE_TRACKING template inline node_data_type resource_node_get_type(intrusive_ptr > node) { return node->get_type(); } template inline node_data_type resource_node_get_type(intrusive_ptr > node) { return resource_node_get_type(intrusive_ptr >(node)); } #elif !(defined DISABLE_RTTI && DISABLE_RTTI) // RTTI version (slower) template inline node_data_type resource_node_get_type(intrusive_ptr > node) { if (node->data_is_empty()) return node_data_type_by_real::type; if (node->template data_is_type()) return node_data_type_by_real::type; if (node->template data_is_type()) return node_data_type_by_real::type; if (node->template data_is_type()) return node_data_type_by_real::type; if (node->template data_is_type()) return node_data_type_by_real::type; if (node->template data_is_type()) return node_data_type_by_real::type; if (node->template data_is_type()) return node_data_type_by_real::type; if (node->template data_is_type()) return node_data_type_by_real::type; if (node->template data_is_type()) return node_data_type_by_real::type; if (node->template data_is_type()) return node_data_type_by_real::type; if (node->template data_is_type()) return node_data_type_by_real::type; return T_UNKNOWN; } template inline node_data_type resource_node_get_type(intrusive_ptr > node) { return resource_node_get_type(intrusive_ptr >(node)); } #endif } // ns #endif /// @} gsmartcontrol-1.1.4/src/rmn/resource_exception.h000066400000000000000000000066551417717230600221000ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2008 - 2012 Alexander Shaduri License: This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ***************************************************************************/ /// \file /// \author Alexander Shaduri /// \ingroup rmn /// \weakgroup rmn /// @{ #ifndef RMN_RESOURCE_EXCEPTION_H #define RMN_RESOURCE_EXCEPTION_H #include #include // std::exception #include "hz/bad_cast_exception.h" namespace rmn { /// This is thrown in case of bad casts. /// Note that this is thrown only if the function has no other means /// of returning an error (e.g. it's returning a reference). DEFINE_BAD_CAST_EXCEPTION(type_convert_error, "Failed data type conversion from \"%s\" to \"%s\".", "Failed data type conversion."); /// This is thrown if the requested type is different than the stored type. DEFINE_BAD_CAST_EXCEPTION(type_mismatch, "Data type mismatch. Original type: \"%s\", requested type: \"%s\".", "Data type mismatch."); /// This is thrown if attempting to retrieve a data from an empty node. /// Note that this is thrown only if the function has no other means /// of returning an error (e.g. it's returning a reference). struct empty_data_retrieval : virtual public std::exception { // from /// Constructor empty_data_retrieval() { } // Data injector (which uses this class) doesn't see its path, so no need for this. // explicit empty_data_retrieval(const std::string& from_path) : path(from_path) // { } /// Virtual destructor virtual ~empty_data_retrieval() throw() { } /// Reimplemented from std::exception virtual const char* what() const throw() { return "rmn::empty_data_retrieval: Attempt to retrieve data from an empty node."; } }; /// This is thrown if attempting to retrieve a data from a non-existent node. /// Note that this is thrown only if the function has no other means /// of returning an error (e.g. it's returning a reference). struct no_such_node : virtual public std::exception { // from /// Constructor no_such_node(const std::string& arg_path) : path(arg_path) { } /// Virtual destructor virtual ~no_such_node() throw() { } /// Reimplemented from std::exception virtual const char* what() const throw() { msg = "rmn::no_such_node: Attempt to retrieve information about non-existent path: \"" + path + "\"."; return msg.c_str(); } private: std::string path; ///< The path of the non-existent node mutable std::string msg; ///< what() helper. This must be a member to avoid its destruction on function call return. }; } // ns #endif /// @} gsmartcontrol-1.1.4/src/rmn/resource_node.h000066400000000000000000000512251417717230600210200ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2003 - 2010 Irakli Elizbarashvili (C) 2008 - 2012 Alexander Shaduri License: This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ***************************************************************************/ /// \file /// \author Irakli Elizbarashvili /// \author Alexander Shaduri /// \ingroup rmn /// \weakgroup rmn /// @{ #ifndef RMN_RESOURCE_NODE_H #define RMN_RESOURCE_NODE_H #include #include // for children list #include // std::stack #include "hz/string_algo.h" // string_split(), string_join() #include "hz/debug.h" // debug_* #include "resource_base.h" #include "resource_exception.h" namespace rmn { using hz::intrusive_ptr; // used as rmn::intrusive_ptr by external users. // these are in rmn:: namespace because it would be difficult to use // them as members of a template class. // these have internal linkage (const integral types). const char PATH_DELIMITER = '/'; ///< Rmn path delimiter character const char* const PATH_DELIMITER_S = "/"; ///< Rmn path delimiter character as a string (const char*) /// Resource node. Resource nodes are nodes of named data. template class resource_node : public resource_base, public Data { public: // Do NOT return parent_node_ptr from public functions, return node_ptr instead. typedef resource_node* parent_node_ptr; ///< Parent node (non-reference-holding pointer). Must be able to assign this to node_ptr. typedef intrusive_ptr > node_ptr; ///< Ref-counting smart pointer to node, also child type. typedef intrusive_ptr > node_const_ptr; ///< Const pointer version of node_ptr. typedef std::list child_list_t; ///< Child list typedef typename child_list_t::iterator child_iterator; ///< Child list iterator typedef typename child_list_t::const_iterator child_const_iterator; ///< Child list const iterator typedef typename child_list_t::size_type child_size_type; ///< Child list size type typedef resource_node self_type; ///< Self type /// Constructor, constructs empty node resource_node() : parent_(0) { } /// Destructor ~resource_node() { #if defined RMN_RESOURCE_NODE_DEBUG && RMN_RESOURCE_NODE_DEBUG debug_out_dump("rmn", "Deleting node " << get_name() << "\n"); #endif } /// child_list_t::begin() child_iterator children_begin(); /// child_list_t::end() child_iterator children_end(); /// child_list_t::begin() const child_const_iterator children_begin() const; /// child_list_t::end() const child_const_iterator children_end() const; /// Get number of children for this node child_size_type get_child_count() const; /// Get a deep copy of the node node_ptr clone() const; /// Copy data from other node bool copy_data_from(const node_ptr& src); /// Copy data from other node bool copy_data_from(const node_const_ptr& src); /// Recursively copy src to *this, replacing current node's children and name. bool deep_copy_from(const node_ptr& src); /// Recursively copy src to *this, replacing current node's children and name. bool deep_copy_from(const node_const_ptr& src); /// Get node's full path (either from cache or generate it if not available). std::string get_path() const; /// Get data by path (absolute or relative). /// Returns false if cast failed if path not found. template inline bool get_data_by_path(const std::string& path, T& put_it_here) const; /// Get data by path (absolute or relative). /// \throw rmn::no_such_node No such node /// \throw rmn::empty_data_retrieval Data is empty /// \throw rmn::type_mismatch Type mismatch template T get_data_by_path(const std::string& path) const; /// Set data by path (absolute or relative). template inline bool set_data_by_path(const std::string& path, T data); /// Add a child node. bool add_child(node_ptr p); /// Create a child note with name \c name. node_ptr create_child(const std::string& name); /// Create a child node with name \c name and data \c data template node_ptr create_child(const std::string& name, T data); /// Remove node by absolute path bool remove_node(const std::string& full_path); /// Remove direct child node by name bool remove_child_node(const std::string& name); /// Remove direct child node by node pointer bool remove_child_node(node_ptr p); /// Remove all children void clear_children(); /// Get direct child node by index node_ptr get_child_node(child_size_type n); /// Get direct child node by index node_const_ptr get_child_node(child_size_type n) const; /// Get direct child node by name node_ptr get_child_node(const std::string& name); /// Get direct child node by name node_const_ptr get_child_node(const std::string& name) const; /// build nodes up to and including path (absolute or relative). /// If allow_side_construction is false, allow constructing side-nodes /// (as opposed to subnodes only). bool build_nodes(const std::string& path, bool allow_side_construction = false); /// Find node by path (absolute or relative). node_ptr find_node(const std::string& path); /// Find node by path (absolute or relative). node_const_ptr find_node(const std::string& path) const; /// Get root node (its path is "/"). node_ptr get_root_node(); /// Get root node (its path is "/"). node_const_ptr get_root_node() const; /// Get parent node, or null pointer if none. node_ptr get_parent(); /// Get parent node, or null pointer if none. node_const_ptr get_parent() const; /// Check if path is absolute. static bool is_abs_path(const std::string& path); protected: /// Set node parent bool set_parent(node_ptr p); /// Clear node parent (orphan it) bool clear_parent(); /// Update path cache. Const because it doesn't change any visible members. std::string update_path_cache() const; /// Clear path cache. Const because no public effect and callable from const methods. void clear_path_cache() const; private: child_list_t children_; ///< Child list (strong reference holding pointers) parent_node_ptr parent_; ///< Parent node (non-reference-holding pointer) mutable std::string path_cache_; ///< Path cache }; template inline typename resource_node::child_iterator resource_node::children_begin() { return children_.begin(); } template inline typename resource_node::child_iterator resource_node::children_end() { return children_.end(); } template inline typename resource_node::child_const_iterator resource_node::children_begin() const { return children_.begin(); } template inline typename resource_node::child_const_iterator resource_node::children_end() const { return children_.end(); } template inline typename resource_node::child_size_type resource_node::get_child_count() const { return children_.size(); } template inline typename resource_node::node_ptr resource_node::clone() const { node_ptr dest(new self_type()); dest->deep_copy_from(this); return dest; } // Copy data from src. template inline bool resource_node::copy_data_from(const typename resource_node::node_const_ptr& src) { return Data::copy_data_from(src); } // Copy data from src. template inline bool resource_node::copy_data_from(const typename resource_node::node_ptr& src) { return copy_data_from(node_const_ptr(src)); } template inline bool resource_node::deep_copy_from(const typename resource_node::node_const_ptr& src) { if (!src) return false; copy_data_from(&src); set_name(src->get_name()); children_.clear(); for (child_const_iterator iter = src->children_.begin(); iter != src->children_.end(); ++iter) { node_ptr n(new self_type); n->deep_copy_from(*iter); add_child(n); } return true; } template inline bool resource_node::deep_copy_from(const typename resource_node::node_ptr& src) { return deep_copy_from(node_const_ptr(src)); } // get the current node's full path. retrieve it from cache if available. template inline std::string resource_node::get_path() const { if (path_cache_.empty()) update_path_cache(); // this updates path_ return path_cache_; } template template inline bool resource_node::get_data_by_path(const std::string& path, T& put_it_here) const { node_ptr p = find_node(path); if (!p) return false; return p->get_data(put_it_here); } // This function may throw. See prototype above. template template inline T resource_node::get_data_by_path(const std::string& path) const { node_ptr p = find_node(path); if (!p) THROW_FATAL(no_such_node(path)); return p->template get_data(); } template template inline bool resource_node::set_data_by_path(const std::string& path, T data) { node_ptr p = find_node(path); if (!p) { build_nodes(path); // build if doesn't exist p = find_node(path); } p->set_data(data); return true; } // add a node to child list and set ourselves as child's parent. template inline bool resource_node::resource_node::add_child(node_ptr p) { if (!p) return false; if (p->get_parent()) { // FIXME: maybe reparent? debug_out_warn("rmn", "resource_node::add_child(): this node has a parent already!\n"); return false; } #if defined RMN_RESOURCE_NODE_DEBUG && RMN_RESOURCE_NODE_DEBUG // debug_out_dump("rmn", p->get_name() << " refcount is " << p->ref_count() << " (before insertion)\n"); #endif if (!get_child_node(p->get_name())) { children_.push_back(p); p->set_parent(this); #if defined RMN_RESOURCE_NODE_DEBUG && RMN_RESOURCE_NODE_DEBUG // debug_out_dump("rmn", p->get_name() << " refcount is " << p->ref_count() << " (after insertion)\n"); #endif } else { // we already have a child by that name! return false; } p->clear_path_cache(); // clear its path cache return true; } template inline typename resource_node::node_ptr resource_node::create_child(const std::string& name) { node_ptr child(new self_type); child->set_name(name); node_ptr cur = this; if (!cur->add_child(child)) return node_ptr(0); return child; } template template inline typename resource_node::node_ptr resource_node::create_child(const std::string& name, T data) { node_ptr child = create_child(name); if (!child) return node_ptr(0); child->set_data(data); return child; } // get node by name from child list template inline typename resource_node::node_ptr resource_node::get_child_node(const std::string& name) { return hz::ptr_const_cast >(node_const_ptr( static_cast*>(this)->get_child_node(name))); } // get node by name from child list template inline typename resource_node::node_const_ptr resource_node::get_child_node(const std::string& name) const { if (name.empty()) return node_const_ptr(0); for (child_const_iterator iter = children_.begin(); iter != children_.end(); ++iter) { if ((*iter)->get_name() == name) return node_const_ptr(*iter); } return node_const_ptr(0); } // get node by index from child list template inline typename resource_node::node_ptr resource_node::get_child_node(typename resource_node::child_size_type n) { return hz::ptr_const_cast >(node_const_ptr( static_cast*>(this)->get_child_node(n))); } // get node by index from child list template inline typename resource_node::node_const_ptr resource_node::get_child_node(typename resource_node::child_size_type n) const { if (n >= children_.size()) return node_const_ptr(0); child_size_type i = 0; child_const_iterator iter = children_.begin(); while (i != n) { ++iter; ++i; } return node_const_ptr(*iter); } // remove from child list and clear its parent. template inline bool resource_node::remove_node(const std::string& full_path) { if (full_path.empty()) return false; node_ptr r(find_node(full_path)); if (!r) // not found return false; node_ptr parent = r->get_parent(); if (!parent) // huh? return false; return parent->remove_child_node(r); } template inline bool resource_node::remove_child_node(const std::string& name) { if (name.empty()) return false; // unsigned int pos = path.rfind(PATH_DELIMITER); // if (pos == std::string::npos) // separator not found // return false; // std::string base(path.begin() + pos, path.end()); // base name from the path. for (child_iterator iter = children_.begin(); iter != children_.end(); ++iter) { if ((*iter)->get_name() == name) { (*iter)->clear_parent(); children_.erase(iter); break; // erase() invalidates the iteration, so we must break here. } } return true; } template inline bool resource_node::remove_child_node(node_ptr p) { if (!p) return false; return remove_child_node(p->get_name()); } template inline void resource_node::clear_children() { return children_.clear(); } // create nodes from path template inline bool resource_node::build_nodes(const std::string& path, bool allow_side_construction) { if (path.empty()) return false; if (path == PATH_DELIMITER_S) { // root debug_out_warn("rmn", "resource_node::build_nodes(\"/\")!\n"); return false; } std::string our_path = get_path(); // Path to construct, absolute. std::string constr_path = ((path[0] == PATH_DELIMITER) ? path : (our_path + PATH_DELIMITER_S + path)); std::list components, components_canonical; // Split to components. This will skip empty results (double slashes, etc...) hz::string_split(constr_path, PATH_DELIMITER, components, true); // We don't compare paths directly - they may contain "..", double-slashes, etc... for (std::list::const_iterator iter = components.begin(); iter != components.end(); ++iter) { if (*iter == ".") continue; // nothing if (*iter == "..") { if (components_canonical.empty()) { debug_out_warn("rmn", "resource_node::build_nodes(\"" + path + "\"): Too many up-dirs.\n"); return false; } components_canonical.pop_back(); } else { components_canonical.push_back(*iter); } } std::string canonical_path = PATH_DELIMITER_S + hz::string_join(components, PATH_DELIMITER); // For absolute paths we accept only ones which are our subnodes. // This is a security measure - a code with access only to // a subnode shouldn't be able to write to other unrelated nodes! // Think "/config" and "/secure", where a code should be able to construct // by "/config/a/b" or "a/b", but not "/secure/a/b". // Now we can compare - both paths are canonical. bool is_subpath = (canonical_path.compare(0, our_path.size(), our_path) == 0); if (!allow_side_construction && !is_subpath) { // Requested path is not our subnode and side construction is disallowed. return false; } node_ptr cur(0); // the base object which we begin construction from. // For our subpaths we can optimize away part of traversing. if (is_subpath) { std::list our_components; hz::string_split(our_path, PATH_DELIMITER, our_components, true); // remove common ancestry for (std::list::const_iterator iter = our_components.begin(); iter != our_components.end(); ++iter) { components_canonical.pop_front(); } cur = this; // start constructing from us } else { cur = get_root_node(); } // Create them for (std::list::const_iterator iter = components_canonical.begin(); iter != components_canonical.end(); ++iter) { node_ptr child = cur->get_child_node(*iter); // get child node by name if (child) { // if child with such name exists cur = child; // switch to it } else { // child doesn't exist, create it node_ptr new_child(new resource_node); new_child->set_name(*iter); cur->add_child(new_child); cur = new_child; // or new_child.get() ? } } return true; } // accepts absolute and relative paths. template inline typename resource_node::node_ptr resource_node::find_node(const std::string& path) { return hz::ptr_const_cast >(node_const_ptr( static_cast*>(this)->find_node(path))); } // accepts absolute and relative paths. template inline typename resource_node::node_const_ptr resource_node::find_node(const std::string& path) const { if (path.empty()) return node_ptr(0); if (path[0] == PATH_DELIMITER) { // absolute path std::string rel_path(path.begin() + 1, path.end()); // remove the first slash. if (rel_path.empty()) // "/" was requested return get_root_node(); return get_root_node()->find_node(rel_path); } // ourselves if (get_name() == path) { return node_const_ptr(this); } node_const_ptr cur(this); std::list components; hz::string_split(path, PATH_DELIMITER, components, true); // this will skip empty results for (std::list::const_iterator iter = components.begin(); iter != components.end(); ++iter) { if (*iter == ".") // this has no effect continue; if (*iter == "..") { cur = cur->get_parent(); } else { node_const_ptr child = cur->get_child_node(*iter); // get child node by name cur = child; // switch to it } if (!cur) return node_const_ptr(0); // either child is 0, or parent not found. } return cur; } // return a root node of current tree template inline typename resource_node::node_ptr resource_node::get_root_node() { return hz::ptr_const_cast >(node_const_ptr( static_cast*>(this)->get_root_node())); } // return a root node of current tree template inline typename resource_node::node_const_ptr resource_node::get_root_node() const { if (get_name() == PATH_DELIMITER_S) // we are root return node_const_ptr(this); node_const_ptr cur = this; while (true) { node_const_ptr par = cur->get_parent(); if (!par) break; cur = par; } return cur; } template inline typename resource_node::node_ptr resource_node::get_parent() { return parent_; // in whatever format it's in. } template inline typename resource_node::node_const_ptr resource_node::get_parent() const { return parent_; // in whatever format it's in. } template inline bool resource_node::is_abs_path(const std::string& path) { return (!path.empty() && path[0] == PATH_DELIMITER); } template inline bool resource_node::set_parent(typename resource_node::node_ptr p) { if (!p) return false; if (parent_) clear_parent(); parent_ = p.get(); return true; } template inline bool resource_node::clear_parent() { if (!parent_) { debug_out_warn("rmn", "resource_node::clear_parent(): no parent exists.\n"); return false; } parent_ = 0; return true; } // generate path for this node in hierarchy, cache it in path_cache_. template inline std::string resource_node::update_path_cache() const { std::stack stk; node_const_ptr cur = this; while(cur) { // until root is reached stk.push(cur->get_name()); cur = cur->get_parent(); } path_cache_.clear(); while( ! stk.empty() ) { std::string name = stk.top(); path_cache_ += name; if (name != PATH_DELIMITER_S && stk.size() > 1) path_cache_ += PATH_DELIMITER_S; stk.pop(); } return path_cache_; } template inline void resource_node::clear_path_cache() const { path_cache_.clear(); } } // namespace #endif /// @} gsmartcontrol-1.1.4/src/rmn/resource_node_dump.h000066400000000000000000000111171417717230600220410ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2003 - 2010 Irakli Elizbarashvili (C) 2008 - 2012 Alexander Shaduri License: This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ***************************************************************************/ /// \file /// \author Irakli Elizbarashvili /// \author Alexander Shaduri /// \ingroup rmn /// \weakgroup rmn /// @{ #ifndef RMN_RESOURCE_NODE_DUMP_H #define RMN_RESOURCE_NODE_DUMP_H #include #include #include // std::ostream #include // setw(), left() #include "resource_node.h" namespace rmn { // Unfortunately, these functions cannot accept // resource_node::node_const_ptr // as parameters, because of something called non-deducible // context (regarding nested type). namespace internal { /// A helper for resource_node_dump_recursive() template inline std::string resource_node_dump_recursive_helper(intrusive_ptr > node, int internal_dump_offset = 0) { std::string str; // int len = node->get_name().size(); int fill = 20 - internal_dump_offset; std::ostringstream ss; ss << std::string(internal_dump_offset, ' '); // "refcount - 1" is needed because we take one reference (argument of this function). ss << std::left << std::setw(fill) << node->get_name() << " [" << (node->ref_count() - 1) << "] " << std::setw(20) << node->get_path() << " " << std::setw(10) << node->dump_data_to_stream() << std::setw(0) << "\n"; str += ss.str(); typename resource_node::child_const_iterator iter = node->children_begin(); for (; iter != node->children_end(); ++iter) { str += resource_node_dump_recursive(*iter, internal_dump_offset + 2); } return str; } } /// Dump node recursively in pretty ascii format (suitable for reading/debugging only). template inline std::string resource_node_dump_recursive(intrusive_ptr > node) { return internal::resource_node_dump_recursive_helper(node); } // Why the hell doesn't the one above capture this one too? /// Non-const overload. template inline std::string resource_node_dump_recursive(intrusive_ptr > node, int internal_dump_offset = 0) { return resource_node_dump_recursive(intrusive_ptr >(node)); } /// Dump node children's _data_ only (non-recursively) in ascii format (suitable for reading/debugging only). template inline std::string resource_node_dump_children_data(intrusive_ptr > node) { std::string str; if (!node->get_child_count()) return str; typename resource_node::child_const_iterator iter = node->children_begin(); for (; iter != node->children_end(); ++iter) { str += (*iter)->dump_data_to_stream() << "\n"; } return str; } /// Non-const overload. template inline std::string resource_node_dump_children_data(intrusive_ptr > node) { return resource_node_dump_children_data(intrusive_ptr >(node)); } } // namespace rmn /// std::ostream output operator for resource node. /// This needs to be in global namespace, else it will be looked up in hz::, not rmn::. template inline std::ostream& operator<< (std::ostream& os, rmn::intrusive_ptr > node) { return (os << rmn::resource_node_dump_recursive(node)); } /// std::ostream output operator for resource node. /// This needs to be in global namespace, else it will be looked up in hz::, not rmn::. template inline std::ostream& operator<< (std::ostream& os, rmn::intrusive_ptr > node) { return (os << rmn::resource_node_dump_recursive(node)); } #endif /// @} gsmartcontrol-1.1.4/src/rmn/resource_serialization.h000066400000000000000000000512021417717230600227430ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2003 - 2010 Irakli Elizbarashvili (C) 2008 - 2012 Alexander Shaduri License: This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ***************************************************************************/ /// \file /// \author Irakli Elizbarashvili /// \author Alexander Shaduri /// \ingroup rmn /// \weakgroup rmn /// @{ #ifndef RMN_RESOURCE_SERIALIZATION_H #define RMN_RESOURCE_SERIALIZATION_H #include // std::string, std::getline() #include #include #include // std::istream definition (calling bad() member) #include // std::ostream definition (calling bad() member) #include // std::ios::* #include // std::locale::classic() #include "hz/cstdint.h" #include "hz/hz_config.h" // DISABLE_RTTI, RMN_* (global_macros.h) #include "hz/string_algo.h" // string_split(), string_trim(), string_trim_copy() #include "hz/string_num.h" // string_is_numeric(), number_to_string() #include "hz/bin2ascii_encoder.h" #include "hz/debug.h" #include "hz/fs_file.h" // hz::File #include "resource_node.h" #include "resource_data_types.h" namespace rmn { // Unfortunately, these functions cannot accept // resource_node::node_const_ptr // as parameters, because of something called non-deducible // context (regarding nested type). // --------------------------- Version Stuff /// Current serializer version identifier const char* version_identifier = "!rmn version "; /// Current serializer major version const int version_major = 0; /// Current serializer minor version const int version_minor = 2; /// Current serializer revision version const int version_revision = 1; /// Get serializer version from string like "1.2.1" inline bool serializer_version_from_string(const std::string& str, int& major, int& minor, int& revision) { std::vector v; hz::string_split(str, '.', v); if (v.size() < 3) return false; if (hz::string_is_numeric(v[0], major) && hz::string_is_numeric(v[0], minor) && hz::string_is_numeric(v[0], revision)) { return true; } return false; } /// Generate serializer version string inline std::string serializer_version_to_string(int major, int minor, int revision) { std::ostringstream ss; ss.imbue(std::locale::classic()); ss << major << "." << minor << "." << revision; return ss.str(); } /// Check if the version in string is currently supported. /// Return the parsed version in \c major, \c minor, \c revision. inline bool serializer_check_version(const std::string& str, int& major, int& minor, int& revision) { std::string version_string = serializer_version_to_string(version_major, version_minor, version_revision); std::string version_header = std::string("#") + version_identifier + version_string; // trim is needed to remove \r (happens when unserializing dos texts) if (hz::string_trim_copy(str).compare(0, version_header.size(), version_header) == 0) return true; return false; } // --------------------------- Types, etc... /// Set _one_ node data, parsing a serialized string value template inline bool resource_node_set_data_from_string(intrusive_ptr > node, node_data_type type, const std::string& value_str) { using hz::string_is_numeric; if (type == T_BOOL) { bool val = 0; if (string_is_numeric(value_str, val, true, true)) return node->set_data(val); return false; } // with boolalpha if (type == T_INT32) { int32_t val = 0; if (string_is_numeric(value_str, val)) return node->set_data(val); return false; } if (type == T_UINT32) { uint32_t val = 0; if (string_is_numeric(value_str, val)) return node->set_data(val); return false; } if (type == T_INT64) { int64_t val = 0; if (string_is_numeric(value_str, val)) return node->set_data(val); return false; } if (type == T_UINT64) { uint64_t val = 0; if (string_is_numeric(value_str, val)) return node->set_data(val); return false; } if (type == T_DOUBLE) { double val = 0; if (string_is_numeric(value_str, val)) return node->set_data(val); return false; } if (type == T_FLOAT) { float val = 0; if (string_is_numeric(value_str, val)) return node->set_data(val); return false; } if (type == T_LDOUBLE) { long double val = 0; if (string_is_numeric(value_str, val)) return node->set_data(val); return false; } if (type == T_STRING) { if (value_str.size() >= 2 && value_str[0] == '"' && value_str[value_str.size() - 1] == '"') { std::string val = value_str.substr(1, value_str.size() - 2); if (!val.empty()) { hz::Bin2AsciiEncoder enc; val = enc.decode(val); // returns empty string on error if (val.empty()) { debug_out_warn("rmn", "resource_node_set_data_from_string(): Error while decoding the data string.\n"); return false; } } return node->set_data(val); } return false; } debug_out_error("rmn", "resource_node_set_data_from_string(): Error while reading data from string: Invalid type given.\n"); return false; } /// Convert node data type to string inline std::string node_data_type_to_string(node_data_type type) { switch(type) { case T_EMPTY: return "empty"; // not intended for general usage case T_BOOL: return "bool"; case T_INT32: return "int32"; case T_UINT32: return "uint32"; case T_INT64: return "int64"; case T_UINT64: return "uint64"; case T_DOUBLE: return "double"; case T_FLOAT: return "float"; case T_LDOUBLE: return "ldouble"; case T_STRING: return "string"; case T_VOIDPTR: return "voidptr"; case T_UNKNOWN: return "unknown"; } return std::string(); // maybe same as unknown? } /// Convert a string (serialized node data type) to actual node data type. inline node_data_type node_data_type_from_string(const std::string& str) { if (str == "empty") return T_EMPTY; if (str == "bool") return T_BOOL; if (str == "int32") return T_INT32; if (str == "int") return T_INT32; // alias on int32 if (str == "uint32") return T_UINT32; if (str == "int64") return T_INT64; if (str == "uint64") return T_UINT64; if (str == "double") return T_DOUBLE; if (str == "float") return T_FLOAT; if (str == "ldouble") return T_LDOUBLE; if (str == "string") return T_STRING; if (str == "voidptr") return T_VOIDPTR; if (str == "unknown") return T_UNKNOWN; // along with "" return T_UNKNOWN; // all others are treated as unknown type } // --------------------------- Saving // Serializer works only if type tracking or RTTI is enabled #if (defined RMN_TYPE_TRACKING && RMN_TYPE_TRACKING) \ || !(defined DISABLE_RTTI && DISABLE_RTTI) /// Whether rmn serialization is available or not. // Serializer works only if type tracking or RTTI is enabled. #define RMN_SERIALIZE_AVAILABLE 1 /// Serialize _one_ node data to string (one line). template inline std::string serialize_node_data(intrusive_ptr > node) { using hz::number_to_string; node_data_type type = resource_node_get_type(node); switch(type) { case T_BOOL: { bool val = 0; if (node->get_data(val)) { return number_to_string(val, true); } break; } // use boolalpha case T_INT32: { int32_t val = 0; if (node->get_data(val)) { return number_to_string(val); } break; } case T_UINT32: { uint32_t val = 0; if (node->get_data(val)) { return number_to_string(val); } break; } case T_INT64: { int64_t val = 0; if (node->get_data(val)) { return number_to_string(val); } break; } case T_UINT64: { uint64_t val = 0; if (node->get_data(val)) { return number_to_string(val); } break; } // the (non-fixed) precision here is std::numeric_limits::digits10+1 case T_DOUBLE: { double val = 0; if (node->get_data(val)) { return number_to_string(val); } break; } case T_FLOAT: { float val = 0; if (node->get_data(val)) { return number_to_string(val); } break; } case T_LDOUBLE: { long double val = 0; if (node->get_data(val)) { return number_to_string(val); } break; } case T_STRING: { hz::Bin2AsciiEncoder enc; std::string val; if (node->get_data(val)) return std::string("\"") + enc.encode(val) + "\""; // there are no quotes inside. break; } case T_EMPTY: case T_VOIDPTR: case T_UNKNOWN: break; // nofin' } // This is not an error, just an unsupported node type. return std::string(); } /// Serialize _one_ node data to string (one line). Non-const version. template inline std::string serialize_node_data(intrusive_ptr > node) { return serialize_node_data(intrusive_ptr >(node)); } namespace internal { /// Serialization helper. Non-recursive, one-line serialization. Returns "" if not serializable. template inline std::string serialize_node_to_string_helper(intrusive_ptr > node, const char* from_path = 0) { std::string str; if (!node) return str; std::string data_str = serialize_node_data(node); // returns empty string if non-serializable if (data_str.empty()) return str; std::string path = node->get_path(); // if current node path contains from_path, remove it if (from_path && !path.compare(0, strlen(from_path), from_path)) { if (path.size() > (strlen(from_path) + 1)) { // leading path + separator path.erase(0, strlen(from_path) + 1); } } if (path.empty()) { debug_out_error("rmn", "serialize_node_to_string(): Error: Unable to parse path: " << node->get_path() << "\n"); return std::string(); } std::string rval = node_data_type_to_string(resource_node_get_type(node)) + " " + data_str; return path + " = " + rval; } /// Serialization helper. Recursive serialization. Version information is NOT prepended. template inline bool serialize_node_to_stream_recursive_helper(intrusive_ptr > node, std::ostream& os, const char* from_path = 0) { if (!node) { debug_out_warn("rmn", "serialize_node_to_stream_recursive(): Empty node given!\n"); return false; } std::string node_line = serialize_node_to_string_helper(node, from_path); if (!node_line.empty()) { os << node_line << "\n"; if (os.bad()) { debug_out_error("rmn", "serialize_node_to_stream_recursive(): Error while writing to stream.\n"); return false; } } if (!from_path) { // make children save with relative paths to the first called node's path. from_path = node->get_path().c_str(); } typename resource_node::child_const_iterator iter = node->children_begin(); for ( ; iter != node->children_end(); ++iter) { serialize_node_to_stream_recursive_helper(intrusive_ptr >(*iter), os, from_path); } return true; } } // ns internal /// Serialize a single node to string, non-recursively. Creates one line. Returns "" if not serializable. template inline std::string serialize_node_to_string(intrusive_ptr > node) { return internal::serialize_node_to_string_helper(node); } /// Serialize a single node to string, non-recursively. Creates one line. Returns "" if not serializable. template inline std::string serialize_node_to_string(intrusive_ptr > node) { return serialize_node_to_string(intrusive_ptr >(node)); } /// Serialize a node to stream, recursively. Version information is NOT prepended. template inline bool serialize_node_to_stream_recursive(intrusive_ptr > node, std::ostream& os) { return internal::serialize_node_to_stream_recursive_helper(node, os); } /// Serialize a node to stream, recursively. Version information is NOT prepended. template inline bool serialize_node_to_stream_recursive(intrusive_ptr > node, std::ostream& os) { return serialize_node_to_stream_recursive(intrusive_ptr >(node), os); } /// Serialize a node to string, recursively. Version information is prepended. template inline bool serialize_node_to_string_recursive(intrusive_ptr > node, std::string& put_here) { std::ostringstream ss; std::string version_string = serializer_version_to_string(version_major, version_minor, version_revision); ss << "#" << version_identifier << version_string << "\n"; debug_out_info("rmn", "Serializing: \"" << node->get_path() << "\"" << " version: " << version_string << "\n"); if (!serialize_node_to_stream_recursive(node, ss)) return false; put_here = ss.str(); return true; } /// Serialize a node to string, recursively. Version information is prepended. template inline bool serialize_node_to_string_recursive(intrusive_ptr > node, std::string& put_here) { return serialize_node_to_string_recursive(intrusive_ptr >(node), put_here); } /// Serialize a node to a file, recursively. Version information is prepended. template inline bool serialize_node_to_file_recursive(intrusive_ptr > node, const std::string& file) { debug_out_info("rmn", "Saving: \"" << node->get_path() << "\"" << " to file: \"" << file << "\".\n"); // Don't use std::fstream, it's not safe with localized filenames on win32. std::string s; if (!serialize_node_to_string_recursive(node, s)) { // warns on error return false; } hz::File f(file); if (!f.put_contents(s)) { debug_print_error("rmn", "serialize_node_to_file_recursive(): Unable to write to file \"%s\".\n", file.c_str()); return false; } return true; } /// Serialize a node to a file, recursively. Version information is prepended. template inline bool serialize_node_to_file_recursive(intrusive_ptr > node, const std::string& file) { return serialize_node_to_file_recursive(intrusive_ptr >(node), file); } #endif // defined RMN_TYPE_TRACKING || !defined DISABLE_RTTI // --------------------------- Loading /// Check if a string is a vaild path. Paths support only alphanumeric characters, '.' and '_' for components. inline bool string_is_path(const std::string& s) { if (s.empty()) return false; hz::Bin2AsciiEncoder enc; for (std::string::const_iterator iter = s.begin(); iter != s.end(); ++iter) { if (!enc.char_is_encoded(*iter) && (*iter != PATH_DELIMITER)) // a safe name-char or a separator return false; } return true; } /// Create (or replace) a node from a serialized node string (one line). template inline typename resource_node::node_ptr create_node_from_serialized_line(intrusive_ptr > under_this_node, std::string line, int line_no) { typename resource_node::node_ptr empty_node; hz::string_trim(line); // remove \r, etc... if (line.empty() || line[0] == '#') // "#" starts comment lines return empty_node; std::vector components; hz::string_split(line, "=", components); if (components.size() != 2) { debug_out_warn("rmn", "Error while unserializing node on line " << line_no << ": Invalid component count.\n"); return empty_node; } std::string path = hz::string_trim_copy(components[0]); std::string rval = hz::string_trim_copy(components[1]); if (!string_is_path(path)) { debug_out_warn("rmn", "Error while unserializing node on line " << line_no << ": The first component is not a valid path.\n"); return empty_node; } components.clear(); hz::string_split(rval, " ", components); node_data_type type; std::string value_str; if (components.size() == 2) { type = node_data_type_from_string(hz::string_trim_copy(components[0])); value_str = hz::string_trim_copy(components[1]); if (type == T_UNKNOWN) { debug_out_warn("rmn", "Error while unserializing node on line " << line_no << " with path \"" << path << "\": The specified type is invalid.\n"); return empty_node; } // try to autodetect the type } else if (components.size() == 1) { value_str = hz::string_trim_copy(components[0]); bool b = 0; int32_t i = 0; double d = 0; if (hz::string_is_numeric(value_str, b, true, true)) { // true / false. use boolalpha. type = T_BOOL; } else if (hz::string_is_numeric(value_str, i)) { type = T_INT32; } else if (hz::string_is_numeric(value_str, d)) { type = T_DOUBLE; } else if (value_str.size() >= 2 && value_str[0] == '"' && value_str[value_str.size() - 1] == '"') { type = T_STRING; } else { debug_out_warn("rmn", "Error while unserializing node on line " << line_no << " with path \"" << path << "\": Cannot auto-detect value type.\n"); return empty_node; } } else { debug_out_warn("rmn", "Error while unserializing node on line " << line_no << " with path \"" << path << "\": No value given.\n"); return empty_node; } typename resource_node::node_ptr node_with_data(new resource_node); if (!resource_node_set_data_from_string(node_with_data, type, value_str)) { // this won't touch the node if error debug_out_warn("rmn", "Error while unserializing node on line " << line_no << " with path \"" << path << "\": Cannot convert the specified value to requested type.\n"); return empty_node; } if (!under_this_node->build_nodes(path)) { debug_out_warn("rmn", "Error while unserializing node on line " << line_no << " with path \"" << path << "\": Cannot build node.\n"); // may happen if the node is outside /config. return empty_node; } // Note: from this point, any error will leave the node path constructed! typename resource_node::node_ptr node = under_this_node->find_node(path); if (!node) { debug_out_error("rmn", "Error while unserializing node on line " << line_no << " with path \"" << path << "\": Cannot read the just-built node!\n"); // internal error? return empty_node; } if (! node->copy_data_from(node_with_data)) { debug_out_error("rmn", "Error while unserializing node on line " << line_no << " with path \"" << path << "\": Cannot copy data from temporary node!\n"); // internal error? return empty_node; } return node; } /// Create nodes from a stream, putting the ones with relative paths under \c under_this_node. template inline bool unserialize_nodes_from_stream(intrusive_ptr > under_this_node, std::istream& is) { std::string line; // we use \n here, because we save in unix mode. however, if \r is encountered, // it will be trimmed afterwards (so dos texts can be loaded too). std::getline(is, line, '\n'); // first line, version string int major = 0; int minor = 0; int revision = 0; if (!serializer_check_version(line, major, minor, revision)) { // invalid version debug_out_warn("rmn", "Error while loading nodes from data stream: " << "The stream has invalid or unsupported version information.\n"); return false; } int line_no = 1; // line counter for diagnostics while (std::getline(is, line, '\n')) { ++line_no; typename resource_node::node_ptr node = create_node_from_serialized_line(under_this_node, line, line_no); } return !is.bad(); } /// Create nodes from a stream, putting the ones with relative paths under \c under_this_node. template inline bool unserialize_nodes_from_string(intrusive_ptr > under_this_node, const std::string& str) { std::stringstream ss; ss << str; return unserialize_nodes_from_stream(under_this_node, ss); } /// Create nodes from a file, putting the ones with relative paths under \c under_this_node. template inline bool unserialize_nodes_from_file(intrusive_ptr > under_this_node, const std::string& file) { debug_out_info("rmn", "Loading under \"" << under_this_node->get_path() << "\"" << " from file \"" << file << "\".\n"); // Don't use std::fstream, it's not safe with localized filenames on win32. hz::File f(file); std::string s; if (!f.get_contents(s)) { // this warns debug_print_error("rmn", "unserialize_nodes_from_file(): Unable to read from file \"%s\".\n", file.c_str()); return false; } return unserialize_nodes_from_string(under_this_node, s); } } // namespace rmn #endif /// @} gsmartcontrol-1.1.4/src/rmn/rmn.h000066400000000000000000000063761417717230600167670ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2003 - 2010 Irakli Elizbarashvili (C) 2008 - 2012 Alexander Shaduri License: This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ***************************************************************************/ /// \file /// \author Irakli Elizbarashvili /// \author Alexander Shaduri /// \ingroup rmn /// \weakgroup rmn /// @{ #ifndef RMN_RMN_H #define RMN_RMN_H /** \file Include this file to use the whole rmn. Otherwise, include individual headers. Configuration macros: RMN_TYPE_TRACKING to enable explicit data type tracking in resource_data providers. Note: Either RTTI or RMN_TYPE_TRACKING must be enabled to be able to serialize the node data! (Loading the serialized data will still work). Note: Type tracking allows you to track them only from a predefined set of types. Type tracking and RTTI effects on resource_data_any: Type tracking (RMN_TYPE_TRACKING): - Fast (one integer comparison when checking types). - One more member variable in each node. - All type checks are limited to predefined types. RTTI (disabled through DISABLE_RTTI, otherwise always present): - Possibly slower (type checks are performed through std::type_info comparisons). - No additional member variables. - Can check any type, thus it's more reliable when working with non-predefined types. */ /** \namespace rmn Resource manager - a facility similar to runtime registry with serialization. */ /** \namespace rmn::internal Resource manager library internal implementation helpers. */ /// \def RMN_TYPE_TRACKING /// If 1, enables rmn runtime type tracking (to report type errors), /// useful if there's no RTTI available. /// \def RMN_RESOURCE_NODE_DEBUG /// Define this to 1 to enable debug printing of resource node operations. // #ifndef RMN_TYPE_TRACKING // #define RMN_TYPE_TRACKING 0 // #endif #include "resource_base.h" // needed for node #include "resource_node.h" // resource_node type #include "resource_exception.h" // exceptions thrown in critical situations #include "resource_data_types.h" // type tracking helpers for data #include "resource_data_locking.h" // locking policies for data #include "resource_data_any.h" // any_type data provider #include "resource_data_one.h" // a specific type data provider #include "resource_node_dump.h" // node dumper functions #include "resource_serialization.h" // serialize / unserialize nodes #endif /// @} gsmartcontrol-1.1.4/src/rmn/rmn_test.cpp000066400000000000000000000077551417717230600203630ustar00rootroot00000000000000/************************************************************************** Copyright: (C) 2003 - 2010 Irakli Elizbarashvili (C) 2008 - 2012 Alexander Shaduri License: See LICENSE_unlicense.txt ***************************************************************************/ /// \file /// \author Irakli Elizbarashvili /// \author Alexander Shaduri /// \ingroup rmn_tests /// \weakgroup rmn_tests /// @{ #undef INTRUSIVE_PTR_RUNTIME_CHECKS #define INTRUSIVE_PTR_RUNTIME_CHECKS 1 #undef RMN_RESOURCE_NODE_DEBUG #define RMN_RESOURCE_NODE_DEBUG 1 // disable libdebug, we don't link to it #undef HZ_USE_LIBDEBUG #define HZ_USE_LIBDEBUG 0 // enable libdebug emulation through std::cerr #undef HZ_EMULATE_LIBDEBUG #define HZ_EMULATE_LIBDEBUG 1 // The first header should be then one we're testing, to avoid missing // header pitfalls. #include "rmn.h" #include #include #include // enable printing of std::string inside any_type. // ANY_TYPE_SET_PRINTABLE(std::string, true); /// Main function for the test int main() { typedef rmn::resource_node< rmn::ResourceDataAny > resource_node; typedef resource_node::node_ptr resource_node_ptr; resource_node_ptr root(new resource_node); root->set_name("/"); // std::cerr << root; { // create some nodes... resource_node_ptr app(new resource_node); app->set_name("app"); root->add_child(app); resource_node_ptr sys(new resource_node); sys->set_name("sys"); root->add_child(sys); resource_node_ptr plugins(new resource_node); plugins->set_name("plugins"); app->add_child(plugins); plugins->create_child("plug1"); plugins->create_child("plug2", std::string("plug2_data")); plugins->create_child("plug3", 6); plugins->build_nodes("video/gui"); plugins->build_nodes("/app/video2/gui"); // should be denied plugins->build_nodes("/app/video3/gui", true); // should be allowed, side-construction is enabled. plugins->create_child("plug4", "plug4_data"); // this will store it as std::string! // std::cerr << "Data: " << plugins->get_child_node("plug3")->get_data(); // std::cerr << root; } std::cerr << "--- begin dump root:\n"; std::cerr << root; std::cerr << "--- end dump root\n"; std::cerr << "--- build path test: -----------------\n"; std::cerr << "root->build_nodes(\"/app/video/gui\"); - 2 times;" << "\n"; root->build_nodes("/app/video/gui"); root->build_nodes("/app/video/gui"); std::cerr << "--- /app/fingor/gui, /app/fingor/state, /app/biocalc/gui, /sys/conf/video ... " << "\n"; root->build_nodes("/app/fingor/gui"); root->build_nodes("/app/fingor/state"); root->build_nodes("/app/biocalc/gui"); root->build_nodes("sys/conf/video"); root->build_nodes("sys/conf/fingor"); root->build_nodes("/sys/conf/biocalc"); std::cerr << "--- root dump:" << "\n"; std::cerr << root; /* std::cerr << "--- get_path test: -------\n"; std::cerr << "sys->get_path():\t" ; std::cerr << sys->get_path() << "\n"; std::cerr << "plugins->get_path():\t" ; std::cerr << plugins->get_path() << "\n"; std::cerr << "plug2->get_path():\t" ; std::cerr << plug2->get_path() << "\n"; */ std::cerr << "--- find_node test: -------\n"; resource_node_ptr n; std::cerr << ((n = root->find_node("/sys/conf/fingor")) ? n->get_name() : "NULL") << "\n"; std::cerr << ((n = root->find_node("/sys/conf/fingor/nonexistent")) ? n->get_name() : "NULL") << "\n"; std::cerr << ((n = root->find_node("state")) ? n->get_name() : "NULL") << "\n"; std::cerr << ((n = root->find_node("sys")) ? n->get_name() : "NULL") << "\n"; std::cerr << ((n = root->find_node("/state")) ? n->get_name() : "NULL") << "\n"; std::cerr << ((n = root->find_node("video/")) ? n->get_name() : "NULL") << "\n"; /* std::cerr << "--- root dump:" << "\n"; std::cerr << root; */ // this throws exception // root->get_data(); // empty root->find_node("/app/plugins/plug3")->get_data(); // int -> string return EXIT_SUCCESS; } /// @} gsmartcontrol-1.1.4/version.txt.in000066400000000000000000000000111417717230600172460ustar00rootroot00000000000000@VERSION@