diff --git a/gsmartcontrol/.svnignore.txt b/.svnignore.txt similarity index 91% rename from gsmartcontrol/.svnignore.txt rename to .svnignore.txt index 6b40be3..4d2c657 100644 --- a/gsmartcontrol/.svnignore.txt +++ b/.svnignore.txt @@ -8,8 +8,6 @@ config.guess config.sub aclocal.m4 config.h.in -compile -ar-lib missing install-sh *.glade.cpp diff --git a/gsmartcontrol/AUTHORS.txt b/AUTHORS.txt similarity index 100% rename from gsmartcontrol/AUTHORS.txt rename to AUTHORS.txt diff --git a/gsmartcontrol/COPYING b/COPYING similarity index 100% rename from gsmartcontrol/COPYING rename to COPYING diff --git a/gsmartcontrol/ChangeLog b/ChangeLog similarity index 100% rename from gsmartcontrol/ChangeLog rename to ChangeLog diff --git a/gsmartcontrol/Doxyfile b/Doxyfile similarity index 100% rename from gsmartcontrol/Doxyfile rename to Doxyfile diff --git a/gsmartcontrol/INSTALL b/INSTALL similarity index 100% rename from gsmartcontrol/INSTALL rename to INSTALL diff --git a/gsmartcontrol/LICENSE_boost_1_0.txt b/LICENSE_boost_1_0.txt similarity index 100% rename from gsmartcontrol/LICENSE_boost_1_0.txt rename to LICENSE_boost_1_0.txt diff --git a/gsmartcontrol/LICENSE_bsd-ac.txt b/LICENSE_bsd-ac.txt similarity index 100% rename from gsmartcontrol/LICENSE_bsd-ac.txt rename to LICENSE_bsd-ac.txt diff --git a/gsmartcontrol/LICENSE_bsd-ucb.txt b/LICENSE_bsd-ucb.txt similarity index 100% rename from gsmartcontrol/LICENSE_bsd-ucb.txt rename to LICENSE_bsd-ucb.txt diff --git a/gsmartcontrol/LICENSE_gpl2.txt b/LICENSE_gpl2.txt similarity index 100% rename from gsmartcontrol/LICENSE_gpl2.txt rename to LICENSE_gpl2.txt diff --git a/gsmartcontrol/LICENSE_gpl3.txt b/LICENSE_gpl3.txt similarity index 100% rename from gsmartcontrol/LICENSE_gpl3.txt rename to LICENSE_gpl3.txt diff --git a/gsmartcontrol/LICENSE_gsmartcontrol.txt b/LICENSE_gsmartcontrol.txt similarity index 93% rename from gsmartcontrol/LICENSE_gsmartcontrol.txt rename to LICENSE_gsmartcontrol.txt index 9ef6668..f2a2e5a 100644 --- a/gsmartcontrol/LICENSE_gsmartcontrol.txt +++ b/LICENSE_gsmartcontrol.txt @@ -23,6 +23,10 @@ examples are covered under the Unlicense. See LICENSE_unlicense.txt for details. +Portions of this software are copyright Google Inc. See LICENSE_pcrecpp.txt +for details. + + This product includes the HZ library (or portions of it). See LICENSE_hz.txt for details. diff --git a/gsmartcontrol/LICENSE_hz.txt b/LICENSE_hz.txt similarity index 100% rename from gsmartcontrol/LICENSE_hz.txt rename to LICENSE_hz.txt diff --git a/LICENSE_pcrecpp.txt b/LICENSE_pcrecpp.txt new file mode 100644 index 0000000..d76a461 --- /dev/null +++ b/LICENSE_pcrecpp.txt @@ -0,0 +1,28 @@ +Copyright (c) 2005 - 2010, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of Google Inc. 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 COPYRIGHT HOLDERS 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 COPYRIGHT +OWNER 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. diff --git a/gsmartcontrol/LICENSE_unlicense.txt b/LICENSE_unlicense.txt similarity index 100% rename from gsmartcontrol/LICENSE_unlicense.txt rename to LICENSE_unlicense.txt diff --git a/gsmartcontrol/LICENSE_zlib.txt b/LICENSE_zlib.txt similarity index 100% rename from gsmartcontrol/LICENSE_zlib.txt rename to LICENSE_zlib.txt diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..d437fab --- /dev/null +++ b/Makefile.am @@ -0,0 +1,154 @@ + +# 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 README.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_pcrecpp.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 + + + +# PCH support + +global_macros.h.gch: $(top_srcdir)/src/global_macros.h + $(CXX) $(CXXFLAGS) $(DEFS) -x "c++-header" $(top_srcdir)/src/global_macros.h -o global_macros.h.gch + +gch: global_macros.h.gch + +gch-clean: + rm -f global_macros.h.gch + + + +# rpm support + +src-rpm: dist + rpmbuild -ts $(distdir).tar.bz2 + + +# Don't use this - it does something weird, and gcc errors out with "long long long" errors, etc... +#rpm: dist +# rpmbuild -ta $(distdir).tar.bz2 + + + +# NSIS installer support. +# Requires wine, installed NSIS, smartctl-nc.exe, smartctl.exe, update-smart-drivedb.exe. +# Gtkmm dlls and pcre dlls are required if linking against them. +# dos2unix on build machine. +# Execute only with win32 build present. + +WIN_ZIP_NAME = $(PACKAGE)-$(VERSION)-win32 +WIN_ZIP_NOGTK_NAME = $(PACKAGE)-$(VERSION)-win32-nogtk + + +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 + + +# 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 + + cp src/gsmartcontrol.exe src/gsmartcontrol.exe.manifest win-dist/ + + unix2dos win-dist/doc/*.txt + + cp -p "@WINDOWS_BINDIR@"/smartctl-nc.exe win-dist/ + cp -p "@WINDOWS_BINDIR@"/smartctl.exe win-dist/ + cp -p "@WINDOWS_BINDIR@"/update-smart-drivedb.exe 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 + cp "@WINDOWS_BINDIR@"/"@GTK_INSTALLER_NAME@".exe nsis-dist/ + +nsis-dist-nocleanup: nsis-dist-prepare + cd nsis-dist && @NSIS_EXEC@ gsmartcontrol.nsi + mv nsis-dist/$(PACKAGE)-$(VERSION).exe . + cd nsis-dist && @NSIS_EXEC@ /DNO_GTK gsmartcontrol.nsi + mv nsis-dist/$(PACKAGE)-$(VERSION)-nogtk.exe . + +nsis-dist-cleanup: + rm -rf nsis-dist + +nsis-dist: nsis-dist-nocleanup nsis-dist-cleanup + + +win-zip-dist-prepare: + cp -a win-dist $(WIN_ZIP_NAME) + cp -a win-dist $(WIN_ZIP_NOGTK_NAME) + cp -a "@WINDOWS_BINDIR@"/"@GTK_INSTALLER_NAME@"/* $(WIN_ZIP_NAME)/ + +win-zip-dist-nocleanup: win-zip-dist-prepare + zip -K -9r $(WIN_ZIP_NAME).zip $(WIN_ZIP_NAME) -x $(WIN_ZIP_NAME)/doc/distribution-nogtk.txt + zip -K -9r $(WIN_ZIP_NOGTK_NAME).zip $(WIN_ZIP_NOGTK_NAME) -x $(WIN_ZIP_NOGTK_NAME)/doc/distribution.txt + +win-zip-dist-cleanup: + rm -rf $(WIN_ZIP_NAME) + rm -rf $(WIN_ZIP_NOGTK_NAME) + +win-zip-dist: win-zip-dist-nocleanup win-zip-dist-cleanup + + + + +distclean-local: gch-clean win-dist-clean + + + diff --git a/gsmartcontrol/NEWS b/NEWS similarity index 79% rename from gsmartcontrol/NEWS rename to NEWS index b255f8f..a2b1e0a 100644 --- a/gsmartcontrol/NEWS +++ b/NEWS @@ -1,56 +1,3 @@ -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 diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..900f6b5 --- /dev/null +++ b/README.txt @@ -0,0 +1,422 @@ + +About GSmartControl + +GSmartControl - Hard disk drive and SSD health inspection tool. + +GSmartControl is a graphical user interface for smartctl (from smartmontools +package, see http://smartmontools.sourceforge.net), 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. + +Note: GSmartControl supports ATA drives (both PATA and SATA), various USB to +ATA bridges and drives behind some RAID controllers (selected models of +3ware, Areca, Adaptec, Intel RAID controllers). + +http://gsmartcontrol.sourceforge.net + + + +Features + +* automatically reports and highlights any anomalies; + +* allows enabling/disabling SMART; + +* allows enabling/disabling Automatic Offline Data Collection - a short +self-check that the drive will perform automatically every four hours with no +impact on performance; + +* supports configuration of global and per-drive options for smartctl; + +* performs SMART self-tests; + +* displays drive identity information, capabilities, attributes, and +self-test/error logs; + +* can read in smartctl output from a saved file, interpreting it as a +read-only virtual device; + +* works on most smartctl-supported operating systems; + +* has extensive help information. + + + +What Is SMART? + +Short answer: SMART is a technology which provides hard disk drives with +methods to predict certain kinds of failures with certain chance of success. + +Long answer: read below. + +Self-Monitoring, Analysis, and Reporting Technology, or SMART, is a +monitoring system for hard drives and SSDs to detect and report various +indicators of reliability, in the hope of anticipating failures. SMART is +implemented inside the drives, providing several ways of monitoring the drive +health. It may present information about general health, various drive +attributes (for example, number of unreadable sectors), error logs, and so on. +It may also provide ways to instruct the drive to run various self-tests, +which may report valuable information. It may even automatically scan the disk +surface in when the drive is idle, repairing the defects while reallocating +the data to more safe areas. + +While having SMART sounds really good, there are some nuances to consider. One +of the common pitfalls is that it may create a false sense of security. That +is, a perfectly good SMART data is NOT an indication that the drive won't fail +the next minute. The reverse is also true - some drives may function perfectly +even with not-so-good-looking SMART data. However, as studies indicate, given +a large population of drives, some SMART attributes may reliably predict +drive failures within up to two months. + +Another common mistake is to assume that the attribute values are the real +physical values, as experienced by the drive. As manufacturers do not +necessarily agree on precise attribute definitions and measurement units, the +exact meaning of the attributes may vary greatly across different drive +models. + +At present SMART is implemented individually by manufacturers. While some +aspects are standardized for compatibility, others are not. In fact, most +manufacturers refer the users to their own health monitoring utilities and +advice against taking SMART data seriously. Nevertheless, SMART may prove an +effective measure against data loss. + +Yet another issue is that quite often the drives have bugs which prevent +correct SMART usage. This is usually due to buggy firmware, or the +manufacturer ignoring the standards. Luckily, smartmontools usually detects +these bugs and works around them. + + + +Software Requirements + +You need to have the following software installed: + +* pcre - http://www.pcre.org . + +* smartmontools - see http://smartmontools.sourceforge.net . + +* GTK+, version 2.6 or higher - see http://www.gtk.org . + +* Gtkmm, version 2.6 or higher - see http://www.gtkmm.org . + +* libglademm, version 2.4 or higher - see http://www.gtkmm.org . +Note: libglademm is not needed when using GTK+ / Gtkmm 2.12 or newer. + +Note that having GTK+ 2.12 and Gtkmm 2.12 is HIGHLY recommended. While earlier +versions may work, they may produce suboptimal results and buggy behavior. + +Most of these packages are probably already provided by your distribution. +Here are the packages you need to have to build GSmartControl on some Linux +distributions: + +openSUSE, SLES, SLED: +gcc-c++, pcre-devel, gtkmm2-devel, (possibly) libglademm-devel. + +Fedora, CentOS, RHEL: +gcc-c++, pcre-devel, gtkmm24-devel, (possibly) libglademm24-devel. + +Mandriva: +gcc-c++, gcc-cpp, libpcre-devel, gtkmm2.4-devel, (possibly) +libglademm2.4-devel. + +Ubuntu, Debian GNU/Linux: +g++, libpcre3-dev, libgtkmm-2.4-dev, (possibly) libglademm-2.4-dev. + +Note that usually you need to specify only these packages - the rest is +installed automatically by the package manager's dependency resolver. Keep in +mind that you also need smartmontools to run the program. + +Note: If using the official Windows package, the requirements are listed on +GSmartControl's website. + + +The following operating systems are supported: + +* Linux - All the popular configurations should work. + +* FreeBSD - Tested with DesktopBSD 1.6 (FreeBSD 6.3) / x86. + +* NetBSD - Tested with NetBSD 4.0.1 / x86. + +* OpenBSD - Tested with OpenBSD 4.3 / x86-64 / gcc-3.3.5. + +* Windows (2000 SP4 or newer) - Tested with Windows 2000 SP4, Windows XP SP3, +Windows Vista SP2 (32-bit and 64-bit), Windows 7 SP1 (32-bit and 64-bit). The +Windows port uses pd0, pd1, etc... for physical drives 0, 1, etc... . + +* Solaris - Tested with Solaris 10 / x86 / gcc-3.4.3 / blastwave, +Solaris 10 / x86 / sunstudio12 / sunfreeware. OpenSolaris should work but has +not been tested yet. + +* Mac OS X - 3rd-party testing by the MacPorts and Fink projects. + +* DragonFlyBSD - Code written but no testing has been performed yet. Expected +to work without any issues. + +* QNX - Code written but no testing has been performed yet. + + + +Installation + +Short answer: build and install via: ./configure; make; make install + +Run gsmartcontrol-root to invoke gsmartcontrol with your desktop's su +mechanism, or use the desktop menu entry. + + +Long answer: read below. + +First, check if you can find a pre-built package for your distribution or +operating system - they usually provide the best integration and the easiest +installation procedure. For Linux, one option is to try the openSUSE Build +Service - it provides ready-to-install packages for various popular Linux +distributions (openSUSE, Fedora, etc...). See +http://download.opensuse.org/repositories/home:/alex_sh/ . + +If you want to compile from source, check that you have all the required +dependencies (see Software Requirements section). Then the usual + +./configure; make; make install + +will build and install it. Installation usually requires administrative +privileges, but you don't need to install the program in order to run it +directly from the build directory. + + + +Smartctl Options + +GSmartControl tries its best to guard the user from having to specify smartctl +options. However, this is not always possible due to drive firmware bugs, +unimplemented features, and so on. The smartctl manual page contains all the +information you may need when dealing with smartctl. +See http://smartmontools.sourceforge.net/man/smartctl.8.html + +Additional information is available at http://smartmontools.sourceforge.net + + + +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. However, --verbose will still +increase the verbosity of a log saved via "Options -> View Execution Log -> +Save All". + +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]. + + + +Permission Problems + +Short answer: you need to be root (that's Administrator in Windows). +In X11, use kdesu, gnomesu, sux, xdg-su or similar. + +Long answer: read below. + +Most operating systems prohibit direct access to hardware to users with +non-administrative privileges. Unfortunately, to access SMART data, smartctl +needs to directly access the hard drive. + +The provided X11 desktop and menu icons should show the "Please enter the root +password" dialog boxes, and, after correct information is entered, should run +this program with root privileges. The dialogs should be available in most +commonly used X11 desktop environments. + +Another way is to use the included gsmartcontrol-root script, which finds the +available su program and runs gsmartcontrol with it. + +Yet another way is to manually invoke the program with kdesu, gnomesu, sux or +similar programs. For example, + +kdesu -u root -c gsmartcontrol + +will ask for root password and run gsmartcontrol with root privileges. Replace +"kdesu" with "gnomesu" if using Gnome. The "sux" or "xdg-su" commands may also +help, if neither KDE or GNOME are available. + +Please don't set the "setuid" flag on smartctl binary. It is considered a +security risk. + + + +Enable SMART Permanently + +Specifications say that once you set a SMART-related property, it will be +preserved across reboots. So, when you, say, enable SMART and Automatic +Offline Data Collection, both will stay enabled until you disable them. + +However, BIOS, your operating system, your other operating systems (if +present), and various startup programs may affect that. For example, BIOS may +enable SMART each time you start your computer, so if you disabled SMART +previously, it will be re-enabled on reboot. + +The easiest way to work around this is to set the desired settings on system +startup. You may use smartctl or smartd to do that. For example, to enable +both SMART and Automatic Offline Data Collection on /dev/sda, one would write +the following to the system startup script (e.g. boot.local, rc.local or +similar on Linux): + +smartctl -s on -o on /dev/sda + +For more information, see smartctl and smartd documentation. + + + +Known Issues + +Only ATA drives (both PATA and SATA), various USB to ATA bridges and drives +behind some RAID controllers are supported for now. The main reasons are: + +* We can't support drives which don't work with smartmontools. This affects +drives which don't support SMART or don't export SMART data correctly (e.g. +some USB enclosures, RAIDs, etc...). + +* Smartctl's output for SCSI drives is completely different compared to ATA. +Also, SCSI drives are rarely found in desktop systems and the servers rarely +have X11 / Gtkmm running, so this is a low priority task. + +* I only have ATA drives, so testing anything else is very difficult. + +Immediate Offline Tests are not supported. I haven't found a way to reliably +monitor them yet. Besides, they run automatically anyway if Automatic Offline +Data Collection is enabled. + +Testing is only supported on drives which correctly report their progress +information in capabilities. + +Not all drives support disabling Automatic Offline Data Collection, even if +they report otherwise. Unfortunately, there's no way to detect such drives. + +Running on GTK+ / Gtkmm versions earlier than 2.12 may cause visual artifacts, +usability issues (especially with tooltips and icons), instability, etc... + +The texts probably contain a lot of grammatical errors, English being my third +language and all. + + + +Reporting Bugs + +If it is a SMART or drive-related problem, please try to test it with smartctl +first. Chances are, the problem you're experiencing is not tied to +GSmartControl, but is a drive firmware or smartctl problem. For example, to +see a complete information about your /dev/sda drive, type the following in a +terminal emulator (e.g., xterm, konsole or gnome-terminal): + +smartctl -a /dev/sda + +Note: If using Windows, the device name should be /dev/pd1 for the second +physical drive, etc... . + +If you still think it's a GSmartControl issue, please collect the following +information about your system. Without it, it may be very hard or impossible +to fix the bug. + +* Which operating system you use (for example, openSUSE Linux 11.4). + +* Which version of GTK and Gtkmm you have installed. Finding this out is very +distribution-specific. For example, on openSUSE it would be +"rpm -q gtk2 gtkmm2". Some distributions have gtkmm24 instead. You may also +search them in your distribution's graphical package manager, if there is one. + +* Execution log from the program, if possible. To obtain it, run the program +with -v option, e.g. (type the following in a terminal emulator or Run +dialog): + +gsmartcontrol-root auto -v + +Note: On Windows, run gsmartcontrol.exe with "-v" switch as Administrator. + +Perform the steps needed to reproduce the bug, then go to +"Options -> View Execution Log", and click "Save All". + +* Detailed description of steps you performed when the bug occurred. + +Once you have this information, send an email to me, Alexander Shaduri +. Note that I may refer you to smartmontools support +if it's a bug in smartmontools and not GSmartControl. Normally, I won't +redirect your support request to them myself, because they may ask questions +which only you have the answers to. + +Please read the "License and Copyright" and "Patch Licensing" sections before +sending any patches. + + + +License and Copyright + +For license information, see LICENSE_gsmartcontrol.txt file. + +You may notice that GSmartControl is not licensed under "GNU GPL version X or +later", but under "GNU GPL version X and Y". I firmly believe that it's unwise +to license a piece of code under non-existent licenses, whatever anyone else +might say. The reason for this is that one simply CANNOT know that, say, in 20 +years FSF won't be bought by some corporation who will release GPL version Z +which will completely reverse the reasons GPL was created for. + +The removal of "or later" clause somewhat imposes responsibility on the +copyright holders to review every future version of the license once it's +released, and, if deemed acceptable, re-license the code under the new license +(possibly retaining the old licenses). Unfortunately, this is a necessary +inconvenience we will have to deal with. + + + +Patch Licensing + +Due to reasons described in "License and Copyright" section, to make it +possible to re-license the code without tracking down all the people who ever +wrote a patch, the copyrights of all minor patches must be assigned to the +central copyright holder of the project. If the patch is major enough (that +is, it forms a significant part of the program source code), the author may +retain the copyright, if he or she chooses to do so. However, unless the +author plans to maintain his part of the source code, he / she is humbly asked +to consider assigning away his / her copyright. A simple "I disclaim all +copyright to this patch" by the author is sufficient. All credits will be +mentioned in product documentation, whatever the size of the patch is. + +Please note that centralization of copyright is needed to maintain a +reasonably healthy legal status of the project. Also note that this method is +not unique - FSF and many other organizations require exactly the same thing. + +Some contributors may have reasonable doubts about the future status of this +project. Let me assure you that this project will never have more restrictive +license than GPLv2. If, some time in the future, the GPL is somehow +invalidated in court, the project will be re-licensed under similar (in +spirit) license, if possible, or a license less restrictive than GPL (for +example, the three-clause BSD license). + + diff --git a/TODO b/TODO new file mode 100644 index 0000000..ab4d8ba --- /dev/null +++ b/TODO @@ -0,0 +1,250 @@ + + +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 +} + + +A treeview (also iconview, combobox) from glade comes +with existing model, which should be unset. + + +Bugs / patches: + Patch pangomm for Pango::strip_markup(). + + + +-------------------------------------------------------- + +TODO: + +Don't rely on smartctl return code (2), parse the output instead. + This will allow us to show the Info page. + +?Fix not being able to quit while executing commands. + Cannot reproduce. + https://www.google.com/search?client=opera&q=IA__gtk_main_quit:+assertion+%60main_loops+!=+NULL'+failed&sourceid=opera&ie=utf-8&oe=utf-8&channel=suggest + https://developer.gnome.org/gtk2/2.24/gtk2-General.html#gtk-main-quit + https://www.google.com/search?num=50&hl=en&safe=off&client=opera&hs=Dr5&channel=suggest&q=calling+gtk_main_quit+during+gtk_main_iteration+warning&oq=calling+gtk_main_quit+during+gtk_main_iteration+warning&gs_l=serp.12...7064.8155.0.12753.8.8.0.0.0.0.216.935.0j6j1.7.0.les%3B..0.0...1c.1.4.serp.GUiDL5IM2U8 + https://developer.gnome.org/glib/2.34/glib-The-Main-Event-Loop.html#g-main-loop-quit + +Port to GTK2 with disable-deprecated flags, then GTK3. + Drop support for old distros. + + +In main window add multi-selection support and right-click menu with various + tests, so that it's possible to run multiple tests in parallel from there. + + +Areca detection + Linux: + Somehow differentiate between the cards with expanders (-d areca,N/E) and without (-d areca,N). + The expander ones seem to have "ix" (case-insensitive) as their name suffixes. + areca,N - N is [1,24]. + areca,N/E - N is [1,128], E is [1,8]. + We should limit these using some config key (36 for N and 4 for E seems ok). + N/E syntax is since smartmontools 5.43. + For older versions we get: /dev/: Option -d areca,N requires N to be a non-negative integer + Windows Detection: + Credit Richard Kagerer. + + +Credit Fabrice Bacchella for Linux CCISS detection. + + + + +Add ability (through ctrl-C?) to copy individual rows from attributes page, + in tab-separated format (for easy pasting into libreoffice). + + +Investiage the new "-l ssd" and "-l devstat" options (since 5.42) + +Check also other options in 5.42 and 5.43. + "-g all" looks promising alongside -i (shows APM and other stuff as well). + +Add --identify option to context menu, for convenience. + +Add menu item - "update drive database" (not sure about linux, should + check if it's supported first; also need to run it in terminal). + + +Testing: + If ETA time has elapsed, but it's still only at 10% completion, + ETA 0 is displayed. Fix. + If smartctl -t ... returns an error, testing=true should not be set. + Can't reproduce it? + Detect running tests on launch (maybe ask the user too? some tests + may be stuck due to bad firmware, e.g. 3ware/windows). + + + +48-bit ATA command support: + '-l xerror' -> Extended Comprehensive Error log: An multi-sector and + LBA48 capable version of '-l error'. Output is similar, except more + entries can exists and the high bytes of the registers are also printed. + Some newer drives (Samsung) only write to this log. + + '-l xselftest' -> Extended Self Test Log: An multi-sector and LBA48 + capable version of '-l selftest'. Output will be similar, except more + entries can exists and the LBA values can be somewhat longer. + + "smartctl -l this_wont_exist" gives the list of supported logs - this + is needed to detect xerror / xselftest support in smartctl. + + +Rework drive properties UI: + use 48-bit or not or auto. + Enable/disable/dont_touch SMART on startup for this drive. + Enable/disable/dont_touch AODC on startup for this drive. + Add --init-smart-only option to only apply the SMART/AODC enabling status + from the drive properties. + Add also --no-init-smart to avoid doing that on GUI startup. + +Notifications: + Tied to (drive::subdrive, notif_key, exact_value(optional)). + If exact_value isn't specified when ignoring, notify on every value change. + Ignore options: + Per-drive: Ignore all notifications for this key, ignore only this value. + Don't generate multiple notifications for the same drive/key combo. + Clearing in preferences: Clear all ignored, clear ignored for this drive, clear individual. + Soft-warnings require "-a" by default, tricky for scsi (maybe try -a, then -i?). + +Monitor: + Needs root access, so not gonna happen until the dbus/udisks stuff + is finalized. + Same program, add --tray option to start in tray only. + Option to enable the tray icon. + Option to continue running in the tray on window close. + Use freedesktop notifications when notifications are generated + and we're running in tray only. + Don't generate the same notification at a single run time if it was + ignored (untouched) by the user; Change the tray icon and + show them in the main window instead. + Periodically re-run smartctl to refresh the notifications. + Periodically see if drives were added / removed (possibly use OS callbacks + instead of polling?) + +Win7 widget: + Can be done in html/js. + Shows temperature + Detects drives + Opens GSmartControl + +If a drive is not recognized, suggest using -d (sat, sat,12, ...) + Refer the user to + http://sourceforge.net/apps/trac/smartmontools/wiki/Supported_USB-Devices + + + +RAID: + http://sourceforge.net/apps/trac/smartmontools/wiki/Supported_RAID-Controllers + + Areca Windows: + Detection not supported by smartctl --scan-open. + + Areca FreeBSD: since 5.42. + Call as: smartctl -a -d areca,[1-24] /dev/arcmsr1 + + 3ware FreeBSD: + Call as: smartctl -i -d 3ware,[0-127] /dev/twa0 (or twe0) + Detection: unknown. + + LSI MegaRAID (SCSI/SAS), Linux: + Call as: smartctl -i -d megaraid,[0-?] /dev/sda + We should differentiate between ordinary sda and RAID one (not sure if sda is detected). + Detection: unknown. + + HighPoint RocketRAID ((S)ATA) Linux: + Call as: smartctl -i -d hpt,[1-8]/[1-8] /dev/sda + Call as: smartctl -i -d hpt,[1-8]/[1-8]/[1-15] /dev/sda + Note: /dev/sda should be derived from this controller. + We should differentiate between ordinary sda and RAID one (not sure if sda is detected). + Smartctl docs specify smaller limits, but the code says these are the ones. + Detection: unknown. + + HighPoint RocketRAID ((S)ATA) FreeBSD: + Call as: smartctl -i -d hpt,[1-8]/[1-8] /dev/hptrr + Call as: smartctl -i -d hpt,[1-8]/[1-8]/[1-15] /dev/hptrr + Note: /dev/hptrr should be derived from this controller. + Smartctl docs specify smaller limits, but the code says these are the ones. + Detection: unknown. + TODO: Analyze sent info. + + CCISS (HP (Compaq) Smart Array Controller) FreeBSD: + Seems to be the same as Linux (uses the same driver?). + Detection: unknown. + + + + +libdebug: + per-channel disabling. + per-channel formatting. + all flags should be per-channel. + bug: if -q is passed, Execution log doesn't contain libdebug log. (requires per-channel status control). + use format_time() instead of ctime() and friends, with ISO formatting. + + + +'-l sataphy' -> SATA Phy event counters: Non-SMART feature, useful to detect SATA cabling problems. + + +SCT Temperatures + -l scttemp prints temperature and history. + -t scttempint,N[,p] (N - count, p - preserve across reset or not). + This configures temperature logging interval (1 min by default) and clears the history. + -l scterc[,...] to get/set the SCT Error Recovery Control time limit (5.40 and newer). + + +Add detection for Interix (i586-pc-interix3), maybe with debian? + + + + +----------- SMART stuff ---------- + + +Run one-time Immediate Offline test (-t offline). + Offline tests only update Attributes, and if errors are found they will + appear in SMART error log. Self-test logs are unaffected. + Abort (-X). Abort works with Offline only if there's + "Abort Offline collection upon new command" capability. + If the drive has "Suspend Offline collection upon new command" capability, + immediate offline test may be tracked through --capabilities (not on mine!). + If it's "Abort Offline collection upon new command", then the test + will abort on --capabilities or --abort. + + +Polling time +The self-test routine recommended polling time shall be equal to the number of minutes that is the minimum +recommended time before which the host should first poll for test completion status. Actual test time could +be several times this value. Polling before this time could extend the self-test execution time or abort the test +depending on the state of bit 2 of the off-line data capability bits. + +Autosave +This command enables and disables the optional attribute autosave feature of the device. This command +may either allow the device, after some vendor specified event, to save the device updated attribute values +to non-volatile memory; or this command may cause the autosave feature to be disabled. The state of the +attribute autosave feature (either enabled or disabled) shall be preserved by the device across power cycles. +A value of zero written by the host into the device’s Sector Count register before issuing this command shall +cause this feature to be disabled. Disabling this feature does not preclude the device from saving SMART +data to non-volatile memory during some other normal operation such as during a power-on or power-off +sequence or during an error recovery sequence. +A value of F1h written by the host into the device’s Sector Count register before issuing this command shall +cause this feature to be enabled. Any other meaning of this value or any other non-zero value written by the +host into this register before issuing this command may differ from device to device. The meaning of any +non-zero value written to this register at this time shall be preserved by the device across power cycles. + + + diff --git a/gsmartcontrol/autoconf.m4/ac_cxx_exceptions.m4 b/autoconf.m4/ac_cxx_exceptions.m4 similarity index 100% rename from gsmartcontrol/autoconf.m4/ac_cxx_exceptions.m4 rename to autoconf.m4/ac_cxx_exceptions.m4 diff --git a/gsmartcontrol/autoconf.m4/ac_cxx_namespaces.m4 b/autoconf.m4/ac_cxx_namespaces.m4 similarity index 100% rename from gsmartcontrol/autoconf.m4/ac_cxx_namespaces.m4 rename to autoconf.m4/ac_cxx_namespaces.m4 diff --git a/gsmartcontrol/autoconf.m4/ac_cxx_rtti.m4 b/autoconf.m4/ac_cxx_rtti.m4 similarity index 100% rename from gsmartcontrol/autoconf.m4/ac_cxx_rtti.m4 rename to autoconf.m4/ac_cxx_rtti.m4 diff --git a/gsmartcontrol/autoconf.m4/ac_cxx_verbose_terminate_handler.m4 b/autoconf.m4/ac_cxx_verbose_terminate_handler.m4 similarity index 100% rename from gsmartcontrol/autoconf.m4/ac_cxx_verbose_terminate_handler.m4 rename to autoconf.m4/ac_cxx_verbose_terminate_handler.m4 diff --git a/gsmartcontrol/autoconf.m4/app_auto_clear_flags.m4 b/autoconf.m4/app_auto_clear_flags.m4 similarity index 100% rename from gsmartcontrol/autoconf.m4/app_auto_clear_flags.m4 rename to autoconf.m4/app_auto_clear_flags.m4 diff --git a/autoconf.m4/app_choose_gld_reader.m4 b/autoconf.m4/app_choose_gld_reader.m4 new file mode 100644 index 0000000..1eddda6 --- /dev/null +++ b/autoconf.m4/app_choose_gld_reader.m4 @@ -0,0 +1,157 @@ + +############################################################################ +# Copyright: +# (C) 2008 - 2009 Alexander Shaduri +# License: See LICENSE_zlib.txt file +############################################################################ + + +# APP_CHOOSE_GLD_READER([C|C++][, flags_prefix]) +# This macro detects whether GtkBuilder (from gtk >= 2.12) or libglade +# are present. If the first parameter is C++, then gtkmm and libglademm +# are searched for instead. +# flags_prefixCFLAGS, flags_prefixCXXFLAGS and flags_prefixLIBS +# will be initialized (or appended to) with the correct flags in case of libglade(mm). +# app_cv_gld_reader will be set to none, libglade or gtkbuilder. + +# ENABLE_LIBGLADE or ENABLE_GTKBUILDER will be exported to config.h + +# You must call AX_COMPILER_VENDOR before using this macro. + +AC_DEFUN([APP_CHOOSE_GLD_READER], [ + # These requirements check compile-time presence, not that they were called + AC_REQUIRE([AX_COMPILER_VENDOR]) + + AC_ARG_ENABLE(abort-if-no-glade-reader, AS_HELP_STRING([--disable-abort-if-no-glade-reader], + [disable abort if no glade file reader is found (default: configure aborts when glade file reader is not found)]), + [app_cv_abort_if_no_glade_reader=${enableval}], [app_cv_abort_if_no_glade_reader=yes]) + + AC_MSG_NOTICE([Abort if no glade file reader is found: $app_cv_abort_if_no_glade_reader]) + + # This is just in case + # PKG_PROG_PKG_CONFIG() + + # this variable will have values "none", "libglade" or "gtkbuilder". + app_cv_gld_reader="none"; + + # in case libglade(mm) is found, these will contain the flags. + app_cv_gld_reader_CFLAGS=""; + app_cv_gld_reader_CXXFLAGS=""; + app_cv_gld_reader_LIBS=""; + + app_cv_gld_reader_libglade_name="libglade"; + app_cv_gld_reader_libglade_pkg_name="libglade-2.0"; + app_cv_gld_reader_gtk_name="gtk"; + app_cv_gld_reader_gtk_pkg_name="gtk+-2.0"; + if test "x$1" = "xC++"; then + app_cv_gld_reader_libglade_name="libglademm"; + app_cv_gld_reader_libglade_pkg_name="libglademm-2.4"; + app_cv_gld_reader_gtk_name="gtkmm"; + app_cv_gld_reader_gtk_pkg_name="gtkmm-2.4"; + fi + + + # Manual control. AC_ARG_ENABLE doesn't get bash variables, so we have + # to do it this way. If put inside a shell conditional, it still shows up in configure help. + + AC_ARG_ENABLE(libglade, AS_HELP_STRING([--enable-libglade], + [use libglade(mm) instead of GtkBuilder (default: auto)]), + [with_libglade=${enableval}], [with_libglade=auto]) + + + # Try gtkbuilder first + if test "x$with_libglade" = "xno" || test "x$with_libglade" = "xauto"; then + PKG_CHECK_EXISTS([$app_cv_gld_reader_gtk_pkg_name >= 2.12.0], + [app_cv_gld_reader="gtkbuilder"]) + fi + if test "$with_libglade" = "no" && test "$app_cv_gld_reader" = "none"; then + AC_MSG_ERROR([GtkBuilder support not found (you need $app_cv_gld_reader_gtk_name version 2.12 or higher). Try using $app_cv_gld_reader_libglade_name instead.]) + fi + + # libglade specified, or auto and no gtkbuilder. gtkmm2.4-compatible libglademm is >= 2.4.0. + # (not sure about libglade version). + if test "x$app_cv_gld_reader" = "xnone"; then + if test "x$app_cv_gld_reader_libglade_name" = "xlibglade"; then + # libglade + PKG_CHECK_MODULES([LIBGLADE], [$app_cv_gld_reader_libglade_pkg_name >= 2.4.0], + [app_cv_gld_reader="libglade"], [AC_MSG_WARN([$LIBGLADE_PKG_ERRORS])]) + app_cv_gld_reader_CFLAGS="$LIBGLADE_CFLAGS"; + app_cv_gld_reader_CXXFLAGS="$LIBGLADE_CFLAGS"; + app_cv_gld_reader_LIBS="$LIBGLADE_LIBS"; + else + # libglademm + PKG_CHECK_MODULES([LIBGLADEMM], [$app_cv_gld_reader_libglade_pkg_name >= 2.4.0], + [app_cv_gld_reader="libglade"], [AC_MSG_WARN([$LIBGLADEMM_PKG_ERRORS])]) + app_cv_gld_reader_CFLAGS="" + app_cv_gld_reader_CXXFLAGS="$LIBGLADEMM_CFLAGS"; + app_cv_gld_reader_LIBS="$LIBGLADEMM_LIBS"; + fi + # reset them so they don't interfere later + LIBGLADE_CFLAGS=""; + LIBGLADE_LIBS=""; + fi + + if test "x$with_libglade" = "xyes" && test "x$app_cv_gld_reader" = "xnone"; then + AC_MSG_ERROR([$app_cv_gld_reader_libglade_name not found. Try building with GtkBuilder support (you need $app_cv_gld_reader_gtk_name version 2.12 or higher).]) + fi + + # still not found. + if test "$app_cv_gld_reader" = "none"; then + if test "x$app_cv_abort_if_no_glade_reader" = "xno"; then + AC_MSG_WARN([$LIBGLADE_PKG_ERRORS]) + AC_MSG_WARN([Neither GtkBuilder nor $app_cv_gld_reader_libglade_name found. The program will be unable to compile fully.]) + else + AC_MSG_WARN([Neither GtkBuilder nor $app_cv_gld_reader_libglade_name found.]) + AC_MSG_ERROR([$LIBGLADE_PKG_ERRORS]) + fi + fi + + + # export the flags + if test "x$2" != "x"; then + if test "x$$2[]CFLAGS" = "x"; then + $2[]CFLAGS="$app_cv_gld_reader_CFLAGS"; + else + $2[]CFLAGS="$$2[]CFLAGS $app_cv_gld_reader_CFLAGS"; + fi + if test "x$$2[]CXXFLAGS" = "x"; then + $2[]CXXFLAGS="$app_cv_gld_reader_CXXFLAGS"; + else + $2[]CXXFLAGS="$$2[]CXXFLAGS $app_cv_gld_reader_CXXFLAGS"; + fi + if test "x$$2[]LIBS" = "x"; then + $2[]LIBS="$app_cv_gld_reader_LIBS"; + else + $2[]LIBS="$$2[]LIBS $app_cv_gld_reader_LIBS"; + fi + fi + + + # export to automake.am-s + AM_CONDITIONAL([ENABLE_LIBGLADE], [test "$app_cv_gld_reader" = "libglade"]) + AM_CONDITIONAL([ENABLE_GTKBUILDER], [test "$app_cv_gld_reader" = "gtkbuilder"]) + + + # export to config.h + if test "$app_cv_gld_reader" = "libglade"; then + AC_DEFINE(ENABLE_LIBGLADE, 1, [Use libglade(mm) instead of GtkBuilder]) + else + AC_DEFINE(ENABLE_LIBGLADE, 0, [Use libglade(mm) instead of GtkBuilder]) + fi + if test "$app_cv_gld_reader" = "gtkbuilder"; then + AC_DEFINE(ENABLE_GTKBUILDER, 1, [Use GtkBuilder instead of libglademm]) + else + AC_DEFINE(ENABLE_GTKBUILDER, 1, [Use GtkBuilder instead of libglademm]) + fi + + # If we move this to the start, some messages get in between, so we have to have it here. + AC_MSG_CHECKING([for glade file support]) + + AC_MSG_RESULT([$app_cv_gld_reader]) + +]) + + + + + diff --git a/gsmartcontrol/autoconf.m4/app_compiler_options.m4 b/autoconf.m4/app_compiler_options.m4 similarity index 93% rename from gsmartcontrol/autoconf.m4/app_compiler_options.m4 rename to autoconf.m4/app_compiler_options.m4 index 2337ab8..8ad0a00 100644 --- a/gsmartcontrol/autoconf.m4/app_compiler_options.m4 +++ b/autoconf.m4/app_compiler_options.m4 @@ -31,7 +31,7 @@ AC_DEFUN([APP_COMPILER_OPTIONS], [ 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]) + [app_cv_compiler_common_options=${enableval}], [app_cv_compiler_common_options=auto]) # 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 @@ -54,14 +54,14 @@ AC_DEFUN([APP_COMPILER_OPTIONS], [ || 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. + # -mms-bitfields - make structures compatible with msvc. recommended default for non-cygwin. 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 \ +-Wctor-dtor-privacy -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 @@ -183,23 +183,10 @@ AC_DEFUN([APP_COMPILER_OPTIONS], [ 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"; + app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -g0 -O3 -s -mtune=generic"; + app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -g0 -O3 -s -mtune=generic"; + app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -g0 -O3 -s -mtune=generic"; 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 diff --git a/gsmartcontrol/autoconf.m4/app_cxx_extern_c_overload.m4 b/autoconf.m4/app_cxx_extern_c_overload.m4 similarity index 100% rename from gsmartcontrol/autoconf.m4/app_cxx_extern_c_overload.m4 rename to autoconf.m4/app_cxx_extern_c_overload.m4 diff --git a/gsmartcontrol/autoconf.m4/app_cxx_func.m4 b/autoconf.m4/app_cxx_func.m4 similarity index 100% rename from gsmartcontrol/autoconf.m4/app_cxx_func.m4 rename to autoconf.m4/app_cxx_func.m4 diff --git a/gsmartcontrol/autoconf.m4/app_detect_os.m4 b/autoconf.m4/app_detect_os.m4 similarity index 100% rename from gsmartcontrol/autoconf.m4/app_detect_os.m4 rename to autoconf.m4/app_detect_os.m4 diff --git a/gsmartcontrol/autoconf.m4/app_get_mt_flags.m4 b/autoconf.m4/app_get_mt_flags.m4 similarity index 100% rename from gsmartcontrol/autoconf.m4/app_get_mt_flags.m4 rename to autoconf.m4/app_get_mt_flags.m4 diff --git a/gsmartcontrol/autoconf.m4/app_use_system_extensions.m4 b/autoconf.m4/app_use_system_extensions.m4 similarity index 94% rename from gsmartcontrol/autoconf.m4/app_use_system_extensions.m4 rename to autoconf.m4/app_use_system_extensions.m4 index 06c6747..2ce353f 100644 --- a/gsmartcontrol/autoconf.m4/app_use_system_extensions.m4 +++ b/autoconf.m4/app_use_system_extensions.m4 @@ -89,7 +89,7 @@ AC_DEFUN([APP_USE_SYSTEM_EXTENSIONS], [ # See sys/featuretest.h ;; - mingw*) + mingw32) # 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, @@ -98,8 +98,14 @@ AC_DEFUN([APP_USE_SYSTEM_EXTENSIONS], [ # 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" + app_cv_target_extension_cflags="-D__USE_MINGW_ANSI_STDIO=1 -DWINVER=0x0500" + app_cv_target_extension_cxxflags="-D__USE_MINGW_ANSI_STDIO=1 -DWINVER=0x0500" + ;; + + mingw64) + # Same as ming32, but enable "from xp" mode, since there's no x86_64 2k. + app_cv_target_extension_cflags="-D__USE_MINGW_ANSI_STDIO=1 -DWINVER=0x0501" + app_cv_target_extension_cxxflags="-D__USE_MINGW_ANSI_STDIO=1 -DWINVER=0x0501" ;; interix) diff --git a/gsmartcontrol/autoconf.m4/ax_compiler_vendor.m4 b/autoconf.m4/ax_compiler_vendor.m4 similarity index 100% rename from gsmartcontrol/autoconf.m4/ax_compiler_vendor.m4 rename to autoconf.m4/ax_compiler_vendor.m4 diff --git a/gsmartcontrol/autoconf.m4/ax_cxx_gcc_abi_demangle.m4 b/autoconf.m4/ax_cxx_gcc_abi_demangle.m4 similarity index 100% rename from gsmartcontrol/autoconf.m4/ax_cxx_gcc_abi_demangle.m4 rename to autoconf.m4/ax_cxx_gcc_abi_demangle.m4 diff --git a/gsmartcontrol/autogen.sh b/autogen.sh similarity index 89% rename from gsmartcontrol/autogen.sh rename to autogen.sh index eb630b4..eabc95f 100755 --- a/gsmartcontrol/autogen.sh +++ b/autogen.sh @@ -16,7 +16,7 @@ # autoconf -autoreconf --verbose --install -W all --force +autoreconf --verbose --install -W all rm -f config.cache diff --git a/baqup b/baqup new file mode 100755 index 0000000..5863207 --- /dev/null +++ b/baqup @@ -0,0 +1,19 @@ +#!/bin/bash + +stamp="../gsmartcontrol-`date +%F`" +echo $stamp + +mkdir $stamp +cp -dp * $stamp/ + +rm -f $stamp/*pcs # kdevelop persistent store files + +cp -a autoconf.m4/ $stamp/ +cp -a data/ $stamp/ +cp -a src/ $stamp/ +cp -a templates/ $stamp/ +# cp -a test-data/ $stamp/ + +tar cvf $stamp.tar $stamp +bzip2 -9 $stamp.tar + diff --git a/gsmartcontrol/compilation_flags.in b/compilation_flags.in similarity index 100% rename from gsmartcontrol/compilation_flags.in rename to compilation_flags.in diff --git a/configure-dev b/configure-dev new file mode 100755 index 0000000..795da55 --- /dev/null +++ b/configure-dev @@ -0,0 +1,73 @@ +#!/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 "debug optimized icc clang win32 win32-debug win64-debug sun portland open64"; + echo " is ../configure by default" + exit 1; +fi + +if [ "$configure_script" == "" ]; then + configure_script="../configure"; +fi + + +flags=""; + +case $type in + optimized) + flags="--disable-user-flags --enable-optimize-options --enable-tests" ;; + debug) + flags="--enable-debug-options --enable-tests" ;; + clang) + flags="CC=clang CXX=clang++ --enable-debug-options --enable-tests" ;; + icc) + flags="CC=icc64 CXX=icpc64 --disable-user-flags --enable-tests" ;; + win32) + flags="CC=i386-pc-mingw32-gcc CXX=i386-pc-mingw32-g++ \ + PKG_CONFIG_PATH=/cross/w32/target/lib/pkgconfig PKG_CONFIG_LIBDIR=\"\" \ + PATH=\"/cross/w32/gcc-bin:/cross/w32/target/bin:$PATH\" \ + --with-windows-gtk-name=gtk2-runtime-2.24.10-2012-10-10-ash --disable-user-flags \ + --with-nsis=/root/.wine/drive_c/Programs_x86/NSIS/makensis.exe \ + --enable-optimize-options --enable-tests --build=i686-linux --host=i386-pc-mingw32" ;; + win32-debug) + flags="CC=i386-pc-mingw32-gcc CXX=i386-pc-mingw32-g++ \ + PKG_CONFIG_PATH=/cross/w32/target/lib/pkgconfig PKG_CONFIG_LIBDIR=\"\" \ + PATH=\"/cross/w32/gcc-bin:/cross/w32/target/bin:$PATH\" \ + --disable-user-flags --enable-debug-options --enable-tests --build=i686-linux --host=i386-pc-mingw32" ;; + win64-debug) + flags="CC=x86_64-pc-mingw32-gcc CXX=x86_64-pc-mingw32-g++ + PKG_CONFIG_PATH=/cross/w64/target/lib/pkgconfig \ + PKG_CONFIG_LIBDIR=\"\" PATH=\"/cross/w64/gcc-bin:/cross/w64/target/bin:$PATH\" \ + --disable-user-flags --enable-optimize-options --enable-tests --build=i686-linux \ + --host=x86_64-pc-mingw32" ;; + sun) + flags="CC=suncc CXX=sunCC 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" ;; + portland) + flags="CC=pgcc CXX=pgCC LDFLAGS=\"-s\" CFLAGS=\"-Minform=warn -Bstatic_pgi\" \ + CXXFLAGS=\"-Minform=warn -Bstatic_pgi -noswitcherror -define_macro=HZ_NO_COMPILER_AUTOINCLUDE\" \ + --enable-tests" ;; + + open64) + flags="CC=/opt/open64/bin/opencc CXX=/opt/open64/bin/openCC CXXFLAGS=\"-Wall\" \ + --enable-tests" ;; +esac + + +echo "Running:" +echo $configure_script $flags; + +$configure_script $flags + + +exit $? diff --git a/gsmartcontrol/configure.ac b/configure.ac similarity index 73% rename from gsmartcontrol/configure.ac rename to configure.ac index 5a10f97..e152afe 100644 --- a/gsmartcontrol/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ -AC_INIT([GSmartControl],[1.1.3],[],[gsmartcontrol]) +AC_INIT([GSmartControl],[0.9.0],[],[gsmartcontrol]) AC_CONFIG_SRCDIR([configure.ac]) AC_CONFIG_HEADERS([config.h]) @@ -22,7 +22,6 @@ AC_PROG_CXX #AC_PROG_CXXCPP #AM_PROG_LIBTOOL -AM_PROG_AR # C++ support in checks #AC_LANG([C]) @@ -64,10 +63,6 @@ APP_DETECT_OS_KERNEL([build], [CONFIG_HOST_KERNEL_]) # 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 @@ -132,6 +127,9 @@ APP_CXX_EXTERN_C_OVERLOAD # This may be needed for pch support AC_CHECK_HEADERS([stdc++.h]) +# pcrecpp needs this (break on first function found) +AC_CHECK_FUNCS([strtoll _strtoi64], [break]) + # 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, @@ -177,7 +175,8 @@ AC_ARG_ENABLE(abort-if-no-gtkmm, AS_HELP_STRING([--disable-abort-if-no-gtkmm], AC_MSG_NOTICE([Abort if no gtkmm is found: $app_cv_abort_if_no_gtkmm]) -pkg_modules="gtkmm-3.0 >= 3.4.0" +# iconview is available since 2.6.0. gthread is not enabled by default. +pkg_modules="gtkmm-2.4 >= 2.6.0" # gthread-2.0 >= 2.0 # define ENABLE_GLIB and friends if it's found. gtkmm_found="no" @@ -202,38 +201,67 @@ if test "x$gtkmm_found" = "xno"; then fi fi +# GTK flags must be prepended, or glib_config.h conflict between +# gtk 2.x and 1.2 may occur. + #CFLAGS="$GTKMM_CFLAGS $CFLAGS" CXXFLAGS="$GTKMM_CFLAGS $CXXFLAGS" LIBS="$GTKMM_LIBS $LIBS" +# Detect libglade / gtkbuilder support + +APP_CHOOSE_GLD_READER([C++], [LIBGLADE_]) + +if test "$app_cv_gld_reader" = "libglade"; then + CFLAGS="$CFLAGS $LIBGLADE_CFLAGS" + CXXFLAGS="$CXXFLAGS $LIBGLADE_CXXFLAGS" + LIBS="$LIBS $LIBGLADE_LIBS" +fi + # ------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------- -PCRECPP_CFLAGS=""; -PCRECPP_LIBS=""; +PCRE_CFLAGS=""; +PCRE_LIBS=""; # try pkg-config first -PKG_CHECK_MODULES(PCRECPP, [libpcrecpp], [pcrecpp_found=yes], [pcrecpp_found=no]) +PKG_CHECK_MODULES(PCRE, [libpcre], [pcre_found=yes], [pcre_found=no]) # if not found, try pcre-config (solaris had it but not pkg-config). -if test "x$pcrecpp_found" = "xno"; then +if test "x$pcre_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`" + PCRE_LIBS="`pcre-config --libs`" 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]) + # AC_MSG_ERROR([Could not find pcre through pkg-config and pcre-config. Make sure the pcre libraries are installed]) + AC_MSG_ERROR([$PCRE_PKG_ERRORS]) fi fi -CFLAGS="$CFLAGS $PCRECPP_CFLAGS" -CXXFLAGS="$CXXFLAGS $PCRECPP_CFLAGS" -LIBS="$LIBS $PCRECPP_LIBS" +CFLAGS="$CFLAGS $PCRE_CFLAGS" +CXXFLAGS="$CXXFLAGS $PCRE_CFLAGS" +LIBS="$LIBS $PCRE_LIBS" + + +# ------------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------------- + + +# Test stuff here... +# + +#SDL_VERSION=1.2.0 +#AM_PATH_SDL($SDL_VERSION, :, +# AC_MSG_ERROR([*** SDL version $SDL_VERSION not found!]) ) +#CFLAGS="$CFLAGS $SDL_CFLAGS" +#CXXFLAGS="$CXXFLAGS $SDL_CFLAGS " +#LIBS="$LIBS $SDL_LIBS" # ------------------------------------------------------------------------------------- @@ -249,13 +277,17 @@ AC_ARG_WITH([nsis], [AS_HELP_STRING([--with-nsis=|auto], ["~/.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_ENABLE([nsis-wine], [AS_HELP_STRING([--enable-nsis-wine=yes|no|auto], + [use wine to run makensis.exe when making NSIS packages (default: auto, which is no for windows, yes for others)])], + [with_nsis_wine=${enableval}], [with_nsis_wine=yes]) -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]) +AC_ARG_WITH([windows-bindir], [AS_HELP_STRING([--with-windows-bindir], + [specify location of Windows dlls and executables when making Windows packages (default: /cross/w32/target/bin)])], + [with_windows_bindir=${withval}], [with_windows_bindir=auto]) + +AC_ARG_WITH([windows-gtk-name], [AS_HELP_STRING([--with-windows-gtk-name], + [specify name of Windows GTK+ runtime (installer and unpacked directory) when making Windows packages. It will be looked in windows-bindir.])], + [with_windows_gtk_name=${withval}], [with_windows_gtk_name=]) if test "x$with_nsis" = "xauto" || test "x$with_nsis" = "x"; then # if building on windows, use native paths @@ -266,26 +298,30 @@ if test "x$with_nsis" = "xauto" || test "x$with_nsis" = "x"; then fi fi +if test "x$with_nsis_wine" = "xauto"; then + # if not building on windows, use wine + if test "x$app_cv_build_os_kernel" = "xwindows32" || test "x$app_cv_build_os_kernel" = "xwindows64"; then + with_nsis_wine="no"; + else + with_nsis_wine="yes"; + fi +fi + NSIS_EXEC="$with_nsis"; -if test "x$with_nsis_wine" = "xyes"; then +if test "x$with_nsis_wine" != "xno"; 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" +if test "x$with_windows_bindir" = "xauto" || test "x$with_windows_bindir" = "x"; then + with_windows_bindir="/cross/w32/target/bin" fi -WINDOWS_SYSROOT="$with_windows_sysroot"; +WINDOWS_BINDIR="$with_windows_bindir"; -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 +GTK_INSTALLER_NAME="$with_windows_gtk_name"; AC_SUBST(NSIS_EXEC) -AC_SUBST(WINDOWS_SYSROOT) -AC_SUBST(WINDOWS_SUFFIX) +AC_SUBST(WINDOWS_BINDIR) +AC_SUBST(GTK_INSTALLER_NAME) # ------------------------------------------------------------------------------------- @@ -310,20 +346,26 @@ 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" +RES_FILES="src/res/gsc_about_dialog.glade \ + src/res/gsc_add_device_window.glade \ + src/res/gsc_executor_log_window.glade \ + src/res/gsc_help_window.glade \ + src/res/gsc_info_window.glade \ + src/res/gsc_main_window.glade \ + src/res/gsc_preferences_window.glade \ + src/res/gsc_text_window.glade \ + AUTHORS.txt LICENSE_gsmartcontrol.txt README.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 +RES_GLD_EXT="glade" +if test "$app_cv_gld_reader" = "gtkbuilder"; then + RES_GLD_EXT="ui" +fi + 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" @@ -339,14 +381,28 @@ do # on separate line for portability 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 + # We use separate source files for gtkbuilder and libglademm, because + # make dist should be able to bundle both of them separately. + + if test "$RES_BASE_EXT" = "glade"; then + + RES_LIBADD="$RES_LIBADD $RES_BASE_NOEXT.$RES_GLD_EXT.$ac_objext" # file.ui.o or file.glade.o + + # Output both glade and ui rules. This way make dist will be able + # to build cpp files of both of them. + 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 + gtk-builder-convert \"$RES_FILE_CANON\" \"$RES_BASE_NOEXT\".tmp_ui + \"\$(top_srcdir)/file2csource.sh\" \"$RES_BASE_NOEXT\".tmp_ui \"$RES_BASE_NOEXT.ui.cpp\" \"$RES_BASE_NOEXT\"_ui +" + RES_TARGETS="$RES_TARGETS +$RES_BASE_NOEXT.glade.cpp: $RES_FILE_CANON + \"\$(top_srcdir)/file2csource.sh\" \"$RES_FILE_CANON\" \"$RES_BASE_NOEXT.glade.cpp\" \"$RES_BASE_NOEXT\"_ui " else # txt, etc... + SRC_FILE="$RES_BASE.cpp" RES_LIBADD="$RES_LIBADD $RES_BASE.$ac_objext" # file.txt.o @@ -389,20 +445,21 @@ CXXFLAGS="$CXXFLAGS $ADDITIONAL_FLAGS" # 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" +# Add pcrecpp too, because its files use # on each other. +ADDITIONAL_INCLUDES="-I\$(top_builddir) -I\$(top_srcdir)/src -I\$(top_srcdir)/src/pcrecpp" # -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 +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" @@ -453,8 +510,6 @@ AC_SUBST(LDFLAGS) # print them out for debug purposes AC_MSG_NOTICE([ -CXX: -$CXX CXXFLAGS: $CXXFLAGS LDFLAGS: @@ -475,7 +530,7 @@ AC_SUBST(AUTODIRS) AC_CONFIG_FILES([ data/gsmartcontrol.desktop data/gsmartcontrol.appdata.xml \ - data/nsis/distribution.txt data/nsis/gsmartcontrol.nsi \ + data/nsis/distribution.txt data/nsis/distribution-nogtk.txt data/nsis/gsmartcontrol.nsi \ debian.dist/changelog \ src/gsc_winres.rc src/gsmartcontrol.exe.manifest \ gsmartcontrol.spec version compilation_flags ]) @@ -487,7 +542,7 @@ 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 \ + src/libdebug/Makefile src/pcrecpp/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]) diff --git a/gsmartcontrol/contrib/cron-based_noadmin/README b/contrib/cron-based_noadmin/README similarity index 100% rename from gsmartcontrol/contrib/cron-based_noadmin/README rename to contrib/cron-based_noadmin/README diff --git a/gsmartcontrol/contrib/cron-based_noadmin/cron_gather_smart.sh b/contrib/cron-based_noadmin/cron_gather_smart.sh similarity index 100% rename from gsmartcontrol/contrib/cron-based_noadmin/cron_gather_smart.sh rename to contrib/cron-based_noadmin/cron_gather_smart.sh diff --git a/gsmartcontrol/contrib/cron-based_noadmin/crontab.example b/contrib/cron-based_noadmin/crontab.example similarity index 100% rename from gsmartcontrol/contrib/cron-based_noadmin/crontab.example rename to contrib/cron-based_noadmin/crontab.example diff --git a/gsmartcontrol/contrib/cron-based_noadmin/smartctl_subst.sh b/contrib/cron-based_noadmin/smartctl_subst.sh similarity index 100% rename from gsmartcontrol/contrib/cron-based_noadmin/smartctl_subst.sh rename to contrib/cron-based_noadmin/smartctl_subst.sh diff --git a/gsmartcontrol/data/128/Makefile.am b/data/128/Makefile.am similarity index 100% rename from gsmartcontrol/data/128/Makefile.am rename to data/128/Makefile.am diff --git a/gsmartcontrol/data/128/gsmartcontrol.png b/data/128/gsmartcontrol.png similarity index 100% rename from gsmartcontrol/data/128/gsmartcontrol.png rename to data/128/gsmartcontrol.png diff --git a/gsmartcontrol/data/16/Makefile.am b/data/16/Makefile.am similarity index 100% rename from gsmartcontrol/data/16/Makefile.am rename to data/16/Makefile.am diff --git a/gsmartcontrol/data/16/gsmartcontrol.png b/data/16/gsmartcontrol.png similarity index 100% rename from gsmartcontrol/data/16/gsmartcontrol.png rename to data/16/gsmartcontrol.png diff --git a/gsmartcontrol/data/22/Makefile.am b/data/22/Makefile.am similarity index 100% rename from gsmartcontrol/data/22/Makefile.am rename to data/22/Makefile.am diff --git a/gsmartcontrol/data/22/gsmartcontrol.png b/data/22/gsmartcontrol.png similarity index 100% rename from gsmartcontrol/data/22/gsmartcontrol.png rename to data/22/gsmartcontrol.png diff --git a/gsmartcontrol/data/24/Makefile.am b/data/24/Makefile.am similarity index 100% rename from gsmartcontrol/data/24/Makefile.am rename to data/24/Makefile.am diff --git a/gsmartcontrol/data/24/gsmartcontrol.png b/data/24/gsmartcontrol.png similarity index 100% rename from gsmartcontrol/data/24/gsmartcontrol.png rename to data/24/gsmartcontrol.png diff --git a/gsmartcontrol/data/256/Makefile.am b/data/256/Makefile.am similarity index 100% rename from gsmartcontrol/data/256/Makefile.am rename to data/256/Makefile.am diff --git a/gsmartcontrol/data/256/gsmartcontrol.png b/data/256/gsmartcontrol.png similarity index 100% rename from gsmartcontrol/data/256/gsmartcontrol.png rename to data/256/gsmartcontrol.png diff --git a/gsmartcontrol/data/32/Makefile.am b/data/32/Makefile.am similarity index 100% rename from gsmartcontrol/data/32/Makefile.am rename to data/32/Makefile.am diff --git a/gsmartcontrol/data/32/gsmartcontrol.png b/data/32/gsmartcontrol.png similarity index 100% rename from gsmartcontrol/data/32/gsmartcontrol.png rename to data/32/gsmartcontrol.png diff --git a/gsmartcontrol/data/48/Makefile.am b/data/48/Makefile.am similarity index 100% rename from gsmartcontrol/data/48/Makefile.am rename to data/48/Makefile.am diff --git a/gsmartcontrol/data/48/gsmartcontrol.png b/data/48/gsmartcontrol.png similarity index 100% rename from gsmartcontrol/data/48/gsmartcontrol.png rename to data/48/gsmartcontrol.png diff --git a/gsmartcontrol/data/64/Makefile.am b/data/64/Makefile.am similarity index 100% rename from gsmartcontrol/data/64/Makefile.am rename to data/64/Makefile.am diff --git a/gsmartcontrol/data/64/gsmartcontrol.png b/data/64/gsmartcontrol.png similarity index 100% rename from gsmartcontrol/data/64/gsmartcontrol.png rename to data/64/gsmartcontrol.png diff --git a/gsmartcontrol/data/Makefile.am b/data/Makefile.am similarity index 84% rename from gsmartcontrol/data/Makefile.am rename to data/Makefile.am index 7495112..5fa727f 100644 --- a/gsmartcontrol/data/Makefile.am +++ b/data/Makefile.am @@ -13,7 +13,7 @@ uninstall-extra-mans: # 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 +gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor install-data-hook: install-extra-mans update-icon-cache uninstall-hook: uninstall-extra-mans update-icon-cache @@ -44,10 +44,6 @@ desktop_DATA = gsmartcontrol.desktop 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. @@ -57,7 +53,7 @@ pixmaps_DATA = 48/gsmartcontrol.png gsmartcontrol.xpm # Put everything inside the distribution -EXTRA_DIST = $(appdata_DATA) $(polkit_DATA) gsmartcontrol.ico gsmartcontrol.xpm +EXTRA_DIST = $(appdata_DATA) gsmartcontrol.ico gsmartcontrol.xpm bin_SCRIPTS = gsmartcontrol-root diff --git a/gsmartcontrol/data/gsmartcontrol-root.in b/data/gsmartcontrol-root.in similarity index 87% rename from gsmartcontrol/data/gsmartcontrol-root.in rename to data/gsmartcontrol-root.in index 50fd643..969bcd2 100644 --- a/gsmartcontrol/data/gsmartcontrol-root.in +++ b/data/gsmartcontrol-root.in @@ -8,7 +8,7 @@ # 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"; +EXEC_BIN="@prefix@/bin/gsmartcontrol"; prog_name="gsmartcontrol" @@ -71,12 +71,11 @@ fi # 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"; +gnome_sus="su-to-root gnomesu gksu kdesu beesu xdg-su sux"; +kde_sus="su-to-root kdesu gnomesu gksu beesu xdg-su sux"; other_sus="$gnome_sus"; @@ -102,7 +101,7 @@ if [ "$GSMARTCONTROL_SU" = "" ]; then if [ "$found_su" = "" ]; then xmessage "Error launching ${prog_name}: No suitable su mechanism found. -Try installing PolKit, kdesu, gnomesu, gksu, beesu or sux first."; +Try installing kdesu, gnomesu, gksu, beesu or sux first."; exit 1; fi fi @@ -137,15 +136,6 @@ 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'"; diff --git a/gsmartcontrol/data/gsmartcontrol.1 b/data/gsmartcontrol.1 similarity index 100% rename from gsmartcontrol/data/gsmartcontrol.1 rename to data/gsmartcontrol.1 diff --git a/gsmartcontrol/data/gsmartcontrol.appdata.xml.in b/data/gsmartcontrol.appdata.xml.in similarity index 85% rename from gsmartcontrol/data/gsmartcontrol.appdata.xml.in rename to data/gsmartcontrol.appdata.xml.in index ec09b03..b0e8b88 100644 --- a/gsmartcontrol/data/gsmartcontrol.appdata.xml.in +++ b/data/gsmartcontrol.appdata.xml.in @@ -16,19 +16,19 @@

gsmartcontrol.desktop - https://gsmartcontrol.sourceforge.io + http://gsmartcontrol.sourceforge.net Main window - https://gsmartcontrol.sourceforge.io/home/images/main_ok.png + http://gsmartcontrol.sourceforge.net/home/images/main_ok.png Drive identity information - https://gsmartcontrol.sourceforge.io/home/images/info_identity.png + http://gsmartcontrol.sourceforge.net/home/images/info_identity.png Attribute list of a failing drive - https://gsmartcontrol.sourceforge.io/home/images/info_failing.png + http://gsmartcontrol.sourceforge.net/home/images/info_failing.png diff --git a/gsmartcontrol/data/gsmartcontrol.desktop.in b/data/gsmartcontrol.desktop.in similarity index 83% rename from gsmartcontrol/data/gsmartcontrol.desktop.in rename to data/gsmartcontrol.desktop.in index 0e13f51..bcafcd3 100644 --- a/gsmartcontrol/data/gsmartcontrol.desktop.in +++ b/data/gsmartcontrol.desktop.in @@ -9,11 +9,8 @@ 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. diff --git a/gsmartcontrol/data/gsmartcontrol.ico b/data/gsmartcontrol.ico similarity index 100% rename from gsmartcontrol/data/gsmartcontrol.ico rename to data/gsmartcontrol.ico diff --git a/gsmartcontrol/data/gsmartcontrol.xpm b/data/gsmartcontrol.xpm similarity index 100% rename from gsmartcontrol/data/gsmartcontrol.xpm rename to data/gsmartcontrol.xpm diff --git a/data/icon_cddvd.png b/data/icon_cddvd.png new file mode 100644 index 0000000..4ecbd37 Binary files /dev/null and b/data/icon_cddvd.png differ diff --git a/data/icon_hdd.png b/data/icon_hdd.png new file mode 100644 index 0000000..1ca0a45 Binary files /dev/null and b/data/icon_hdd.png differ diff --git a/gsmartcontrol/data/nsis/Makefile.am b/data/nsis/Makefile.am similarity index 100% rename from gsmartcontrol/data/nsis/Makefile.am rename to data/nsis/Makefile.am diff --git a/data/nsis/distribution-nogtk.txt.in b/data/nsis/distribution-nogtk.txt.in new file mode 100644 index 0000000..bd6a9c2 --- /dev/null +++ b/data/nsis/distribution-nogtk.txt.in @@ -0,0 +1,29 @@ + +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 +http://gsmartcontrol.sourceforge.net +and +https://sourceforge.net/projects/gsmartcontrol/files/ + +You need GTK+ for Windows Runtime package to use this program. You can get one +at http://gtk-win.sf.net (you must download at least version 2.16.6-2010-02-21 +of gtk2-runtime). + + +This distribution includes: + +* GSmartControl binary (gsmartcontrol.exe) and associated resources. The +binary was compiled for 32-bit Windows, and should work on both 32-bit and +64-bit Windows. The binary was statically compiled against Gtkmm +(http://www.gtkmm.org/) and related libraries, and PCRE (http://www.pcre.org/). + +* Smartctl binaries (from smartmontools 6.5, 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. diff --git a/gsmartcontrol/data/nsis/distribution.txt.in b/data/nsis/distribution.txt.in similarity index 58% rename from gsmartcontrol/data/nsis/distribution.txt.in rename to data/nsis/distribution.txt.in index 9d8d954..433a1be 100644 --- a/gsmartcontrol/data/nsis/distribution.txt.in +++ b/data/nsis/distribution.txt.in @@ -4,21 +4,25 @@ Version @VERSION@ The latest version of this distribution, as well as the source code of GSmartControl, can be found at -https://gsmartcontrol.sourceforge.io +http://gsmartcontrol.sourceforge.net 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/). +* GSmartControl binary (gsmartcontrol.exe) and associated resources. The +binary was compiled for 32-bit Windows, and should work on both 32-bit and +64-bit Windows. The binary was statically compiled against Gtkmm +(http://www.gtkmm.org/) and related libraries, and PCRE (http://www.pcre.org/). -* Smartctl binaries (from smartmontools 6.6, http://smartmontools.sf.net/) +* Smartctl binaries (from smartmontools 6.5, http://smartmontools.sf.net/) - smartctl-nc.exe (compiled without console output), smartctl.exe (compiled with console output), update-smart-drivedb.exe (drive database updater). +* GTK+ for Windows Runtime package (@GTK_INSTALLER_NAME@) +from http://gtk-win.sf.net, either in original or unpacked form. + The combined distribution is multi-licensed under GNU GPL versions 2 and 3. See LICENSE_gpl2.txt and LICENSE_gpl3.txt for details. diff --git a/gsmartcontrol/data/nsis/gsmartcontrol.nsi.in b/data/nsis/gsmartcontrol.nsi.in similarity index 52% rename from gsmartcontrol/data/nsis/gsmartcontrol.nsi.in rename to data/nsis/gsmartcontrol.nsi.in index 28d18b4..e7dc7c3 100644 --- a/gsmartcontrol/data/nsis/gsmartcontrol.nsi.in +++ b/data/nsis/gsmartcontrol.nsi.in @@ -4,16 +4,23 @@ ; Compatible with NSIS Unicode 2.45. ; Public Domain +; pass /DNO_GTK to makensis to disable inclusion of gtk. + +; gtk installer name for embedding +!define GTK_INSTALLER_EXE "@GTK_INSTALLER_NAME@.exe" + !define PRODUCT_VERSION "@VERSION@" !define PRODUCT_NAME "GSmartControl" !define PRODUCT_NAME_SMALL "gsmartcontrol" !define PRODUCT_PUBLISHER "Alexander Shaduri" -!define PRODUCT_WEB_SITE "https://gsmartcontrol.sourceforge.io" +!define PRODUCT_WEB_SITE "http://gsmartcontrol.sourceforge.net" ;!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" +!define REGISTRY_APP_PATHS "Software\Microsoft\Windows\CurrentVersion\App Paths" + !include "FileFunc.nsh" ; GetOptions @@ -35,12 +42,16 @@ CRCCheck On Name "${PRODUCT_NAME}" ; ${PRODUCT_VERSION} ; Output File Name -OutFile "${PRODUCT_NAME_SMALL}-${PRODUCT_VERSION}-@WINDOWS_SUFFIX@.exe" +!ifdef NO_GTK + OutFile "${PRODUCT_NAME_SMALL}-${PRODUCT_VERSION}-nogtk.exe" +!else + OutFile "${PRODUCT_NAME_SMALL}-${PRODUCT_VERSION}.exe" +!endif ; The Default Installation Directory: ; Try to install to the same directory as runtime. -InstallDir "$PROGRAMFILES64\${PRODUCT_NAME}" +InstallDir "$PROGRAMFILES\${PRODUCT_NAME}" ; If already installed, try here InstallDirRegKey HKLM "SOFTWARE\${PRODUCT_NAME}" "InstallationDirectory" @@ -78,11 +89,19 @@ ShowUnInstDetails show ;!define MUI_LANGDLL_REGISTRY_KEY "${PRODUCT_UNINST_KEY}" ;!define MUI_LANGDLL_REGISTRY_VALUENAME "NSIS:Language" -!define LICENSE_FILE "doc\distribution.txt" +!ifdef NO_GTK + !define LICENSE_FILE "doc\distribution-nogtk.txt" +!else + !define LICENSE_FILE "doc\distribution.txt" +!endif ; Pages to show during installation !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_LICENSE "${LICENSE_FILE}" +!ifndef NO_GTK + !define MUI_PAGE_CUSTOMFUNCTION_LEAVE on_components_page_leave + !insertmacro MUI_PAGE_COMPONENTS +!endif !insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_INSTFILES @@ -132,19 +151,21 @@ SectionIn 1 RO ; File "gsmartcontrol.conf" SetOverwrite On - File *.exe - File *.dll + File gsmartcontrol.exe File gsmartcontrol.exe.manifest File gsmartcontrol.ico File icon_*.png - File drivedb.h + ; File *.dll + File smartctl-nc.exe + File smartctl.exe + File update-smart-drivedb.exe - ; Include the "doc" directory completely. - File /r doc - - ; GTK and stuff - File /r etc - File /r share + ; Include the "doc" directory completely, excluding the file we're not using (e.g. the -nogtk file in gtk installer). +!ifdef NO_GTK + File /r /x distribution.txt doc +!else + File /r /x distribution-nogtk.txt doc +!endif ; Add Shortcuts (this inherits the exe's run permissions) CreateShortCut "$SMPROGRAMS\GSmartControl.lnk" "$INSTDIR\gsmartcontrol.exe" "" \ @@ -154,6 +175,42 @@ SectionEnd +!ifndef NO_GTK + +; The SecGtkPrivate and SecGtkPublic sections are mutually exclusive. + +Section "GTK+ (for this program only)" SecGtkPrivate + SectionIn 1 + SetShellVarContext all ; use all user variables as opposed to current user + AddSize 12200 ; ~ size of unpacked gtk + SetOutPath "$INSTDIR" + File "${GTK_INSTALLER_EXE}" + ; TODO: in the future, when we have translations for this program, + ; make the GTK+ translations installation dependent on their installation status. + ExecWait '"${GTK_INSTALLER_EXE}" /sideeffects=no /dllpath=root /translations=no /compatdlls=no /S /D=$INSTDIR' + Delete "$INSTDIR\${GTK_INSTALLER_EXE}" +SectionEnd + +; disabled by default +Section /o "GTK+ (shared installation)" SecGtkPublic + SectionIn 1 + SetShellVarContext all ; use all user variables as opposed to current user + AddSize 12200 ; ~ size of unpacked gtk + SetOutPath "$INSTDIR" + File "${GTK_INSTALLER_EXE}" + ExecWait '"${GTK_INSTALLER_EXE}"' + Delete "$INSTDIR\${GTK_INSTALLER_EXE}" +SectionEnd + +!endif ; !NO_GTK + + +!ifndef NO_GTK +var gtk_mode ; "public", "private" or "none" +var gtk_tmp ; temporary variable +!endif + + ; Executed on installation start Function .onInit SetShellVarContext all ; use all user variables as opposed to current user @@ -163,16 +220,119 @@ Function .onInit Call PreventMultipleInstances Call DetectPrevInstallation + +; ask for gtk only if it's a non-gtk installer. +!ifdef NO_GTK + Call AskForGtk +!endif + +!ifndef NO_GTK + StrCpy $gtk_mode "private" ; default +!endif + FunctionEnd +function .onselchange + +!ifndef NO_GTK + ; Remember which gtk section was selected. + ; Deselect the other section. + + ; If it was private, we check if public is checked and uncheck private. + + StrCmp $gtk_mode "private" check_public ; old selection + StrCmp $gtk_mode "public" check_private ; old selection + goto check_exit + + check_public: + SectionGetFlags ${SecGtkPublic} $gtk_tmp ; see if it's checked + IntOp $gtk_tmp $gtk_tmp & ${SF_SELECTED} + IntCmp $gtk_tmp ${SF_SELECTED} "" check_exit check_exit + SectionGetFlags ${SecGtkPrivate} $gtk_tmp ; unselect the other one + IntOp $gtk_tmp $gtk_tmp & ${SECTION_OFF} + SectionSetFlags ${SecGtkPrivate} $gtk_tmp + goto check_exit + + check_private: + SectionGetFlags ${SecGtkPrivate} $gtk_tmp ; see if it's checked + IntOp $gtk_tmp $gtk_tmp & ${SF_SELECTED} + IntCmp $gtk_tmp ${SF_SELECTED} "" check_exit check_exit + SectionGetFlags ${SecGtkPublic} $gtk_tmp ; unselect the other one + IntOp $gtk_tmp $gtk_tmp & ${SECTION_OFF} + SectionSetFlags ${SecGtkPublic} $gtk_tmp + + check_exit: + + + ; store the current mode + StrCpy $gtk_mode "none" + + SectionGetFlags ${SecGtkPrivate} $gtk_tmp + IntOp $gtk_tmp $gtk_tmp & ${SF_SELECTED} + IntCmp $gtk_tmp ${SF_SELECTED} "" mode_end_private mode_end_private + StrCpy $gtk_mode "private" + mode_end_private: + + SectionGetFlags ${SecGtkPublic} $gtk_tmp + IntOp $gtk_tmp $gtk_tmp & ${SF_SELECTED} + IntCmp $gtk_tmp ${SF_SELECTED} "" mode_end_public mode_end_public + StrCpy $gtk_mode "public" + mode_end_public: + + ; MessageBox MB_ICONINFORMATION|MB_OK "gtk_mode: $gtk_mode" /SD IDOK +!endif ; !NO_GTK + +functionend + + +!ifndef NO_GTK +Function on_components_page_leave + StrCmp $gtk_mode "none" "" noabort + Call AskForGtk + noabort: +FunctionEnd +!endif ; !NO_GTK + + + + +; Section descriptions +!ifndef NO_GTK ; this page is shown only when using gtk +!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN + !insertmacro MUI_DESCRIPTION_TEXT ${SecMain} "GSmartControl - hard disk drive and SSD health inspection tool" + !insertmacro MUI_DESCRIPTION_TEXT ${SecGtkPrivate} "GTK+ libraries, needed by GSmartControl. \ + This will install a private version of GTK+, usable only by GSmartControl." + !insertmacro MUI_DESCRIPTION_TEXT ${SecGtkPublic} "GTK+ libraries, needed by GSmartControl. \ + This will install a system-wide version of GTK+, shareable with other programs." +!insertmacro MUI_FUNCTION_DESCRIPTION_END +!endif + + ; ------------------ POST INSTALL +var gtk_dll_abs_path + Section -post SetShellVarContext all ; use all user variables as opposed to current user + ; Don't set any paths for this exe if it has a private GTK+ installation. +!ifndef NO_GTK + StrCmp $gtk_mode "private" skip_exe_PATH +!endif + ; set a special path for this exe, as GTK may not be in a global path. + ReadRegStr $gtk_dll_abs_path HKLM "SOFTWARE\GTK\2.0" "DllPath" + WriteRegStr HKLM "${REGISTRY_APP_PATHS}\gsmartcontrol.exe" "Path" "$gtk_dll_abs_path" +!ifndef NO_GTK + skip_exe_PATH: +!endif + +!ifndef NO_GTK + WriteRegStr HKLM "SOFTWARE\${PRODUCT_NAME}" "GtkInstalledMode" "$gtk_mode" +!endif + WriteRegStr HKLM "SOFTWARE\${PRODUCT_NAME}" "InstallationDirectory" "$INSTDIR" WriteRegStr HKLM "SOFTWARE\${PRODUCT_NAME}" "Vendor" "${PRODUCT_PUBLISHER}" @@ -218,26 +378,41 @@ Section Uninstall SetShellVarContext all ; use all user variables as opposed to current user SetAutoClose false +!ifndef NO_GTK + ReadRegStr $gtk_mode HKLM "SOFTWARE\${PRODUCT_NAME}" "GtkInstalledMode" + StrCmp $gtk_mode "private" "" skip_gtk_remove + ; remove private GTK+, specify the same custom options are during installation + ExecWait "$INSTDIR\gtk2_runtime_uninst.exe /remove_config=yes /sideeffects=no /dllpath=root /translations=no /compatdlls=no /S" + ; _?=$INSTDIR + ; Delete "$INSTDIR\gtk2_runtime_uninst.exe" ; If using _? flag, it won't get deleted automatically, do it manually. + skip_gtk_remove: +!endif + ; 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.exe" Delete "$INSTDIR\gsmartcontrol.exe.manifest" Delete "$INSTDIR\gsmartcontrol.ico" Delete "$INSTDIR\icon_*.png" + ; Delete "$INSTDIR\*.dll" + Delete "$INSTDIR\smartctl-nc.exe" + Delete "$INSTDIR\smartctl.exe" + Delete "$INSTDIR\update-smart-drivedb.exe" ; update-smart-drivedb may leave these Delete "$INSTDIR\drivedb.h" - Delete "$INSTDIR\drivedb.h.*" + Delete "$INSTDIR\drivedb.h.new" + Delete "$INSTDIR\drivedb.h.old" + Delete "$INSTDIR\drivedb.h.error" + Delete "$INSTDIR\drivedb.h.lastcheck" RMDir /r "$INSTDIR\doc" - ; GTK and stuff - RMDir /r "$INSTDIR\etc" + ; leftover from gtk RMDir /r "$INSTDIR\share" ; clean up generated stuff @@ -246,6 +421,17 @@ Section Uninstall RMDir "$INSTDIR" ; only if empty + ; For some reason, sometimes it's not deleted in the sections, so do it here. + Delete "$INSTDIR\${GTK_INSTALLER_EXE}" + +!ifndef NO_GTK + StrCmp $gtk_mode "private" skip_exe_PATH_remove +!endif + DeleteRegKey HKLM "${REGISTRY_APP_PATHS}\gsmartcontrol.exe" +!ifndef NO_GTK + skip_exe_PATH_remove: +!endif + Delete "$SMPROGRAMS\GSmartControl.lnk" ;Delete "$SMPROGRAMS\GSmartControl\Uninstall GSmartControl.lnk" @@ -303,6 +489,35 @@ FunctionEnd +; detect GTK installation (any of available versions) +Function AskForGtk + SetShellVarContext all ; use all user variables as opposed to current user + push $R0 + + ReadRegStr $R0 HKLM "SOFTWARE\GTK\2.0" "DllPath" + StrCmp $R0 "" no_gtk have_gtk + + no_gtk: + MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \ + "GTK2-Runtime is not installed. This product needs it to function properly.$\n\ + Please install GTK2-Runtime from http://gtk-win.sf.net/ first.$\n$\n\ + Click 'Cancel' to abort the installation \ + or 'OK' to continue anyway." \ + /SD IDOK IDOK have_gtk + ;Abort ; Abort has different meaning from onpage callbacks, so use Quit + Quit + goto end_gtk_check + + have_gtk: + ; do nothing + + end_gtk_check: + + pop $R0 +FunctionEnd + + + ; Prevent running multiple instances of the installer Function PreventMultipleInstances Push $R0 diff --git a/gsmartcontrol/data/nsis/nsi_install.ico b/data/nsis/nsi_install.ico similarity index 100% rename from gsmartcontrol/data/nsis/nsi_install.ico rename to data/nsis/nsi_install.ico diff --git a/gsmartcontrol/data/nsis/nsi_uninstall.ico b/data/nsis/nsi_uninstall.ico similarity index 100% rename from gsmartcontrol/data/nsis/nsi_uninstall.ico rename to data/nsis/nsi_uninstall.ico diff --git a/gsmartcontrol/debian.dist/Makefile.am b/debian.dist/Makefile.am similarity index 100% rename from gsmartcontrol/debian.dist/Makefile.am rename to debian.dist/Makefile.am diff --git a/gsmartcontrol/debian.dist/changelog.in b/debian.dist/changelog.in similarity index 100% rename from gsmartcontrol/debian.dist/changelog.in rename to debian.dist/changelog.in diff --git a/gsmartcontrol/debian.dist/compat b/debian.dist/compat similarity index 100% rename from gsmartcontrol/debian.dist/compat rename to debian.dist/compat diff --git a/gsmartcontrol/debian.dist/control b/debian.dist/control similarity index 66% rename from gsmartcontrol/debian.dist/control rename to debian.dist/control index 22a0809..f580f22 100644 --- a/gsmartcontrol/debian.dist/control +++ b/debian.dist/control @@ -1,15 +1,18 @@ Source: gsmartcontrol Section: utils Priority: extra -Homepage: https://gsmartcontrol.sourceforge.io/ +Homepage: http://gsmartcontrol.sourceforge.net/ Maintainer: Alexander Shaduri +# libglademm is also needed if libgtkmm-2.4 < 2.12, but don't write it +# here - newer distributions don't contain it. Build-Depends: debhelper (>= 5), autotools-dev, - libpcre3-dev, libgtkmm-3.0-dev (>= 3.4.0) + libpcre3-dev, libgtkmm-2.4-dev (>= 2.6.0) Standards-Version: 3.7.3 Package: gsmartcontrol Architecture: any -Depends: ${shlibs:Depends}, smartmontools (>= 5.43), xterm, menu +#Depends: ${shlibs:Depends}, ${misc:Depends}, smartmontools, menu +Depends: ${shlibs:Depends}, smartmontools, 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 diff --git a/gsmartcontrol/debian.dist/copyright b/debian.dist/copyright similarity index 84% rename from gsmartcontrol/debian.dist/copyright rename to debian.dist/copyright index 0a03ed5..530d4c9 100644 --- a/gsmartcontrol/debian.dist/copyright +++ b/debian.dist/copyright @@ -1,11 +1,11 @@ This package was debianized by Alexander Shaduri on Sat, 15 Nov 2008 00:12:04 +0400. -It was downloaded from +It was downloaded from Copyright: - Copyright (C) 2008 - 2017 Alexander Shaduri + Copyright (C) 2008 - 2012 Alexander Shaduri License: @@ -27,6 +27,6 @@ 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 - 2017 Alexander Shaduri and +The Debian packaging is (C) 2008 - 2012 Alexander Shaduri and is licensed under the GPLv2 and GPLv3, see above. diff --git a/gsmartcontrol/debian.dist/menu b/debian.dist/menu similarity index 100% rename from gsmartcontrol/debian.dist/menu rename to debian.dist/menu diff --git a/gsmartcontrol/debian.dist/postinst b/debian.dist/postinst similarity index 100% rename from gsmartcontrol/debian.dist/postinst rename to debian.dist/postinst diff --git a/gsmartcontrol/debian.dist/postrm b/debian.dist/postrm similarity index 100% rename from gsmartcontrol/debian.dist/postrm rename to debian.dist/postrm diff --git a/gsmartcontrol/debian.dist/rules b/debian.dist/rules similarity index 100% rename from gsmartcontrol/debian.dist/rules rename to debian.dist/rules diff --git a/gsmartcontrol/doxy_main_page.h b/doxy_main_page.h similarity index 100% rename from gsmartcontrol/doxy_main_page.h rename to doxy_main_page.h diff --git a/gsmartcontrol/file2csource.sh b/file2csource.sh similarity index 100% rename from gsmartcontrol/file2csource.sh rename to file2csource.sh diff --git a/gsmartcontrol/gsmartcontrol.kdev4 b/gsmartcontrol.kdev4 similarity index 100% rename from gsmartcontrol/gsmartcontrol.kdev4 rename to gsmartcontrol.kdev4 diff --git a/gsmartcontrol/gsmartcontrol.kdevelop b/gsmartcontrol.kdevelop similarity index 100% rename from gsmartcontrol/gsmartcontrol.kdevelop rename to gsmartcontrol.kdevelop diff --git a/gsmartcontrol.spec.in b/gsmartcontrol.spec.in new file mode 100644 index 0000000..6d4261c --- /dev/null +++ b/gsmartcontrol.spec.in @@ -0,0 +1,218 @@ +Name: gsmartcontrol +Version: @VERSION@ +Release: 1as +License: GPL-2.0 AND GPL-3.0 +Url: http://gsmartcontrol.sourceforge.net +Vendor: Alexander Shaduri +# Packager: Alexander Shaduri +Source: gsmartcontrol-%{version}.tar.bz2 +BuildRoot: %{_tmppath}/%{name}-%{version}-build +Summary: GSmartControl - Hard Disk Drive and SSD Health Inspection Tool + +%if 0%{?mandriva_version} +Group: System/Kernel and hardware +%else +Group: Hardware/Other +%endif + + +# Dependencies for various distributions. The actual deps are: +# smartmontools, pcre, (gtkmm2 >= 2.12 || (gtkmm2 >= 2.6.0 && libglademm >= 2.4.0)) +# (with the respective -devel packages of the libraries for build requirements). +# For non-specified distributions we don't specify any dependencies to avoid errors. + +# For mandriva, these contain the su programs. +# Suggests: libgnomesu, gksu, kdebase-progs + + +# SUSE / OpenSUSE. SLES also defines the correct suse_version. +%if 0%{?suse_version} + +%if 0%{?%suse_version} >= 1030 +Requires: smartmontools, pcre, gtkmm2 >= 2.12.0 +BuildRequires: gcc-c++, libstdc++-devel, pcre-devel, gtkmm2-devel >= 2.12.0 +%else +%if 0%{?%suse_version} == 1020 +Requires: smartmontools, pcre, gtkmm2 >= 2.6.0, libglademm >= 2.4.0 +BuildRequires: gcc-c++, libstdc++-devel, pcre-devel, gtkmm2-devel >= 2.6.0, libglademm-devel >= 2.4.0 +%else +Requires: smartmontools, pcre, gtkmm24 >= 2.6.0, libglademm >= 2.4.0 +BuildRequires: gcc-c++, libstdc++-devel, pcre-devel, gtkmm24-devel >= 2.6.0, libglademm-devel >= 2.4.0 +%endif +%endif +BuildRequires: update-desktop-files + +%if 0%{?suse_version} >= 1030 +BuildRequires: fdupes +%endif + +%endif + + +# Fedora, CentOS, RHEL +%if 0%{?fedora_version} || 0%{?rhel_version} || 0%{?centos_version} + +# I'm not sure about centos and rhel versions +%if 0%{?fedora_version} >= 8 || 0%{?rhel_version} >= 600 || 0%{?centos_version} >= 600 +Requires: smartmontools, pcre, gtkmm24 >= 2.12.0 +BuildRequires: gcc-c++, pcre-devel, gtkmm24-devel >= 2.12.0 +%else +Requires: smartmontools, pcre, gtkmm24 >= 2.6.0, libglademm24 >= 2.4.0 +BuildRequires: gcc-c++, pcre-devel, gtkmm24-devel >= 2.6.0, libglademm24-devel >= 2.4.0 +%endif + +# Fedora with consolehelper. +# usermode-gtk is for consolehelper-gtk. +%if 0%{?fedora_version} +Requires: usermode-gtk +%endif + +%endif + + +# Mandriva. Check the latest official spec file at +# http://svn.mandriva.com/cgi-bin/viewvc.cgi/packages/cooker/gsmartcontrol/current/ +%if 0%{?mandriva_version} + +%if 0%{?mandriva_version} >= 2008 +Requires: smartmontools, libpcre0, gtkmm2.4 >= 2.12.0 +BuildRequires: gcc-c++, gcc-cpp, pcre-devel, gtkmm2.4-devel >= 2.12.0, desktop-file-utils +%else +Requires: smartmontools, libpcre0, gtkmm2.4 >= 2.6.0, libglademm2.4 >= 2.4.0 +BuildRequires: gcc-c++, gcc-cpp, pcre-devel, gtkmm2.4-devel >= 2.6.0, libglademm2.4-devel >= 2.4.0, desktop-file-utils +%endif + +%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 +%if 0%{?mandriva_version} <= 2006 +%configure +%else +%configure --enable-optimize-options=auto +%endif + +%build +# Set up for parallel builds +#numprocs=`grep -E -c ^cpu[0-9]+ /proc/stat || :` +#if [ "$numprocs" = "0" ]; then +# numprocs=1 +#fi +#make -j$numprocs +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. +%if 0%{?suse_version} >= 1030 +%fdupes +%endif + + +# 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 + + +%if 0%{?fedora_version} +# We still retain gsmartcontrol-root for consistency with other distributions. + +# Make the desktop file run gsmartcontrol instead of gsmartcontrol-root +sed "s|gsmartcontrol-root|gsmartcontrol|g" %{buildroot}%{_datadir}/applications/gsmartcontrol.desktop > %{buildroot}%{_datadir}/applications/gsmartcontrol.desktop.new +mv -f %{buildroot}%{_datadir}/applications/gsmartcontrol.desktop.new %{buildroot}%{_datadir}/applications/gsmartcontrol.desktop + +# The rest is taken from the official fedora spec: +# http://pkgs.fedoraproject.org/cgit/gsmartcontrol.git/tree/ + +# Set up for console-helper +mkdir -p %{buildroot}%{_sbindir} +# Add a copy as "gsmartcontrol-noroot", since gsmartcontrol will always +# ask for root password. +cp %{buildroot}%{_bindir}/%{name} %{buildroot}%{_bindir}/%{name}-noroot +mv %{buildroot}%{_bindir}/%{name} %{buildroot}%{_sbindir}/%{name} +ln -s consolehelper %{buildroot}%{_bindir}/%{name} + +mkdir -p %{buildroot}%{_sysconfdir}/pam.d +cat <%{buildroot}%{_sysconfdir}/pam.d/%{name} +#%%PAM-1.0 +auth include config-util +account include config-util +session include config-util +EOF + +mkdir -p %{buildroot}%{_sysconfdir}/security/console.apps +cat <%{buildroot}%{_sysconfdir}/security/console.apps/%{name} +USER=root +PROGRAM=/usr/sbin/gsmartcontrol +SESSION=true +EOF + +# endif fedora +%endif + + +%if 0%{?mandriva_version} +desktop-file-install --vendor="" \ + --remove-category="Application" \ + --add-category="System" \ + --add-category="X-MandrivaLinux-System-Configuration-Hardware;Settings;HardwareSettings" \ + --dir $RPM_BUILD_ROOT%{_datadir}/applications $RPM_BUILD_ROOT%{_datadir}/applications/%{name}.desktop +%endif + + +%clean +rm -rf %buildroot + + +%files +%defattr(-,root,root) + +%attr(0755,root,root) %{_bindir}/* +# %%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/*.appdata.xml +%{_datadir}/icons/* +%{_datadir}/pixmaps/* + +%if 0%{?fedora_version} +%config(noreplace) %{_sysconfdir}/pam.d/%{name} +%config(noreplace) %{_sysconfdir}/security/console.apps/%{name} +%{_sbindir}/gsmartcontrol +%endif + +%changelog diff --git a/gsmartcontrol/.update-ignores b/gsmartcontrol/.update-ignores deleted file mode 100644 index a65051a..0000000 --- a/gsmartcontrol/.update-ignores +++ /dev/null @@ -1 +0,0 @@ -svn propset svn:ignore -F .svnignore.txt . diff --git a/gsmartcontrol/Makefile.am b/gsmartcontrol/Makefile.am deleted file mode 100644 index 60c7598..0000000 --- a/gsmartcontrol/Makefile.am +++ /dev/null @@ -1,262 +0,0 @@ - -# 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+ -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 \ - libbz2-*.dll \ - libcairo-*.dll \ - libcairomm-1*.dll \ - libepoxy-*.dll \ - libffi-*.dll \ - libexpat-*.dll \ - libfontconfig-*.dll \ - libfreetype-*.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 \ - libstdc++-*.dll \ - libtiff-*.dll \ - libwinpthread-*.dll \ - libxml2-*.dll \ - zlib1.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 - - cp src/gsmartcontrol.exe src/gsmartcontrol.exe.manifest 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/ - - $(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. - $(MKDIR_P) win-dist/share/icons/Adwaita/16x16/actions -# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/actions/window-close-symbolic.symbolic.png win-dist/share/icons/Adwaita/16x16/actions -# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/actions/window-maximize-symbolic.symbolic.png win-dist/share/icons/Adwaita/16x16/actions -# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/actions/window-minimize-symbolic.symbolic.png win-dist/share/icons/Adwaita/16x16/actions -# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/actions/window-restore-symbolic.symbolic.png win-dist/share/icons/Adwaita/16x16/actions - cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/actions/pan-down-symbolic.symbolic.png win-dist/share/icons/Adwaita/16x16/actions - cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/16x16/actions/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.png win-dist/share/icons/Adwaita/16x16/status - $(MKDIR_P) win-dist/share/icons/Adwaita/22x22/status - cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/22x22/status/dialog-information.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/actions/window-close-symbolic.symbolic.png win-dist/share/icons/Adwaita/24x24/actions -# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/actions/window-maximize-symbolic.symbolic.png win-dist/share/icons/Adwaita/24x24/actions -# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/actions/window-minimize-symbolic.symbolic.png win-dist/share/icons/Adwaita/24x24/actions -# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/actions/window-restore-symbolic.symbolic.png win-dist/share/icons/Adwaita/24x24/actions - cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/actions/pan-down-symbolic.symbolic.png win-dist/share/icons/Adwaita/24x24/actions - cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/actions/pan-up-symbolic.symbolic.png win-dist/share/icons/Adwaita/24x24/actions - $(MKDIR_P) win-dist/share/icons/Adwaita/24x24/status - cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/24x24/status/dialog-information.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/actions/window-close-symbolic.symbolic.png win-dist/share/icons/Adwaita/32x32/actions -# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/actions/window-maximize-symbolic.symbolic.png win-dist/share/icons/Adwaita/32x32/actions -# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/actions/window-minimize-symbolic.symbolic.png win-dist/share/icons/Adwaita/32x32/actions -# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/actions/window-restore-symbolic.symbolic.png win-dist/share/icons/Adwaita/32x32/actions - cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/actions/pan-down-symbolic.symbolic.png win-dist/share/icons/Adwaita/32x32/actions - cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/actions/pan-up-symbolic.symbolic.png win-dist/share/icons/Adwaita/32x32/actions - $(MKDIR_P) win-dist/share/icons/Adwaita/32x32/status - cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/32x32/status/dialog-information.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/actions/window-close-symbolic.symbolic.png win-dist/share/icons/Adwaita/48x48/actions -# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/actions/window-maximize-symbolic.symbolic.png win-dist/share/icons/Adwaita/48x48/actions -# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/actions/window-minimize-symbolic.symbolic.png win-dist/share/icons/Adwaita/48x48/actions -# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/actions/window-restore-symbolic.symbolic.png win-dist/share/icons/Adwaita/48x48/actions - cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/actions/pan-down-symbolic.symbolic.png win-dist/share/icons/Adwaita/48x48/actions - cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/actions/pan-up-symbolic.symbolic.png win-dist/share/icons/Adwaita/48x48/actions - $(MKDIR_P) win-dist/share/icons/Adwaita/48x48/status - cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/48x48/status/dialog-information.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/actions/window-close-symbolic.symbolic.png win-dist/share/icons/Adwaita/64x64/actions -# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/actions/window-maximize-symbolic.symbolic.png win-dist/share/icons/Adwaita/64x64/actions -# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/actions/window-minimize-symbolic.symbolic.png win-dist/share/icons/Adwaita/64x64/actions -# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/actions/window-restore-symbolic.symbolic.png win-dist/share/icons/Adwaita/64x64/actions - cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/actions/pan-down-symbolic.symbolic.png win-dist/share/icons/Adwaita/64x64/actions - cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/actions/pan-up-symbolic.symbolic.png win-dist/share/icons/Adwaita/64x64/actions -# $(MKDIR_P) win-dist/share/icons/Adwaita/64x64/status -# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/64x64/status/dialog-information.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/actions/window-close-symbolic.symbolic.png win-dist/share/icons/Adwaita/96x96/actions -# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/actions/window-maximize-symbolic.symbolic.png win-dist/share/icons/Adwaita/96x96/actions -# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/actions/window-minimize-symbolic.symbolic.png win-dist/share/icons/Adwaita/96x96/actions -# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/actions/window-restore-symbolic.symbolic.png win-dist/share/icons/Adwaita/96x96/actions - cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/actions/pan-down-symbolic.symbolic.png win-dist/share/icons/Adwaita/96x96/actions - cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/actions/pan-up-symbolic.symbolic.png win-dist/share/icons/Adwaita/96x96/actions -# $(MKDIR_P) win-dist/share/icons/Adwaita/96x96/status -# cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/96x96/status/dialog-information.png win-dist/share/icons/Adwaita/96x96/status - $(MKDIR_P) win-dist/share/icons/Adwaita/256x256/status - cp -p "@WINDOWS_SYSROOT@"/share/icons/Adwaita/256x256/status/dialog-information.png win-dist/share/icons/Adwaita/256x256/status - -# 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 -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 - - - diff --git a/gsmartcontrol/README.txt b/gsmartcontrol/README.txt deleted file mode 100644 index 008f4df..0000000 --- a/gsmartcontrol/README.txt +++ /dev/null @@ -1,101 +0,0 @@ - -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.sourceforge.io/ - - - -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. diff --git a/gsmartcontrol/TODO b/gsmartcontrol/TODO deleted file mode 100644 index b8993a0..0000000 --- a/gsmartcontrol/TODO +++ /dev/null @@ -1,49 +0,0 @@ - - -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) - - diff --git a/gsmartcontrol/autoconf.m4/ax_cxx_compile_stdcxx.m4 b/gsmartcontrol/autoconf.m4/ax_cxx_compile_stdcxx.m4 deleted file mode 100644 index 5032bba..0000000 --- a/gsmartcontrol/autoconf.m4/ax_cxx_compile_stdcxx.m4 +++ /dev/null @@ -1,982 +0,0 @@ -# =========================================================================== -# 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 - -]]) diff --git a/gsmartcontrol/configure-dev b/gsmartcontrol/configure-dev deleted file mode 100755 index 9f98880..0000000 --- a/gsmartcontrol/configure-dev +++ /dev/null @@ -1,83 +0,0 @@ -#!/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\" \ - --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\" \ - --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\" \ - --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\" \ - --enable-debug-options --enable-tests --host=x86_64-w64-mingw32" ;; -esac - - -echo "Running:" -echo $configure_script $flags; - -$configure_script $flags - - -exit $? diff --git a/gsmartcontrol/data/icon_cddvd.png b/gsmartcontrol/data/icon_cddvd.png deleted file mode 100644 index c11bf8a..0000000 Binary files a/gsmartcontrol/data/icon_cddvd.png and /dev/null differ diff --git a/gsmartcontrol/data/icon_hdd.png b/gsmartcontrol/data/icon_hdd.png deleted file mode 100644 index 68351d1..0000000 Binary files a/gsmartcontrol/data/icon_hdd.png and /dev/null differ diff --git a/gsmartcontrol/data/org.gsmartcontrol.policy b/gsmartcontrol/data/org.gsmartcontrol.policy deleted file mode 100644 index 3d8ae3f..0000000 --- a/gsmartcontrol/data/org.gsmartcontrol.policy +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - Authentication is required to run GSmartControl - gsmartcontrol - - auth_admin - auth_admin - auth_admin - - /usr/sbin/gsmartcontrol - true - - - diff --git a/gsmartcontrol/gsmartcontrol.spec.in b/gsmartcontrol/gsmartcontrol.spec.in deleted file mode 100644 index d4a4c4b..0000000 --- a/gsmartcontrol/gsmartcontrol.spec.in +++ /dev/null @@ -1,115 +0,0 @@ - -# 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.sourceforge.io/ -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 diff --git a/gsmartcontrol/src/applib/app_gtkmm_utils.cpp b/gsmartcontrol/src/applib/app_gtkmm_utils.cpp deleted file mode 100644 index 6b61852..0000000 --- a/gsmartcontrol/src/applib/app_gtkmm_utils.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/************************************************************************** - 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); -} - - - - - - - -/// @} diff --git a/gsmartcontrol/src/applib/app_ui_res_utils.h b/gsmartcontrol/src/applib/app_ui_res_utils.h deleted file mode 100644 index 1c9cdbb..0000000 --- a/gsmartcontrol/src/applib/app_ui_res_utils.h +++ /dev/null @@ -1,268 +0,0 @@ -/************************************************************************** - 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 - -/// @} diff --git a/gsmartcontrol/src/applib/app_ui_res_utils_test_data.cpp b/gsmartcontrol/src/applib/app_ui_res_utils_test_data.cpp deleted file mode 100644 index 91823d5..0000000 --- a/gsmartcontrol/src/applib/app_ui_res_utils_test_data.cpp +++ /dev/null @@ -1,242 +0,0 @@ -/************************************************************************** - 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; - - diff --git a/gsmartcontrol/src/applib/storage_property.cpp b/gsmartcontrol/src/applib/storage_property.cpp deleted file mode 100644 index aa0d656..0000000 --- a/gsmartcontrol/src/applib/storage_property.cpp +++ /dev/null @@ -1,279 +0,0 @@ -/************************************************************************** - 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; - ss.imbue(std::locale("")); - 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; - ss.imbue(std::locale("")); - 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; - ss.imbue(std::locale("")); - 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; - ss.imbue(std::locale("")); - 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]"; -} - - - - - - - -/// @} diff --git a/gsmartcontrol/src/hz/launch_url.h b/gsmartcontrol/src/hz/launch_url.h deleted file mode 100644 index dd356ab..0000000 --- a/gsmartcontrol/src/hz/launch_url.h +++ /dev/null @@ -1,82 +0,0 @@ -/************************************************************************** - 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 - -/// @} diff --git a/gsmartcontrol/src/res/Makefile.am b/gsmartcontrol/src/res/Makefile.am deleted file mode 100644 index 916a99d..0000000 --- a/gsmartcontrol/src/res/Makefile.am +++ /dev/null @@ -1,44 +0,0 @@ - -# 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@ - - diff --git a/gsmartcontrol/src/res/gsc_info_window.ui b/gsmartcontrol/src/res/gsc_info_window.ui deleted file mode 100644 index 9112d22..0000000 --- a/gsmartcontrol/src/res/gsc_info_window.ui +++ /dev/null @@ -1,1134 +0,0 @@ - - - - - - - - - - - - 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 - - - - - - diff --git a/gsmartcontrol/src/res/gsc_main_window.ui b/gsmartcontrol/src/res/gsc_main_window.ui deleted file mode 100644 index a58c39b..0000000 --- a/gsmartcontrol/src/res/gsc_main_window.ui +++ /dev/null @@ -1,200 +0,0 @@ - - - - - - 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 - - - - - - diff --git a/gsmartcontrol/src/res/gsc_text_window.ui b/gsmartcontrol/src/res/gsc_text_window.ui deleted file mode 100644 index 0b2ebed..0000000 --- a/gsmartcontrol/src/res/gsc_text_window.ui +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - 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 - - - - - - diff --git a/gsmartcontrol/src/Makefile.am b/src/Makefile.am similarity index 73% rename from gsmartcontrol/src/Makefile.am rename to src/Makefile.am index c91aee1..5db5c6d 100644 --- a/gsmartcontrol/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,7 @@ AM_CPPFLAGS = $(all_includes) METASOURCES = AUTO -SUBDIRS = libdebug hz rmn rconfig applib res +SUBDIRS = libdebug hz rmn rconfig pcrecpp applib res if TARGET_OS_KERNEL_FAMILY_WINDOWS @@ -24,16 +24,16 @@ noinst_DATA = gsc_winres.rc.in gsmartcontrol.exe.manifest.in # endif -sbin_PROGRAMS = gsmartcontrol +bin_PROGRAMS = gsmartcontrol gsmartcontrol_LDADD = $(top_builddir)/src/applib/libapplib.a \ - $(top_builddir)/src/libdebug/libdebug.a \ + $(top_builddir)/src/pcrecpp/libpcrecpp.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_executor_log_window.h gsc_help_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_executor_error_dialog.cpp gsc_executor_log_window.cpp gsc_help_window.cpp gsc_info_window.cpp \ gsc_init.cpp gsc_main.cpp gsc_main_window.cpp gsc_preferences_window.cpp diff --git a/gsmartcontrol/src/applib/Makefile.am b/src/applib/Makefile.am similarity index 88% rename from gsmartcontrol/src/applib/Makefile.am rename to src/applib/Makefile.am index 1e92704..25a52dc 100644 --- a/gsmartcontrol/src/applib/Makefile.am +++ b/src/applib/Makefile.am @@ -11,7 +11,7 @@ noinst_HEADERS = app_gtkmm_features.h app_gtkmm_utils.h app_pango_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 + storage_property_descr.h storage_settings.h wrapping_label.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) @@ -26,16 +26,16 @@ smartctl_executor_test_LDADD = libapplib.a \ smartctl_parser_test_SOURCES = smartctl_parser_test.cpp smartctl_parser_test_LDADD = libapplib.a \ - $(top_builddir)/src/libdebug/libdebug.a + $(top_builddir)/src/pcrecpp/libpcrecpp.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 + $(top_builddir)/src/pcrecpp/libpcrecpp.a $(top_builddir)/src/libdebug/libdebug.a spawn_test_SOURCES = spawn_test.cpp # Build them first -SUBDIRS=../libdebug +SUBDIRS=../libdebug ../pcrecpp # we don't list them in a separate variable because otherwise kdevelop won't see them. diff --git a/gsmartcontrol/src/applib/app_gtkmm_features.h b/src/applib/app_gtkmm_features.h similarity index 90% rename from gsmartcontrol/src/applib/app_gtkmm_features.h rename to src/applib/app_gtkmm_features.h index 7d5d1a7..b6fd808 100644 --- a/gsmartcontrol/src/applib/app_gtkmm_features.h +++ b/src/applib/app_gtkmm_features.h @@ -12,7 +12,7 @@ #ifndef APP_GTKMM_FEATURES_H #define APP_GTKMM_FEATURES_H -#include +#include /// \def APP_GTKMM_CHECK_VERSION(major, minor, micro) @@ -29,6 +29,14 @@ +/// \def APP_GTKMM_OLD_TOOLTIPS +/// Defined to 1 if the new (since gtkmm 2.12) tooltips API is not available. +#if !APP_GTKMM_CHECK_VERSION(2, 12, 0) + #define APP_GTKMM_OLD_TOOLTIPS 1 +#endif + + + // If default virtual on_* members are disabled in gtkmm, use this in constructors // to connect on_signal_name methods. diff --git a/src/applib/app_gtkmm_utils.cpp b/src/applib/app_gtkmm_utils.cpp new file mode 100644 index 0000000..d54963f --- /dev/null +++ b/src/applib/app_gtkmm_utils.cpp @@ -0,0 +1,295 @@ +/************************************************************************** + Copyright: + (C) 2008 - 2012 Alexander Shaduri + License: See LICENSE_gsmartcontrol.txt +***************************************************************************/ +/// \file +/// \author Alexander Shaduri +/// \ingroup applib +/// \weakgroup applib +/// @{ + +#include +#include // gtk_* stuff +#include // std::strlen +#include +#include + +#include "app_gtkmm_features.h" // APP_GTKMM_OLD_TOOLTIPS + +#if defined APP_GTKMM_OLD_TOOLTIPS && APP_GTKMM_OLD_TOOLTIPS + #include // gtk_tooltips_* +#endif + +#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) +{ + // set_tooltip_* is available since 2.12 +#if !(defined APP_GTKMM_OLD_TOOLTIPS && APP_GTKMM_OLD_TOOLTIPS) + if (use_markup) { + widget.set_tooltip_markup(tooltip_text); + } else { + widget.set_tooltip_text(tooltip_text); + } + +#else // use the old tooltips api + Gtk::Widget* toplevel = widget.get_toplevel(); + if (toplevel && toplevel->is_toplevel()) { // orphan widgets return themselves, so check toplevelness. + GtkTooltips* tooltips = static_cast(toplevel->get_data("window_tooltips")); + if (tooltips) { + if (use_markup) { + // strip markup + Glib::ustring stripped; + if (app_pango_strip_markup(tooltip_text, stripped)) { + gtk_tooltips_set_tip(tooltips, widget.gobj(), stripped.c_str(), ""); + } + + } else { + gtk_tooltips_set_tip(tooltips, widget.gobj(), tooltip_text.c_str(), ""); + } + } + } +#endif +} + + + +// unset model on treeview, cross-gtkmm-version. +void app_gtkmm_treeview_unset_model(Gtk::TreeView* treeview) +{ + // gtkmm's TreeView::unset_model() is since gtkmm 2.8 + // (and there's no version info in docs), so use this instead. + if (treeview) + gtk_tree_view_set_model(treeview->gobj(), 0); +} + + + +// unset model on combobox, cross-gtkmm-version. +void app_gtkmm_combobox_unset_model(Gtk::ComboBox* box) +{ + // there's no ComboBox::unset_model() in gtkmm (huh?) + if (box) + gtk_combo_box_set_model(box->gobj(), 0); +} + + + + + + +#if defined APP_GTKMM_OLD_TOOLTIPS && APP_GTKMM_OLD_TOOLTIPS + +namespace { + + /// A helper function for gtkmm_set_treeview_tooltip_column() + inline bool app_on_treeview_motion_notify_event_tooltip(GdkEventMotion* event, Gtk::TreeView* treeview, + Gtk::TreeModelColumn tooltip_column) + { + // debug_out_dump("app", DBG_FUNC_MSG << "x=" << int(event->x) << ", y=" << int(event->y) << "\n"); + + Gtk::TreePath path; + Gtk::TreeViewColumn* mouse_column = 0; + int cell_x = 0, cell_y = 0; + + if (treeview->get_path_at_pos(static_cast(event->x), static_cast(event->y), + path, mouse_column, cell_x, cell_y)) { // returns true if we're on a cell + app_gtkmm_set_widget_tooltip(*treeview, (*(treeview->get_model()->get_iter(path)))[tooltip_column], true); + } + + return false; // continue handling (no conflicts there?) + } + + + /// A helper function for gtkmm_set_iconview_tooltip_column() + inline bool app_on_iconview_motion_notify_event_tooltip(GdkEventMotion* event, Gtk::IconView* iconview, + Gtk::TreeModelColumn tooltip_column, Glib::RefPtr model) + { + // debug_out_dump("app", DBG_FUNC_MSG << "x=" << int(event->x) << ", y=" << int(event->y) << "\n"); + + // don't use get_item_at_pos() - it's not available in gtkmm < 2.8. + Gtk::TreePath tpath = iconview->get_path_at_pos(static_cast(event->x), static_cast(event->y)); + if (tpath.gobj() && !tpath.empty()) { // without gobj() check gtkmm 2.6 (but not 2.12) prints lots of errors + // somehow, getting model from iconview doesn't work (segfault). + // Glib::RefPtr model = iconview->get_model(); + if (tpath.gobj()) + app_gtkmm_set_widget_tooltip(*iconview, (*(model->get_iter(tpath)))[tooltip_column], true); + } + + return false; // continue handling (no conflicts there?) + } + +} + +#endif + + + +void gtkmm_set_treeview_tooltip_column(Gtk::TreeView* treeview, + Gtk::TreeModelColumn& col_tooltip) +{ + if (!treeview) + return; + +#if !(defined APP_GTKMM_OLD_TOOLTIPS && APP_GTKMM_OLD_TOOLTIPS) + treeview->set_tooltip_column(col_tooltip.index()); // gtkmm has only index-based api here + +#else // manually set tooltips when hovering over cells + treeview->signal_motion_notify_event().connect( // connect before other events, or we'll get some screwed event data + sigc::bind(sigc::bind(sigc::ptr_fun(&app_on_treeview_motion_notify_event_tooltip), col_tooltip), treeview), false); +#endif +} + + + +void gtkmm_set_iconview_tooltip_column(Gtk::IconView* iconview, + Gtk::TreeModelColumn& col_tooltip, Glib::RefPtr model) +{ + if (!iconview) + return; + +#if !(defined APP_GTKMM_OLD_TOOLTIPS && APP_GTKMM_OLD_TOOLTIPS) + iconview->set_tooltip_column(col_tooltip.index()); // gtkmm has only index-based api here + +#else // manually set tooltips when hovering over cells + iconview->signal_motion_notify_event().connect( // connect before other events, or we'll get some screwed event data + sigc::bind(sigc::bind(sigc::bind(sigc::ptr_fun(&app_on_iconview_motion_notify_event_tooltip), model), col_tooltip), iconview), false); +#endif +} + + + +bool app_gtkmm_icon_theme_has_icon(Glib::RefPtr theme, + const Glib::ustring& icon_name, int size) +{ + if (!theme || !theme->has_icon(icon_name)) // check if it exists first, or exception may be thrown. + return false; + + std::vector sizes = theme->get_icon_sizes(icon_name); + + for (std::vector::const_iterator iter = sizes.begin(); iter != sizes.end(); ++iter) { + if (*iter == size || *iter == -1) // -1 means scalable + return true; + } + + return false; +} + + + +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_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); +} + + + + + + + +/// @} diff --git a/gsmartcontrol/src/applib/app_gtkmm_utils.h b/src/applib/app_gtkmm_utils.h similarity index 70% rename from gsmartcontrol/src/applib/app_gtkmm_utils.h rename to src/applib/app_gtkmm_utils.h index 74de72f..5577e7d 100644 --- a/gsmartcontrol/src/applib/app_gtkmm_utils.h +++ b/src/applib/app_gtkmm_utils.h @@ -13,7 +13,15 @@ #define APP_GTKMM_UTILS_H #include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // must include (even if gtkmm.h is included) for older gtkmm (at least 2.6, but not 2.12) #include "hz/down_cast.h" @@ -29,11 +37,29 @@ Gtk::Widget* app_gtkmm_get_column_header(Gtk::TreeViewColumn& column); Gtk::Widget* app_gtkmm_labelize_column(Gtk::TreeViewColumn& column); +/// Unset model on treeview, cross-gtkmm-version. +void app_gtkmm_treeview_unset_model(Gtk::TreeView* treeview); + + +/// Unset model on combobox (there's no straight way to do it in gtkmm, I think) +void app_gtkmm_combobox_unset_model(Gtk::ComboBox* box); + + /// 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); +/// A portable wrapper around TreeView::set_tooltip_column(). +void gtkmm_set_treeview_tooltip_column(Gtk::TreeView* treeview, + Gtk::TreeModelColumn& col_tooltip); + + +/// A portable wrapper around IconView::set_tooltip_column(). +void gtkmm_set_iconview_tooltip_column(Gtk::IconView* iconview, + Gtk::TreeModelColumn& col_tooltip, Glib::RefPtr model); + + /// Convenience function for creating a TreeViewColumn . template @@ -68,8 +94,9 @@ int app_gtkmm_create_tree_view_column(Gtk::TreeModelColumn& mcol, Gtk::TreeVi -/// Get Glib::ustring from gchar*, freeing gchar*. -Glib::ustring app_ustring_from_gchar(gchar* str); +/// Check whether the icon theme has the specified icon of size \c size. +bool app_gtkmm_icon_theme_has_icon(Glib::RefPtr theme, + const Glib::ustring& icon_name, int size); /// Convert a possibly invalid utf-8 string to valid utf-8. diff --git a/gsmartcontrol/src/applib/app_pango_utils.cpp b/src/applib/app_pango_utils.cpp similarity index 100% rename from gsmartcontrol/src/applib/app_pango_utils.cpp rename to src/applib/app_pango_utils.cpp diff --git a/gsmartcontrol/src/applib/app_pango_utils.h b/src/applib/app_pango_utils.h similarity index 100% rename from gsmartcontrol/src/applib/app_pango_utils.h rename to src/applib/app_pango_utils.h diff --git a/gsmartcontrol/src/applib/app_pcrecpp.h b/src/applib/app_pcrecpp.h similarity index 97% rename from gsmartcontrol/src/applib/app_pcrecpp.h rename to src/applib/app_pcrecpp.h index 2526843..ff388f1 100644 --- a/gsmartcontrol/src/applib/app_pcrecpp.h +++ b/src/applib/app_pcrecpp.h @@ -12,11 +12,18 @@ #ifndef APP_PCRECPP_H #define APP_PCRECPP_H -// A wrapper header for pcrecpp +// A wrapper header for bundled pcrecpp + +// disable all that declspec noise +#ifndef PCRE_STATIC + #define PCRE_STATIC +#endif + + +#include "pcrecpp/pcrecpp_internal.h" +#include "pcrecpp/pcrecpp.h" -#include #include - #include "hz/debug.h" diff --git a/src/applib/app_ui_res_utils.h b/src/applib/app_ui_res_utils.h new file mode 100644 index 0000000..0df7187 --- /dev/null +++ b/src/applib/app_ui_res_utils.h @@ -0,0 +1,423 @@ +/************************************************************************** + 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_*) + +#if defined ENABLE_LIBGLADE && ENABLE_LIBGLADE + #include +#elif defined ENABLE_GTKBUILDER && ENABLE_GTKBUILDER + #include +#endif + +// 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 "app_gtkmm_features.h" // APP_GTKMM_OLD_TOOLTIPS + +#if defined APP_GTKMM_OLD_TOOLTIPS && APP_GTKMM_OLD_TOOLTIPS + #include // gtk_tooltips_* +#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.glade or main_window.ui (depending on whether +/// you're using libglade or gtkbuilder) in data file search paths. +/// Or, if you're using compiled-in buffers, it will make them available. +#if defined ENABLE_LIBGLADE && ENABLE_LIBGLADE + + #define APP_UI_RES_DATA_INIT(res_name) \ + HZ_RES_DATA_INIT_NAMED(res_name##_ui, #res_name ".glade", UIResDataBase); \ + struct UIResData : public UIResDataBase { \ + UIResData() \ + { \ + UIResDataBase::root_name = #res_name; /* we need original name here, not with _ui */ \ + } \ + } + + +#elif defined ENABLE_GTKBUILDER && ENABLE_GTKBUILDER + + #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 */ \ + } \ + } + + +#else // none, just avoid errors + + #define APP_UI_RES_DATA_INIT(res_name) \ + HZ_RES_DATA_DUMMY_INIT_NAMED(res_name##_ui, 0, UIResDataBase); \ + struct UIResData : public UIResDataBase { \ + UIResData() \ + { \ + UIResDataBase::root_name = #res_name; /* we need original name here, not with _ui */ \ + } \ + } + +#endif + + + + +/// \typedef app_ui_res_ref_t +/// A reference-counting pointer to application UI resource + +/// \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. + +#if defined ENABLE_LIBGLADE && ENABLE_LIBGLADE + + typedef Glib::RefPtr app_ui_res_ref_t; + + + 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 { + // Glib::RefPtr ref = Gnome::Glade::Xml::create("main_window.glade"); + ref = Gnome::Glade::Xml::create_from_buffer(reinterpret_cast(buf), + static_cast(buf_size)); + } + catch (Gnome::Glade::XmlError& ex) { + error_msg = ex.what(); + return false; + } + + #else + + std::auto_ptr error; + ref = Gnome::Glade::Xml::create_from_buffer(reinterpret_cast(buf), + static_cast(buf_size),"", "", error); + + if (error.get()) { + error_msg = error->what(); + return false; + } + #endif + + return true; + } + + +#elif defined ENABLE_GTKBUILDER && ENABLE_GTKBUILDER + + typedef Glib::RefPtr app_ui_res_ref_t; + + + 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; + } + + +#else // none, just avoid compilation errors + + /// A dummy structure that can be used to emulate a (non-functional) resource. + struct AppUIResDummy { + template + void get_widget_derived(const Glib::ustring& name, T*& widget) + { } + template + void get_widget(const Glib::ustring& name, T*& widget) + { } + }; + + typedef AppUIResDummy* app_ui_res_ref_t; + + 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) + { + error_msg = "Neither Gtk::Builder (gtkmm >= 2.12) nor libglademm were present during compilation." + " Recompile when either dependency is resolved."; + return false; + } + +#endif + + + + +// These allow easy attaching of glade 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 glade/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 glade/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 Glade-enabled windows (or any other glade-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; + +#if defined ENABLE_LIBGLADE && ENABLE_LIBGLADE + friend class Gnome::Glade::Xml; // allow construction through libglade +#elif defined ENABLE_GTKBUILDER && ENABLE_GTKBUILDER + friend class Gtk::Builder; // allow construction through gtkbuilder +#endif + 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; +#if defined ENABLE_LIBGLADE && ENABLE_LIBGLADE + app_ui_res_ref_t ui; +#elif defined ENABLE_GTKBUILDER && ENABLE_GTKBUILDER + app_ui_res_ref_t ui = Gtk::Builder::create(); +#else // none, just avoid compilation errors + app_ui_res_ref_t ui = 0; +#endif + const typename Child::UIResData data; // this holds the glade 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) + { +#if defined ENABLE_LIBGLADE && ENABLE_LIBGLADE + return static_cast(lookup_widget(name)); // all glade objects are widgets, it's an upcast +#elif defined ENABLE_GTKBUILDER && ENABLE_GTKBUILDER + return ref_ui_->get_object(name).operator->(); // silly RefPtr doesn't have get(). +#else // none, just avoid compilation errors + return 0; +#endif + } + + + /// 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_GLADE_ macros + typedef WidgetType widget_type; ///< This is needed by APP_GLADE_ macros + + + // protected constructor / destructor, use create() / destroy() instead of new / delete. + + /// Glade 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) + { + // if gtkmm is older than 2.12, use the old tooltips API +#if defined APP_GTKMM_OLD_TOOLTIPS && APP_GTKMM_OLD_TOOLTIPS + // attach it to the window + this->create_tooltips_data(this); +#endif + + + // manually connecting signals: + // this->signal_delete_event().connect(sigc::mem_fun(*this, &MainWindow::on_main_window_delete)); + + // signals of glade-created objects: + // Gtk::ToolButton* rescan_devices_toolbutton = 0; + // APP_UI_RES_AUTO_CONNECT(rescan_devices_toolbutton, clicked); + + // show(); + } + + + /// Virtual destructor + virtual ~AppUIResWidget() + { } + + +#if defined APP_GTKMM_OLD_TOOLTIPS && APP_GTKMM_OLD_TOOLTIPS + // differentiate Window and its children from other widgets through overloading + + /// Create tooltips for a window + void create_tooltips_data(Gtk::Window* window) + { + if (window) { + // gtkmm tooltips api is somewhat broken (no create?), so use gtk + if (!window->get_data("window_tooltips")) { + window->set_data("window_tooltips", gtk_tooltips_new()); + } + } + } + + /// Create tooltips for an object (a dummy function used for overload). + void create_tooltips_data(Gtk::Object* o) + { } +#endif + + + private: + + app_ui_res_ref_t ref_ui_; ///< UI resource + +}; + + + + + + +#endif + +/// @} diff --git a/gsmartcontrol/src/applib/app_ui_res_utils_test.cpp b/src/applib/app_ui_res_utils_test.cpp similarity index 89% rename from gsmartcontrol/src/applib/app_ui_res_utils_test.cpp rename to src/applib/app_ui_res_utils_test.cpp index b1703d0..91c789a 100644 --- a/gsmartcontrol/src/applib/app_ui_res_utils_test.cpp +++ b/src/applib/app_ui_res_utils_test.cpp @@ -11,7 +11,11 @@ #include -#include +#include +#include +#include +#include +#include #include "app_ui_res_utils.h" // #include "hz/instance_manager.h" @@ -26,7 +30,7 @@ class AppUiResTestWindow : public AppUIResWidget { public: - // name of ui file without a .ui extension and quotes + // name of glade/ui file without a .glade/.ui extension and quotes APP_UI_RES_DATA_INIT(app_ui_res_test_window); @@ -40,7 +44,7 @@ class AppUiResTestWindow : public AppUIResWidget 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)); + Gtk::Box* vbox = Gtk::manage(new Gtk::VBox(false, 5)); add(*vbox); Gtk::Button* button = Gtk::manage(new Gtk::Button("Clicky")); diff --git a/src/applib/app_ui_res_utils_test_data.cpp b/src/applib/app_ui_res_utils_test_data.cpp new file mode 100644 index 0000000..20bb27e --- /dev/null +++ b/src/applib/app_ui_res_utils_test_data.cpp @@ -0,0 +1,582 @@ +/************************************************************************** + Copyright: + (C) 2008 - 2012 Alexander Shaduri + License: See LICENSE_unlicense.txt +***************************************************************************/ + +#include "hz/hz_config.h" // feature test macros (ENABLE_*) + + +// A simple empty window definition + +#if defined ENABLE_LIBGLADE && ENABLE_LIBGLADE + + +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, +0x20, +0x65, +0x6E, +0x63, +0x6F, +0x64, +0x69, +0x6E, +0x67, +0x3D, +0x22, +0x55, +0x54, +0x46, +0x2D, +0x38, +0x22, +0x20, +0x73, +0x74, +0x61, +0x6E, +0x64, +0x61, +0x6C, +0x6F, +0x6E, +0x65, +0x3D, +0x22, +0x6E, +0x6F, +0x22, +0x3F, +0x3E, +0xA, +0x3C, +0x21, +0x44, +0x4F, +0x43, +0x54, +0x59, +0x50, +0x45, +0x20, +0x67, +0x6C, +0x61, +0x64, +0x65, +0x2D, +0x69, +0x6E, +0x74, +0x65, +0x72, +0x66, +0x61, +0x63, +0x65, +0x20, +0x53, +0x59, +0x53, +0x54, +0x45, +0x4D, +0x20, +0x22, +0x67, +0x6C, +0x61, +0x64, +0x65, +0x2D, +0x32, +0x2E, +0x30, +0x2E, +0x64, +0x74, +0x64, +0x22, +0x3E, +0xA, +0x3C, +0x67, +0x6C, +0x61, +0x64, +0x65, +0x2D, +0x69, +0x6E, +0x74, +0x65, +0x72, +0x66, +0x61, +0x63, +0x65, +0x3E, +0xA, +0x20, +0x20, +0x3C, +0x77, +0x69, +0x64, +0x67, +0x65, +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, +0x77, +0x69, +0x64, +0x67, +0x65, +0x74, +0x3E, +0xA, +0x3C, +0x2F, +0x67, +0x6C, +0x61, +0x64, +0x65, +0x2D, +0x69, +0x6E, +0x74, +0x65, +0x72, +0x66, +0x61, +0x63, +0x65, +0x3E, +0xA, + + 0x0 }; + +extern const unsigned int app_ui_res_test_window_ui_size = 319; + + + + +#elif defined ENABLE_GTKBUILDER && ENABLE_GTKBUILDER + + +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; + + +#endif + + + diff --git a/gsmartcontrol/src/applib/cli_executors.h b/src/applib/cli_executors.h similarity index 100% rename from gsmartcontrol/src/applib/cli_executors.h rename to src/applib/cli_executors.h diff --git a/gsmartcontrol/src/applib/cmdex.cpp b/src/applib/cmdex.cpp similarity index 89% rename from gsmartcontrol/src/applib/cmdex.cpp rename to src/applib/cmdex.cpp index 6ed29c1..54be93d 100644 --- a/gsmartcontrol/src/applib/cmdex.cpp +++ b/src/applib/cmdex.cpp @@ -92,10 +92,8 @@ extern "C" { bool Cmdex::execute() { - DBG_FUNCTION_ENTER_MSG; - if (this->running_ || this->stopped_cleanup_needed()) { + if (this->running_ || this->stopped_cleanup_needed()) return false; - } cleanup_members(); clear_errors(); @@ -227,11 +225,9 @@ bool Cmdex::execute() 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. @@ -242,7 +238,6 @@ bool Cmdex::execute() this->running_ = true; // the process is running now. - DBG_FUNCTION_EXIT_MSG; return true; } @@ -252,7 +247,6 @@ bool Cmdex::execute() // send SIGTERM(15) (terminate) bool Cmdex::try_stop(hz::signal_t sig) { - DBG_FUNCTION_ENTER_MSG; if (!this->running_ || this->pid_ <= 0) return false; @@ -268,7 +262,6 @@ bool Cmdex::try_stop(hz::signal_t sig) // Possible: EPERM (no permissions), ESRCH (no such process, or zombie) push_error(Error("errno", ErrorLevel::error, errno), false); - DBG_FUNCTION_EXIT_MSG; return false; } @@ -276,7 +269,6 @@ bool Cmdex::try_stop(hz::signal_t sig) bool Cmdex::try_kill() { - DBG_TRACE_POINT_AUTO; return try_stop(hz::SIGNAL_SIGKILL); } @@ -284,7 +276,6 @@ bool Cmdex::try_kill() 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 @@ -297,8 +288,6 @@ void Cmdex::set_stop_timeouts(int term_timeout_msec, int kill_timeout_msec) if (kill_timeout_msec != 0) event_source_id_kill = g_timeout_add(kill_timeout_msec, &cmdex_on_kill_timeout, this); - - DBG_FUNCTION_EXIT_MSG; } @@ -306,7 +295,6 @@ void Cmdex::set_stop_timeouts(int term_timeout_msec, int kill_timeout_msec) 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) @@ -320,7 +308,6 @@ void Cmdex::unset_stop_timeouts() g_source_destroy(source_kill); event_source_id_kill = 0; } - DBG_FUNCTION_EXIT_MSG; } @@ -328,7 +315,8 @@ void Cmdex::unset_stop_timeouts() // executed in main thread, manually by the caller. void Cmdex::stopped_cleanup() { - DBG_FUNCTION_ENTER_MSG; +// DBG_FUNCTION_ENTER_MSG; + if (this->running_ || !this->stopped_cleanup_needed()) // huh? return; @@ -365,7 +353,6 @@ void Cmdex::stopped_cleanup() cleanup_members(); this->running_ = false; - DBG_FUNCTION_EXIT_MSG; } @@ -384,25 +371,6 @@ void Cmdex::on_child_watch_handler(GPid arg_pid, int waitpid_status, gpointer da 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). @@ -433,10 +401,9 @@ void Cmdex::on_child_watch_handler(GPid arg_pid, int waitpid_status, gpointer da -gboolean Cmdex::on_channel_io(GIOChannel* channel, +gboolean Cmdex::on_channel_io(GIOChannel* source, 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"); @@ -453,10 +420,13 @@ gboolean Cmdex::on_channel_io(GIOChannel* channel, const gsize count = 1; gchar buf[count] = {0}; + GIOChannel* channel = 0; std::string* output_str = 0; if (type == channel_type_stdout) { + channel = self->channel_stdout_; output_str = &self->str_stdout_; } else if (type == channel_type_stderr) { + channel = self->channel_stderr_; output_str = &self->str_stderr_; } @@ -489,20 +459,6 @@ gboolean Cmdex::on_channel_io(GIOChannel* channel, -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; -} - - - diff --git a/gsmartcontrol/src/applib/cmdex.h b/src/applib/cmdex.h similarity index 93% rename from gsmartcontrol/src/applib/cmdex.h rename to src/applib/cmdex.h index 2be0ad0..5382c7e 100644 --- a/gsmartcontrol/src/applib/cmdex.h +++ b/src/applib/cmdex.h @@ -7,8 +7,8 @@ #ifndef APP_CMDEX_H #define APP_CMDEX_H -#include #include +#include #include "hz/process_signal.h" // hz::SIGNAL_* #include "hz/error_holder.h" @@ -217,14 +217,36 @@ class Cmdex : public hz::ErrorHolder { 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); + static gboolean on_channel_io(GIOChannel* source, + GIOCondition cond, Cmdex* self, channel_t type); private: /// Clean up the member variables and shut down the channels if needed. - void cleanup_members(); + void 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; + + if (channel_stdout_) { + g_io_channel_shutdown(channel_stdout_, false, NULL); + g_io_channel_unref(channel_stdout_); + channel_stdout_ = 0; + } + if (channel_stderr_) { + g_io_channel_shutdown(channel_stderr_, false, NULL); + g_io_channel_unref(channel_stderr_); + channel_stderr_ = 0; + } + } diff --git a/gsmartcontrol/src/applib/cmdex_sync.cpp b/src/applib/cmdex_sync.cpp similarity index 100% rename from gsmartcontrol/src/applib/cmdex_sync.cpp rename to src/applib/cmdex_sync.cpp diff --git a/gsmartcontrol/src/applib/cmdex_sync.h b/src/applib/cmdex_sync.h similarity index 100% rename from gsmartcontrol/src/applib/cmdex_sync.h rename to src/applib/cmdex_sync.h diff --git a/gsmartcontrol/src/applib/cmdex_sync_gui.cpp b/src/applib/cmdex_sync_gui.cpp similarity index 91% rename from gsmartcontrol/src/applib/cmdex_sync_gui.cpp rename to src/applib/cmdex_sync_gui.cpp index 288f1bf..453a491 100644 --- a/gsmartcontrol/src/applib/cmdex_sync_gui.cpp +++ b/src/applib/cmdex_sync_gui.cpp @@ -9,12 +9,12 @@ /// \weakgroup applib /// @{ -#include // Gtk::Main -#include +#include // Gtk::Main #include "hz/fs_path.h" #include "hz/string_sprintf.h" +#include "app_gtkmm_features.h" // APP_GTKMM_CHECK_VERSION #include "cmdex_sync_gui.h" @@ -28,8 +28,15 @@ bool CmdexSyncGui::execute() } -#define CMDEX_DIALOG_MESSAGE_TYPE Gtk::MESSAGE_OTHER -#define CMDEX_DIALOG_HINT_TYPE Gdk::WINDOW_TYPE_HINT_NOTIFICATION +#if APP_GTKMM_CHECK_VERSION(2, 10, 0) + // these are available since gtkmm 2.10 + #define CMDEX_DIALOG_MESSAGE_TYPE Gtk::MESSAGE_OTHER + // not present in Gdk:: in 2.10 (it's in 2.12), we have to use GDK_ + #define CMDEX_DIALOG_HINT_TYPE GDK_WINDOW_TYPE_HINT_NOTIFICATION +#else + #define CMDEX_DIALOG_MESSAGE_TYPE Gtk::MESSAGE_INFO + #define CMDEX_DIALOG_HINT_TYPE GDK_WINDOW_TYPE_HINT_UTILITY +#endif @@ -54,7 +61,9 @@ Gtk::MessageDialog* CmdexSyncGui::create_running_dialog(Gtk::Window* parent, con &CmdexSyncGui::on_running_dialog_response)); running_dialog_->set_decorated(false); - running_dialog_->set_deletable(false); +#if APP_GTKMM_CHECK_VERSION(2, 10, 0) + running_dialog_->set_deletable(false); // since 2.10 +#endif 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)); diff --git a/gsmartcontrol/src/applib/cmdex_sync_gui.h b/src/applib/cmdex_sync_gui.h similarity index 100% rename from gsmartcontrol/src/applib/cmdex_sync_gui.h rename to src/applib/cmdex_sync_gui.h diff --git a/gsmartcontrol/src/applib/executor_factory.cpp b/src/applib/executor_factory.cpp similarity index 100% rename from gsmartcontrol/src/applib/executor_factory.cpp rename to src/applib/executor_factory.cpp diff --git a/gsmartcontrol/src/applib/executor_factory.h b/src/applib/executor_factory.h similarity index 100% rename from gsmartcontrol/src/applib/executor_factory.h rename to src/applib/executor_factory.h diff --git a/gsmartcontrol/src/applib/gui_utils.cpp b/src/applib/gui_utils.cpp similarity index 93% rename from gsmartcontrol/src/applib/gui_utils.cpp rename to src/applib/gui_utils.cpp index 8a6413d..6289d8e 100644 --- a/gsmartcontrol/src/applib/gui_utils.cpp +++ b/src/applib/gui_utils.cpp @@ -9,6 +9,13 @@ /// \weakgroup applib /// @{ +#include +#include +#include +#include +#include +#include + #include "app_pango_utils.h" // app_pango_strip_markup() #include "gui_utils.h" @@ -113,7 +120,7 @@ bool gui_show_text_entry_dialog(const std::string& title, const std::string& mes + (sec_message.empty() ? "\n" : "") + ""); main_label.set_line_wrap(true); main_label.set_selectable(true); - main_label.set_alignment(Gtk::ALIGN_START); + main_label.set_alignment(0.0, 0.0); Gtk::Label sec_label; if (sec_msg_markup) { @@ -123,13 +130,13 @@ bool gui_show_text_entry_dialog(const std::string& title, const std::string& mes } sec_label.set_line_wrap(true); sec_label.set_selectable(true); - sec_label.set_alignment(Gtk::ALIGN_START); + sec_label.set_alignment(0.0, 0.0); Gtk::Entry input_entry; input_entry.set_activates_default(true); - Gtk::Box vbox; + Gtk::VBox vbox; vbox.set_spacing(12); vbox.pack_start(main_label, false, false, 0); vbox.pack_start(sec_label, true, true, 0); @@ -146,7 +153,7 @@ bool gui_show_text_entry_dialog(const std::string& title, const std::string& mes dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); Gtk::Button ok_button(Gtk::Stock::OK); - ok_button.set_can_default(true); + ok_button.set_flags(ok_button.get_flags() | Gtk::CAN_DEFAULT); ok_button.show_all(); dialog.add_action_widget(ok_button, Gtk::RESPONSE_OK); ok_button.grab_default(); // make it the default widget diff --git a/gsmartcontrol/src/applib/gui_utils.h b/src/applib/gui_utils.h similarity index 98% rename from gsmartcontrol/src/applib/gui_utils.h rename to src/applib/gui_utils.h index 59b9357..4bdba23 100644 --- a/gsmartcontrol/src/applib/gui_utils.h +++ b/src/applib/gui_utils.h @@ -14,7 +14,7 @@ #include -#include +#include // These functions won't return until the dialogs are closed. diff --git a/gsmartcontrol/src/applib/selftest.cpp b/src/applib/selftest.cpp similarity index 95% rename from gsmartcontrol/src/applib/selftest.cpp rename to src/applib/selftest.cpp index f6348ba..7f7b134 100644 --- a/gsmartcontrol/src/applib/selftest.cpp +++ b/src/applib/selftest.cpp @@ -108,7 +108,7 @@ std::string SelfTest::start(hz::intrusive_ptr smartctl_ex) return "Invalid test specified"; std::string output; - std::string error_msg = drive_->execute_device_smartctl("--test=" + test_param, smartctl_ex, output); + std::string error_msg = drive_->execute_device_smartctl("-t " + test_param, smartctl_ex, output); // --test= if (!error_msg.empty()) // checks for empty output too return error_msg; @@ -168,7 +168,7 @@ std::string SelfTest::force_stop(hz::intrusive_ptr smartctl_ex) // 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); + std::string error_msg = drive_->execute_device_smartctl("-X", smartctl_ex, output); // --abort if (!error_msg.empty()) // checks for empty output too return error_msg; @@ -207,8 +207,8 @@ std::string SelfTest::update(hz::intrusive_ptr smartctl_ex) 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); +// std::string error_msg = drive_->execute_device_smartctl("-l selftest", smartctl_ex, output); // --log= + std::string error_msg = drive_->execute_device_smartctl("-c", smartctl_ex, output); // --capabilities if (!error_msg.empty()) // checks for empty output too return error_msg; diff --git a/gsmartcontrol/src/applib/selftest.h b/src/applib/selftest.h similarity index 99% rename from gsmartcontrol/src/applib/selftest.h rename to src/applib/selftest.h index 397e08f..706d5f0 100644 --- a/gsmartcontrol/src/applib/selftest.h +++ b/src/applib/selftest.h @@ -12,8 +12,8 @@ #ifndef SELFTEST_H #define SELFTEST_H -#include #include +#include #include "hz/cstdint.h" #include "hz/intrusive_ptr.h" diff --git a/gsmartcontrol/src/applib/smartctl_executor.cpp b/src/applib/smartctl_executor.cpp similarity index 90% rename from gsmartcontrol/src/applib/smartctl_executor.cpp rename to src/applib/smartctl_executor.cpp index 1614323..cfab020 100644 --- a/gsmartcontrol/src/applib/smartctl_executor.cpp +++ b/src/applib/smartctl_executor.cpp @@ -34,20 +34,16 @@ std::string get_smartctl_binary() if (!use_smt) break; - std::string smt_regpath, smt_regpath_wow, smt_regkey, smt_smartctl; + std::string smt_regpath, 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()) + if (smt_regpath.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\\" diff --git a/gsmartcontrol/src/applib/smartctl_executor.h b/src/applib/smartctl_executor.h similarity index 100% rename from gsmartcontrol/src/applib/smartctl_executor.h rename to src/applib/smartctl_executor.h diff --git a/gsmartcontrol/src/applib/smartctl_executor_gui.h b/src/applib/smartctl_executor_gui.h similarity index 100% rename from gsmartcontrol/src/applib/smartctl_executor_gui.h rename to src/applib/smartctl_executor_gui.h diff --git a/gsmartcontrol/src/applib/smartctl_executor_test.cpp b/src/applib/smartctl_executor_test.cpp similarity index 98% rename from gsmartcontrol/src/applib/smartctl_executor_test.cpp rename to src/applib/smartctl_executor_test.cpp index 731a89a..b8e0248 100644 --- a/gsmartcontrol/src/applib/smartctl_executor_test.cpp +++ b/src/applib/smartctl_executor_test.cpp @@ -10,7 +10,7 @@ /// @{ #include -#include +#include // #include "smartctl_executor_gui.h" #include "smartctl_executor.h" diff --git a/gsmartcontrol/src/applib/smartctl_parser.cpp b/src/applib/smartctl_parser.cpp similarity index 61% rename from gsmartcontrol/src/applib/smartctl_parser.cpp rename to src/applib/smartctl_parser.cpp index ba02f9d..dc2f57f 100644 --- a/gsmartcontrol/src/applib/smartctl_parser.cpp +++ b/src/applib/smartctl_parser.cpp @@ -72,7 +72,7 @@ SmartctlParser::SmartctlParser() -// Parse full "smartctl -x" output +// Parse full "smartctl -a" output bool SmartctlParser::parse_full(const std::string& full, StorageAttribute::DiskType disk_type) { this->clear(); // clear previous data @@ -140,71 +140,22 @@ bool SmartctlParser::parse_full(const std::string& full, StorageAttribute::DiskT // 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). + // Fix that. { 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 + app_pcre_replace(re1, "\n" + match, s); // add an extra newline if (app_pcre_match(re2, s, &match)) - app_pcre_replace(re2, "\n" + match + "\n", s); // add extra newlines + app_pcre_replace(re2, "\n" + match, s); // add an extra newline if (app_pcre_match(re3, s, &match)) - app_pcre_replace(re3, "\n" + match + "\n", s); // add extra newlines + app_pcre_replace(re3, "\n" + match, s); // add an extra newline - 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 } @@ -239,7 +190,7 @@ bool SmartctlParser::parse_full(const std::string& full, StorageAttribute::DiskT } } - if (!check_parsed_version(version, version_full)) { + if (!check_version(version, version_full)) { set_error_msg("Incompatible smartctl version."); debug_out_warn("app", DBG_FUNC_MSG << "Incompatible smartctl version. Returning.\n"); return false; @@ -254,7 +205,7 @@ bool SmartctlParser::parse_full(const std::string& full, StorageAttribute::DiskT // 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) { + && (section_start_pos = s.find("===", 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. @@ -264,7 +215,7 @@ bool SmartctlParser::parse_full(const std::string& full, StorageAttribute::DiskT 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_end_pos = s.find("===", 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))); } @@ -287,9 +238,7 @@ bool SmartctlParser::parse_full(const std::string& full, StorageAttribute::DiskT // 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" + // e.g. "smartctl version 5.37" or "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)) { @@ -305,18 +254,18 @@ bool SmartctlParser::parse_version(const std::string& s, std::string& version, s // 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) +bool SmartctlParser::check_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 minimum_version = 5.0; double version = 0; if (hz::string_is_numeric(version_str, version, false)) { - if (version >= minimum_req_version) + if (version >= minimum_version) return true; } @@ -415,6 +364,7 @@ bool SmartctlParser::parse_section(const std::string& header, const std::string& // ------------------------------------------------ INFO SECTION +// Parse the info section (without "===" header) bool SmartctlParser::parse_section_info(const std::string& body) { this->set_data_section_info(body); @@ -425,98 +375,26 @@ bool SmartctlParser::parse_section_info(const std::string& body) // 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; + std::string name, value; + pcrecpp::StringPiece input(body); // position tracker -// 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; - } + while (re.FindAndConsume(&input, &name, &value)) { + hz::string_trim(name); + hz::string_trim(value); // 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)) { + if (app_pcre_match("/mandatory SMART command failed/mi", name)) 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; - 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. - 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"); - } + add_property(p); } return true; @@ -560,11 +438,6 @@ bool SmartctlParser::parse_section_info_property(StorageProperty& p) 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; @@ -575,11 +448,6 @@ bool SmartctlParser::parse_section_info_property(StorageProperty& p) 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; @@ -615,11 +483,6 @@ bool SmartctlParser::parse_section_info_property(StorageProperty& p) 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; @@ -677,59 +540,7 @@ bool SmartctlParser::parse_section_info_property(StorageProperty& p) 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)) { + } else if (app_pcre_match("/^scsiMode/mi", p.reported_name)) { // these are some debug warnings from smartctl on usb flash drives p.show_in_ui = false; } else { @@ -758,9 +569,8 @@ bool SmartctlParser::parse_section_data(const std::string& body) // 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. + // subsections are separated by double newlines, except the "error log" subsection, + // which contains double-newline-separated blocks. hz::string_split(body, "\n\n", split_subsections, true); bool status = false; // at least one subsection was parsed @@ -768,21 +578,14 @@ bool SmartctlParser::parse_section_data(const std::string& body) 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". + // merge "error log" parts. each part begins with a double-space or "Error nn". 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 (app_pcre_re("^ ").PartialMatch(sub) || app_pcre_re("^Error [0-9]+").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"); + debug_out_warn("app", DBG_FUNC_MSG << "Error Log's Error block found without any data subsections present.\n"); } } else { // not an Error block, process as usual subsections.push_back(sub); @@ -796,95 +599,30 @@ bool SmartctlParser::parse_section_data(const std::string& body) if (sub.empty()) continue; - if (app_pcre_match("/^SMART overall-health self-assessment/mi", sub)) { + 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)) { + } 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)) { + } 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 + } else if (app_pcre_match("/SMART Error Log Version/mi", sub) + || app_pcre_match("/Warning: device does not support Error Logging/mi", sub) + || app_pcre_match("/SMART Error Log not supported/mi", sub)) { 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 + } else if (app_pcre_match("/SMART Self-test log/mi", sub) + || app_pcre_match("/Warning: device does not support Self Test Logging/mi", sub) + || app_pcre_match("/SMART Self-test Log not supported/mi", sub)) { 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) ) { + } 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)) { 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"); @@ -903,18 +641,6 @@ bool SmartctlParser::parse_section_data(const std::string& body) 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; @@ -947,41 +673,6 @@ Device is: In smartctl database [for details use: -P show] 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; @@ -1405,37 +1096,24 @@ bool SmartctlParser::parse_section_data_subsection_attributes(const std::string& // 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. - + // So, parse until we encounter the next column. + // * "Old Brief" format: One WD drive had non-integer flags, something like "PO--C-", with several + // lines of their descriptions after the attributes block (each line started with spaces and |) + // (seems to be a CVS version from 2006). We don't support this (it's difficult to separate + // the name with spaces from the flag). // * 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 -*/ + // "ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE" + // " 1 Raw_Read_Error_Rate 0x000f 115 099 006 Pre-fail Always - 90981479" + // " 12 Power_Cycle_Count 0x0000 --- --- --- Old_age Offline - 167" // "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 -*/ + // "ID# ATTRIBUTE_NAME FLAGS VALUE WORST THRESH FAIL RAW_VALUE" + // " 1 Raw_Read_Error_Rate POSR-- 118 099 006 - 174772734" + enum { FormatStyleOld, FormatStyleNoUpdated, // old format without UPDATED column @@ -1467,8 +1145,8 @@ ID# ATTRIBUTE_NAME FLAGS VALUE WORST THRESH FAIL RAW_VALUE 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]; + for(unsigned int i = 0; i < lines.size(); ++i) { + std::string line = lines[i]; // skip the non-informative lines if (line.empty() || app_pcre_match("/SMART Attributes with Thresholds/mi", line)) @@ -1617,58 +1295,8 @@ ID# ATTRIBUTE_NAME FLAGS VALUE WORST THRESH FAIL RAW_VALUE -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; -} - +// -------------------- Error Log bool SmartctlParser::parse_section_data_subsection_error_log(const std::string& sub) { @@ -1681,49 +1309,11 @@ bool SmartctlParser::parse_section_data_subsection_error_log(const std::string& // 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"); + pcrecpp::RE re = app_pcre_re("/^(SMART Error Log Version):[ \\t]*(.*)$/mi"); std::string name, value; if (re.PartialMatch(sub, &name, &value)) { @@ -1757,10 +1347,10 @@ Error 1 [0] occurred at disk power-on lifetime: 1 hours (0 days + 1 hours) } } - // Error log entry count + // Error log enty count { // note: these represent the same information - pcrecpp::RE re1 = app_pcre_re("/^(?:ATA|Device) Error Count:[ \\t]*([0-9]+)/mi"); + pcrecpp::RE re1 = app_pcre_re("/^ATA Error Count:[ \\t]*([0-9]+)/mi"); pcrecpp::RE re2 = app_pcre_re("/^No Errors Logged$/mi"); std::string value; @@ -1768,7 +1358,7 @@ Error 1 [0] occurred at disk power-on lifetime: 1 hours (0 days + 1 hours) hz::string_trim(value); StorageProperty p(pt); - p.set_name("ATA Error Count", "error_log_error_count"); + p.set_name("ATA Error Count", "error_count"); p.reported_value = value; p.value_type = StorageProperty::value_type_integer; @@ -1786,17 +1376,14 @@ Error 1 [0] occurred at disk power-on lifetime: 1 hours (0 days + 1 hours) // 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"); + pcrecpp::RE re_block = app_pcre_re("/^((Error[ \\t]*([0-9]+))[ \\t]*occurred at disk power-on lifetime:[ \\t]*([0-9]+) hours.*(?:\\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"); + pcrecpp::RE re_type = app_pcre_re("/[ \\t]+Error:[ \\t]*([ ,a-z]+)[ \\t]+((?:[0-9]+|at )[ \\t]*.*)$/mi"); std::string block, name, value_num, value_time; pcrecpp::StringPiece input(sub); // position tracker @@ -1874,20 +1461,13 @@ bool SmartctlParser::parse_section_data_subsection_selftest_log(const std::strin // 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.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; @@ -1912,15 +1492,13 @@ Num Test_Description Status Remaining LifeTime(hours) LBA // Self-test log version { - // SMART Self-test log structure revision number 1 - // SMART Extended Self-test Log Version: 1 (1 sectors) + // newer smartctl (since smartctl 5.1-16) 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) + // older smartctl 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)) { + if (re1.PartialMatch(sub, &name, &value) || re2.PartialMatch(sub, &name, &value)) { hz::string_trim(value); StorageProperty p(pt); @@ -1941,6 +1519,8 @@ Num Test_Description Status Remaining LifeTime(hours) LBA int64_t test_count = 0; // type is of p.value_integer + // TODO: Support SCSI (it has different output format). + // individual entries { // split by columns. @@ -2036,18 +1616,10 @@ bool SmartctlParser::parse_section_data_subsection_selective_selftest_log(const 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. -*/ + // * 5 (maybe less/more?) test entries, each with + // span / min_lba / max_lba / current_test_status. + // * flags + // * notes bool data_found = false; // true if something was found. @@ -2063,6 +1635,7 @@ If Selective self-test is pending on power-up, resume after 0 minute delay. data_found = true; } + // supported / unsupported { StorageProperty p(pt); @@ -2081,361 +1654,6 @@ If Selective self-test is pending on power-up, resume after 0 minute delay. -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... diff --git a/gsmartcontrol/src/applib/smartctl_parser.h b/src/applib/smartctl_parser.h similarity index 85% rename from gsmartcontrol/src/applib/smartctl_parser.h rename to src/applib/smartctl_parser.h index 72510c3..2c30955 100644 --- a/gsmartcontrol/src/applib/smartctl_parser.h +++ b/src/applib/smartctl_parser.h @@ -34,7 +34,7 @@ class SmartctlParser { SmartctlParser(); - /// Parse full "smartctl -x" output + /// Parse full "smartctl -a" output bool parse_full(const std::string& s, StorageAttribute::DiskType disk_type); @@ -43,7 +43,7 @@ class SmartctlParser { /// 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); + static bool check_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] @@ -57,8 +57,7 @@ class SmartctlParser { bool parse_section(const std::string& header, const std::string& body); - /// Parse the info section (without "===" header). - /// This includes --info and --get=all. + /// Parse the info section (without "===" header) bool parse_section_info(const std::string& body); /// Parse a component (one line) of the info section @@ -72,14 +71,9 @@ class SmartctlParser { 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); @@ -138,21 +132,21 @@ class SmartctlParser { void add_property(StorageProperty p); - /// Set "full" data ("smartctl -x" output) + /// Set "full" data ("smartctl -a" 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) + /// Set "info" section data ("smartctl -i" output, or the first part of "smartctl -a" 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). + /// Parse "data" section data (the second part of "smartctl -a" output). void set_data_section_data(const std::string& s) { data_section_data_ = s; diff --git a/gsmartcontrol/src/applib/smartctl_parser_test.cpp b/src/applib/smartctl_parser_test.cpp similarity index 96% rename from gsmartcontrol/src/applib/smartctl_parser_test.cpp rename to src/applib/smartctl_parser_test.cpp index 3d834f2..d7dc560 100644 --- a/gsmartcontrol/src/applib/smartctl_parser_test.cpp +++ b/src/applib/smartctl_parser_test.cpp @@ -18,7 +18,7 @@ #include #include -#include "libdebug/libdebug.h" +#include "hz/debug.h" #include "hz/fs_file.h" #include "storage_property.h" #include "smartctl_parser.h" @@ -33,8 +33,6 @@ int main(int argc, char** argv) return EXIT_FAILURE; } - debug_register_domain("app"); - std::string file_str = argv[1]; hz::File file(file_str); diff --git a/gsmartcontrol/src/applib/spawn_test.cpp b/src/applib/spawn_test.cpp similarity index 100% rename from gsmartcontrol/src/applib/spawn_test.cpp rename to src/applib/spawn_test.cpp diff --git a/gsmartcontrol/src/applib/storage_detector.cpp b/src/applib/storage_detector.cpp similarity index 100% rename from gsmartcontrol/src/applib/storage_detector.cpp rename to src/applib/storage_detector.cpp diff --git a/gsmartcontrol/src/applib/storage_detector.h b/src/applib/storage_detector.h similarity index 100% rename from gsmartcontrol/src/applib/storage_detector.h rename to src/applib/storage_detector.h diff --git a/gsmartcontrol/src/applib/storage_detector_helpers.h b/src/applib/storage_detector_helpers.h similarity index 100% rename from gsmartcontrol/src/applib/storage_detector_helpers.h rename to src/applib/storage_detector_helpers.h diff --git a/gsmartcontrol/src/applib/storage_detector_linux.cpp b/src/applib/storage_detector_linux.cpp similarity index 100% rename from gsmartcontrol/src/applib/storage_detector_linux.cpp rename to src/applib/storage_detector_linux.cpp diff --git a/gsmartcontrol/src/applib/storage_detector_linux.h b/src/applib/storage_detector_linux.h similarity index 100% rename from gsmartcontrol/src/applib/storage_detector_linux.h rename to src/applib/storage_detector_linux.h diff --git a/gsmartcontrol/src/applib/storage_detector_other.cpp b/src/applib/storage_detector_other.cpp similarity index 100% rename from gsmartcontrol/src/applib/storage_detector_other.cpp rename to src/applib/storage_detector_other.cpp diff --git a/gsmartcontrol/src/applib/storage_detector_other.h b/src/applib/storage_detector_other.h similarity index 100% rename from gsmartcontrol/src/applib/storage_detector_other.h rename to src/applib/storage_detector_other.h diff --git a/gsmartcontrol/src/applib/storage_detector_test.cpp b/src/applib/storage_detector_test.cpp similarity index 100% rename from gsmartcontrol/src/applib/storage_detector_test.cpp rename to src/applib/storage_detector_test.cpp diff --git a/gsmartcontrol/src/applib/storage_detector_win32.cpp b/src/applib/storage_detector_win32.cpp similarity index 86% rename from gsmartcontrol/src/applib/storage_detector_win32.cpp rename to src/applib/storage_detector_win32.cpp index 296fe16..e50f431 100644 --- a/gsmartcontrol/src/applib/storage_detector_win32.cpp +++ b/src/applib/storage_detector_win32.cpp @@ -17,9 +17,6 @@ #include // CreateFileA(), CloseHandle(), etc... #include #include -#include -#include -#include #include "hz/win32_tools.h" #include "hz/string_sprintf.h" @@ -58,12 +55,6 @@ Newer (added after 5.39.1) smartctl supports --scan-open, which will give us: 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 @@ -84,94 +75,25 @@ The drives may be duplicated as pdX (with X and N being unrelated). 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). + +smartctl --scan-open output for win32 with 3ware RAID: +------------------------------------------------------------------ +/dev/sda,0 -d ata (opened) +/dev/sda,1 -d ata (opened) +------------------------------------------------------------------ */ - -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 + 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]; - string drive_str = 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) + ExecutorFactoryRefPtr ex_factory, std::set& equivalent_pds) { debug_out_info("app", "Getting multi-port devices through smartctl --scan-open...\n"); @@ -226,26 +148,14 @@ std::string get_scan_open_multiport_devices(std::vector& dr 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)) { + std::string letter; + if (dev_re.PartialMatch(dev, &letter)) { // don't use pd* devices equivalent to these sd* devices. - drive_num = sd_letter.at(0) - 'a'; - equivalent_pds.insert(drive_num); + equivalent_pds.insert(letter.at(0) - 'a'); } 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); + drives.push_back(StorageDeviceRefPtr(new StorageDevice(full_dev, type))); } } @@ -652,18 +562,12 @@ std::string detect_drives_win32(std::vector& drives, Execut 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); + error_msg = get_scan_open_multiport_devices(drives, ex_factory, used_pds); if (!error_msg.empty()) { error_msgs.push_back(error_msg); } @@ -672,7 +576,7 @@ std::string detect_drives_win32(std::vector& drives, Execut 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; + std::set 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()) { @@ -681,8 +585,7 @@ std::string detect_drives_win32(std::vector& drives, Execut } 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); + serials.insert(drives.at(i)->get_model_name() + "_" + drives.at(i)->get_serial_number()); } // See if there are any areca devices. This is not implemented yet by smartctl (as of 6.0), @@ -694,7 +597,7 @@ std::string detect_drives_win32(std::vector& drives, Execut } - // Scan PhysicalDriveN entries + // Scan PhysicalDrive entries debug_out_info("app", "Starting sequential scan of \\\\.\\PhysicalDriveN devices...\n"); @@ -733,15 +636,6 @@ std::string detect_drives_win32(std::vector& drives, Execut 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. @@ -752,14 +646,11 @@ std::string detect_drives_win32(std::vector& drives, Execut 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) { + if (!drive->get_serial_number().empty() + && serials.count(drive->get_model_name() + "_" + drive->get_serial_number()) > 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; } } diff --git a/gsmartcontrol/src/applib/storage_detector_win32.h b/src/applib/storage_detector_win32.h similarity index 100% rename from gsmartcontrol/src/applib/storage_detector_win32.h rename to src/applib/storage_detector_win32.h diff --git a/gsmartcontrol/src/applib/storage_device.cpp b/src/applib/storage_device.cpp similarity index 89% rename from gsmartcontrol/src/applib/storage_device.cpp rename to src/applib/storage_device.cpp index bd2993a..88315f2 100644 --- a/gsmartcontrol/src/applib/storage_device.cpp +++ b/src/applib/storage_device.cpp @@ -58,7 +58,7 @@ std::string StorageDevice::get_status_name(StorageDevice::status_t status, bool -StorageDevice::StorageDevice(const std::string& dev_or_vfile, bool is_virtual) +StorageDevice::StorageDevice(const string& dev_or_vfile, bool is_virtual) { detected_type_ = detected_type_unknown; // force_type_ = false; @@ -76,7 +76,7 @@ StorageDevice::StorageDevice(const std::string& dev_or_vfile, bool is_virtual) -StorageDevice::StorageDevice(const std::string& dev, const std::string& type_arg) +StorageDevice::StorageDevice(const string& dev, const string& type_arg) { detected_type_ = detected_type_unknown; // force_type_ = false; @@ -162,7 +162,8 @@ std::string StorageDevice::fetch_basic_data_and_parse(hz::intrusive_ptrinfo_output_, true); // set type to invalid if needed + // --info --health --capabilities + std::string error_msg = execute_device_smartctl("-i -H -c", 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, @@ -319,15 +320,15 @@ std::string StorageDevice::fetch_data_and_parse(hz::intrusive_ptr sma 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. + // instead of -a, we use all the individual options -a encompasses, so that + // an addition to default -a 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); + // SCSI equivalent of -a: + error_msg = execute_device_smartctl("-H -i -A -l error -l selftest", 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", + // ATA equivalent of -a: + error_msg = execute_device_smartctl("-H -i -c -A -l error -l selftest -l selective", smartctl_ex, output, true); // set type to invalid if needed } // See notes above (in fetch_basic_data_and_parse()). @@ -422,7 +423,8 @@ A mandatory SMART command failed: exiting. To continue, add one or more '-T perm */ std::string output; - std::string error_msg = execute_device_smartctl((b ? "--smart=on --saveauto=on" : "--smart=off"), smartctl_ex, output); + // --smart=on --saveauto=on, --smart=off + std::string error_msg = execute_device_smartctl((b ? "-s on -S on" : "-s off"), smartctl_ex, output); if (!error_msg.empty()) return error_msg; @@ -456,7 +458,8 @@ SMART Automatic Offline Testing Disabled. 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); + // --offlineauto=on, --offlineauto=off + std::string error_msg = execute_device_smartctl((b ? "-o on" : "-o off"), smartctl_ex, output); if (!error_msg.empty()) return error_msg; @@ -572,14 +575,14 @@ StorageProperty StorageDevice::get_health_property() const -std::string StorageDevice::get_device() const +string StorageDevice::get_device() const { return device_; } -std::string StorageDevice::get_device_base() const +string StorageDevice::get_device_base() const { if (is_virtual_) return ""; @@ -592,7 +595,7 @@ std::string StorageDevice::get_device_base() const -std::string StorageDevice::get_device_with_type() const +string StorageDevice::get_device_with_type() const { if (this->get_is_virtual()) { std::string ret = "Virtual"; @@ -623,7 +626,7 @@ StorageDevice::detected_type_t StorageDevice::get_detected_type() const -void StorageDevice::set_type_argument(const std::string& arg) +void StorageDevice::set_type_argument(const string& arg) { type_arg_ = arg; } @@ -637,7 +640,7 @@ std::string StorageDevice::get_type_argument() const -void StorageDevice::set_extra_arguments(const std::string& args) +void StorageDevice::set_extra_arguments(const string& args) { extra_args_ = args; } @@ -651,34 +654,6 @@ std::string StorageDevice::get_extra_arguments() const -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_; @@ -686,7 +661,7 @@ bool StorageDevice::get_is_virtual() const -std::string StorageDevice::get_virtual_file() const +string StorageDevice::get_virtual_file() const { return (is_virtual_ ? virtual_file_ : std::string()); } @@ -707,7 +682,7 @@ const SmartctlParser::prop_list_t& StorageDevice::get_properties() const -StorageProperty StorageDevice::lookup_property(const std::string& generic_name, StorageProperty::section_t section, StorageProperty::subsection_t subsection) const +StorageProperty StorageDevice::lookup_property(const 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) @@ -723,21 +698,21 @@ StorageProperty StorageDevice::lookup_property(const std::string& generic_name, -std::string StorageDevice::get_model_name() const +string StorageDevice::get_model_name() const { return (model_name_.defined() ? model_name_.value() : ""); } -std::string StorageDevice::get_family_name() const +string StorageDevice::get_family_name() const { return (family_name_.defined() ? family_name_.value() : ""); } -std::string StorageDevice::get_serial_number() const +string StorageDevice::get_serial_number() const { return (serial_number_.defined() ? serial_number_.value() : ""); } @@ -751,28 +726,28 @@ bool StorageDevice::get_is_hdd() const -void StorageDevice::set_info_output(const std::string& s) +void StorageDevice::set_info_output(const string& s) { info_output_ = s; } -std::string StorageDevice::get_info_output() const +string StorageDevice::get_info_output() const { return info_output_; } -void StorageDevice::set_full_output(const std::string& s) +void StorageDevice::set_full_output(const string& s) { full_output_ = s; } -std::string StorageDevice::get_full_output() const +string StorageDevice::get_full_output() const { return full_output_; } diff --git a/gsmartcontrol/src/applib/storage_device.h b/src/applib/storage_device.h similarity index 94% rename from gsmartcontrol/src/applib/storage_device.h rename to src/applib/storage_device.h index f3c6587..884fdc2 100644 --- a/gsmartcontrol/src/applib/storage_device.h +++ b/src/applib/storage_device.h @@ -13,7 +13,6 @@ #define STORAGE_DEVICE_H #include -#include #include #include "hz/optional_value.h" @@ -166,16 +165,6 @@ class StorageDevice : public hz::intrusive_ptr_referenced { 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; @@ -272,12 +261,10 @@ class StorageDevice : public hz::intrusive_ptr_referenced { 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 device_; ///< e.g. /dev/sda. 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. diff --git a/src/applib/storage_property.cpp b/src/applib/storage_property.cpp new file mode 100644 index 0000000..5cd2074 --- /dev/null +++ b/src/applib/storage_property.cpp @@ -0,0 +1,191 @@ +/************************************************************************** + 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 "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 "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::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.raw_value_int << ")"; + return os; +} + + + +std::string StorageErrorBlock::get_readable_error_types(const std::vector& types) +{ + std::map m; + + 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, ", "); +} + + + +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::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; + + if (value_type == StorageProperty::value_type_unknown) { + os << "[empty]"; + + } else if (value_type == StorageProperty::value_type_string) { + os << "\"" << value_string << "\""; + + } else if (value_type == StorageProperty::value_type_integer) { + os << value_integer << " [" << reported_value << "]"; + + } else if (value_type == StorageProperty::value_type_bool) { + os << value_bool << " [" << reported_value << "]"; + + } else if (value_type == StorageProperty::value_type_time_length) { + os << value_time_length << " [" << reported_value << "]"; + + } else if (value_type == StorageProperty::value_type_capability) { + os << value_capability; + + } else if (value_type == StorageProperty::value_type_attribute) { + os << value_attribute; + + } else if (value_type == StorageProperty::value_type_error_block) { + os << value_error_block; + + } else if (value_type == StorageProperty::value_type_selftest_entry) { + os << value_selftest_entry; + } + +} + + + +std::string StorageProperty::format_value(bool add_reported_too) const +{ + if (!readable_value.empty()) + return readable_value; + + if (value_type == StorageProperty::value_type_unknown) { + return "[unknown]"; + + } else if (value_type == StorageProperty::value_type_string) { + return value_string; + + } else if (value_type == StorageProperty::value_type_integer) { + return hz::number_to_string(value_integer) + (add_reported_too ? (" [" + reported_value + "]") : ""); + + } else if (value_type == StorageProperty::value_type_bool) { + return std::string(value_bool ? "Yes" : "No") + (add_reported_too ? (" [" + reported_value + "]") : ""); + + } else if (value_type == StorageProperty::value_type_time_length) { + return hz::format_time_length(value_time_length) + (add_reported_too ? (" [" + reported_value + "]") : ""); + + } else if (value_type == StorageProperty::value_type_capability) { + return hz::stream_cast(value_capability); + + } else if (value_type == StorageProperty::value_type_attribute) { + return hz::stream_cast(value_attribute); + + } else if (value_type == StorageProperty::value_type_error_block) { + return hz::stream_cast(value_error_block); + + } else if (value_type == StorageProperty::value_type_error_block) { + return hz::stream_cast(value_selftest_entry); + } + + return "[error]"; +} + + + + + + + +/// @} diff --git a/gsmartcontrol/src/applib/storage_property.h b/src/applib/storage_property.h similarity index 86% rename from gsmartcontrol/src/applib/storage_property.h rename to src/applib/storage_property.h index b0d9147..3674d0f 100644 --- a/gsmartcontrol/src/applib/storage_property.h +++ b/src/applib/storage_property.h @@ -124,12 +124,8 @@ class StorageAttribute { { } - /// 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-". + std::string flag; ///< Some have it in 0xXXXX format, others in "PO--C-" format (some WD drives?). 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 ("---"). @@ -148,38 +144,6 @@ 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: @@ -191,12 +155,6 @@ class StorageErrorBlock { /// 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... @@ -291,17 +249,13 @@ class StorageSelftestEntry { } - /// 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. + std::string lba_of_first_error; ///< LBA of the first error. "-" or value (format?). capability: unused. }; @@ -325,7 +279,6 @@ class StorageProperty { 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 }; @@ -341,7 +294,6 @@ class StorageProperty { 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"; } @@ -376,14 +328,9 @@ class StorageProperty { 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) + subsection_error_log, ///< Error Log (-l error) + subsection_selftest_log, ///< Self-test log (-l selftest) + subsection_selective_selftest_log ///< Selective self-test log and settings }; /// Get displayable subsection type name @@ -394,14 +341,9 @@ class StorageProperty { 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]"; } @@ -466,6 +408,7 @@ class StorageProperty { } + 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. @@ -493,7 +436,6 @@ class StorageProperty { 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) diff --git a/gsmartcontrol/src/applib/storage_property_colors.h b/src/applib/storage_property_colors.h similarity index 100% rename from gsmartcontrol/src/applib/storage_property_colors.h rename to src/applib/storage_property_colors.h diff --git a/gsmartcontrol/src/applib/storage_property_descr.cpp b/src/applib/storage_property_descr.cpp similarity index 70% rename from gsmartcontrol/src/applib/storage_property_descr.cpp rename to src/applib/storage_property_descr.cpp index f535fed..0ca45cf 100644 --- a/gsmartcontrol/src/applib/storage_property_descr.cpp +++ b/src/applib/storage_property_descr.cpp @@ -22,15 +22,6 @@ 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 @@ -73,10 +64,18 @@ namespace { // 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://sourceforge.net/apps/trac/smartmontools/wiki/TocDoc // http://www.ocztechnologyforum.com/staff/ryderocz/misc/Sandforce.jpg // Intel Solid-State Drive Toolbox User Guide // as well as various other sources. + std::string 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."; + // 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. " @@ -91,14 +90,14 @@ namespace { 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", + add(5, StorageAttribute::DiskHDD, "Reallocated_Sector_Ct", "Reallocated Sector Count", "reallocated_sector_count", "Number of reallocated sectors (Raw value). Non-zero Raw value indicates a disk surface failure." - "\n\n" + s_unc_text); + "\n\n" + unc_text); // SSD: Reallocated Sector Count (smartctl) - add(5, StorageAttribute::DiskSSD, "Reallocated_Sector_Ct", "Reallocated Sector Count", "attr_reallocated_sector_count", + add(5, StorageAttribute::DiskSSD, "Reallocated_Sector_Ct", "Reallocated Sector Count", "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", + add(5, StorageAttribute::DiskSSD, "Retired_Block_Count", "Retired Block Rate", "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", "", @@ -130,7 +129,7 @@ namespace { 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", + add(10, StorageAttribute::DiskHDD, "Spin_Retry_Count", "Spin-Up Retry Count", "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", "", @@ -139,7 +138,7 @@ namespace { 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", + add(13, "Read_Soft_Error_Rate", "Soft Read Error Rate", "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"); @@ -261,7 +260,7 @@ namespace { 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", + add(169, StorageAttribute::DiskSSD, "Remaining_Lifetime_Perc", "Remaining Lifetime %", "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", "", @@ -321,10 +320,10 @@ namespace { 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", "", + add(174, StorageAttribute::DiskSSD, "Unexpect_Power_Loss_Ct", "Unexpected Power Loss", "", "Number of unexpected power loss events."); // OCZ SSD (smartctl) - add(174, StorageAttribute::DiskSSD, "Pwr_Cycle_Ct_Unplanned", "Unexpected Power Loss Count", "", + add(174, StorageAttribute::DiskSSD, "Pwr_Cycle_Ct_Unplanned", "Unexpected Power Loss", "", "Number of unexpected power loss events."); // Apple SSD (smartctl) add(174, StorageAttribute::DiskSSD, "Host_Reads_MiB", "Host Read (MiB)", "", @@ -450,7 +449,7 @@ namespace { add(190, "Temperature_Exceed_Cnt", "Temperature Exceed Count", "", ""); // OCZ SSD (smartctl) - add(190, "Temperature_Celsius", "Temperature (Celsius)", "attr_temperature_celsius", + add(190, "Temperature_Celsius", "Temperature (Celsius)", "temperature_celsius", "Drive temperature. The Raw value shows built-in heat sensor registrations (in Celsius)."); // Intel SSD add(190, "Temperature_Case", "Case Temperature (Celsius)", "", @@ -476,20 +475,20 @@ namespace { 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", + add(194, "Temperature_Celsius", "Temperature (Celsius)", "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", + add(194, StorageAttribute::DiskSSD, "Airflow_Temperature", "Airflow Temperature (Celsius)", "temperature_celsius", "Drive temperature (Celsius)"); // Temperature Celsius x 10 (smartctl) - add(194, "Temperature_Celsius_x10", "Temperature (Celsius) x 10", "attr_temperature_celsius_x10", + add(194, "Temperature_Celsius_x10", "Temperature (Celsius) x 10", "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)."); + add(194, "Temperature_Internal", "Internal Temperature (Celsius)", "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."); @@ -511,9 +510,9 @@ namespace { 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", + add(196, StorageAttribute::DiskAny, "Reallocated_Event_Count", "Reallocation Event Count", "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); + "\n\n" + 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."); @@ -521,30 +520,30 @@ namespace { 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", + add(197, "Current_Pending_Sector", "Current Pending Sector Count", "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); + "\n\n" + 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", + add(197, "Total_Pending_Sectors", "Total Pending Sectors", "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); + "\n\n" + 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", + add(198, "Offline_Uncorrectable", "Offline Uncorrectable", "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); + "\n\n" + 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", + add(198, "Total_Offl_Uncorrectabl", "Total Offline Uncorrectable", "total_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); + "\n\n" + 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?) @@ -585,7 +584,7 @@ namespace { 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", + add(201, StorageAttribute::DiskHDD, "Soft_Read_Error_Rate", "Soft Read Error Rate", "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"); @@ -611,7 +610,7 @@ namespace { 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", + add(202, StorageAttribute::DiskSSD, "Percent_Lifetime_Used", "Rated Life Used (%)", "ssd_life_used", "Used drive life in %."); // Samsung SSD: (smartctl) (description?) add(202, StorageAttribute::DiskSSD, "Exception_Mode_Status", "Exception Mode Status", "", @@ -620,7 +619,7 @@ namespace { 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", + add(202, StorageAttribute::DiskSSD, "Percent_Lifetime_Remain", "Remaining Lifetime (%)", "ssd_life_left", "Remaining drive life in %."); // Run Out Cancel (smartctl). (description?) add(203, "Run_Out_Cancel", "Run Out Cancel", "", @@ -681,7 +680,7 @@ namespace { 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", + add(209, StorageAttribute::DiskSSD, "Remaining_Lifetime_Perc", "Remaining Lifetime (%)", "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", "", @@ -830,10 +829,10 @@ namespace { add(230, StorageAttribute::DiskSSD, "Perc_Write/Erase_Count", "Write / Erase Count (%)", "", ""); // Temperature (Some drives) (smartctl) - add(231, "Temperature_Celsius", "Temperature", "attr_temperature_celsius", + add(231, "Temperature_Celsius", "Temperature", "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", + add(231, StorageAttribute::DiskSSD, "SSD_Life_Left", "SSD Life Left", "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."); @@ -853,17 +852,17 @@ namespace { 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", + add(232, StorageAttribute::DiskSSD, "Spares_Remaining_Perc", "Spare Blocks Remaining (%)", "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", + add(232, StorageAttribute::DiskSSD, "Perc_Avail_Resrvd_Space", "Available Reserved Space (%)", "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", + add(233, StorageAttribute::DiskSSD, "Media_Wearout_Indicator", "Media Wear Out Indicator", "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", + add(233, StorageAttribute::DiskSSD, "Remaining_Lifetime_Perc", "Remaining Lifetime %", "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", "", @@ -1125,238 +1124,6 @@ namespace { - - /// 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) { @@ -1504,41 +1271,6 @@ namespace { 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; - } - - } @@ -1561,13 +1293,7 @@ bool storage_property_autoset_description(StorageProperty& p, StorageAttribute:: || 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"); + || auto_set(p, "smart_enabled", "Whether the device has SMART enabled. If not, most of the reported values will be incorrect."); // set just its name as a tooltip if (!found) { @@ -1575,83 +1301,49 @@ bool storage_property_autoset_description(StorageProperty& p, StorageAttribute:: found = true; } - } else if (p.section == StorageProperty::section_data) { + } else if (p.section == StorageProperty::section_data && p.subsection == 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!"); - 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; + } else if (p.section == StorageProperty::section_data && p.subsection == 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."); - 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; + } else if (p.section == StorageProperty::section_data && p.subsection == 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". } + + } else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_error_log) { + found = auto_set(p, "error_log_version", p.readable_name.c_str()); + found = auto_set(p, "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 + + + } else if (p.section == StorageProperty::section_data && p.subsection == 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: This log usually contains only the last 20 or so manual tests. "); +// || auto_set(p, "selftest_log_unsupported", "This device does not support self-test logging."); // the property text already says that + + } else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_selective_selftest_log) { + // nothing here + } + return found; } @@ -1679,261 +1371,140 @@ StorageProperty::warning_t storage_property_autoset_warning(StorageProperty& p) w = StorageProperty::warning_notice; reason = "SMART is disabled. You shoud 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")) { + } else if (p.section == StorageProperty::section_data && p.subsection == 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!"; + } + + } else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_capabilities) { + // nothing + + } else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_attributes) { + + // Set notices for known pre-fail attributes + + // Reallocated Sector Count + if (attr_match(p, "reallocated_sector_count") && p.value_attribute.raw_value_int > 0) { w = StorageProperty::warning_notice; - reason = "Your drive may be affected by the warning, please see the details."; + 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, "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, "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, "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, "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, "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, "current_pending_sector_count") || attr_match(p, "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, "offline_uncorrectable") || attr_match(p, "total_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, "ssd_life_left")) + && p.value_attribute.value.value() < 50) { + w = StorageProperty::warning_notice; + reason = "The drive has less than half of its life left."; + + // SSD Life Used (%) + } else if ((attr_match(p, "ssd_life_used")) + && p.value_attribute.value.value() >= 50) { + w = StorageProperty::warning_notice; + reason = "The drive has less than half of its life left."; } - } else if (p.section == StorageProperty::section_data) { - switch(p.subsection) { - case StorageProperty::subsection_health: - if (name_match(p, "overall_health") && p.value_string != "PASSED") { + // Now override this with SMART 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 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."; + reason = "The drive has a failing pre-fail attribute. Usually this indicates a that the drive will FAIL soon. Please back up immediately!"; } - // Now override this with reported SMART attribute failure warnings / errors + } else if (p.value_attribute.when_failed == StorageAttribute::fail_time_past) { // PAST - 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."; - } - } + 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; + } } - } + + } else if (p.section == StorageProperty::section_data && p.subsection == 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_count") && p.value_integer > 0) { + w = StorageProperty::warning_warn; + 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."; + } + + + } else if (p.section == StorageProperty::section_data && p.subsection == 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."; + } + + } else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_selective_selftest_log) { + // nothing here + + } + p.warning = w; p.warning_reason = reason; diff --git a/gsmartcontrol/src/applib/storage_property_descr.h b/src/applib/storage_property_descr.h similarity index 100% rename from gsmartcontrol/src/applib/storage_property_descr.h rename to src/applib/storage_property_descr.h diff --git a/gsmartcontrol/src/applib/storage_settings.h b/src/applib/storage_settings.h similarity index 100% rename from gsmartcontrol/src/applib/storage_settings.h rename to src/applib/storage_settings.h diff --git a/src/applib/wrapping_label.h b/src/applib/wrapping_label.h new file mode 100644 index 0000000..af3158d --- /dev/null +++ b/src/applib/wrapping_label.h @@ -0,0 +1,138 @@ +/************************************************************************** + Copyright: + (C) 2008 - 2012 Alexander Shaduri + License: See LICENSE_gsmartcontrol.txt +***************************************************************************/ +/// \file +/// \author Alexander Shaduri +/// \ingroup applib +/// \weakgroup applib +/// @{ + +#ifndef WRAPPING_LABEL_H +#define WRAPPING_LABEL_H + +#include + +#include "app_gtkmm_features.h" // APP_GTKMM_CONNECT_VIRTUAL + + + +/// An auto-wrapping label. The GtkLabel widget has a bug +/// (or "design decision", as gtk devs say), when the widget can't +/// control its wrapping width according to parent container's size. +/// This class should fix that. +class WrappingLabel : public Gtk::Label { + public: + + /// Constructor + WrappingLabel(const Glib::ustring& label, Gtk::AlignmentEnum xalign, + Gtk::AlignmentEnum yalign = Gtk::ALIGN_CENTER, bool mnemonic = false) + : Gtk::Label(label, xalign, yalign, mnemonic) + { + this->construct(); + } + + /// Constructor + WrappingLabel(const Glib::ustring& label, float xalign, float yalign, bool mnemonic = false) + : Gtk::Label(label, xalign, yalign, mnemonic) + { + this->construct(); + } + + /// Constructor + WrappingLabel (const Glib::ustring& label, bool mnemonic=false) + : Gtk::Label(label, mnemonic) + { + this->construct(); + } + + /// Constructor + WrappingLabel() + { + this->construct(); + } + + + // Reimplemented from Gtk::Label + virtual void set_text(const Glib::ustring& label) + { + Label::set_text(label); + set_width(width_); + } + + // Reimplemented from Gtk::Label + virtual void set_markup(const Glib::ustring& label) + { + Label::set_markup(label); + set_width(width_); + } + + // Reimplemented from Gtk::Label + virtual void set_label(const Glib::ustring& label) + { + Label::set_label(label); + set_width(width_); + } + + + protected: + + /// size_allocate handler + void on_size_allocate_before(Gtk::Allocation& alloc) + { + Gtk::Label::on_size_allocate(alloc); + set_width(alloc.get_width()); + } + + + /// size_request handler + void on_size_request_before(Gtk::Requisition* req) + { + int w = 0, h = 0; + get_layout()->get_pixel_size(w, h); + req->width = -1; + req->height = h; + } + + + /// Set widget width + void set_width(int width) + { + if (width == 0) + return; + get_layout()->set_width(width * Pango::SCALE); + if (width_ != width) { + width_ = width; + queue_resize(); + } + } + + + private: + + /// Called from constructors + void construct() + { + width_ = 0; + + // set_line_wrap_mode is from 2.10, so use this instead. + get_layout()->set_wrap(Pango::WRAP_WORD_CHAR); + + typedef WrappingLabel self_type; + APP_GTKMM_CONNECT_VIRTUAL(size_allocate); + APP_GTKMM_CONNECT_VIRTUAL(size_request); + } + + + int width_; ///< wrapping width + +}; + + + + + +#endif + +/// @} diff --git a/gsmartcontrol/src/global_macros.h b/src/global_macros.h similarity index 80% rename from gsmartcontrol/src/global_macros.h rename to src/global_macros.h index ae6d93b..b5b058b 100644 --- a/gsmartcontrol/src/global_macros.h +++ b/src/global_macros.h @@ -33,8 +33,77 @@ Additionally, it may be included through compiler's "-include" option // #define WINVER 0x0501 // #endif + +// List most of the included system headers here - they will +// be precompiled together with this file. + +#ifdef ENABLE_PCH + + #ifdef HAVE_STDC___H + // All libstdc++ headers + #include + + #else + #include + + #include + #include + #include + #include + + #include + #include + #include + #include + #include + + #include + #include + #include + #include + + #include + #include + #include + #include + #include + #include + #include + #include + + #include // is this included in stdc++.h? + #endif + + // most of the other headers + #include + +// #include + + #if defined ENABLE_GLIB && ENABLE_GLIB + #include + #endif + #if defined ENABLE_GLIBMM && ENABLE_GLIBMM + #include + #include // if we have glibmm, we're building everything + #include + #include + #endif + + #ifdef _WIN32 + #include + #endif + +#endif // pch + + + // this is either defined to 1 (by autoconf), or undefined #ifdef HAVE_LONG_LONG_INT + // pcrecpp uses this + #ifndef HAVE_LONG_LONG + #define HAVE_LONG_LONG + #endif + #define DISABLE_LL_INT 0 #else // there is no long long int @@ -46,6 +115,11 @@ Additionally, it may be included through compiler's "-include" option // this is either defined to 1 (by autoconf), or undefined #ifdef HAVE_UNSIGNED_LONG_LONG_INT + // pcrecpp uses this + #ifndef HAVE_UNSIGNED_LONG_LONG + #define HAVE_UNSIGNED_LONG_LONG + #endif + #define DISABLE_ULL_INT 0 #else // there is no long long int diff --git a/gsmartcontrol/src/gsc_about_dialog.cpp b/src/gsc_about_dialog.cpp similarity index 60% rename from gsmartcontrol/src/gsc_about_dialog.cpp rename to src/gsc_about_dialog.cpp index a77423b..32fdb80 100644 --- a/gsmartcontrol/src/gsc_about_dialog.cpp +++ b/src/gsc_about_dialog.cpp @@ -11,44 +11,46 @@ #include "hz/hz_config.h" // VERSION -#include +#include // list is more header-lightweight than vector #include "hz/debug.h" #include "hz/string_algo.h" // hz::string_* -#include "hz/launch_url.h" -#include "applib/app_gtkmm_features.h" +#include "hz/launch_url.h" // hz::launch_url + #include "applib/gui_utils.h" // gui_show_error_dialog #include "gsc_about_dialog.h" -// GtkBuilder needs this constructor +// glade/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); + // Note: The dialogs have ESC accelerator attached by default. + + + set_url_hook(sigc::mem_fun(*this, &GscAboutDialog::on_activate_url)); + set_email_hook(sigc::mem_fun(*this, &GscAboutDialog::on_activate_email)); + + + // Note: AboutDialog changed "name" property to "program-name" in gtk 2.12. + // We don't set either, but rely on Glib::set_application_name() during init, which + // works with both older and newer versions. set_version(VERSION); // set these properties here (after setting hooks) to make the links work. - set_website("https://gsmartcontrol.sourceforge.io/"); + set_website("http://gsmartcontrol.sourceforge.net/"); 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 - 2017 Alexander Shaduri " ""); @@ -56,10 +58,10 @@ GscAboutDialog::GscAboutDialog(BaseObjectType* gtkcobj, const app_ui_res_ref_t& std::string authors_str = AuthorsTextResData().get_string(); hz::string_any_to_unix(authors_str); - std::vector authors; + std::list authors; hz::string_split(authors_str, '\n', authors, true); - for (std::vector::iterator iter = authors.begin(); iter != authors.end(); ++iter) { + for (std::list::iterator iter = authors.begin(); iter != authors.end(); ++iter) { std::string s = *iter; hz::string_replace(s, " '@' ", "@"); // despammer hz::string_replace(s, " 'at' ", "@"); // despammer @@ -77,6 +79,32 @@ GscAboutDialog::GscAboutDialog(BaseObjectType* gtkcobj, const app_ui_res_ref_t& +void GscAboutDialog::on_activate_url(Gtk::AboutDialog& about_dialog, const Glib::ustring& link) +{ + std::string error_msg = hz::launch_url(link, this->get_screen()->gobj()); + if (!error_msg.empty()) { + gui_show_error_dialog("Cannot open URL", error_msg, &about_dialog); + } +} + + + +void GscAboutDialog::on_activate_email(Gtk::AboutDialog& about_dialog, const Glib::ustring& link) +{ + std::string email = link; + // about dialog passes us the link without mailto:, so add it. + if (email.compare(0, 7, "mailto:") != 0) + email = "mailto:" + email; + + std::string error_msg = hz::launch_url(email, this->get_screen()->gobj()); + if (!error_msg.empty()) { + gui_show_error_dialog("Cannot open an email client", error_msg, &about_dialog); + } +} + + + + void GscAboutDialog::on_response_before(int response_id) { debug_out_info("app", DBG_FUNC_MSG << "Response ID: " << response_id << "\n"); @@ -90,15 +118,6 @@ void GscAboutDialog::on_response_before(int response_id) -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(); -} - - - diff --git a/gsmartcontrol/src/gsc_about_dialog.h b/src/gsc_about_dialog.h similarity index 79% rename from gsmartcontrol/src/gsc_about_dialog.h rename to src/gsc_about_dialog.h index 85fcf6c..7749fde 100644 --- a/gsmartcontrol/src/gsc_about_dialog.h +++ b/src/gsc_about_dialog.h @@ -12,7 +12,8 @@ #ifndef GSC_ABOUT_DIALOG_H #define GSC_ABOUT_DIALOG_H -#include +#include +#include #include "applib/app_ui_res_utils.h" @@ -24,7 +25,7 @@ class GscAboutDialog : public AppUIResWidget { public: - // name of ui file without a .ui extension and quotes + // name of glade/ui file without a .glade/.ui extension and quotes APP_UI_RES_DATA_INIT(gsc_about_dialog); // we need the license file to show it. @@ -35,7 +36,7 @@ class GscAboutDialog : public AppUIResWidget -#include // GDK_KEY_Escape -#include +#include +#include +#include +#include +#include +#include +#include // GDK_Escape #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" @@ -65,8 +68,8 @@ GscAddDeviceWindow::GscAddDeviceWindow(BaseObjectType* gtkcobj, const app_ui_res 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); + if (Gtk::Entry* device_type_entry = lookup_widget("device_type_entry")) { + app_gtkmm_set_widget_tooltip(*device_type_entry, device_type_tooltip); } @@ -74,7 +77,7 @@ GscAddDeviceWindow::GscAddDeviceWindow(BaseObjectType* gtkcobj, const app_ui_res Glib::RefPtr accel_group = this->get_accel_group(); if (window_cancel_button) { - window_cancel_button->add_accelerator("clicked", accel_group, GDK_KEY_Escape, + window_cancel_button->add_accelerator("clicked", accel_group, GDK_Escape, Gdk::ModifierType(0), Gtk::AccelFlags(0)); } @@ -88,26 +91,35 @@ GscAddDeviceWindow::GscAddDeviceWindow(BaseObjectType* gtkcobj, const app_ui_res // Populate type combo with common types - Gtk::ComboBoxText* type_combo = lookup_widget("device_type_combo"); + Gtk::ComboBoxEntry* 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"); + Gtk::TreeModelColumnRecord column_record; ///< Columns for type combo + Gtk::TreeModelColumn col_type; ///< Model column + + column_record.add(col_type); + + Glib::RefPtr model = Gtk::ListStore::create(column_record); + type_combo->set_model(model); + type_combo->set_text_column(col_type); + + (*(model->append()))[col_type] = "sat,12"; + (*(model->append()))[col_type] = "sat,16"; + (*(model->append()))[col_type] = "usbcypress"; + (*(model->append()))[col_type] = "usbjmicron"; + (*(model->append()))[col_type] = "usbsunplus"; + (*(model->append()))[col_type] = "ata"; + (*(model->append()))[col_type] = "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"); + (*(model->append()))[col_type] = "marvell"; + (*(model->append()))[col_type] = "megaraid,N"; + (*(model->append()))[col_type] = "areca,N"; + (*(model->append()))[col_type] = "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"); + (*(model->append()))[col_type] = "3ware,N"; // this option is not needed in windows + (*(model->append()))[col_type] = "cciss,N"; + (*(model->append()))[col_type] = "hpt,L/M"; + (*(model->append()))[col_type] = "hpt,L/M/N"; #endif } @@ -148,8 +160,8 @@ void GscAddDeviceWindow::on_window_ok_button_clicked() 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("device_type_entry")) { + type = entry->get_text(); } if (Gtk::Entry* entry = lookup_widget("smartctl_params_entry")) { params = entry->get_text(); @@ -171,20 +183,6 @@ void GscAddDeviceWindow::on_device_name_browse_button_clicked() 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); @@ -193,24 +191,18 @@ void GscAddDeviceWindow::on_device_name_browse_button_clicked() 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. + hz::FsPath p(entry->get_text()); + if (p.is_absolute()) + dialog.set_filename(p.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 + int result = dialog.run(); // the main cycle blocks here // 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); + entry->set_text(std::string(dialog.get_filename())); break; } diff --git a/gsmartcontrol/src/gsc_add_device_window.h b/src/gsc_add_device_window.h similarity index 92% rename from gsmartcontrol/src/gsc_add_device_window.h rename to src/gsc_add_device_window.h index 3230dee..5259d18 100644 --- a/gsmartcontrol/src/gsc_add_device_window.h +++ b/src/gsc_add_device_window.h @@ -12,7 +12,7 @@ #ifndef GSC_ADD_DEVICE_WINDOW_H #define GSC_ADD_DEVICE_WINDOW_H -#include +#include #include "applib/app_ui_res_utils.h" @@ -26,11 +26,11 @@ class GscMainWindow; class GscAddDeviceWindow : public AppUIResWidget { public: - // name of ui file without a .ui extension and quotes + // name of glade/ui file without a .glade/.ui extension and quotes APP_UI_RES_DATA_INIT(gsc_add_device_window); - /// Constructor, GtkBuilder needs this. + /// Constructor, gtkbuilder/glade needs this. GscAddDeviceWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui); /// Virtual destructor diff --git a/gsmartcontrol/src/gsc_executor_error_dialog.cpp b/src/gsc_executor_error_dialog.cpp similarity index 95% rename from gsmartcontrol/src/gsc_executor_error_dialog.cpp rename to src/gsc_executor_error_dialog.cpp index 0d1403f..97b5409 100644 --- a/gsmartcontrol/src/gsc_executor_error_dialog.cpp +++ b/src/gsc_executor_error_dialog.cpp @@ -9,7 +9,8 @@ /// \weakgroup gsc /// @{ -#include +#include +#include #include "gsc_executor_log_window.h" #include "gsc_executor_error_dialog.h" @@ -42,7 +43,7 @@ namespace { Gtk::Button ok_button(Gtk::Stock::OK); ok_button.show_all(); - ok_button.set_can_default(true); + ok_button.set_flags(ok_button.get_flags() | Gtk::CAN_DEFAULT); dialog.add_action_widget(ok_button, Gtk::RESPONSE_OK); diff --git a/gsmartcontrol/src/gsc_executor_error_dialog.h b/src/gsc_executor_error_dialog.h similarity index 97% rename from gsmartcontrol/src/gsc_executor_error_dialog.h rename to src/gsc_executor_error_dialog.h index 2d347f6..65f3ac7 100644 --- a/gsmartcontrol/src/gsc_executor_error_dialog.h +++ b/src/gsc_executor_error_dialog.h @@ -13,7 +13,7 @@ #define GSC_EXECUTOR_ERROR_DIALOG_H #include -#include +#include diff --git a/gsmartcontrol/src/gsc_executor_log_window.cpp b/src/gsc_executor_log_window.cpp similarity index 69% rename from gsmartcontrol/src/gsc_executor_log_window.cpp rename to src/gsc_executor_log_window.cpp index fb43b66..63469c3 100644 --- a/gsmartcontrol/src/gsc_executor_log_window.cpp +++ b/src/gsc_executor_log_window.cpp @@ -11,13 +11,15 @@ #include #include // std::size_t -#include -#include // GDK_KEY_Escape +#include +#include +#include +#include +#include +#include +#include // GDK_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() @@ -51,7 +53,7 @@ GscExecutorLogWindow::GscExecutorLogWindow(BaseObjectType* gtkcobj, const app_ui Glib::RefPtr accel_group = this->get_accel_group(); if (window_close_button) { - window_close_button->add_accelerator("clicked", accel_group, GDK_KEY_Escape, + window_close_button->add_accelerator("clicked", accel_group, GDK_Escape, Gdk::ModifierType(0), Gtk::AccelFlags(0)); } @@ -96,7 +98,7 @@ GscExecutorLogWindow::GscExecutorLogWindow(BaseObjectType* gtkcobj, const app_ui // The problem seems to have disappeared (new compiler/runtime?) // #ifdef _WIN32 -// Gtk::Box* command_hbox = this->lookup_widget("command_hbox"); +// Gtk::HBox* command_hbox = this->lookup_widget("command_hbox"); // if (command_hbox) // command_hbox->hide(); // #endif @@ -160,10 +162,13 @@ void GscExecutorLogWindow::on_command_output_received(const CmdexSyncCommandInfo 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)); + if (this->is_visible()) { + Gtk::TreeView* treeview = this->lookup_widget("command_list_treeview"); + + if (treeview) { + selection->select(row); + treeview->scroll_to_row(list_store->get_path(row)); + } } } @@ -192,37 +197,9 @@ void GscExecutorLogWindow::on_window_save_current_button_clicked() 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); @@ -230,38 +207,23 @@ void GscExecutorLogWindow::on_window_save_current_button_clicked() 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 APP_GTKMM_CHECK_VERSION(2, 8, 0) + dialog.set_do_overwrite_confirmation(true); // since gtkmm 2.8 +#endif 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 + int result = dialog.run(); // the main cycle blocks here // 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"; - } + last_dir = dialog.get_current_folder(); // safe for the future + std::string file = dialog.get_filename(); 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); @@ -310,36 +272,7 @@ void GscExecutorLogWindow::on_window_save_all_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()); - - 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); @@ -347,38 +280,23 @@ void GscExecutorLogWindow::on_window_save_all_button_clicked() 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 APP_GTKMM_CHECK_VERSION(2, 8, 0) + dialog.set_do_overwrite_confirmation(true); // since gtkmm 2.8 +#endif 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 + int result = dialog.run(); // the main cycle blocks here // 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"; - } + last_dir = dialog.get_current_folder(); // safe for the future + std::string file = dialog.get_filename(); hz::File f(file); if (!f.put_contents(exss.str())) { gui_show_error_dialog("Cannot save data to file", f.get_error_utf8(), this); diff --git a/gsmartcontrol/src/gsc_executor_log_window.h b/src/gsc_executor_log_window.h similarity index 90% rename from gsmartcontrol/src/gsc_executor_log_window.h rename to src/gsc_executor_log_window.h index 4680a98..5a417e5 100644 --- a/gsmartcontrol/src/gsc_executor_log_window.h +++ b/src/gsc_executor_log_window.h @@ -14,7 +14,10 @@ #include #include // std::size_t -#include +#include +#include +#include +#include #include "applib/app_ui_res_utils.h" #include "applib/cmdex_sync.h" @@ -27,11 +30,11 @@ class GscExecutorLogWindow : public AppUIResWidget { public: - // name of ui file without a .ui extension and quotes + // name of glade/ui file without a .glade/.ui extension and quotes APP_UI_RES_DATA_INIT(gsc_executor_log_window); - /// Constructor, GtkBuilder needs this. + /// Constructor, gtkbuilder/glade needs this. GscExecutorLogWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui); /// Virtual destructor diff --git a/src/gsc_help_window.cpp b/src/gsc_help_window.cpp new file mode 100644 index 0000000..a5678b6 --- /dev/null +++ b/src/gsc_help_window.cpp @@ -0,0 +1,224 @@ +/************************************************************************** + Copyright: + (C) 2008 - 2012 Alexander Shaduri + License: See LICENSE_gsmartcontrol.txt +***************************************************************************/ +/// \file +/// \author Alexander Shaduri +/// \ingroup gsc +/// \weakgroup gsc +/// @{ + +#include +#include +#include +#include +#include // GDK_Escape + +#include "hz/string_algo.h" +#include "applib/app_gtkmm_utils.h" // app_gtkmm_create_tree_view_column +#include "applib/app_pcrecpp.h" + +#include "gsc_help_window.h" + + + +HZ_RES_DATA_INIT_NAMED(README_txt, "README.txt", ReadmeTextResData); + + + +GscHelpWindow::GscHelpWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui) + : AppUIResWidget(gtkcobj, ref_ui), selection_callback_enabled(true) +{ + // 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); + + + // Accelerators + + Glib::RefPtr accel_group = this->get_accel_group(); + if (window_close_button) { + window_close_button->add_accelerator("clicked", accel_group, GDK_Escape, + Gdk::ModifierType(0), Gtk::AccelFlags(0)); + } + + + // --------------- Make a treeview + + Gtk::TreeView* treeview = this->lookup_widget("topics_treeview"); + if (treeview) { + Gtk::TreeModelColumnRecord model_columns; + // Topic + model_columns.add(col_topic); + app_gtkmm_create_tree_view_column(col_topic, *treeview, "Topic", "Topic"); + + // create a TreeModel (ListStore) + list_store = Gtk::ListStore::create(model_columns); + treeview->set_model(list_store); + + selection = treeview->get_selection(); + selection->signal_changed().connect(sigc::mem_fun(*this, + &self_type::on_tree_selection_changed) ); + + } + + + // --------------- Parse help text + + /* + README.txt File Format + + The whole text is converted to unix newline format before parsing. + Sections are separated by 3 newlines (two empty lines). + The first line of the section is its header. + When splitting the file to sections and headers, any leading or trailing + whitespace is removed. + If there is a single newline inside a section, it is converted to + space to enable correct wrapping. + If there are two consequent newlines, they are left as they are, + essentially making a paragraph break. + */ + + std::string readme = hz::string_any_to_unix_copy(ReadmeTextResData().get_string()); + + + // Paragraphs are delimited by 3 empty lines + std::vector topics; + hz::string_split(readme, "\n\n\n\n", topics, true); // skip empty + + + // Add to treeview and textview + + Gtk::TextView* content = this->lookup_widget("content_textview"); + + if (treeview && content) { + Glib::RefPtr buffer = content->get_buffer(); + + buffer->create_mark("Begin", buffer->begin(), true); + + for (unsigned int i = 0; i < topics.size(); ++i) { + std::string topic = hz::string_trim_copy(topics[i]); + + // The first line of topic is its title + std::vector topic_split; + hz::string_split(topic, "\n\n", topic_split, true, 2); // skip empty, get 2 elements only + + if (topic_split.size() < 2) { + debug_out_warn("app", DBG_FUNC_MSG << "Cannot extract topic title in topic " << i << "\n"); + continue; + } + + std::string topic_title = hz::string_trim_copy(topic_split[0]); + std::string topic_body = hz::string_trim_copy(topic_split[1]); + + buffer->create_mark(topic_title, buffer->end(), true); // set topic mark to the end of what's there + + // add the title and make it bold + buffer->insert(buffer->end(), "\n" + topic_title); + + Gtk::TextIter first = buffer->end(), last = first; + first.backward_lines(1); + + Glib::RefPtr tag = buffer->create_tag(); + tag->property_weight() = Pango::WEIGHT_BOLD; + tag->property_size_points() = 14; + + buffer->apply_tag(tag, first, last); + + // add the rest + + // single newlines to spaces, to allow proper wrapping. + app_pcre_replace("/([^\\n])\\n([^\\n])/", "\\1 \\2", topic_body); + buffer->insert(buffer->end(), "\n\n" + topic_body + "\n\n"); + + + // Add to treeview + + Gtk::TreeRow row = *(list_store->append()); + row[col_topic] = topic_title; + + } + + } + + + // --------------- + + + // show(); +} + + + +void GscHelpWindow::set_topic(const Glib::ustring& topic) +{ + this->selection_callback_enabled = false; // temporarily disable it + + // scroll to it + + Gtk::TextView* content = this->lookup_widget("content_textview"); + if (content) { + Glib::RefPtr buffer = content->get_buffer(); + + Glib::RefPtr mark = buffer->get_mark(topic); + if (mark) + content->scroll_to(mark, 0., 0., 0.); + } + + // select it in tree view + Gtk::TreeView* treeview = this->lookup_widget("topics_treeview"); + + if (treeview && !list_store->children().empty()) { + for (Gtk::TreeIter iter = list_store->children().begin(); iter != list_store->children().end(); ++iter) { + if (iter->get_value(col_topic) == topic) { + selection->select(*iter); + // about 30% from top + treeview->scroll_to_cell(list_store->get_path(iter), *(treeview->get_column(0)), 0.3f, 0.f); + break; + } + } + } + + this->selection_callback_enabled = true; // enable it back +} + + + +bool GscHelpWindow::on_delete_event_before(GdkEventAny* e) +{ + destroy(this); + return true; // event handled, don't call default virtual handler +} + + + +void GscHelpWindow::on_window_close_button_clicked() +{ + destroy(this); +} + + + +void GscHelpWindow::on_tree_selection_changed() +{ + if (!this->selection_callback_enabled) + return; + + if (selection->count_selected_rows()) { + Gtk::TreeIter iter = selection->get_selected(); + Gtk::TreeRow row = *iter; + + set_topic(row[col_topic]); + } +} + + + + + + +/// @} diff --git a/src/gsc_help_window.h b/src/gsc_help_window.h new file mode 100644 index 0000000..69db73a --- /dev/null +++ b/src/gsc_help_window.h @@ -0,0 +1,82 @@ +/************************************************************************** + Copyright: + (C) 2008 - 2012 Alexander Shaduri + License: See LICENSE_gsmartcontrol.txt +***************************************************************************/ +/// \file +/// \author Alexander Shaduri +/// \ingroup gsc +/// \weakgroup gsc +/// @{ + +#ifndef GSC_HELP_WINDOW_H +#define GSC_HELP_WINDOW_H + +#include +#include +#include +#include + +#include "applib/app_ui_res_utils.h" + + + + +/// The Help window. +/// Use create() / destroy() with this class instead of new / delete! +class GscHelpWindow : public AppUIResWidget { + public: + + // name of glade/ui file without a .glade/.ui extension and quotes + APP_UI_RES_DATA_INIT(gsc_help_window); + + + /// Constructor, gtkbuilder/glade needs this. + GscHelpWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui); + + /// Virtual destructor + virtual ~GscHelpWindow() + { } + + + /// Set the current help topic + void set_topic(const Glib::ustring& topic); + + + 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_close_button_clicked(); + + /// Callback + void on_tree_selection_changed(); + + + private: + + Glib::RefPtr list_store; ///< List store + Glib::RefPtr selection; ///< Tree selection + + Gtk::TreeModelColumn col_topic; /// Tree column + + bool selection_callback_enabled; ///< Helper for set_topic(), temporarily disables the tree selection changed callback + +}; + + + + + + +#endif + +/// @} diff --git a/gsmartcontrol/src/gsc_info_window.cpp b/src/gsc_info_window.cpp similarity index 63% rename from gsmartcontrol/src/gsc_info_window.cpp rename to src/gsc_info_window.cpp index 4d79ae8..a253663 100644 --- a/gsmartcontrol/src/gsc_info_window.cpp +++ b/src/gsc_info_window.cpp @@ -9,12 +9,11 @@ /// \weakgroup gsc /// @{ -#include -#include // GDK_KEY_Escape #include // better use vector, it's needed by others too #include // std::min, std::max +#include +#include // GDK_Escape -#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 @@ -27,6 +26,7 @@ #include "applib/storage_property_colors.h" #include "applib/gui_utils.h" // gui_show_error_dialog #include "applib/smartctl_executor_gui.h" +#include "applib/app_gtkmm_features.h" // APP_GTKMM_CHECK_VERSION #include "gsc_text_window.h" #include "gsc_info_window.h" @@ -37,13 +37,12 @@ /// A label for StorageProperty struct PropertyLabel { /// Constructor - PropertyLabel(const std::string& label_, const StorageProperty* prop, bool markup_ = false) : - label(label_), property(prop), markup(markup_) + PropertyLabel(const std::string& label_, const StorageProperty* prop) : + label(label_), property(prop) { } std::string label; ///< Label text const StorageProperty* property; ///< Storage property - bool markup; ///< Whether the label text uses markup }; @@ -57,7 +56,7 @@ 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) + inline void app_set_top_labels(Gtk::VBox* vbox, const label_list_t& label_strings) { if (!vbox) return; @@ -69,26 +68,21 @@ namespace { 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); + Gtk::Label* label = Gtk::manage(new Gtk::Label("No data available", Gtk::ALIGN_LEFT)); + 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); + std::string label_text = Glib::Markup::escape_text(iter->label); + WrappingLabel* label = Gtk::manage(new WrappingLabel(label_text, Gtk::ALIGN_LEFT)); 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); + label->set_flags(label->get_flags() & ~Gtk::CAN_FOCUS); std::string fg; if (app_property_get_label_highlight_color(iter->property->warning, fg)) @@ -108,8 +102,8 @@ namespace { - /// Cell renderer functions for list cells - inline void app_list_cell_renderer_func(Gtk::CellRenderer* cr, const Gtk::TreeModel::iterator& iter, + /// Cell renderer functions for attribute cells + inline void app_attr_cell_renderer_func(Gtk::CellRenderer* cr, const Gtk::TreeModel::iterator& iter, Gtk::TreeModelColumn storage_column) { const StorageProperty* p = (*iter)[storage_column]; @@ -127,11 +121,6 @@ namespace { crt->property_cell_background().reset_value(); crt->property_foreground().reset_value(); } - if (p->value_statistic.is_header) { - crt->property_weight() = Pango::WEIGHT_BOLD; - } else { - crt->property_weight().reset_value(); - } } } @@ -185,20 +174,11 @@ GscInfoWindow::GscInfoWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& re : 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"); + Gtk::HBox* 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 = Gtk::manage(new WrappingLabel("No data available", Gtk::ALIGN_LEFT)); device_name_label->set_selectable(true); device_name_label->show(); device_name_hbox->pack_start(*device_name_label, true, true); @@ -233,35 +213,10 @@ GscInfoWindow::GscInfoWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& re // Accelerators if (close_window_button) { - close_window_button->add_accelerator("clicked", this->get_accel_group(), GDK_KEY_Escape, + close_window_button->add_accelerator("clicked", this->get_accel_group(), GDK_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 - } - } - - // --------------- @@ -281,103 +236,40 @@ GscInfoWindow::GscInfoWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& re 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_label = lookup_widget("identity_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("attributes_tab_label"); + tab_attributes_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("error_log_tab_label"); + tab_error_log_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("selftest_log_tab_label"); + tab_selftest_log_name = (tab_label ? tab_label->get_label() : ""); - tab_label = lookup_widget("directory_tab_label"); - tab_directory_name = (tab_label ? tab_label->get_label() : ""); + tab_label = lookup_widget("test_tab_label"); + tab_test_name = (tab_label ? tab_label->get_label() : ""); + + + // remove "Perform Test" tab for now... +// Gtk::Notebook* book = lookup_widget("main_notebook"); +// if (book) +// book->remove_page(book->get_n_pages() - 1); // 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. @@ -437,42 +329,33 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests bool smart_enabled = (drive->get_smart_status() == StorageDevice::status_enabled); Gtk::Widget* note_page_box = 0; + if ((note_page_box = lookup_widget("capabilities_tab_vbox")) != 0) { + if (smart_enabled) { note_page_box->show(); } else { note_page_box->hide(); } + } 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 ((note_page_box = lookup_widget("selftest_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 ((note_page_box = lookup_widget("test_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)); + std::string device = drive->get_device_with_type(); + std::string model = (drive->get_model_name().empty() ? "Unknown model" : drive->get_model_name()); 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); + device_name_label->set_markup("Device: " + device + " Model: " + model); } } @@ -515,42 +398,41 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests id_props.push_back(*iter); - Gtk::Grid* identity_table = lookup_widget("identity_table"); + Gtk::Table* 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; + StorageProperty::warning_t max_warning = StorageProperty::warning_none; + int row = 1; // row 0 is always empty. this way it's much easier. 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); + if (iter->generic_name == "overall_health") // a little distance for this one ++row; - } + // Note: Using WrappingLabel makes it invisible (huh?), so use plain Label instead. + // WrappingLabel* name = Gtk::manage(new WrappingLabel()); Gtk::Label* name = Gtk::manage(new Gtk::Label()); // name->set_ellipsize(Pango::ELLIPSIZE_END); - name->set_alignment(Gtk::ALIGN_END); // right-align + name->set_alignment(1., 0.5); // right-align name->set_selectable(true); - name->set_can_focus(false); + name->set_flags(name->get_flags() & ~Gtk::CAN_FOCUS); 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. + // WrappingLabel* value = Gtk::manage(new WrappingLabel()); Gtk::Label* value = Gtk::manage(new Gtk::Label()); // value->set_ellipsize(Pango::ELLIPSIZE_END); - value->set_alignment(Gtk::ALIGN_START); // left-align + value->set_alignment(0., 0.5); // left-align value->set_selectable(true); - value->set_can_focus(false); + value->set_flags(value->get_flags() & ~Gtk::CAN_FOCUS); value->set_markup(Glib::Markup::escape_text(iter->format_value())); std::string fg; @@ -559,23 +441,120 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests value->set_markup(""+ value->get_label() + ""); } - identity_table->attach(*name, 0, row, 1, 1); - identity_table->attach(*value, 1, row, 1, 1); + identity_table->attach(*name, 0, 1, row, row+1); + identity_table->attach(*value, 1, 2, row, row+1, Gtk::EXPAND | Gtk::FILL); 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; + if (int(iter->warning) > int(max_warning)) + max_warning = iter->warning; ++row; } + identity_table->resize(row+1, 2); // add one more row for symmetry + identity_table->show_all(); // tab label - app_highlight_tab_label(lookup_widget("general_tab_label"), max_tab_warning, tab_identity_name); + app_highlight_tab_label(lookup_widget("identity_tab_label"), max_warning, tab_identity_name); + + } while (false); + + + + + // ------------------------------------------- Capabilities + + + 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, "Flag", "Flag value", 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); + gtkmm_set_treeview_tooltip_column(treeview, col_tooltip); + + 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_renderer()), + sigc::bind(sigc::ptr_fun(app_attr_cell_renderer_func), col_storage)); + } + + + StorageProperty::warning_t max_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_warning)) + max_warning = iter->warning; + + ++index; + } + + // tab label + app_highlight_tab_label(lookup_widget("capabilities_tab_label"), max_warning, tab_capabilities_name); } while (false); @@ -609,7 +588,7 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests 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; + cr_name->property_weight() = Pango::WEIGHT_BOLD ; Gtk::TreeModelColumn col_failed; model_columns.add(col_failed); @@ -641,19 +620,18 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests 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_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" + "Flag", "Flag value\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" + "P: pre-failure warning\n" + "O: updated continuously\n" "S: speed / performance attribute\n" "R: error rate\n" "C: event count\n" @@ -662,7 +640,7 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests Gtk::TreeModelColumn col_tooltip; model_columns.add(col_tooltip); - treeview->set_tooltip_column(col_tooltip.index()); + gtkmm_set_treeview_tooltip_column(treeview, col_tooltip); Gtk::TreeModelColumn col_storage; @@ -676,12 +654,14 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests 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)); + tcol->set_cell_data_func(*(tcol->get_first_cell_renderer()), + sigc::bind(sigc::ptr_fun(app_attr_cell_renderer_func), col_storage)); } - StorageProperty::warning_t max_tab_warning = StorageProperty::warning_none; + + StorageProperty::warning_t max_warning = StorageProperty::warning_none; + int index = 1; label_list_t label_strings; // outside-of-tree properties for (prop_iterator iter = props.begin(); iter != props.end(); ++iter) { @@ -692,8 +672,8 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests 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; + if (int(iter->warning) > int(max_warning)) + max_warning = iter->warning; continue; } @@ -713,72 +693,221 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests 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_raw] = iter->value_attribute.raw_value; row[col_type] = attr_type; -// row[col_updated] = StorageAttribute::get_update_type_name(iter->value_attribute.update_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; + if (int(iter->warning) > int(max_warning)) + max_warning = iter->warning; + + ++index; } - Gtk::Box* label_vbox = lookup_widget("attributes_label_vbox"); + Gtk::VBox* 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); + app_highlight_tab_label(lookup_widget("attributes_tab_label"), max_warning, tab_attributes_name); } while (false); - // ------------------------------------------- Statistics + + + // ------------------------------------------- Error Log do { - - Gtk::TreeView* treeview = lookup_widget("statistics_treeview"); + Gtk::TreeView* treeview = lookup_widget("error_log_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 ; + // Error Number, Lifetime Hours, State, Type, Details, [tooltips] - 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_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_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_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_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_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()); + gtkmm_set_treeview_tooltip_column(treeview, col_tooltip); + 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_renderer()), + sigc::bind(sigc::ptr_fun(app_attr_cell_renderer_func), col_storage)); + } + + + StorageProperty::warning_t max_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); + + // 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_count") + label_strings.back().label += " (Note: Usually only the last five errors are stored.)"; + + } 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.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_warning)) + max_warning = iter->warning; + + } + + Gtk::VBox* label_vbox = lookup_widget("error_log_label_vbox"); + app_set_top_labels(label_vbox, label_strings); + + // tab label + app_highlight_tab_label(lookup_widget("error_log_tab_label"), max_warning, tab_error_log_name); + + } while (false); + + + + + // ------------------------------------------- Selftest Log + + + 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); + gtkmm_set_treeview_tooltip_column(treeview, col_tooltip); Gtk::TreeModelColumn col_storage; model_columns.add(col_storage); @@ -786,60 +915,91 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests // 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); - // 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)); + tcol->set_cell_data_func(*(tcol->get_first_cell_renderer()), + sigc::bind(sigc::ptr_fun(app_attr_cell_renderer_func), col_storage)); } - - StorageProperty::warning_t max_tab_warning = StorageProperty::warning_none; + StorageProperty::warning_t max_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) + if (iter->section != StorageProperty::section_data || iter->subsection != StorageProperty::subsection_selftest_log) continue; - // add non-entry-type properties to label above - if (iter->value_type != StorageProperty::value_type_statistic) { + 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; + if (int(iter->warning) > int(max_warning)) + max_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_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.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; + if (int(iter->warning) > int(max_warning)) + max_warning = iter->warning; } - Gtk::Box* label_vbox = lookup_widget("statistics_label_vbox"); + Gtk::VBox* label_vbox = lookup_widget("selftest_log_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); + app_highlight_tab_label(lookup_widget("selftest_log_tab_label"), max_warning, tab_selftest_log_name); } while (false); - // ------------------------------------------- Tests + // ------------------------------------------- Selective self-test log + + + do { + + Gtk::TextView* textview = lookup_widget("selective_selftest_log_textview"); + if (!textview) + break; + + for (prop_iterator iter = props.begin(); iter != props.end(); ++iter) { + if (iter->section != StorageProperty::section_data || iter->subsection != StorageProperty::subsection_selective_selftest_log) + continue; + + // add complete error log to textview window + // 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); + } + } + + } while (false); + + + + + // ------------------------------------------- Perform Tests if (clear_tests) { do { // Modify only after clearing tests! @@ -889,7 +1049,7 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests 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." + " 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" @@ -903,7 +1063,7 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests 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."; + " built into the drive. Its result is reported in the Self-test Log."; row[test_combo_col_self_test] = test_long; } @@ -929,576 +1089,10 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests 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); - - // 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); - } - } - - 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); - } - } - - 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); - } - } - - 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); - } - } - - 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); - } - } - - 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); - } @@ -1519,21 +1113,38 @@ void GscInfoWindow::clear_ui_info(bool clear_tests_too) } { - Gtk::Grid* identity_table = lookup_widget("identity_table"); + Gtk::Table* 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)); } + identity_table->resize(1, 2); // row 0 is always empty, so it clears it. (0, 0) is prohibited. } // tab label - app_highlight_tab_label(lookup_widget("general_tab_label"), StorageProperty::warning_none, tab_identity_name); + app_highlight_tab_label(lookup_widget("identity_tab_label"), StorageProperty::warning_none, tab_identity_name); } { - Gtk::Box* label_vbox = lookup_widget("attributes_label_vbox"); + 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(); + app_gtkmm_treeview_unset_model(treeview); + } + + // tab label + app_highlight_tab_label(lookup_widget("capabilities_tab_label"), StorageProperty::warning_none, tab_capabilities_name); + } + + { + Gtk::VBox* label_vbox = lookup_widget("attributes_label_vbox"); app_set_top_labels(label_vbox, label_list_t()); Gtk::TreeView* treeview = lookup_widget("attributes_treeview"); @@ -1542,7 +1153,7 @@ void GscInfoWindow::clear_ui_info(bool clear_tests_too) // if (model) // model->clear(); treeview->remove_all_columns(); - treeview->unset_model(); + app_gtkmm_treeview_unset_model(treeview); } // tab label @@ -1550,81 +1161,7 @@ void GscInfoWindow::clear_ui_info(bool clear_tests_too) } { - 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"); + Gtk::VBox* 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"); @@ -1633,7 +1170,7 @@ void GscInfoWindow::clear_ui_info(bool clear_tests_too) // if (model) // model->clear(); treeview->remove_all_columns(); - treeview->unset_model(); + app_gtkmm_treeview_unset_model(treeview); } Gtk::TextView* textview = lookup_widget("error_log_textview"); @@ -1648,44 +1185,20 @@ void GscInfoWindow::clear_ui_info(bool clear_tests_too) } { - Gtk::TextView* textview = lookup_widget("temperature_log_textview"); - if (textview) { - Glib::RefPtr buffer = textview->get_buffer(); - buffer->set_text("\nNo data available"); - } + Gtk::VBox* label_vbox = lookup_widget("selftest_log_label_vbox"); + app_set_top_labels(label_vbox, label_list_t()); - // 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"); + Gtk::TreeView* treeview = lookup_widget("selftest_log_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(); + app_gtkmm_treeview_unset_model(treeview); } // 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); + app_highlight_tab_label(lookup_widget("selftest_log_tab_label"), StorageProperty::warning_none, tab_selftest_log_name); } { @@ -1694,32 +1207,56 @@ void GscInfoWindow::clear_ui_info(bool clear_tests_too) 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"); + 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 + // app_gtkmm_combobox_unset_model(test_type_combo); + if (test_combo_model) + test_combo_model->clear(); } - // tab label - app_highlight_tab_label(lookup_widget("phy_tab_label"), StorageProperty::warning_none, tab_phy_name); - } + 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::TextView* textview = lookup_widget("directory_log_textview"); - if (textview) { - Glib::RefPtr buffer = textview->get_buffer(); - buffer->set_text("\nNo data available"); + 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::HBox* 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("directory_tab_label"), StorageProperty::warning_none, tab_directory_name); + app_highlight_tab_label(lookup_widget("test_tab_label"), StorageProperty::warning_none, tab_test_name); } + } @@ -1740,7 +1277,7 @@ void GscInfoWindow::show_tests() { Gtk::Notebook* book = lookup_widget("main_notebook"); if (book) - book->set_current_page(3); // the Tests tab + book->set_current_page(book->get_n_pages() - 1); // the Tests tab is the last one for now. } @@ -1788,39 +1325,7 @@ void GscInfoWindow::on_view_output_button_clicked() 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); @@ -1828,39 +1333,28 @@ void GscInfoWindow::on_save_info_button_clicked() 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 APP_GTKMM_CHECK_VERSION(2, 8, 0) + dialog.set_do_overwrite_confirmation(true); // since gtkmm 2.8 +#endif if (!last_dir.empty()) dialog.set_current_folder(last_dir); + std::string filename = drive->get_save_filename(); 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 + int result = dialog.run(); // the main cycle blocks here // 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"; - } + last_dir = dialog.get_current_folder(); // safe for the future + std::string file = dialog.get_filename(); hz::File f(file); std::string data = this->drive->get_full_output(); if (data.empty()) { @@ -1887,7 +1381,11 @@ void GscInfoWindow::on_save_info_button_clicked() void GscInfoWindow::on_close_window_button_clicked() { - on_delete_event_before(0); + 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; + } + destroy(this); } @@ -2048,7 +1546,7 @@ gboolean GscInfoWindow::test_idle_callback(void* data) } if (severity != StorageSelftestEntry::severity_none) { - result_msg += "\nCheck the Self-Test Log for more information."; + result_msg += "\nCheck the Self-test Log for more information."; } @@ -2085,7 +1583,7 @@ gboolean GscInfoWindow::test_idle_callback(void* data) if (test_result_label) test_result_label->set_markup(result_msg); - Gtk::Box* test_result_hbox = self->lookup_widget("test_result_hbox"); + Gtk::HBox* test_result_hbox = self->lookup_widget("test_result_hbox"); if (test_result_hbox) test_result_hbox->show(); @@ -2114,7 +1612,7 @@ void GscInfoWindow::on_test_execute_button_clicked() return; // hide previous test results from GUI - Gtk::Box* test_result_hbox = this->lookup_widget("test_result_hbox"); + Gtk::HBox* test_result_hbox = this->lookup_widget("test_result_hbox"); if (test_result_hbox) test_result_hbox->hide(); @@ -2222,65 +1720,9 @@ void GscInfoWindow::on_drive_changed(StorageDevice* pdrive) // 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); - } +#if APP_GTKMM_CHECK_VERSION(2, 10, 0) + this->set_deletable(!test_active); // since 2.10 +#endif } diff --git a/gsmartcontrol/src/gsc_info_window.h b/src/gsc_info_window.h similarity index 78% rename from gsmartcontrol/src/gsc_info_window.h rename to src/gsc_info_window.h index 6074030..3fb1cf5 100644 --- a/gsmartcontrol/src/gsc_info_window.h +++ b/src/gsc_info_window.h @@ -12,11 +12,14 @@ #ifndef GSC_INFO_WINDOW_H #define GSC_INFO_WINDOW_H -#include -#include +#include +#include +#include +#include #include "applib/app_ui_res_utils.h" #include "applib/storage_device.h" +#include "applib/wrapping_label.h" // WrappingLabel #include "applib/selftest.h" @@ -27,18 +30,16 @@ class GscInfoWindow : public AppUIResWidget { public: - // name of ui file without a .ui extension and quotes + // name of glade/ui file without a .glade/.ui extension and quotes APP_UI_RES_DATA_INIT(gsc_info_window); - /// Constructor, GtkBuilder needs this. + /// Constructor, gtkbuilder/glade needs this. GscInfoWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui); /// Virtual destructor - virtual ~GscInfoWindow(); - - // Overridden from InstanceManager - virtual void obj_destroy(); + virtual ~GscInfoWindow() + { } /// Set the drive to show @@ -100,15 +101,9 @@ class GscInfoWindow : public AppUIResWidget { void on_test_stop_button_clicked(); - /// Callback attached to StorageDevice change signal. + // 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: @@ -123,24 +118,15 @@ class GscInfoWindow : public AppUIResWidget { // --------- 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 + Glib::ustring tab_attributes_name; ///< Tab header name + Glib::ustring tab_error_log_name; ///< Tab header name + Glib::ustring tab_selftest_log_name; ///< Tab header name + Glib::ustring tab_test_name; ///< Tab header name - Gtk::Label* device_name_label; ///< Top label + WrappingLabel* device_name_label; ///< Top label StorageDeviceRefPtr drive; ///< The drive we're showing diff --git a/gsmartcontrol/src/gsc_init.cpp b/src/gsc_init.cpp similarity index 85% rename from gsmartcontrol/src/gsc_init.cpp rename to src/gsc_init.cpp index 57a4e84..0ded7bc 100644 --- a/gsmartcontrol/src/gsc_init.cpp +++ b/src/gsc_init.cpp @@ -15,15 +15,15 @@ #include // std::printf #include #include -#include -#include +#include +#include +#include // gtk_window_set_default_icon_name, gtk_icon_theme_* #include // g_, G* -#include // set_application_name - -#ifdef _WIN32 - #include - #include -#endif +#include +#include // set_application_name +#include // thread_init +// #include +// #include #include "hz/hz_config.h" // ENABLE_GLIB, VERSION, DEBUG_BUILD @@ -35,7 +35,6 @@ #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" @@ -211,9 +210,7 @@ namespace { 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()) + arg_add_device(NULL) { } // Note: Use GLib types here: @@ -223,8 +220,6 @@ namespace { 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 }; @@ -248,13 +243,6 @@ namespace { "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 } }; @@ -307,7 +295,7 @@ namespace { 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 - 2017 Alexander Shaduri \n\n"; + warningtext += "\nCopyright (C) 2008 - 2012 Alexander Shaduri \n\n"; std::fprintf(stdout, "%s%s", versiontext.c_str(), warningtext.c_str()); } @@ -322,10 +310,6 @@ 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 - // 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. @@ -398,21 +382,10 @@ bool app_init_and_loop(int& argc, char**& argv) << "\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"); + << "\targ_add_device: " << (load_devices_str.empty() ? "[empty]" : load_devices_str) << "\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(); @@ -423,8 +396,8 @@ bool app_init_and_loop(int& argc, char**& argv) 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" }; + "Pango", "Gtk", "Gdk", "GdkPixbuf", "libglade", "libgnomevfs", + "glibmm", "giomm", "atkmm", "pangomm", "gdkmm", "gtkmm", "libglademm" }; 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 @@ -478,43 +451,26 @@ bool app_init_and_loop(int& argc, char**& argv) #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 + // set default icon for all windows. + // set_default_icon_name is available since 2.12 in gtkmm, but since 2.6 in gtk. + +#ifndef _WIN32 // win32 version has its icon compiled-in, so no need to set it there. { // we load it via icontheme to provide multi-size version. + GtkIconTheme* default_icon_theme = gtk_icon_theme_get_default(); // application-installed, /usr/share/icons//apps/ - if (Gtk::IconTheme::get_default()->has_icon("gsmartcontrol")) { - Gtk::Window::set_default_icon_name("gsmartcontrol"); + if (gtk_icon_theme_has_icon(default_icon_theme, "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"); + } else if (gtk_icon_theme_has_icon(default_icon_theme, "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"); + gtk_window_set_default_icon_name("gtk-harddisk"); } } #endif @@ -557,6 +513,7 @@ bool app_init_and_loop(int& argc, char**& argv) m.run(); debug_out_info("app", "Main loop exited.\n"); + // close the main window and delete its object GscMainWindow::destroy(); diff --git a/gsmartcontrol/src/gsc_init.h b/src/gsc_init.h similarity index 100% rename from gsmartcontrol/src/gsc_init.h rename to src/gsc_init.h diff --git a/gsmartcontrol/src/gsc_main.cpp b/src/gsc_main.cpp similarity index 100% rename from gsmartcontrol/src/gsc_main.cpp rename to src/gsc_main.cpp diff --git a/gsmartcontrol/src/gsc_main_window.cpp b/src/gsc_main_window.cpp similarity index 84% rename from gsmartcontrol/src/gsc_main_window.cpp rename to src/gsc_main_window.cpp index d6423fa..fff7b29 100644 --- a/gsmartcontrol/src/gsc_main_window.cpp +++ b/src/gsc_main_window.cpp @@ -15,8 +15,6 @@ #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" @@ -30,6 +28,7 @@ #include "gsc_init.h" // app_quit() #include "gsc_about_dialog.h" #include "gsc_info_window.h" +#include "gsc_help_window.h" #include "gsc_preferences_window.h" #include "gsc_executor_log_window.h" #include "gsc_executor_error_dialog.h" // gsc_executor_error_dialog_show @@ -56,29 +55,9 @@ GscMainWindow::GscMainWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& re // 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(); @@ -129,18 +108,6 @@ GscMainWindow::GscMainWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& re 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(); @@ -157,15 +124,6 @@ GscMainWindow::GscMainWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& re -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) { @@ -262,13 +220,14 @@ bool GscMainWindow::create_widgets() " " " " - " " " " " " " " - " " - " " + " " + " " + " " + " " " " " " @@ -309,20 +268,24 @@ bool GscMainWindow::create_widgets() actiongroup_main->add(Gtk::Action::create("device_menu", "_Device")); +#if APP_GTKMM_CHECK_VERSION(2, 8, 0) // Gtk::Stock::INFO is since gtkmm 2.8 action = Gtk::Action::create(APP_ACTION_NAME(action_view_details), Gtk::Stock::INFO, "_View details", "View detailed information"); +#else + action = Gtk::Action::create(APP_ACTION_NAME(action_view_details), "_View details", "View detailed information"); +#endif 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); + action->connect_proxy(*lookup_widget("status_smart_enabled_check")); 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); + action->connect_proxy(*lookup_widget("status_aodc_enabled_check")); 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)); @@ -368,23 +331,27 @@ bool GscMainWindow::create_widgets() 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_general_help), Gtk::Stock::HELP, "A_ll Help Topics"); + actiongroup_main->add((action_map[action_general_help] = action), Gtk::AccelKey("F1"), + sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_general_help)); - 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_permission_problems), "Resolving _Permission Problems"); + actiongroup_main->add((action_map[action_permission_problems] = action), + sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_permission_problems)); + + action = Gtk::Action::create(APP_ACTION_NAME(action_how_to_enable_smart), "How to _Enable SMART Permanently"); + actiongroup_main->add((action_map[action_how_to_enable_smart] = action), + sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_how_to_enable_smart)); + + action = Gtk::Action::create(APP_ACTION_NAME(action_report_bug), "_Reporting Bugs"); + actiongroup_main->add((action_map[action_report_bug] = action), + sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_report_bug)); action = Gtk::Action::create(APP_ACTION_NAME(action_about), Gtk::Stock::ABOUT); actiongroup_main->add((action_map[action_about] = action), @@ -414,17 +381,14 @@ bool GscMainWindow::create_widgets() // 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)); + rescan_item->add_accelerator("activate", get_accel_group(), GDK_F5, Gdk::ModifierType(0), Gtk::AccelFlags(0)); // look after the created widgets - Gtk::Box* menubar_vbox = lookup_widget("menubar_vbox"); + Gtk::VBox* 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); - } + if (menubar_vbox && menubar) + menubar_vbox->pack_start(*menubar, Gtk::PACK_SHRINK); // Set tooltips on menu items - gtk does that only on toolbar items. @@ -446,28 +410,25 @@ bool GscMainWindow::create_widgets() // ----------------------------------------- Labels // create and add labels - Gtk::Box* name_label_box = lookup_widget("status_name_label_hbox"); + Gtk::HBox* 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 = Gtk::manage(new WrappingLabel("No drive selected", Gtk::ALIGN_LEFT)); 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"); + Gtk::HBox* 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 = Gtk::manage(new WrappingLabel("No drive selected", Gtk::ALIGN_LEFT)); 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"); + Gtk::HBox* 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 = Gtk::manage(new WrappingLabel("No drive selected", Gtk::ALIGN_LEFT)); family_label->set_selectable(true); family_label->show(); family_label_box->pack_start(*family_label, true, true); @@ -529,7 +490,13 @@ void GscMainWindow::on_action_activated(GscMainWindow::action_t action_type) switch (action_type) { case action_quit: - quit_requested(); + // if at least one drive is having a test performed, disallow. + if (this->testing_active()) { + if (!ask_about_quit_on_test(*this)) { + break; + } + } + app_quit(); // ends the main loop break; case action_view_details: @@ -597,12 +564,6 @@ void GscMainWindow::on_action_activated(GscMainWindow::action_t action_type) break; } - case action_update_drivedb: - { - run_update_drivedb(); - break; - } - case action_preferences: { GscPreferencesWindow* win = GscPreferencesWindow::create(); // destroyed on close @@ -613,15 +574,39 @@ void GscMainWindow::on_action_activated(GscMainWindow::action_t action_type) break; } - case action_online_documentation: + case action_general_help: { - hz::launch_url(gobj(), "https://gsmartcontrol.sourceforge.io/documentation.html"); + GscHelpWindow* win = GscHelpWindow::create(); // destroyed on close + // win->set_transient_for(*this); // for "destroy with parent", always-on-top + win->set_topic("Begin"); // special mark, top of the text. + win->show(); break; } - case action_support: + case action_permission_problems: { - hz::launch_url(gobj(), "https://gsmartcontrol.sourceforge.io/support.html"); + GscHelpWindow* win = GscHelpWindow::create(); // destroyed on close + // win->set_transient_for(*this); // for "destroy with parent", always-on-top + win->set_topic("Permission Problems"); + win->show(); + break; + } + + case action_how_to_enable_smart: + { + GscHelpWindow* win = GscHelpWindow::create(); // destroyed on close + // win->set_transient_for(*this); // for "destroy with parent", always-on-top + win->set_topic("Enable SMART Permanently"); + win->show(); + break; + } + + case action_report_bug: + { + GscHelpWindow* win = GscHelpWindow::create(); // destroyed on close + // win->set_transient_for(*this); // for "destroy with parent", always-on-top + win->set_topic("Reporting Bugs"); + win->show(); break; } @@ -719,7 +704,7 @@ void GscMainWindow::on_action_enable_aodc_toggled(Gtk::ToggleAction* action) 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.set_flags(enable_button.get_flags() | Gtk::CAN_DEFAULT); enable_button.show_all(); dialog.add_action_widget(enable_button, Gtk::RESPONSE_YES); enable_button.grab_default(); // make it the default widget @@ -865,11 +850,10 @@ void GscMainWindow::set_drive_menu_status(StorageDeviceRefPtr drive) } 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)))) { + 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); + action->set_sensitive(smart_status != StorageDevice::status_unsupported); } @@ -941,19 +925,16 @@ void GscMainWindow::update_status_widgets() 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 device = (drive->get_is_virtual() ? ("Virtual: " + drive->get_virtual_filename()) : drive->get_device_with_type()); + std::string size = drive->get_device_size_str(); + std::string model = (drive->get_model_name().empty() ? std::string("Unknown model") : drive->get_model_name()); + std::string family = (drive->get_family_name().empty() ? "Unknown" : drive->get_family_name()); + std::string family_fallback = (drive->get_family_name().empty() ? model : drive->get_family_name()); - std::string info_str = device - + (drive_letters_str.empty() ? "" : (" (" + drive_letters_str + ")")) - + (size.empty() ? "" : (", " + size)) - + (model.empty() ? "" : (", " + model)); + + std::string info_str = device + (size.empty() ? "" : (", " + size)) + (model.empty() ? "" : (", " + model)); if (name_label) { - name_label->set_markup(info_str); + name_label->set_text(info_str); app_gtkmm_set_widget_tooltip(*name_label, info_str, false); // in case it doesn't fit } @@ -1090,35 +1071,6 @@ void GscMainWindow::rescan_devices() -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 @@ -1300,35 +1252,7 @@ void GscMainWindow::show_add_device_chooser() 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); @@ -1336,43 +1260,20 @@ void GscMainWindow::show_load_virtual_file_chooser() 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 + int result = dialog.run(); // the main cycle blocks here // Handle the response switch (result) { case Gtk::RESPONSE_ACCEPT: { - std::vector files; + last_dir = dialog.get_current_folder(); // safe for the future -#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]); - } - } + std::string file = dialog.get_filename(); // in fs encoding + this->add_virtual_drive(file); break; } @@ -1388,7 +1289,9 @@ void GscMainWindow::show_load_virtual_file_chooser() -bool GscMainWindow::quit_requested() + +// by default, delete_event calls hide(). +bool GscMainWindow::on_delete_event_before(GdkEventAny* e) { // if at least one drive is having a test performed, disallow. if (this->testing_active()) { @@ -1396,35 +1299,12 @@ bool GscMainWindow::quit_requested() 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(); -} - - - diff --git a/gsmartcontrol/src/gsc_main_window.h b/src/gsc_main_window.h similarity index 88% rename from gsmartcontrol/src/gsc_main_window.h rename to src/gsc_main_window.h index a96b88a..c69fa7e 100644 --- a/gsmartcontrol/src/gsc_main_window.h +++ b/src/gsc_main_window.h @@ -13,11 +13,17 @@ #define GSC_MAIN_WINDOW_H #include -#include +#include +#include +#include +#include +#include +#include #include "applib/app_ui_res_utils.h" #include "applib/storage_device.h" #include "applib/app_gtkmm_features.h" // APP_GTKMM_CONNECT_VIRTUAL +#include "applib/wrapping_label.h" @@ -34,29 +40,22 @@ class GscMainWindow : public AppUIResWidget { friend class GscMainWindowIconView; // It needs our privates - // name of ui file without a .ui extension and quotes + // name of glade/ui file without a .glade/.ui extension and quotes APP_UI_RES_DATA_INIT(gsc_main_window); - /// Constructor, GtkBuilder needs this. + /// Constructor, gtkbuilder/glade 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); @@ -94,11 +93,12 @@ class GscMainWindow : public AppUIResWidget { action_rescan_devices, action_executor_log, - action_update_drivedb, action_preferences, - action_online_documentation, - action_support, + action_general_help, + action_permission_problems, + action_how_to_enable_smart, + action_report_bug, action_about }; @@ -126,10 +126,6 @@ class GscMainWindow : public AppUIResWidget { 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 @@ -169,9 +165,9 @@ class GscMainWindow : public AppUIResWidget { 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 + WrappingLabel* name_label; ///< A UI label + WrappingLabel* health_label; ///< A UI label + WrappingLabel* family_label; ///< A UI label bool scanning_; ///< If the scanning is in process or not diff --git a/gsmartcontrol/src/gsc_main_window_iconview.h b/src/gsc_main_window_iconview.h similarity index 72% rename from gsmartcontrol/src/gsc_main_window_iconview.h rename to src/gsc_main_window_iconview.h index 81e2857..e67cb58 100644 --- a/gsmartcontrol/src/gsc_main_window_iconview.h +++ b/src/gsc_main_window_iconview.h @@ -13,9 +13,13 @@ #define GSC_MAIN_WINDOW_ICONVIEW_H #include +#include // must include manually for older gtkmm (at least 2.6, but not 2.12) #include #include // std::floor -#include + +#if APP_GTKMM_CHECK_VERSION(2, 8, 0) + #include // gtk_icon_view_set_cursor, GTK_ICON_VIEW +#endif #include "hz/string_algo.h" // string_join #include "hz/debug.h" @@ -29,7 +33,7 @@ /// 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). +/// Note: The IconView must have a fixed icon width set (e.g. in glade file). /// Otherwise, it doesn't re-compute it when clearing and adding new icons. class GscMainWindowIconView : public Gtk::IconView { public: @@ -47,25 +51,19 @@ class GscMainWindowIconView : public Gtk::IconView { }; - /// Constructor, GtkBuilder needs this. + /// Constructor, gtkbuilder/glade 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) { + APP_GTKMM_CONNECT_VIRTUAL(expose_event); // make sure the event handler is called + 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); @@ -75,35 +73,24 @@ class GscMainWindowIconView : public Gtk::IconView { this->set_model(ref_list_model); // we add it here because list model must be created already - set_tooltip_column(col_description.index()); + gtkmm_set_iconview_tooltip_column(this, col_description, ref_list_model); // icons - Glib::RefPtr default_icon_theme; - try { - default_icon_theme = Gtk::IconTheme::get_default(); - } catch (...) { - // nothing - } + Glib::RefPtr default_icon_theme = Gtk::IconTheme::get_default(); - // 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 Gnome icons icons first, they are usually more consistent with Gnome desktop + // (this should work only if gnome-settings-daemon is running, so no harm on other desktops). + // The problem with stock gtk icons is that they are not available in 48x48 size, so + // confirm all sizes manually. - // 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. + if (app_gtkmm_icon_theme_has_icon(default_icon_theme, "gnome-dev-harddisk", 48)) + hd_icon = default_icon_theme->load_icon("gnome-dev-harddisk", 48, Gtk::IconLookupFlags(0)); + + if (!hd_icon && app_gtkmm_icon_theme_has_icon(default_icon_theme, "gtk-harddisk", 48)) + hd_icon = default_icon_theme->load_icon("gtk-harddisk", 48, Gtk::IconLookupFlags(0)); + + 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 { @@ -112,20 +99,18 @@ class GscMainWindowIconView : public Gtk::IconView { } } - // 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. + // last resort. gtk has gtk-harddisk built-in. it may not be the right size, but what else can we do? + if (!hd_icon) + hd_icon = default_icon_theme->load_icon("gtk-harddisk", 48, Gtk::IconLookupFlags(0)); + + + if (app_gtkmm_icon_theme_has_icon(default_icon_theme, "gnome-dev-cdrom", 48)) + cddvd_icon = default_icon_theme->load_icon("gnome-dev-cdrom", 48, Gtk::IconLookupFlags(0)); + + if (!cddvd_icon && app_gtkmm_icon_theme_has_icon(default_icon_theme, "gtk-cdrom", 48)) + cddvd_icon = default_icon_theme->load_icon("gtk-cdrom", 48, Gtk::IconLookupFlags(0)); + + 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 { @@ -134,6 +119,10 @@ class GscMainWindowIconView : public Gtk::IconView { } } + if (!cddvd_icon) + cddvd_icon = default_icon_theme->load_icon("gtk-cdrom", 48, Gtk::IconLookupFlags(0)); + + this->signal_item_activated().connect(sigc::mem_fun(*this, &self_type::on_iconview_item_activated) ); @@ -166,10 +155,16 @@ class GscMainWindowIconView : public Gtk::IconView { } - // Overridden from Gtk::Widget - bool on_draw(const Cairo::RefPtr& cr) + /// Overridden from Gtk::Widget + bool on_expose_event_before(GdkEventExpose* exp_event) { + // this->on_expose_event(exp_event); // parent's stuff + if (empty_view_message != message_none && this->num_icons == 0) { // no icons + Glib::RefPtr win = Glib::wrap(exp_event->window, true); + // Cairo::RefPtr cr = win->create_cairo_context(); + Glib::RefPtr gc = Gdk::GC::create(win); + std::string msg; switch(empty_view_message) { case message_scan_disabled: msg = "Automatic scanning is disabled.\nPress Ctrl+R to scan manually."; break; @@ -180,7 +175,7 @@ class GscMainWindowIconView : public Gtk::IconView { default: msg = "[error - invalid message]"; } - Glib::RefPtr layout = this->create_pango_layout(""); + Glib::RefPtr layout = create_pango_layout(""); layout->set_alignment(Pango::ALIGN_CENTER); layout->set_markup(msg); @@ -189,47 +184,15 @@ class GscMainWindowIconView : public Gtk::IconView { 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; + win->draw_layout(gc, pos_x, pos_y, layout); } - return Gtk::IconView::on_draw(cr); + return false; // without false the icons don't get drawn... } -#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; - Glib::RefPtr pixbuf = row[col_pixbuf]; - - // 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) { @@ -246,13 +209,15 @@ class GscMainWindowIconView : public Gtk::IconView { if (scroll_to_it) { Gtk::TreeModel::Path tpath(row); // scroll_to_path() and set/get_cursor() are since gtkmm 2.8. - +#if APP_GTKMM_CHECK_VERSION(2, 8, 0) 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); + // 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); } +#endif this->select_path(tpath); // highlight it } @@ -282,23 +247,12 @@ class GscMainWindowIconView : public Gtk::IconView { // 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()); } @@ -306,30 +260,15 @@ class GscMainWindowIconView : public Gtk::IconView { 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()) + ""); } @@ -355,24 +294,22 @@ class GscMainWindowIconView : public Gtk::IconView { 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(); + 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 - } + 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 } } } @@ -424,7 +361,7 @@ class GscMainWindowIconView : public Gtk::IconView { ref_list_model->clear(); // this is needed to update the label from "disabled" to "scanning" - if (this->get_realized()) { + if (this->is_realized()) { Gdk::Rectangle rect = this->get_allocation(); Glib::RefPtr win = this->get_window(); win->invalidate_rect(rect, true); // force expose event @@ -520,11 +457,14 @@ class GscMainWindowIconView : public Gtk::IconView { 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) + // set/get_cursor() are since gtkmm 2.8. +#if APP_GTKMM_CHECK_VERSION(2, 8, 0) 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); } +#endif // select the icon this->select_path(tpath); @@ -562,7 +502,6 @@ class GscMainWindowIconView : public Gtk::IconView { 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 diff --git a/gsmartcontrol/src/gsc_preferences_window.cpp b/src/gsc_preferences_window.cpp similarity index 92% rename from gsmartcontrol/src/gsc_preferences_window.cpp rename to src/gsc_preferences_window.cpp index 729a6ec..374721e 100644 --- a/gsmartcontrol/src/gsc_preferences_window.cpp +++ b/src/gsc_preferences_window.cpp @@ -10,12 +10,19 @@ /// @{ #include -#include -#include // GDK_KEY_Escape +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // GDK_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" @@ -33,7 +40,7 @@ class GscPreferencesDeviceOptionsTreeView : public Gtk::TreeView { typedef GscPreferencesDeviceOptionsTreeView self_type; ///< Self type, needed for CONNECT_VIRTUAL - /// Constructor, GtkBuilder needs this. + /// Constructor, gtkbuilder/glade needs this. GscPreferencesDeviceOptionsTreeView(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui) : Gtk::TreeView(gtkcobj), preferences_window(0) { @@ -292,7 +299,7 @@ GscPreferencesWindow::GscPreferencesWindow(BaseObjectType* gtkcobj, const app_ui Glib::RefPtr accel_group = this->get_accel_group(); if (window_cancel_button) { - window_cancel_button->add_accelerator("clicked", accel_group, GDK_KEY_Escape, + window_cancel_button->add_accelerator("clicked", accel_group, GDK_Escape, Gdk::ModifierType(0), Gtk::AccelFlags(0)); } @@ -572,35 +579,6 @@ void GscPreferencesWindow::on_smartctl_binary_browse_button_clicked() 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); @@ -608,30 +586,19 @@ void GscPreferencesWindow::on_smartctl_binary_browse_button_clicked() 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. + hz::FsPath p(entry->get_text()); + if (p.is_absolute()) + dialog.set_filename(p.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 + int result = dialog.run(); // the main cycle blocks here // 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); + entry->set_text(std::string(dialog.get_filename())); break; } diff --git a/gsmartcontrol/src/gsc_preferences_window.h b/src/gsc_preferences_window.h similarity index 94% rename from gsmartcontrol/src/gsc_preferences_window.h rename to src/gsc_preferences_window.h index 0ca4af6..cceac77 100644 --- a/gsmartcontrol/src/gsc_preferences_window.h +++ b/src/gsc_preferences_window.h @@ -12,7 +12,7 @@ #ifndef GSC_PREFERENCES_WINDOW_H #define GSC_PREFERENCES_WINDOW_H -#include +#include #include "applib/app_ui_res_utils.h" @@ -29,11 +29,11 @@ class GscMainWindow; // declared in gsc_main_window.h class GscPreferencesWindow : public AppUIResWidget { public: - // name of ui file without a .ui extension and quotes + // name of glade/ui file without a .glade/.ui extension and quotes APP_UI_RES_DATA_INIT(gsc_preferences_window); - /// Constructor, GtkBuilder needs this. + /// Constructor, gtkbuilder/glade needs this. GscPreferencesWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui); /// Virtual destructor diff --git a/gsmartcontrol/src/gsc_settings.h b/src/gsc_settings.h similarity index 95% rename from gsmartcontrol/src/gsc_settings.h rename to src/gsc_settings.h index 2b4058c..9ff7bab 100644 --- a/gsmartcontrol/src/gsc_settings.h +++ b/src/gsc_settings.h @@ -41,9 +41,8 @@ inline void init_default_settings() #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_regpath", "SOFTWARE\\smartmontools"); // in HKLM + rconfig::set_default_data("system/win32_smartmontools_regkey", "Install_Dir"); 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) diff --git a/gsmartcontrol/src/gsc_text_window.h b/src/gsc_text_window.h similarity index 69% rename from gsmartcontrol/src/gsc_text_window.h rename to src/gsc_text_window.h index 6dc2d8d..2335a89 100644 --- a/gsmartcontrol/src/gsc_text_window.h +++ b/src/gsc_text_window.h @@ -12,15 +12,16 @@ #ifndef GSC_TEXT_WINDOW_H #define GSC_TEXT_WINDOW_H -#include -#include // GDK_KEY_Escape +#include +#include +#include +#include +#include +#include // GDK_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" @@ -38,14 +39,14 @@ template class GscTextWindow : public AppUIResWidget, InstanceSwitch::multi_instance> { public: - // name of ui file without a .ui extension and quotes + // name of glade/ui file without a .glade/.ui extension and quotes APP_UI_RES_DATA_INIT(gsc_text_window); - /// Self type, needed for GtkBuilder, not inherited from parent because of templates + /// Self type, needed for glade, not inherited from parent because of templates typedef GscTextWindow self_type; - /// Constructor, GtkBuilder needs this. + /// Constructor, gtkbuilder/glade needs this. GscTextWindow(typename Gtk::Window::BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui) : AppUIResWidget, InstanceSwitch::multi_instance>(gtkcobj, ref_ui) { @@ -64,7 +65,7 @@ class GscTextWindow : public AppUIResWidget, Insta Glib::RefPtr accel_group = this->get_accel_group(); if (close_window_button) { - close_window_button->add_accelerator("clicked", accel_group, GDK_KEY_Escape, + close_window_button->add_accelerator("clicked", accel_group, GDK_Escape, Gdk::ModifierType(0), Gtk::AccelFlags(0)); } @@ -142,37 +143,7 @@ class GscTextWindow : public AppUIResWidget, Insta 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); @@ -180,10 +151,9 @@ class GscTextWindow : public AppUIResWidget, Insta 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 APP_GTKMM_CHECK_VERSION(2, 8, 0) + dialog.set_do_overwrite_confirmation(true); // since gtkmm 2.8 +#endif if (!last_dir.empty()) dialog.set_current_folder(last_dir); @@ -192,27 +162,15 @@ class GscTextWindow : public AppUIResWidget, Insta dialog.set_current_name(save_filename_); // Show the dialog and wait for a user response - result = dialog.run(); // the main cycle blocks here -#endif + int result = dialog.run(); // the main cycle blocks here // 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"; - } + last_dir = dialog.get_current_folder(); // safe for the future + std::string file = dialog.get_filename(); 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); diff --git a/gsmartcontrol/src/gsc_winres.rc.in b/src/gsc_winres.rc.in similarity index 100% rename from gsmartcontrol/src/gsc_winres.rc.in rename to src/gsc_winres.rc.in diff --git a/gsmartcontrol/src/gsmartcontrol.exe.manifest.in b/src/gsmartcontrol.exe.manifest.in similarity index 85% rename from gsmartcontrol/src/gsmartcontrol.exe.manifest.in rename to src/gsmartcontrol.exe.manifest.in index c2f8b3c..2c42ef2 100644 --- a/gsmartcontrol/src/gsmartcontrol.exe.manifest.in +++ b/src/gsmartcontrol.exe.manifest.in @@ -18,11 +18,14 @@ - + + + diff --git a/gsmartcontrol/src/hz/Makefile.am b/src/hz/Makefile.am similarity index 100% rename from gsmartcontrol/src/hz/Makefile.am rename to src/hz/Makefile.am diff --git a/gsmartcontrol/src/hz/any_convert.h b/src/hz/any_convert.h similarity index 100% rename from gsmartcontrol/src/hz/any_convert.h rename to src/hz/any_convert.h diff --git a/gsmartcontrol/src/hz/any_type.h b/src/hz/any_type.h similarity index 100% rename from gsmartcontrol/src/hz/any_type.h rename to src/hz/any_type.h diff --git a/gsmartcontrol/src/hz/any_type_holder.h b/src/hz/any_type_holder.h similarity index 100% rename from gsmartcontrol/src/hz/any_type_holder.h rename to src/hz/any_type_holder.h diff --git a/gsmartcontrol/src/hz/any_type_test.cpp b/src/hz/any_type_test.cpp similarity index 100% rename from gsmartcontrol/src/hz/any_type_test.cpp rename to src/hz/any_type_test.cpp diff --git a/gsmartcontrol/src/hz/ascii.h b/src/hz/ascii.h similarity index 98% rename from gsmartcontrol/src/hz/ascii.h rename to src/hz/ascii.h index f977f7e..80e3758 100644 --- a/gsmartcontrol/src/hz/ascii.h +++ b/src/hz/ascii.h @@ -184,17 +184,9 @@ T ascii_strtoi(const char* nptr, char** endptr, int base) // 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 { diff --git a/gsmartcontrol/src/hz/bad_cast_exception.h b/src/hz/bad_cast_exception.h similarity index 100% rename from gsmartcontrol/src/hz/bad_cast_exception.h rename to src/hz/bad_cast_exception.h diff --git a/gsmartcontrol/src/hz/bin2ascii_encoder.h b/src/hz/bin2ascii_encoder.h similarity index 100% rename from gsmartcontrol/src/hz/bin2ascii_encoder.h rename to src/hz/bin2ascii_encoder.h diff --git a/gsmartcontrol/src/hz/bin2ascii_encoder_test.cpp b/src/hz/bin2ascii_encoder_test.cpp similarity index 100% rename from gsmartcontrol/src/hz/bin2ascii_encoder_test.cpp rename to src/hz/bin2ascii_encoder_test.cpp diff --git a/gsmartcontrol/src/hz/common_types.h b/src/hz/common_types.h similarity index 100% rename from gsmartcontrol/src/hz/common_types.h rename to src/hz/common_types.h diff --git a/gsmartcontrol/src/hz/cstdint.h b/src/hz/cstdint.h similarity index 100% rename from gsmartcontrol/src/hz/cstdint.h rename to src/hz/cstdint.h diff --git a/gsmartcontrol/src/hz/cstdint_impl_msvc.h b/src/hz/cstdint_impl_msvc.h similarity index 100% rename from gsmartcontrol/src/hz/cstdint_impl_msvc.h rename to src/hz/cstdint_impl_msvc.h diff --git a/gsmartcontrol/src/hz/data_file.h b/src/hz/data_file.h similarity index 100% rename from gsmartcontrol/src/hz/data_file.h rename to src/hz/data_file.h diff --git a/gsmartcontrol/src/hz/debug.h b/src/hz/debug.h similarity index 100% rename from gsmartcontrol/src/hz/debug.h rename to src/hz/debug.h diff --git a/gsmartcontrol/src/hz/down_cast.h b/src/hz/down_cast.h similarity index 100% rename from gsmartcontrol/src/hz/down_cast.h rename to src/hz/down_cast.h diff --git a/gsmartcontrol/src/hz/down_cast_test.cpp b/src/hz/down_cast_test.cpp similarity index 100% rename from gsmartcontrol/src/hz/down_cast_test.cpp rename to src/hz/down_cast_test.cpp diff --git a/gsmartcontrol/src/hz/env_tools.h b/src/hz/env_tools.h similarity index 100% rename from gsmartcontrol/src/hz/env_tools.h rename to src/hz/env_tools.h diff --git a/gsmartcontrol/src/hz/errno_string.h b/src/hz/errno_string.h similarity index 100% rename from gsmartcontrol/src/hz/errno_string.h rename to src/hz/errno_string.h diff --git a/gsmartcontrol/src/hz/errno_string_test.cpp b/src/hz/errno_string_test.cpp similarity index 100% rename from gsmartcontrol/src/hz/errno_string_test.cpp rename to src/hz/errno_string_test.cpp diff --git a/gsmartcontrol/src/hz/error.h b/src/hz/error.h similarity index 100% rename from gsmartcontrol/src/hz/error.h rename to src/hz/error.h diff --git a/gsmartcontrol/src/hz/error_holder.h b/src/hz/error_holder.h similarity index 100% rename from gsmartcontrol/src/hz/error_holder.h rename to src/hz/error_holder.h diff --git a/gsmartcontrol/src/hz/exceptions.h b/src/hz/exceptions.h similarity index 100% rename from gsmartcontrol/src/hz/exceptions.h rename to src/hz/exceptions.h diff --git a/gsmartcontrol/src/hz/format_unit.h b/src/hz/format_unit.h similarity index 100% rename from gsmartcontrol/src/hz/format_unit.h rename to src/hz/format_unit.h diff --git a/gsmartcontrol/src/hz/format_unit_test.cpp b/src/hz/format_unit_test.cpp similarity index 100% rename from gsmartcontrol/src/hz/format_unit_test.cpp rename to src/hz/format_unit_test.cpp diff --git a/gsmartcontrol/src/hz/fs_common.h b/src/hz/fs_common.h similarity index 100% rename from gsmartcontrol/src/hz/fs_common.h rename to src/hz/fs_common.h diff --git a/gsmartcontrol/src/hz/fs_dir.h b/src/hz/fs_dir.h similarity index 100% rename from gsmartcontrol/src/hz/fs_dir.h rename to src/hz/fs_dir.h diff --git a/gsmartcontrol/src/hz/fs_dir_platform.h b/src/hz/fs_dir_platform.h similarity index 100% rename from gsmartcontrol/src/hz/fs_dir_platform.h rename to src/hz/fs_dir_platform.h diff --git a/gsmartcontrol/src/hz/fs_dir_test.cpp b/src/hz/fs_dir_test.cpp similarity index 100% rename from gsmartcontrol/src/hz/fs_dir_test.cpp rename to src/hz/fs_dir_test.cpp diff --git a/gsmartcontrol/src/hz/fs_error_holder.h b/src/hz/fs_error_holder.h similarity index 100% rename from gsmartcontrol/src/hz/fs_error_holder.h rename to src/hz/fs_error_holder.h diff --git a/gsmartcontrol/src/hz/fs_file.h b/src/hz/fs_file.h similarity index 100% rename from gsmartcontrol/src/hz/fs_file.h rename to src/hz/fs_file.h diff --git a/gsmartcontrol/src/hz/fs_file_test.cpp b/src/hz/fs_file_test.cpp similarity index 100% rename from gsmartcontrol/src/hz/fs_file_test.cpp rename to src/hz/fs_file_test.cpp diff --git a/gsmartcontrol/src/hz/fs_path.h b/src/hz/fs_path.h similarity index 100% rename from gsmartcontrol/src/hz/fs_path.h rename to src/hz/fs_path.h diff --git a/gsmartcontrol/src/hz/fs_path_test.cpp b/src/hz/fs_path_test.cpp similarity index 100% rename from gsmartcontrol/src/hz/fs_path_test.cpp rename to src/hz/fs_path_test.cpp diff --git a/gsmartcontrol/src/hz/fs_path_utils.h b/src/hz/fs_path_utils.h similarity index 100% rename from gsmartcontrol/src/hz/fs_path_utils.h rename to src/hz/fs_path_utils.h diff --git a/gsmartcontrol/src/hz/fs_tools.h b/src/hz/fs_tools.h similarity index 100% rename from gsmartcontrol/src/hz/fs_tools.h rename to src/hz/fs_tools.h diff --git a/gsmartcontrol/src/hz/hz_config.h b/src/hz/hz_config.h similarity index 100% rename from gsmartcontrol/src/hz/hz_config.h rename to src/hz/hz_config.h diff --git a/gsmartcontrol/src/hz/i18n.h b/src/hz/i18n.h similarity index 100% rename from gsmartcontrol/src/hz/i18n.h rename to src/hz/i18n.h diff --git a/gsmartcontrol/src/hz/initialized.h b/src/hz/initialized.h similarity index 100% rename from gsmartcontrol/src/hz/initialized.h rename to src/hz/initialized.h diff --git a/gsmartcontrol/src/hz/instance_manager.h b/src/hz/instance_manager.h similarity index 100% rename from gsmartcontrol/src/hz/instance_manager.h rename to src/hz/instance_manager.h diff --git a/gsmartcontrol/src/hz/intrusive_ptr.h b/src/hz/intrusive_ptr.h similarity index 100% rename from gsmartcontrol/src/hz/intrusive_ptr.h rename to src/hz/intrusive_ptr.h diff --git a/gsmartcontrol/src/hz/intrusive_scoped_lock.h b/src/hz/intrusive_scoped_lock.h similarity index 100% rename from gsmartcontrol/src/hz/intrusive_scoped_lock.h rename to src/hz/intrusive_scoped_lock.h diff --git a/src/hz/launch_url.h b/src/hz/launch_url.h new file mode 100644 index 0000000..5b7e4ec --- /dev/null +++ b/src/hz/launch_url.h @@ -0,0 +1,148 @@ +/************************************************************************** + 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 // g_* +#include // gdk_spawn_command_line_on_screen, GdkScreen + +#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" + #include "env_tools.h" +#endif + + + + +namespace hz { + + + + namespace internal { + + /// Spawn the command (internal helper function) + inline bool launch_url_helper_do_launch(const std::string& command, GError** errorptr, GdkScreen* screen) + { + if (screen) + return gdk_spawn_command_line_on_screen(screen, command.c_str(), errorptr); + return g_spawn_command_line_async(command.c_str(), errorptr); + } + + } + + + + /// 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. + /// Note: This works using Glib / GDK only. + inline std::string launch_url(const std::string& link, GdkScreen* screen = 0) + { + // TODO: Use gtk_show_uri() if gtk 2.14. + if (link.empty()) + return "Error while executing a command: Empty URI specified."; + +#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 + bool is_email = (link.compare(0, 7, "mailto:") == 0); + + std::string browser; + + // susehelp lists this, with alternative being TEXTBROWSER + hz::env_get_value("XBROWSER", browser); + + if (browser.empty()) + hz::env_get_value("BROWSER", browser); // this is the common method + + // try xfce first - it has the most sensible launcher. + if (browser.empty()) + browser = "exo-open"; + + std::string qlink; + + { + // will this break its embedded parameters? + hz::scoped_ptr browser_cstr(g_shell_quote(browser.c_str()), g_free); + if (browser_cstr) + browser = browser_cstr.get(); + + hz::scoped_ptr qlink_cstr(g_shell_quote(link.c_str()), g_free); + if (qlink_cstr) + qlink = qlink_cstr.get(); + } + + std::string command = browser + " " + qlink; + + hz::scoped_ptr error(0, g_error_free); + bool status = internal::launch_url_helper_do_launch(command.c_str(), &error.get_ref(), screen); + + if (!status) { // try kde4 + status = internal::launch_url_helper_do_launch(std::string("kde-open ") + qlink, 0, screen); + } + + if (!status) { // try kde3 + // launches both konq and kmail on mailto:. + status = internal::launch_url_helper_do_launch(std::string("kfmclient openURL") + qlink, 0, screen); + } + + if (!status) { // try gnome + // errors out with "no handler" on mailto: on my system. + status = internal::launch_url_helper_do_launch(std::string("gnome-open ") + qlink, 0, screen); + } + + if (!status && !is_email) { // try XDG + // doesn't support emails at all. + status = internal::launch_url_helper_do_launch(std::string("xdg-open ") + qlink, 0, screen); + } + + // we use the error of the first command, because it could have user-specified. + std::string error_msg; + if (!status) { + error_msg = std::string("Error while executing a command: ") + + ((error && error->message) ? (std::string(": ") + error->message) : "."); + } + + return error_msg; +#endif + } + + + + + +} // ns + + + + + +#endif + +/// @} diff --git a/gsmartcontrol/src/hz/local_algo.h b/src/hz/local_algo.h similarity index 100% rename from gsmartcontrol/src/hz/local_algo.h rename to src/hz/local_algo.h diff --git a/gsmartcontrol/src/hz/locale_tools.h b/src/hz/locale_tools.h similarity index 100% rename from gsmartcontrol/src/hz/locale_tools.h rename to src/hz/locale_tools.h diff --git a/gsmartcontrol/src/hz/noncopyable.h b/src/hz/noncopyable.h similarity index 100% rename from gsmartcontrol/src/hz/noncopyable.h rename to src/hz/noncopyable.h diff --git a/gsmartcontrol/src/hz/optional_value.h b/src/hz/optional_value.h similarity index 100% rename from gsmartcontrol/src/hz/optional_value.h rename to src/hz/optional_value.h diff --git a/gsmartcontrol/src/hz/portable_snprintf.h b/src/hz/portable_snprintf.h similarity index 100% rename from gsmartcontrol/src/hz/portable_snprintf.h rename to src/hz/portable_snprintf.h diff --git a/gsmartcontrol/src/hz/process_signal.h b/src/hz/process_signal.h similarity index 99% rename from gsmartcontrol/src/hz/process_signal.h rename to src/hz/process_signal.h index 3c730fd..34059cc 100644 --- a/gsmartcontrol/src/hz/process_signal.h +++ b/src/hz/process_signal.h @@ -156,7 +156,7 @@ 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) + 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 diff --git a/gsmartcontrol/src/hz/ptr_container.h b/src/hz/ptr_container.h similarity index 100% rename from gsmartcontrol/src/hz/ptr_container.h rename to src/hz/ptr_container.h diff --git a/gsmartcontrol/src/hz/res_data.h b/src/hz/res_data.h similarity index 97% rename from gsmartcontrol/src/hz/res_data.h rename to src/hz/res_data.h index 0fb9762..b72dc9a 100644 --- a/gsmartcontrol/src/hz/res_data.h +++ b/src/hz/res_data.h @@ -34,7 +34,7 @@ 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). +/// (You may use this e.g. with glade 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() \ diff --git a/gsmartcontrol/src/hz/scoped_array.h b/src/hz/scoped_array.h similarity index 100% rename from gsmartcontrol/src/hz/scoped_array.h rename to src/hz/scoped_array.h diff --git a/gsmartcontrol/src/hz/scoped_ptr.h b/src/hz/scoped_ptr.h similarity index 96% rename from gsmartcontrol/src/hz/scoped_ptr.h rename to src/hz/scoped_ptr.h index 73c5437..9b08333 100644 --- a/gsmartcontrol/src/hz/scoped_ptr.h +++ b/src/hz/scoped_ptr.h @@ -125,14 +125,7 @@ class scoped_ptr { // non-copyable 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 } } diff --git a/gsmartcontrol/src/hz/static_assert.h b/src/hz/static_assert.h similarity index 100% rename from gsmartcontrol/src/hz/static_assert.h rename to src/hz/static_assert.h diff --git a/gsmartcontrol/src/hz/stream_cast.h b/src/hz/stream_cast.h similarity index 100% rename from gsmartcontrol/src/hz/stream_cast.h rename to src/hz/stream_cast.h diff --git a/gsmartcontrol/src/hz/string_algo.h b/src/hz/string_algo.h similarity index 100% rename from gsmartcontrol/src/hz/string_algo.h rename to src/hz/string_algo.h diff --git a/gsmartcontrol/src/hz/string_algo_test.cpp b/src/hz/string_algo_test.cpp similarity index 100% rename from gsmartcontrol/src/hz/string_algo_test.cpp rename to src/hz/string_algo_test.cpp diff --git a/gsmartcontrol/src/hz/string_num.h b/src/hz/string_num.h similarity index 100% rename from gsmartcontrol/src/hz/string_num.h rename to src/hz/string_num.h diff --git a/gsmartcontrol/src/hz/string_num_test.cpp b/src/hz/string_num_test.cpp similarity index 100% rename from gsmartcontrol/src/hz/string_num_test.cpp rename to src/hz/string_num_test.cpp diff --git a/gsmartcontrol/src/hz/string_sprintf.h b/src/hz/string_sprintf.h similarity index 100% rename from gsmartcontrol/src/hz/string_sprintf.h rename to src/hz/string_sprintf.h diff --git a/gsmartcontrol/src/hz/string_sprintf_macros.h b/src/hz/string_sprintf_macros.h similarity index 100% rename from gsmartcontrol/src/hz/string_sprintf_macros.h rename to src/hz/string_sprintf_macros.h diff --git a/gsmartcontrol/src/hz/string_wcmatch.h b/src/hz/string_wcmatch.h similarity index 100% rename from gsmartcontrol/src/hz/string_wcmatch.h rename to src/hz/string_wcmatch.h diff --git a/gsmartcontrol/src/hz/string_wcmatch_test.cpp b/src/hz/string_wcmatch_test.cpp similarity index 100% rename from gsmartcontrol/src/hz/string_wcmatch_test.cpp rename to src/hz/string_wcmatch_test.cpp diff --git a/gsmartcontrol/src/hz/sync.h b/src/hz/sync.h similarity index 100% rename from gsmartcontrol/src/hz/sync.h rename to src/hz/sync.h diff --git a/gsmartcontrol/src/hz/sync_lock_ptr.h b/src/hz/sync_lock_ptr.h similarity index 100% rename from gsmartcontrol/src/hz/sync_lock_ptr.h rename to src/hz/sync_lock_ptr.h diff --git a/gsmartcontrol/src/hz/sync_multilock.h b/src/hz/sync_multilock.h similarity index 100% rename from gsmartcontrol/src/hz/sync_multilock.h rename to src/hz/sync_multilock.h diff --git a/gsmartcontrol/src/hz/sync_multilock_test.cpp b/src/hz/sync_multilock_test.cpp similarity index 100% rename from gsmartcontrol/src/hz/sync_multilock_test.cpp rename to src/hz/sync_multilock_test.cpp diff --git a/gsmartcontrol/src/hz/sync_part_get_policy.h b/src/hz/sync_part_get_policy.h similarity index 100% rename from gsmartcontrol/src/hz/sync_part_get_policy.h rename to src/hz/sync_part_get_policy.h diff --git a/gsmartcontrol/src/hz/sync_policy_boost.h b/src/hz/sync_policy_boost.h similarity index 100% rename from gsmartcontrol/src/hz/sync_policy_boost.h rename to src/hz/sync_policy_boost.h diff --git a/gsmartcontrol/src/hz/sync_policy_glib.h b/src/hz/sync_policy_glib.h similarity index 100% rename from gsmartcontrol/src/hz/sync_policy_glib.h rename to src/hz/sync_policy_glib.h diff --git a/gsmartcontrol/src/hz/sync_policy_glibmm.h b/src/hz/sync_policy_glibmm.h similarity index 100% rename from gsmartcontrol/src/hz/sync_policy_glibmm.h rename to src/hz/sync_policy_glibmm.h diff --git a/gsmartcontrol/src/hz/sync_policy_poco.h b/src/hz/sync_policy_poco.h similarity index 100% rename from gsmartcontrol/src/hz/sync_policy_poco.h rename to src/hz/sync_policy_poco.h diff --git a/gsmartcontrol/src/hz/sync_policy_pthread.h b/src/hz/sync_policy_pthread.h similarity index 100% rename from gsmartcontrol/src/hz/sync_policy_pthread.h rename to src/hz/sync_policy_pthread.h diff --git a/gsmartcontrol/src/hz/sync_policy_win32.h b/src/hz/sync_policy_win32.h similarity index 100% rename from gsmartcontrol/src/hz/sync_policy_win32.h rename to src/hz/sync_policy_win32.h diff --git a/gsmartcontrol/src/hz/sync_test.cpp b/src/hz/sync_test.cpp similarity index 100% rename from gsmartcontrol/src/hz/sync_test.cpp rename to src/hz/sync_test.cpp diff --git a/gsmartcontrol/src/hz/system_specific.h b/src/hz/system_specific.h similarity index 100% rename from gsmartcontrol/src/hz/system_specific.h rename to src/hz/system_specific.h diff --git a/gsmartcontrol/src/hz/tls.h b/src/hz/tls.h similarity index 100% rename from gsmartcontrol/src/hz/tls.h rename to src/hz/tls.h diff --git a/gsmartcontrol/src/hz/tls_policy_boost.h b/src/hz/tls_policy_boost.h similarity index 100% rename from gsmartcontrol/src/hz/tls_policy_boost.h rename to src/hz/tls_policy_boost.h diff --git a/gsmartcontrol/src/hz/tls_policy_glib.h b/src/hz/tls_policy_glib.h similarity index 89% rename from gsmartcontrol/src/hz/tls_policy_glib.h rename to src/hz/tls_policy_glib.h index 56cb94c..ede9621 100644 --- a/gsmartcontrol/src/hz/tls_policy_glib.h +++ b/src/hz/tls_policy_glib.h @@ -44,14 +44,16 @@ extern "C" { class TlsPolicyGlib { public: - TlsPolicyGlib(tls_policy_glib_cleanup_func_t native_cleanup) - : key_((GPrivate)G_PRIVATE_INIT(native_cleanup)) - { } + TlsPolicyGlib(tls_policy_glib_cleanup_func_t native_cleanup) : native_cleanup_(native_cleanup) + { + key_ = 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)) - { } + TlsPolicyGlib(tls_policy_glib_cleanup_func_cpp_t native_cleanup) : native_cleanup_(native_cleanup) + { + key_ = G_PRIVATE_INIT(native_cleanup_); + } #endif ~TlsPolicyGlib() @@ -76,6 +78,7 @@ class TlsPolicyGlib { private: mutable GPrivate key_; + tls_policy_glib_cleanup_func_t native_cleanup_; ///< may be NULL TlsPolicyGlib(const TlsPolicyGlib&); TlsPolicyGlib& operator= (const TlsPolicyGlib& from); diff --git a/gsmartcontrol/src/hz/tls_policy_pthread.h b/src/hz/tls_policy_pthread.h similarity index 100% rename from gsmartcontrol/src/hz/tls_policy_pthread.h rename to src/hz/tls_policy_pthread.h diff --git a/gsmartcontrol/src/hz/tls_policy_win32.h b/src/hz/tls_policy_win32.h similarity index 100% rename from gsmartcontrol/src/hz/tls_policy_win32.h rename to src/hz/tls_policy_win32.h diff --git a/gsmartcontrol/src/hz/tls_test.cpp b/src/hz/tls_test.cpp similarity index 100% rename from gsmartcontrol/src/hz/tls_test.cpp rename to src/hz/tls_test.cpp diff --git a/gsmartcontrol/src/hz/type_categories.h b/src/hz/type_categories.h similarity index 100% rename from gsmartcontrol/src/hz/type_categories.h rename to src/hz/type_categories.h diff --git a/gsmartcontrol/src/hz/type_properties.h b/src/hz/type_properties.h similarity index 100% rename from gsmartcontrol/src/hz/type_properties.h rename to src/hz/type_properties.h diff --git a/gsmartcontrol/src/hz/win32_tools.h b/src/hz/win32_tools.h similarity index 100% rename from gsmartcontrol/src/hz/win32_tools.h rename to src/hz/win32_tools.h diff --git a/gsmartcontrol/src/libdebug/Makefile.am b/src/libdebug/Makefile.am similarity index 100% rename from gsmartcontrol/src/libdebug/Makefile.am rename to src/libdebug/Makefile.am diff --git a/gsmartcontrol/src/libdebug/dchannel.cpp b/src/libdebug/dchannel.cpp similarity index 100% rename from gsmartcontrol/src/libdebug/dchannel.cpp rename to src/libdebug/dchannel.cpp diff --git a/gsmartcontrol/src/libdebug/dchannel.h b/src/libdebug/dchannel.h similarity index 100% rename from gsmartcontrol/src/libdebug/dchannel.h rename to src/libdebug/dchannel.h diff --git a/gsmartcontrol/src/libdebug/dcmdarg.cpp b/src/libdebug/dcmdarg.cpp similarity index 99% rename from gsmartcontrol/src/libdebug/dcmdarg.cpp rename to src/libdebug/dcmdarg.cpp index 0f4de5c..a342a63 100644 --- a/gsmartcontrol/src/libdebug/dcmdarg.cpp +++ b/src/libdebug/dcmdarg.cpp @@ -38,11 +38,7 @@ namespace debug_internal { /// Constructor DebugCmdArgs() : // defaults -#ifdef _WIN32 - verbose(TRUE), -#else verbose(FALSE), -#endif quiet(FALSE), #ifdef DEBUG_BUILD verbosity_level(5), // all diff --git a/gsmartcontrol/src/libdebug/dcmdarg.h b/src/libdebug/dcmdarg.h similarity index 100% rename from gsmartcontrol/src/libdebug/dcmdarg.h rename to src/libdebug/dcmdarg.h diff --git a/gsmartcontrol/src/libdebug/dexcept.h b/src/libdebug/dexcept.h similarity index 100% rename from gsmartcontrol/src/libdebug/dexcept.h rename to src/libdebug/dexcept.h diff --git a/gsmartcontrol/src/libdebug/dflags.cpp b/src/libdebug/dflags.cpp similarity index 100% rename from gsmartcontrol/src/libdebug/dflags.cpp rename to src/libdebug/dflags.cpp diff --git a/gsmartcontrol/src/libdebug/dflags.h b/src/libdebug/dflags.h similarity index 100% rename from gsmartcontrol/src/libdebug/dflags.h rename to src/libdebug/dflags.h diff --git a/gsmartcontrol/src/libdebug/dout.cpp b/src/libdebug/dout.cpp similarity index 100% rename from gsmartcontrol/src/libdebug/dout.cpp rename to src/libdebug/dout.cpp diff --git a/gsmartcontrol/src/libdebug/dout.h b/src/libdebug/dout.h similarity index 100% rename from gsmartcontrol/src/libdebug/dout.h rename to src/libdebug/dout.h diff --git a/gsmartcontrol/src/libdebug/dstate.cpp b/src/libdebug/dstate.cpp similarity index 100% rename from gsmartcontrol/src/libdebug/dstate.cpp rename to src/libdebug/dstate.cpp diff --git a/gsmartcontrol/src/libdebug/dstate.h b/src/libdebug/dstate.h similarity index 100% rename from gsmartcontrol/src/libdebug/dstate.h rename to src/libdebug/dstate.h diff --git a/gsmartcontrol/src/libdebug/dstate_pub.h b/src/libdebug/dstate_pub.h similarity index 100% rename from gsmartcontrol/src/libdebug/dstate_pub.h rename to src/libdebug/dstate_pub.h diff --git a/gsmartcontrol/src/libdebug/dstream.cpp b/src/libdebug/dstream.cpp similarity index 100% rename from gsmartcontrol/src/libdebug/dstream.cpp rename to src/libdebug/dstream.cpp diff --git a/gsmartcontrol/src/libdebug/dstream.h b/src/libdebug/dstream.h similarity index 100% rename from gsmartcontrol/src/libdebug/dstream.h rename to src/libdebug/dstream.h diff --git a/gsmartcontrol/src/libdebug/libdebug.h b/src/libdebug/libdebug.h similarity index 100% rename from gsmartcontrol/src/libdebug/libdebug.h rename to src/libdebug/libdebug.h diff --git a/gsmartcontrol/src/libdebug/libdebug_mini.h b/src/libdebug/libdebug_mini.h similarity index 100% rename from gsmartcontrol/src/libdebug/libdebug_mini.h rename to src/libdebug/libdebug_mini.h diff --git a/gsmartcontrol/src/libdebug/libdebug_test.cpp b/src/libdebug/libdebug_test.cpp similarity index 100% rename from gsmartcontrol/src/libdebug/libdebug_test.cpp rename to src/libdebug/libdebug_test.cpp diff --git a/src/pcrecpp/Makefile.am b/src/pcrecpp/Makefile.am new file mode 100644 index 0000000..25e7964 --- /dev/null +++ b/src/pcrecpp/Makefile.am @@ -0,0 +1,10 @@ +AM_CPPFLAGS = $(all_includes) +METASOURCES = AUTO +noinst_LIBRARIES = libpcrecpp.a +libpcrecpp_a_SOURCES = pcre_scanner.cpp pcre_stringpiece.cpp pcrecpp.cpp +noinst_HEADERS = pcre_scanner.h pcre_stringpiece.h pcrecpp.h pcrecpp_internal.h \ + pcrecpparg.h + +# Distribute original, unpatched files too. Append .orig_dist to prevent conflict +# with debian .orig files. +EXTRA_DIST = pcrecpp.cc.orig_dist diff --git a/src/pcrecpp/README b/src/pcrecpp/README new file mode 100644 index 0000000..14a99e8 --- /dev/null +++ b/src/pcrecpp/README @@ -0,0 +1,13 @@ + +Part of PCRE library, version 8.01. + +pcrecpparg.h and pcre_stringpiece.h generated using: + +./configure \ + --with-link-size=2 \ + --with-match-limit=10000000 \ + --enable-newline-is-lf \ + --enable-utf8 \ + --enable-unicode-properties + +The system was OpenSUSE 11.1, with gcc-4.3-34.243 and glibc-2.9-2.12.1. diff --git a/src/pcrecpp/pcre_scanner.cpp b/src/pcrecpp/pcre_scanner.cpp new file mode 100644 index 0000000..661f9c6 --- /dev/null +++ b/src/pcrecpp/pcre_scanner.cpp @@ -0,0 +1,200 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Google Inc. 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 COPYRIGHT HOLDERS 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 COPYRIGHT +// OWNER 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. +// +// Author: Sanjay Ghemawat + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include // Custom +#include + +#include "pcrecpp_internal.h" +#include "pcre_scanner.h" + +using std::vector; + +namespace pcrecpp { + +Scanner::Scanner() + : data_(), + input_(data_), + skip_(NULL), + should_skip_(false), + skip_repeat_(false), + save_comments_(false), + comments_(NULL), + comments_offset_(0) { +} + +Scanner::Scanner(const string& in) + : data_(in), + input_(data_), + skip_(NULL), + should_skip_(false), + skip_repeat_(false), + save_comments_(false), + comments_(NULL), + comments_offset_(0) { +} + +Scanner::~Scanner() { + delete skip_; + delete comments_; +} + +void Scanner::SetSkipExpression(const char* re) { + delete skip_; + if (re != NULL) { + skip_ = new RE(re); + should_skip_ = true; + skip_repeat_ = true; + ConsumeSkip(); + } else { + skip_ = NULL; + should_skip_ = false; + skip_repeat_ = false; + } +} + +void Scanner::Skip(const char* re) { + delete skip_; + if (re != NULL) { + skip_ = new RE(re); + should_skip_ = true; + skip_repeat_ = false; + ConsumeSkip(); + } else { + skip_ = NULL; + should_skip_ = false; + skip_repeat_ = false; + } +} + +void Scanner::DisableSkip() { + assert(skip_ != NULL); + should_skip_ = false; +} + +void Scanner::EnableSkip() { + assert(skip_ != NULL); + should_skip_ = true; + ConsumeSkip(); +} + +int Scanner::LineNumber() const { + // TODO: Make it more efficient by keeping track of the last point + // where we computed line numbers and counting newlines since then. + // We could use std:count, but not all systems have it. :-( + int count = 1; + for (const char* p = data_.data(); p < input_.data(); ++p) + if (*p == '\n') + ++count; + return count; +} + +int Scanner::Offset() const { + return input_.data() - data_.c_str(); +} + +bool Scanner::LookingAt(const RE& re) const { + int consumed; + return re.DoMatch(input_, RE::ANCHOR_START, &consumed, 0, 0); +} + + +bool Scanner::Consume(const RE& re, + const Arg& arg0, + const Arg& arg1, + const Arg& arg2) { + const bool result = re.Consume(&input_, arg0, arg1, arg2); + if (result && should_skip_) ConsumeSkip(); + return result; +} + +// helper function to consume *skip_ and honour save_comments_ +void Scanner::ConsumeSkip() { + const char* start_data = input_.data(); + while (skip_->Consume(&input_)) { + if (!skip_repeat_) { + // Only one skip allowed. + break; + } + } + if (save_comments_) { + if (comments_ == NULL) { + comments_ = new vector; + } + // already pointing one past end, so no need to +1 + int length = input_.data() - start_data; + if (length > 0) { + comments_->push_back(StringPiece(start_data, length)); + } + } +} + + +void Scanner::GetComments(int start, int end, vector *ranges) { + // short circuit out if we've not yet initialized comments_ + // (e.g., when save_comments is false) + if (!comments_) { + return; + } + // TODO: if we guarantee that comments_ will contain StringPieces + // that are ordered by their start, then we can do a binary search + // for the first StringPiece at or past start and then scan for the + // ones contained in the range, quit early (use equal_range or + // lower_bound) + for (vector::const_iterator it = comments_->begin(); + it != comments_->end(); ++it) { + if ((it->data() >= data_.c_str() + start && + it->data() + it->size() <= data_.c_str() + end)) { + ranges->push_back(*it); + } + } +} + + +void Scanner::GetNextComments(vector *ranges) { + // short circuit out if we've not yet initialized comments_ + // (e.g., when save_comments is false) + if (!comments_) { + return; + } + for (vector::const_iterator it = + comments_->begin() + comments_offset_; + it != comments_->end(); ++it) { + ranges->push_back(*it); + ++comments_offset_; + } +} + +} // namespace pcrecpp diff --git a/src/pcrecpp/pcre_scanner.h b/src/pcrecpp/pcre_scanner.h new file mode 100644 index 0000000..c4fbcaf --- /dev/null +++ b/src/pcrecpp/pcre_scanner.h @@ -0,0 +1,173 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Google Inc. 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 COPYRIGHT HOLDERS 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 COPYRIGHT +// OWNER 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. +// +// Author: Sanjay Ghemawat +// +// Regular-expression based scanner for parsing an input stream. +// +// Example 1: parse a sequence of "var = number" entries from input: +// +// Scanner scanner(input); +// string var; +// int number; +// scanner.SetSkipExpression("\\s+"); // Skip any white space we encounter +// while (scanner.Consume("(\\w+) = (\\d+)", &var, &number)) { +// ...; +// } + +#ifndef _PCRE_SCANNER_H +#define _PCRE_SCANNER_H + +#include // Custom +#include +#include +#include + +#include +#include + +namespace pcrecpp { + +class PCRECPP_EXP_DEFN Scanner { + public: + Scanner(); + explicit Scanner(const std::string& input); + ~Scanner(); + + // Return current line number. The returned line-number is + // one-based. I.e. it returns 1 + the number of consumed newlines. + // + // Note: this method may be slow. It may take time proportional to + // the size of the input. + int LineNumber() const; + + // Return the byte-offset that the scanner is looking in the + // input data; + int Offset() const; + + // Return true iff the start of the remaining input matches "re" + bool LookingAt(const RE& re) const; + + // Return true iff all of the following are true + // a. the start of the remaining input matches "re", + // b. if any arguments are supplied, matched sub-patterns can be + // parsed and stored into the arguments. + // If it returns true, it skips over the matched input and any + // following input that matches the "skip" regular expression. + bool Consume(const RE& re, + const Arg& arg0 = RE::no_arg, + const Arg& arg1 = RE::no_arg, + const Arg& arg2 = RE::no_arg + // TODO: Allow more arguments? + ); + + // Set the "skip" regular expression. If after consuming some data, + // a prefix of the input matches this RE, it is automatically + // skipped. For example, a programming language scanner would use + // a skip RE that matches white space and comments. + // + // scanner.SetSkipExpression("\\s+|//.*|/[*](.|\n)*?[*]/"); + // + // Skipping repeats as long as it succeeds. We used to let people do + // this by writing "(...)*" in the regular expression, but that added + // up to lots of recursive calls within the pcre library, so now we + // control repetition explicitly via the function call API. + // + // You can pass NULL for "re" if you do not want any data to be skipped. + void Skip(const char* re); // DEPRECATED; does *not* repeat + void SetSkipExpression(const char* re); + + // Temporarily pause "skip"ing. This + // Skip("Foo"); code ; DisableSkip(); code; EnableSkip() + // is similar to + // Skip("Foo"); code ; Skip(NULL); code ; Skip("Foo"); + // but avoids creating/deleting new RE objects. + void DisableSkip(); + + // Reenable previously paused skipping. Any prefix of the input + // that matches the skip pattern is immediately dropped. + void EnableSkip(); + + /***** Special wrappers around SetSkip() for some common idioms *****/ + + // Arranges to skip whitespace, C comments, C++ comments. + // The overall RE is a disjunction of the following REs: + // \\s whitespace + // //.*\n C++ comment + // /[*](.|\n)*?[*]/ C comment (x*? means minimal repetitions of x) + // We get repetition via the semantics of SetSkipExpression, not by using * + void SkipCXXComments() { + SetSkipExpression("\\s|//.*\n|/[*](?:\n|.)*?[*]/"); + } + + void set_save_comments(bool comments) { + save_comments_ = comments; + } + + bool save_comments() { + return save_comments_; + } + + // Append to vector ranges the comments found in the + // byte range [start,end] (inclusive) of the input data. + // Only comments that were extracted entirely within that + // range are returned: no range splitting of atomically-extracted + // comments is performed. + void GetComments(int start, int end, std::vector *ranges); + + // Append to vector ranges the comments added + // since the last time this was called. This + // functionality is provided for efficiency when + // interleaving scanning with parsing. + void GetNextComments(std::vector *ranges); + + private: + std::string data_; // All the input data + StringPiece input_; // Unprocessed input + RE* skip_; // If non-NULL, RE for skipping input + bool should_skip_; // If true, use skip_ + bool skip_repeat_; // If true, repeat skip_ as long as it works + bool save_comments_; // If true, aggregate the skip expression + + // the skipped comments + // TODO: later consider requiring that the StringPieces be added + // in order by their start position + std::vector *comments_; + + // the offset into comments_ that has been returned by GetNextComments + int comments_offset_; + + // helper function to consume *skip_ and honour + // save_comments_ + void ConsumeSkip(); +}; + +} // namespace pcrecpp + +#endif /* _PCRE_SCANNER_H */ diff --git a/src/pcrecpp/pcre_stringpiece.cpp b/src/pcrecpp/pcre_stringpiece.cpp new file mode 100644 index 0000000..67c0f1f --- /dev/null +++ b/src/pcrecpp/pcre_stringpiece.cpp @@ -0,0 +1,43 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Google Inc. 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 COPYRIGHT HOLDERS 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 COPYRIGHT +// OWNER 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. +// +// Author: wilsonh@google.com (Wilson Hsieh) +// + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "pcrecpp_internal.h" +#include "pcre_stringpiece.h" + +std::ostream& operator<<(std::ostream& o, const pcrecpp::StringPiece& piece) { + return (o << piece.as_string()); +} diff --git a/src/pcrecpp/pcre_stringpiece.h b/src/pcrecpp/pcre_stringpiece.h new file mode 100644 index 0000000..bc85d7c --- /dev/null +++ b/src/pcrecpp/pcre_stringpiece.h @@ -0,0 +1,178 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Google Inc. 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 COPYRIGHT HOLDERS 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 COPYRIGHT +// OWNER 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. +// +// Author: Sanjay Ghemawat +// +// A string like object that points into another piece of memory. +// Useful for providing an interface that allows clients to easily +// pass in either a "const char*" or a "string". +// +// Arghh! I wish C++ literals were automatically of type "string". + +#ifndef _PCRE_STRINGPIECE_H +#define _PCRE_STRINGPIECE_H + +#include // Custom +#include +#include +#include // for ostream forward-declaration + +#if 0 +#define HAVE_TYPE_TRAITS +#include +#elif 0 +#define HAVE_TYPE_TRAITS +#include +#endif + +#include + +using std::string; + +namespace pcrecpp { + +class PCRECPP_EXP_DEFN StringPiece { + private: + const char* ptr_; + int length_; + + public: + // We provide non-explicit singleton constructors so users can pass + // in a "const char*" or a "string" wherever a "StringPiece" is + // expected. + StringPiece() + : ptr_(NULL), length_(0) { } + StringPiece(const char* str) + : ptr_(str), length_(static_cast(strlen(ptr_))) { } + StringPiece(const unsigned char* str) + : ptr_(reinterpret_cast(str)), + length_(static_cast(strlen(ptr_))) { } + StringPiece(const string& str) + : ptr_(str.data()), length_(static_cast(str.size())) { } + StringPiece(const char* offset, int len) + : ptr_(offset), length_(len) { } + + // data() may return a pointer to a buffer with embedded NULs, and the + // returned buffer may or may not be null terminated. Therefore it is + // typically a mistake to pass data() to a routine that expects a NUL + // terminated string. Use "as_string().c_str()" if you really need to do + // this. Or better yet, change your routine so it does not rely on NUL + // termination. + const char* data() const { return ptr_; } + int size() const { return length_; } + bool empty() const { return length_ == 0; } + + void clear() { ptr_ = NULL; length_ = 0; } + void set(const char* buffer, int len) { ptr_ = buffer; length_ = len; } + void set(const char* str) { + ptr_ = str; + length_ = static_cast(strlen(str)); + } + void set(const void* buffer, int len) { + ptr_ = reinterpret_cast(buffer); + length_ = len; + } + + char operator[](int i) const { return ptr_[i]; } + + void remove_prefix(int n) { + ptr_ += n; + length_ -= n; + } + + void remove_suffix(int n) { + length_ -= n; + } + + bool operator==(const StringPiece& x) const { + return ((length_ == x.length_) && + (memcmp(ptr_, x.ptr_, length_) == 0)); + } + bool operator!=(const StringPiece& x) const { + return !(*this == x); + } + +#define STRINGPIECE_BINARY_PREDICATE(cmp,auxcmp) \ + bool operator cmp (const StringPiece& x) const { \ + int r = memcmp(ptr_, x.ptr_, length_ < x.length_ ? length_ : x.length_); \ + return ((r auxcmp 0) || ((r == 0) && (length_ cmp x.length_))); \ + } + STRINGPIECE_BINARY_PREDICATE(<, <); + STRINGPIECE_BINARY_PREDICATE(<=, <); + STRINGPIECE_BINARY_PREDICATE(>=, >); + STRINGPIECE_BINARY_PREDICATE(>, >); +#undef STRINGPIECE_BINARY_PREDICATE + + int compare(const StringPiece& x) const { + int r = memcmp(ptr_, x.ptr_, length_ < x.length_ ? length_ : x.length_); + if (r == 0) { + if (length_ < x.length_) r = -1; + else if (length_ > x.length_) r = +1; + } + return r; + } + + string as_string() const { + return string(data(), size()); + } + + void CopyToString(string* target) const { + target->assign(ptr_, length_); + } + + // Does "this" start with "x" + bool starts_with(const StringPiece& x) const { + return ((length_ >= x.length_) && (memcmp(ptr_, x.ptr_, x.length_) == 0)); + } +}; + +} // namespace pcrecpp + +// ------------------------------------------------------------------ +// Functions used to create STL containers that use StringPiece +// Remember that a StringPiece's lifetime had better be less than +// that of the underlying string or char*. If it is not, then you +// cannot safely store a StringPiece into an STL container +// ------------------------------------------------------------------ + +#ifdef HAVE_TYPE_TRAITS +// This makes vector really fast for some STL implementations +template<> struct __type_traits { + typedef __true_type has_trivial_default_constructor; + typedef __true_type has_trivial_copy_constructor; + typedef __true_type has_trivial_assignment_operator; + typedef __true_type has_trivial_destructor; + typedef __true_type is_POD_type; +}; +#endif + +// allow StringPiece to be logged +std::ostream& operator<<(std::ostream& o, const pcrecpp::StringPiece& piece); + +#endif /* _PCRE_STRINGPIECE_H */ diff --git a/src/pcrecpp/pcrecpp.cc.orig_dist b/src/pcrecpp/pcrecpp.cc.orig_dist new file mode 100644 index 0000000..3a68be2 --- /dev/null +++ b/src/pcrecpp/pcrecpp.cc.orig_dist @@ -0,0 +1,920 @@ +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Google Inc. 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 COPYRIGHT HOLDERS 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 COPYRIGHT +// OWNER 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. +// +// Author: Sanjay Ghemawat + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include /* for SHRT_MIN, USHRT_MAX, etc */ +#include +#include +#include +#include + +#include "pcrecpp_internal.h" +#include "pcre.h" +#include "pcrecpp.h" +#include "pcre_stringpiece.h" + + +namespace pcrecpp { + +// Maximum number of args we can set +static const int kMaxArgs = 16; +static const int kVecSize = (1 + kMaxArgs) * 3; // results + PCRE workspace + +// Special object that stands-in for no argument +Arg RE::no_arg((void*)NULL); + +// This is for ABI compatibility with old versions of pcre (pre-7.6), +// which defined a global no_arg variable instead of putting it in the +// RE class. This works on GCC >= 3, at least. It definitely works +// for ELF, but may not for other object formats (Mach-O, for +// instance, does not support aliases.) We could probably have a more +// inclusive test if we ever needed it. (Note that not only the +// __attribute__ syntax, but also __USER_LABEL_PREFIX__, are +// gnu-specific.) +#if defined(__GNUC__) && __GNUC__ >= 3 && defined(__ELF__) +# define ULP_AS_STRING(x) ULP_AS_STRING_INTERNAL(x) +# define ULP_AS_STRING_INTERNAL(x) #x +# define USER_LABEL_PREFIX_STR ULP_AS_STRING(__USER_LABEL_PREFIX__) +extern Arg no_arg + __attribute__((alias(USER_LABEL_PREFIX_STR "_ZN7pcrecpp2RE6no_argE"))); +#endif + +// If a regular expression has no error, its error_ field points here +static const string empty_string; + +// If the user doesn't ask for any options, we just use this one +static RE_Options default_options; + +void RE::Init(const string& pat, const RE_Options* options) { + pattern_ = pat; + if (options == NULL) { + options_ = default_options; + } else { + options_ = *options; + } + error_ = &empty_string; + re_full_ = NULL; + re_partial_ = NULL; + + re_partial_ = Compile(UNANCHORED); + if (re_partial_ != NULL) { + re_full_ = Compile(ANCHOR_BOTH); + } +} + +void RE::Cleanup() { + if (re_full_ != NULL) (*pcre_free)(re_full_); + if (re_partial_ != NULL) (*pcre_free)(re_partial_); + if (error_ != &empty_string) delete error_; +} + + +RE::~RE() { + Cleanup(); +} + + +pcre* RE::Compile(Anchor anchor) { + // First, convert RE_Options into pcre options + int pcre_options = 0; + pcre_options = options_.all_options(); + + // Special treatment for anchoring. This is needed because at + // runtime pcre only provides an option for anchoring at the + // beginning of a string (unless you use offset). + // + // There are three types of anchoring we want: + // UNANCHORED Compile the original pattern, and use + // a pcre unanchored match. + // ANCHOR_START Compile the original pattern, and use + // a pcre anchored match. + // ANCHOR_BOTH Tack a "\z" to the end of the original pattern + // and use a pcre anchored match. + + const char* compile_error; + int eoffset; + pcre* re; + if (anchor != ANCHOR_BOTH) { + re = pcre_compile(pattern_.c_str(), pcre_options, + &compile_error, &eoffset, NULL); + } else { + // Tack a '\z' at the end of RE. Parenthesize it first so that + // the '\z' applies to all top-level alternatives in the regexp. + string wrapped = "(?:"; // A non-counting grouping operator + wrapped += pattern_; + wrapped += ")\\z"; + re = pcre_compile(wrapped.c_str(), pcre_options, + &compile_error, &eoffset, NULL); + } + if (re == NULL) { + if (error_ == &empty_string) error_ = new string(compile_error); + } + return re; +} + +/***** Matching interfaces *****/ + +bool RE::FullMatch(const StringPiece& text, + const Arg& ptr1, + const Arg& ptr2, + const Arg& ptr3, + const Arg& ptr4, + const Arg& ptr5, + const Arg& ptr6, + const Arg& ptr7, + const Arg& ptr8, + const Arg& ptr9, + const Arg& ptr10, + const Arg& ptr11, + const Arg& ptr12, + const Arg& ptr13, + const Arg& ptr14, + const Arg& ptr15, + const Arg& ptr16) const { + const Arg* args[kMaxArgs]; + int n = 0; + if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1; + if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2; + if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3; + if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4; + if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5; + if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6; + if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7; + if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8; + if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9; + if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10; + if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11; + if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12; + if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13; + if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14; + if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15; + if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16; + done: + + int consumed; + int vec[kVecSize]; + return DoMatchImpl(text, ANCHOR_BOTH, &consumed, args, n, vec, kVecSize); +} + +bool RE::PartialMatch(const StringPiece& text, + const Arg& ptr1, + const Arg& ptr2, + const Arg& ptr3, + const Arg& ptr4, + const Arg& ptr5, + const Arg& ptr6, + const Arg& ptr7, + const Arg& ptr8, + const Arg& ptr9, + const Arg& ptr10, + const Arg& ptr11, + const Arg& ptr12, + const Arg& ptr13, + const Arg& ptr14, + const Arg& ptr15, + const Arg& ptr16) const { + const Arg* args[kMaxArgs]; + int n = 0; + if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1; + if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2; + if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3; + if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4; + if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5; + if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6; + if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7; + if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8; + if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9; + if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10; + if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11; + if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12; + if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13; + if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14; + if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15; + if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16; + done: + + int consumed; + int vec[kVecSize]; + return DoMatchImpl(text, UNANCHORED, &consumed, args, n, vec, kVecSize); +} + +bool RE::Consume(StringPiece* input, + const Arg& ptr1, + const Arg& ptr2, + const Arg& ptr3, + const Arg& ptr4, + const Arg& ptr5, + const Arg& ptr6, + const Arg& ptr7, + const Arg& ptr8, + const Arg& ptr9, + const Arg& ptr10, + const Arg& ptr11, + const Arg& ptr12, + const Arg& ptr13, + const Arg& ptr14, + const Arg& ptr15, + const Arg& ptr16) const { + const Arg* args[kMaxArgs]; + int n = 0; + if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1; + if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2; + if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3; + if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4; + if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5; + if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6; + if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7; + if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8; + if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9; + if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10; + if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11; + if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12; + if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13; + if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14; + if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15; + if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16; + done: + + int consumed; + int vec[kVecSize]; + if (DoMatchImpl(*input, ANCHOR_START, &consumed, + args, n, vec, kVecSize)) { + input->remove_prefix(consumed); + return true; + } else { + return false; + } +} + +bool RE::FindAndConsume(StringPiece* input, + const Arg& ptr1, + const Arg& ptr2, + const Arg& ptr3, + const Arg& ptr4, + const Arg& ptr5, + const Arg& ptr6, + const Arg& ptr7, + const Arg& ptr8, + const Arg& ptr9, + const Arg& ptr10, + const Arg& ptr11, + const Arg& ptr12, + const Arg& ptr13, + const Arg& ptr14, + const Arg& ptr15, + const Arg& ptr16) const { + const Arg* args[kMaxArgs]; + int n = 0; + if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1; + if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2; + if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3; + if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4; + if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5; + if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6; + if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7; + if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8; + if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9; + if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10; + if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11; + if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12; + if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13; + if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14; + if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15; + if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16; + done: + + int consumed; + int vec[kVecSize]; + if (DoMatchImpl(*input, UNANCHORED, &consumed, + args, n, vec, kVecSize)) { + input->remove_prefix(consumed); + return true; + } else { + return false; + } +} + +bool RE::Replace(const StringPiece& rewrite, + string *str) const { + int vec[kVecSize]; + int matches = TryMatch(*str, 0, UNANCHORED, true, vec, kVecSize); + if (matches == 0) + return false; + + string s; + if (!Rewrite(&s, rewrite, *str, vec, matches)) + return false; + + assert(vec[0] >= 0); + assert(vec[1] >= 0); + str->replace(vec[0], vec[1] - vec[0], s); + return true; +} + +// Returns PCRE_NEWLINE_CRLF, PCRE_NEWLINE_CR, or PCRE_NEWLINE_LF. +// Note that PCRE_NEWLINE_CRLF is defined to be P_N_CR | P_N_LF. +// Modified by PH to add PCRE_NEWLINE_ANY and PCRE_NEWLINE_ANYCRLF. + +static int NewlineMode(int pcre_options) { + // TODO: if we can make it threadsafe, cache this var + int newline_mode = 0; + /* if (newline_mode) return newline_mode; */ // do this once it's cached + if (pcre_options & (PCRE_NEWLINE_CRLF|PCRE_NEWLINE_CR|PCRE_NEWLINE_LF| + PCRE_NEWLINE_ANY|PCRE_NEWLINE_ANYCRLF)) { + newline_mode = (pcre_options & + (PCRE_NEWLINE_CRLF|PCRE_NEWLINE_CR|PCRE_NEWLINE_LF| + PCRE_NEWLINE_ANY|PCRE_NEWLINE_ANYCRLF)); + } else { + int newline; + pcre_config(PCRE_CONFIG_NEWLINE, &newline); + if (newline == 10) + newline_mode = PCRE_NEWLINE_LF; + else if (newline == 13) + newline_mode = PCRE_NEWLINE_CR; + else if (newline == 3338) + newline_mode = PCRE_NEWLINE_CRLF; + else if (newline == -1) + newline_mode = PCRE_NEWLINE_ANY; + else if (newline == -2) + newline_mode = PCRE_NEWLINE_ANYCRLF; + else + assert(NULL == "Unexpected return value from pcre_config(NEWLINE)"); + } + return newline_mode; +} + +int RE::GlobalReplace(const StringPiece& rewrite, + string *str) const { + int count = 0; + int vec[kVecSize]; + string out; + int start = 0; + int lastend = -1; + bool last_match_was_empty_string = false; + + while (start <= static_cast(str->length())) { + // If the previous match was for the empty string, we shouldn't + // just match again: we'll match in the same way and get an + // infinite loop. Instead, we do the match in a special way: + // anchored -- to force another try at the same position -- + // and with a flag saying that this time, ignore empty matches. + // If this special match returns, that means there's a non-empty + // match at this position as well, and we can continue. If not, + // we do what perl does, and just advance by one. + // Notice that perl prints '@@@' for this; + // perl -le '$_ = "aa"; s/b*|aa/@/g; print' + int matches; + if (last_match_was_empty_string) { + matches = TryMatch(*str, start, ANCHOR_START, false, vec, kVecSize); + if (matches <= 0) { + int matchend = start + 1; // advance one character. + // If the current char is CR and we're in CRLF mode, skip LF too. + // Note it's better to call pcre_fullinfo() than to examine + // all_options(), since options_ could have changed bewteen + // compile-time and now, but this is simpler and safe enough. + // Modified by PH to add ANY and ANYCRLF. + if (matchend < static_cast(str->length()) && + (*str)[start] == '\r' && (*str)[matchend] == '\n' && + (NewlineMode(options_.all_options()) == PCRE_NEWLINE_CRLF || + NewlineMode(options_.all_options()) == PCRE_NEWLINE_ANY || + NewlineMode(options_.all_options()) == PCRE_NEWLINE_ANYCRLF)) { + matchend++; + } + // We also need to advance more than one char if we're in utf8 mode. +#ifdef SUPPORT_UTF8 + if (options_.utf8()) { + while (matchend < static_cast(str->length()) && + ((*str)[matchend] & 0xc0) == 0x80) + matchend++; + } +#endif + if (start < static_cast(str->length())) + out.append(*str, start, matchend - start); + start = matchend; + last_match_was_empty_string = false; + continue; + } + } else { + matches = TryMatch(*str, start, UNANCHORED, true, vec, kVecSize); + if (matches <= 0) + break; + } + int matchstart = vec[0], matchend = vec[1]; + assert(matchstart >= start); + assert(matchend >= matchstart); + out.append(*str, start, matchstart - start); + Rewrite(&out, rewrite, *str, vec, matches); + start = matchend; + lastend = matchend; + count++; + last_match_was_empty_string = (matchstart == matchend); + } + + if (count == 0) + return 0; + + if (start < static_cast(str->length())) + out.append(*str, start, str->length() - start); + swap(out, *str); + return count; +} + +bool RE::Extract(const StringPiece& rewrite, + const StringPiece& text, + string *out) const { + int vec[kVecSize]; + int matches = TryMatch(text, 0, UNANCHORED, true, vec, kVecSize); + if (matches == 0) + return false; + out->erase(); + return Rewrite(out, rewrite, text, vec, matches); +} + +/*static*/ string RE::QuoteMeta(const StringPiece& unquoted) { + string result; + + // Escape any ascii character not in [A-Za-z_0-9]. + // + // Note that it's legal to escape a character even if it has no + // special meaning in a regular expression -- so this function does + // that. (This also makes it identical to the perl function of the + // same name; see `perldoc -f quotemeta`.) The one exception is + // escaping NUL: rather than doing backslash + NUL, like perl does, + // we do '\0', because pcre itself doesn't take embedded NUL chars. + for (int ii = 0; ii < unquoted.size(); ++ii) { + // Note that using 'isalnum' here raises the benchmark time from + // 32ns to 58ns: + if (unquoted[ii] == '\0') { + result += "\\0"; + } else if ((unquoted[ii] < 'a' || unquoted[ii] > 'z') && + (unquoted[ii] < 'A' || unquoted[ii] > 'Z') && + (unquoted[ii] < '0' || unquoted[ii] > '9') && + unquoted[ii] != '_' && + // If this is the part of a UTF8 or Latin1 character, we need + // to copy this byte without escaping. Experimentally this is + // what works correctly with the regexp library. + !(unquoted[ii] & 128)) { + result += '\\'; + result += unquoted[ii]; + } else { + result += unquoted[ii]; + } + } + + return result; +} + +/***** Actual matching and rewriting code *****/ + +int RE::TryMatch(const StringPiece& text, + int startpos, + Anchor anchor, + bool empty_ok, + int *vec, + int vecsize) const { + pcre* re = (anchor == ANCHOR_BOTH) ? re_full_ : re_partial_; + if (re == NULL) { + //fprintf(stderr, "Matching against invalid re: %s\n", error_->c_str()); + return 0; + } + + pcre_extra extra = { 0, 0, 0, 0, 0, 0 }; + if (options_.match_limit() > 0) { + extra.flags |= PCRE_EXTRA_MATCH_LIMIT; + extra.match_limit = options_.match_limit(); + } + if (options_.match_limit_recursion() > 0) { + extra.flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION; + extra.match_limit_recursion = options_.match_limit_recursion(); + } + + int options = 0; + if (anchor != UNANCHORED) + options |= PCRE_ANCHORED; + if (!empty_ok) + options |= PCRE_NOTEMPTY; + + int rc = pcre_exec(re, // The regular expression object + &extra, + (text.data() == NULL) ? "" : text.data(), + text.size(), + startpos, + options, + vec, + vecsize); + + // Handle errors + if (rc == PCRE_ERROR_NOMATCH) { + return 0; + } else if (rc < 0) { + //fprintf(stderr, "Unexpected return code: %d when matching '%s'\n", + // re, pattern_.c_str()); + return 0; + } else if (rc == 0) { + // pcre_exec() returns 0 as a special case when the number of + // capturing subpatterns exceeds the size of the vector. + // When this happens, there is a match and the output vector + // is filled, but we miss out on the positions of the extra subpatterns. + rc = vecsize / 2; + } + + return rc; +} + +bool RE::DoMatchImpl(const StringPiece& text, + Anchor anchor, + int* consumed, + const Arg* const* args, + int n, + int* vec, + int vecsize) const { + assert((1 + n) * 3 <= vecsize); // results + PCRE workspace + int matches = TryMatch(text, 0, anchor, true, vec, vecsize); + assert(matches >= 0); // TryMatch never returns negatives + if (matches == 0) + return false; + + *consumed = vec[1]; + + if (n == 0 || args == NULL) { + // We are not interested in results + return true; + } + + if (NumberOfCapturingGroups() < n) { + // RE has fewer capturing groups than number of arg pointers passed in + return false; + } + + // If we got here, we must have matched the whole pattern. + // We do not need (can not do) any more checks on the value of 'matches' here + // -- see the comment for TryMatch. + for (int i = 0; i < n; i++) { + const int start = vec[2*(i+1)]; + const int limit = vec[2*(i+1)+1]; + if (!args[i]->Parse(text.data() + start, limit-start)) { + // TODO: Should we indicate what the error was? + return false; + } + } + + return true; +} + +bool RE::DoMatch(const StringPiece& text, + Anchor anchor, + int* consumed, + const Arg* const args[], + int n) const { + assert(n >= 0); + size_t const vecsize = (1 + n) * 3; // results + PCRE workspace + // (as for kVecSize) + int space[21]; // use stack allocation for small vecsize (common case) + int* vec = vecsize <= 21 ? space : new int[vecsize]; + bool retval = DoMatchImpl(text, anchor, consumed, args, n, vec, vecsize); + if (vec != space) delete [] vec; + return retval; +} + +bool RE::Rewrite(string *out, const StringPiece &rewrite, + const StringPiece &text, int *vec, int veclen) const { + for (const char *s = rewrite.data(), *end = s + rewrite.size(); + s < end; s++) { + int c = *s; + if (c == '\\') { + c = *++s; + if (isdigit(c)) { + int n = (c - '0'); + if (n >= veclen) { + //fprintf(stderr, requested group %d in regexp %.*s\n", + // n, rewrite.size(), rewrite.data()); + return false; + } + int start = vec[2 * n]; + if (start >= 0) + out->append(text.data() + start, vec[2 * n + 1] - start); + } else if (c == '\\') { + *out += '\\'; + } else { + //fprintf(stderr, "invalid rewrite pattern: %.*s\n", + // rewrite.size(), rewrite.data()); + return false; + } + } else { + *out += c; + } + } + return true; +} + +// Return the number of capturing subpatterns, or -1 if the +// regexp wasn't valid on construction. +int RE::NumberOfCapturingGroups() const { + if (re_partial_ == NULL) return -1; + + int result; + int pcre_retval = pcre_fullinfo(re_partial_, // The regular expression object + NULL, // We did not study the pattern + PCRE_INFO_CAPTURECOUNT, + &result); + assert(pcre_retval == 0); + return result; +} + +/***** Parsers for various types *****/ + +bool Arg::parse_null(const char* str, int n, void* dest) { + // We fail if somebody asked us to store into a non-NULL void* pointer + return (dest == NULL); +} + +bool Arg::parse_string(const char* str, int n, void* dest) { + if (dest == NULL) return true; + reinterpret_cast(dest)->assign(str, n); + return true; +} + +bool Arg::parse_stringpiece(const char* str, int n, void* dest) { + if (dest == NULL) return true; + reinterpret_cast(dest)->set(str, n); + return true; +} + +bool Arg::parse_char(const char* str, int n, void* dest) { + if (n != 1) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = str[0]; + return true; +} + +bool Arg::parse_uchar(const char* str, int n, void* dest) { + if (n != 1) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = str[0]; + return true; +} + +// Largest number spec that we are willing to parse +static const int kMaxNumberLength = 32; + +// REQUIRES "buf" must have length at least kMaxNumberLength+1 +// REQUIRES "n > 0" +// Copies "str" into "buf" and null-terminates if necessary. +// Returns one of: +// a. "str" if no termination is needed +// b. "buf" if the string was copied and null-terminated +// c. "" if the input was invalid and has no hope of being parsed +static const char* TerminateNumber(char* buf, const char* str, int n) { + if ((n > 0) && isspace(*str)) { + // We are less forgiving than the strtoxxx() routines and do not + // allow leading spaces. + return ""; + } + + // See if the character right after the input text may potentially + // look like a digit. + if (isdigit(str[n]) || + ((str[n] >= 'a') && (str[n] <= 'f')) || + ((str[n] >= 'A') && (str[n] <= 'F'))) { + if (n > kMaxNumberLength) return ""; // Input too big to be a valid number + memcpy(buf, str, n); + buf[n] = '\0'; + return buf; + } else { + // We can parse right out of the supplied string, so return it. + return str; + } +} + +bool Arg::parse_long_radix(const char* str, + int n, + void* dest, + int radix) { + if (n == 0) return false; + char buf[kMaxNumberLength+1]; + str = TerminateNumber(buf, str, n); + char* end; + errno = 0; + long r = strtol(str, &end, radix); + if (end != str + n) return false; // Leftover junk + if (errno) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = r; + return true; +} + +bool Arg::parse_ulong_radix(const char* str, + int n, + void* dest, + int radix) { + if (n == 0) return false; + char buf[kMaxNumberLength+1]; + str = TerminateNumber(buf, str, n); + if (str[0] == '-') return false; // strtoul() on a negative number?! + char* end; + errno = 0; + unsigned long r = strtoul(str, &end, radix); + if (end != str + n) return false; // Leftover junk + if (errno) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = r; + return true; +} + +bool Arg::parse_short_radix(const char* str, + int n, + void* dest, + int radix) { + long r; + if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse + if (r < SHRT_MIN || r > SHRT_MAX) return false; // Out of range + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = static_cast(r); + return true; +} + +bool Arg::parse_ushort_radix(const char* str, + int n, + void* dest, + int radix) { + unsigned long r; + if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse + if (r > USHRT_MAX) return false; // Out of range + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = static_cast(r); + return true; +} + +bool Arg::parse_int_radix(const char* str, + int n, + void* dest, + int radix) { + long r; + if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse + if (r < INT_MIN || r > INT_MAX) return false; // Out of range + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = r; + return true; +} + +bool Arg::parse_uint_radix(const char* str, + int n, + void* dest, + int radix) { + unsigned long r; + if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse + if (r > UINT_MAX) return false; // Out of range + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = r; + return true; +} + +bool Arg::parse_longlong_radix(const char* str, + int n, + void* dest, + int radix) { +#ifndef HAVE_LONG_LONG + return false; +#else + if (n == 0) return false; + char buf[kMaxNumberLength+1]; + str = TerminateNumber(buf, str, n); + char* end; + errno = 0; +#if defined HAVE_STRTOQ + long long r = strtoq(str, &end, radix); +#elif defined HAVE_STRTOLL + long long r = strtoll(str, &end, radix); +#elif defined HAVE__STRTOI64 + long long r = _strtoi64(str, &end, radix); +#elif defined HAVE_STRTOIMAX + long long r = strtoimax(str, &end, radix); +#else +#error parse_longlong_radix: cannot convert input to a long-long +#endif + if (end != str + n) return false; // Leftover junk + if (errno) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = r; + return true; +#endif /* HAVE_LONG_LONG */ +} + +bool Arg::parse_ulonglong_radix(const char* str, + int n, + void* dest, + int radix) { +#ifndef HAVE_UNSIGNED_LONG_LONG + return false; +#else + if (n == 0) return false; + char buf[kMaxNumberLength+1]; + str = TerminateNumber(buf, str, n); + if (str[0] == '-') return false; // strtoull() on a negative number?! + char* end; + errno = 0; +#if defined HAVE_STRTOQ + unsigned long long r = strtouq(str, &end, radix); +#elif defined HAVE_STRTOLL + unsigned long long r = strtoull(str, &end, radix); +#elif defined HAVE__STRTOI64 + unsigned long long r = _strtoui64(str, &end, radix); +#elif defined HAVE_STRTOIMAX + unsigned long long r = strtoumax(str, &end, radix); +#else +#error parse_ulonglong_radix: cannot convert input to a long-long +#endif + if (end != str + n) return false; // Leftover junk + if (errno) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = r; + return true; +#endif /* HAVE_UNSIGNED_LONG_LONG */ +} + +bool Arg::parse_double(const char* str, int n, void* dest) { + if (n == 0) return false; + static const int kMaxLength = 200; + char buf[kMaxLength]; + if (n >= kMaxLength) return false; + memcpy(buf, str, n); + buf[n] = '\0'; + errno = 0; + char* end; + double r = strtod(buf, &end); + if (end != buf + n) return false; // Leftover junk + if (errno) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = r; + return true; +} + +bool Arg::parse_float(const char* str, int n, void* dest) { + double r; + if (!parse_double(str, n, &r)) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = static_cast(r); + return true; +} + + +#define DEFINE_INTEGER_PARSERS(name) \ + bool Arg::parse_##name(const char* str, int n, void* dest) { \ + return parse_##name##_radix(str, n, dest, 10); \ + } \ + bool Arg::parse_##name##_hex(const char* str, int n, void* dest) { \ + return parse_##name##_radix(str, n, dest, 16); \ + } \ + bool Arg::parse_##name##_octal(const char* str, int n, void* dest) { \ + return parse_##name##_radix(str, n, dest, 8); \ + } \ + bool Arg::parse_##name##_cradix(const char* str, int n, void* dest) { \ + return parse_##name##_radix(str, n, dest, 0); \ + } + +DEFINE_INTEGER_PARSERS(short) /* */ +DEFINE_INTEGER_PARSERS(ushort) /* */ +DEFINE_INTEGER_PARSERS(int) /* Don't use semicolons after these */ +DEFINE_INTEGER_PARSERS(uint) /* statements because they can cause */ +DEFINE_INTEGER_PARSERS(long) /* compiler warnings if the checking */ +DEFINE_INTEGER_PARSERS(ulong) /* level is turned up high enough. */ +DEFINE_INTEGER_PARSERS(longlong) /* */ +DEFINE_INTEGER_PARSERS(ulonglong) /* */ + +#undef DEFINE_INTEGER_PARSERS + +} // namespace pcrecpp diff --git a/src/pcrecpp/pcrecpp.cpp b/src/pcrecpp/pcrecpp.cpp new file mode 100644 index 0000000..94ac24c --- /dev/null +++ b/src/pcrecpp/pcrecpp.cpp @@ -0,0 +1,965 @@ +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Google Inc. 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 COPYRIGHT HOLDERS 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 COPYRIGHT +// OWNER 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. +// +// Author: Sanjay Ghemawat + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// Custom: +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include /* for SHRT_MIN, USHRT_MAX, etc */ +#include +#include +#include +#include + +#include "pcrecpp_internal.h" +#include "pcre.h" +#include "pcrecpp.h" +#include "pcre_stringpiece.h" + + +// Custom: Added for compatibility with older pcre versions +#ifndef PCRE_NEWLINE_CR + #define PCRE_NEWLINE_CR 0x00100000 +#endif +#ifndef PCRE_NEWLINE_LF + #define PCRE_NEWLINE_LF 0x00200000 +#endif +#ifndef PCRE_NEWLINE_CRLF + #define PCRE_NEWLINE_CRLF 0x00300000 +#endif +#ifndef PCRE_NEWLINE_ANYCRLF + #define PCRE_NEWLINE_ANYCRLF 0x00500000 +#endif +#ifndef PCRE_NEWLINE_ANY + #define PCRE_NEWLINE_ANY 0x00400000 +#endif + +#ifndef PCRE_NEWLINE_ANY + #define PCRE_EXTRA_MATCH_LIMIT_RECURSION 0x0010 +#endif + + + + +namespace pcrecpp { + +// Maximum number of args we can set +static const int kMaxArgs = 16; +static const int kVecSize = (1 + kMaxArgs) * 3; // results + PCRE workspace + +// Special object that stands-in for no argument +Arg RE::no_arg((void*)NULL); + +// This is for ABI compatibility with old versions of pcre (pre-7.6), +// which defined a global no_arg variable instead of putting it in the +// RE class. This works on GCC >= 3, at least. It definitely works +// for ELF, but may not for other object formats (Mach-O, for +// instance, does not support aliases.) We could probably have a more +// inclusive test if we ever needed it. (Note that not only the +// __attribute__ syntax, but also __USER_LABEL_PREFIX__, are +// gnu-specific.) +#if defined(__GNUC__) && __GNUC__ >= 3 && defined(__ELF__) +# define ULP_AS_STRING(x) ULP_AS_STRING_INTERNAL(x) +# define ULP_AS_STRING_INTERNAL(x) #x +# define USER_LABEL_PREFIX_STR ULP_AS_STRING(__USER_LABEL_PREFIX__) +// Custom: This throws a warning on Intel C++, and we don't need it anyway. +// extern Arg no_arg +// __attribute__((alias(USER_LABEL_PREFIX_STR "_ZN7pcrecpp2RE6no_argE"))); +#endif + +// If a regular expression has no error, its error_ field points here +static const string empty_string; + +// If the user doesn't ask for any options, we just use this one +static RE_Options default_options; + +void RE::Init(const string& pat, const RE_Options* options) { + pattern_ = pat; + if (options == NULL) { + options_ = default_options; + } else { + options_ = *options; + } + error_ = &empty_string; + re_full_ = NULL; + re_partial_ = NULL; + + re_partial_ = Compile(UNANCHORED); + if (re_partial_ != NULL) { + re_full_ = Compile(ANCHOR_BOTH); + } +} + +void RE::Cleanup() { + if (re_full_ != NULL) (*pcre_free)(re_full_); + if (re_partial_ != NULL) (*pcre_free)(re_partial_); + if (error_ != &empty_string) delete error_; +} + + +RE::~RE() { + Cleanup(); +} + + +pcre* RE::Compile(Anchor anchor) { + // First, convert RE_Options into pcre options + int pcre_options = 0; + pcre_options = options_.all_options(); + + // Special treatment for anchoring. This is needed because at + // runtime pcre only provides an option for anchoring at the + // beginning of a string (unless you use offset). + // + // There are three types of anchoring we want: + // UNANCHORED Compile the original pattern, and use + // a pcre unanchored match. + // ANCHOR_START Compile the original pattern, and use + // a pcre anchored match. + // ANCHOR_BOTH Tack a "\z" to the end of the original pattern + // and use a pcre anchored match. + + const char* compile_error; + int eoffset; + pcre* re; + if (anchor != ANCHOR_BOTH) { + re = pcre_compile(pattern_.c_str(), pcre_options, + &compile_error, &eoffset, NULL); + } else { + // Tack a '\z' at the end of RE. Parenthesize it first so that + // the '\z' applies to all top-level alternatives in the regexp. + string wrapped = "(?:"; // A non-counting grouping operator + wrapped += pattern_; + wrapped += ")\\z"; + re = pcre_compile(wrapped.c_str(), pcre_options, + &compile_error, &eoffset, NULL); + } + if (re == NULL) { + if (error_ == &empty_string) error_ = new string(compile_error); + } + return re; +} + +/***** Matching interfaces *****/ + +bool RE::FullMatch(const StringPiece& text, + const Arg& ptr1, + const Arg& ptr2, + const Arg& ptr3, + const Arg& ptr4, + const Arg& ptr5, + const Arg& ptr6, + const Arg& ptr7, + const Arg& ptr8, + const Arg& ptr9, + const Arg& ptr10, + const Arg& ptr11, + const Arg& ptr12, + const Arg& ptr13, + const Arg& ptr14, + const Arg& ptr15, + const Arg& ptr16) const { + const Arg* args[kMaxArgs]; + int n = 0; + if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1; + if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2; + if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3; + if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4; + if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5; + if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6; + if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7; + if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8; + if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9; + if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10; + if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11; + if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12; + if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13; + if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14; + if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15; + if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16; + done: + + int consumed; + int vec[kVecSize]; + return DoMatchImpl(text, ANCHOR_BOTH, &consumed, args, n, vec, kVecSize); +} + +bool RE::PartialMatch(const StringPiece& text, + const Arg& ptr1, + const Arg& ptr2, + const Arg& ptr3, + const Arg& ptr4, + const Arg& ptr5, + const Arg& ptr6, + const Arg& ptr7, + const Arg& ptr8, + const Arg& ptr9, + const Arg& ptr10, + const Arg& ptr11, + const Arg& ptr12, + const Arg& ptr13, + const Arg& ptr14, + const Arg& ptr15, + const Arg& ptr16) const { + const Arg* args[kMaxArgs]; + int n = 0; + if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1; + if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2; + if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3; + if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4; + if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5; + if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6; + if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7; + if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8; + if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9; + if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10; + if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11; + if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12; + if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13; + if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14; + if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15; + if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16; + done: + + int consumed; + int vec[kVecSize]; + return DoMatchImpl(text, UNANCHORED, &consumed, args, n, vec, kVecSize); +} + +bool RE::Consume(StringPiece* input, + const Arg& ptr1, + const Arg& ptr2, + const Arg& ptr3, + const Arg& ptr4, + const Arg& ptr5, + const Arg& ptr6, + const Arg& ptr7, + const Arg& ptr8, + const Arg& ptr9, + const Arg& ptr10, + const Arg& ptr11, + const Arg& ptr12, + const Arg& ptr13, + const Arg& ptr14, + const Arg& ptr15, + const Arg& ptr16) const { + const Arg* args[kMaxArgs]; + int n = 0; + if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1; + if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2; + if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3; + if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4; + if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5; + if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6; + if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7; + if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8; + if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9; + if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10; + if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11; + if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12; + if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13; + if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14; + if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15; + if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16; + done: + + int consumed; + int vec[kVecSize]; + if (DoMatchImpl(*input, ANCHOR_START, &consumed, + args, n, vec, kVecSize)) { + input->remove_prefix(consumed); + return true; + } else { + return false; + } +} + +bool RE::FindAndConsume(StringPiece* input, + const Arg& ptr1, + const Arg& ptr2, + const Arg& ptr3, + const Arg& ptr4, + const Arg& ptr5, + const Arg& ptr6, + const Arg& ptr7, + const Arg& ptr8, + const Arg& ptr9, + const Arg& ptr10, + const Arg& ptr11, + const Arg& ptr12, + const Arg& ptr13, + const Arg& ptr14, + const Arg& ptr15, + const Arg& ptr16) const { + const Arg* args[kMaxArgs]; + int n = 0; + if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1; + if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2; + if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3; + if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4; + if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5; + if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6; + if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7; + if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8; + if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9; + if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10; + if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11; + if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12; + if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13; + if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14; + if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15; + if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16; + done: + + int consumed; + int vec[kVecSize]; + if (DoMatchImpl(*input, UNANCHORED, &consumed, + args, n, vec, kVecSize)) { + input->remove_prefix(consumed); + return true; + } else { + return false; + } +} + +bool RE::Replace(const StringPiece& rewrite, + string *str) const { + int vec[kVecSize]; + int matches = TryMatch(*str, 0, UNANCHORED, true, vec, kVecSize); + if (matches == 0) + return false; + + string s; + if (!Rewrite(&s, rewrite, *str, vec, matches)) + return false; + + assert(vec[0] >= 0); + assert(vec[1] >= 0); + str->replace(vec[0], vec[1] - vec[0], s); + return true; +} + +// Returns PCRE_NEWLINE_CRLF, PCRE_NEWLINE_CR, or PCRE_NEWLINE_LF. +// Note that PCRE_NEWLINE_CRLF is defined to be P_N_CR | P_N_LF. +// Modified by PH to add PCRE_NEWLINE_ANY and PCRE_NEWLINE_ANYCRLF. + +static int NewlineMode(int pcre_options) { + // TODO: if we can make it threadsafe, cache this var + int newline_mode = 0; + /* if (newline_mode) return newline_mode; */ // do this once it's cached + if (pcre_options & (PCRE_NEWLINE_CRLF|PCRE_NEWLINE_CR|PCRE_NEWLINE_LF| + PCRE_NEWLINE_ANY|PCRE_NEWLINE_ANYCRLF)) { + newline_mode = (pcre_options & + (PCRE_NEWLINE_CRLF|PCRE_NEWLINE_CR|PCRE_NEWLINE_LF| + PCRE_NEWLINE_ANY|PCRE_NEWLINE_ANYCRLF)); + } else { + int newline; + pcre_config(PCRE_CONFIG_NEWLINE, &newline); + if (newline == 10) + newline_mode = PCRE_NEWLINE_LF; + else if (newline == 13) + newline_mode = PCRE_NEWLINE_CR; + else if (newline == 3338) + newline_mode = PCRE_NEWLINE_CRLF; + else if (newline == -1) + newline_mode = PCRE_NEWLINE_ANY; + else if (newline == -2) + newline_mode = PCRE_NEWLINE_ANYCRLF; + else + assert(NULL == "Unexpected return value from pcre_config(NEWLINE)"); + } + return newline_mode; +} + +int RE::GlobalReplace(const StringPiece& rewrite, + string *str) const { + int count = 0; + int vec[kVecSize]; + string out; + int start = 0; + int lastend = -1; + bool last_match_was_empty_string = false; + + while (start <= static_cast(str->length())) { + // If the previous match was for the empty string, we shouldn't + // just match again: we'll match in the same way and get an + // infinite loop. Instead, we do the match in a special way: + // anchored -- to force another try at the same position -- + // and with a flag saying that this time, ignore empty matches. + // If this special match returns, that means there's a non-empty + // match at this position as well, and we can continue. If not, + // we do what perl does, and just advance by one. + // Notice that perl prints '@@@' for this; + // perl -le '$_ = "aa"; s/b*|aa/@/g; print' + int matches; + if (last_match_was_empty_string) { + matches = TryMatch(*str, start, ANCHOR_START, false, vec, kVecSize); + if (matches <= 0) { + int matchend = start + 1; // advance one character. + // If the current char is CR and we're in CRLF mode, skip LF too. + // Note it's better to call pcre_fullinfo() than to examine + // all_options(), since options_ could have changed bewteen + // compile-time and now, but this is simpler and safe enough. + // Modified by PH to add ANY and ANYCRLF. + if (matchend < static_cast(str->length()) && + (*str)[start] == '\r' && (*str)[matchend] == '\n' && + (NewlineMode(options_.all_options()) == PCRE_NEWLINE_CRLF || + NewlineMode(options_.all_options()) == PCRE_NEWLINE_ANY || + NewlineMode(options_.all_options()) == PCRE_NEWLINE_ANYCRLF)) { + matchend++; + } + // We also need to advance more than one char if we're in utf8 mode. +#ifdef SUPPORT_UTF8 + if (options_.utf8()) { + while (matchend < static_cast(str->length()) && + ((*str)[matchend] & 0xc0) == 0x80) + matchend++; + } +#endif + if (start < static_cast(str->length())) + out.append(*str, start, matchend - start); + start = matchend; + last_match_was_empty_string = false; + continue; + } + } else { + matches = TryMatch(*str, start, UNANCHORED, true, vec, kVecSize); + if (matches <= 0) + break; + } + int matchstart = vec[0], matchend = vec[1]; + assert(matchstart >= start); + assert(matchend >= matchstart); + out.append(*str, start, matchstart - start); + Rewrite(&out, rewrite, *str, vec, matches); + start = matchend; + lastend = matchend; + count++; + last_match_was_empty_string = (matchstart == matchend); + } + + if (count == 0) + return 0; + + if (start < static_cast(str->length())) + out.append(*str, start, str->length() - start); + swap(out, *str); + return count; +} + +bool RE::Extract(const StringPiece& rewrite, + const StringPiece& text, + string *out) const { + int vec[kVecSize]; + int matches = TryMatch(text, 0, UNANCHORED, true, vec, kVecSize); + if (matches == 0) + return false; + out->erase(); + return Rewrite(out, rewrite, text, vec, matches); +} + +/*static*/ string RE::QuoteMeta(const StringPiece& unquoted) { + string result; + + // Escape any ascii character not in [A-Za-z_0-9]. + // + // Note that it's legal to escape a character even if it has no + // special meaning in a regular expression -- so this function does + // that. (This also makes it identical to the perl function of the + // same name; see `perldoc -f quotemeta`.) The one exception is + // escaping NUL: rather than doing backslash + NUL, like perl does, + // we do '\0', because pcre itself doesn't take embedded NUL chars. + for (int ii = 0; ii < unquoted.size(); ++ii) { + // Note that using 'isalnum' here raises the benchmark time from + // 32ns to 58ns: + if (unquoted[ii] == '\0') { + result += "\\0"; + } else if ((unquoted[ii] < 'a' || unquoted[ii] > 'z') && + (unquoted[ii] < 'A' || unquoted[ii] > 'Z') && + (unquoted[ii] < '0' || unquoted[ii] > '9') && + unquoted[ii] != '_' && + // If this is the part of a UTF8 or Latin1 character, we need + // to copy this byte without escaping. Experimentally this is + // what works correctly with the regexp library. + !(unquoted[ii] & 128)) { + result += '\\'; + result += unquoted[ii]; + } else { + result += unquoted[ii]; + } + } + + return result; +} + +/***** Actual matching and rewriting code *****/ + +int RE::TryMatch(const StringPiece& text, + int startpos, + Anchor anchor, + bool empty_ok, + int *vec, + int vecsize) const { + pcre* re = (anchor == ANCHOR_BOTH) ? re_full_ : re_partial_; + if (re == NULL) { + //fprintf(stderr, "Matching against invalid re: %s\n", error_->c_str()); + return 0; + } + + // Custom: match_limit_recursion field is since pcre 6.5, tables field since 5.0. +#if PCRE_MAJOR > 6 || (PCRE_MAJOR == 6 && PCRE_MINOR >= 5) + pcre_extra extra = { 0, 0, 0, 0, 0, 0 }; +#elif PCRE_MAJOR > 5 || (PCRE_MAJOR == 5 && PCRE_MINOR >= 0) + pcre_extra extra = { 0, 0, 0, 0, 0 }; +#else + pcre_extra extra = { 0, 0, 0, 0 }; +#endif + + if (options_.match_limit() > 0) { + extra.flags |= PCRE_EXTRA_MATCH_LIMIT; + extra.match_limit = options_.match_limit(); + } + + // Custom: match_limit_recursion is since pcre 6.5. +#if PCRE_MAJOR > 6 || (PCRE_MAJOR == 6 && PCRE_MINOR >= 5) + if (options_.match_limit_recursion() > 0) { + extra.flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION; + extra.match_limit_recursion = options_.match_limit_recursion(); + } +#endif + + int options = 0; + if (anchor != UNANCHORED) + options |= PCRE_ANCHORED; + if (!empty_ok) + options |= PCRE_NOTEMPTY; + + int rc = pcre_exec(re, // The regular expression object + &extra, + (text.data() == NULL) ? "" : text.data(), + text.size(), + startpos, + options, + vec, + vecsize); + + // Handle errors + if (rc == PCRE_ERROR_NOMATCH) { + return 0; + } else if (rc < 0) { + //fprintf(stderr, "Unexpected return code: %d when matching '%s'\n", + // re, pattern_.c_str()); + return 0; + } else if (rc == 0) { + // pcre_exec() returns 0 as a special case when the number of + // capturing subpatterns exceeds the size of the vector. + // When this happens, there is a match and the output vector + // is filled, but we miss out on the positions of the extra subpatterns. + rc = vecsize / 2; + } + + return rc; +} + +bool RE::DoMatchImpl(const StringPiece& text, + Anchor anchor, + int* consumed, + const Arg* const* args, + int n, + int* vec, + int vecsize) const { + assert((1 + n) * 3 <= vecsize); // results + PCRE workspace + int matches = TryMatch(text, 0, anchor, true, vec, vecsize); + assert(matches >= 0); // TryMatch never returns negatives + if (matches == 0) + return false; + + *consumed = vec[1]; + + if (n == 0 || args == NULL) { + // We are not interested in results + return true; + } + + if (NumberOfCapturingGroups() < n) { + // RE has fewer capturing groups than number of arg pointers passed in + return false; + } + + // If we got here, we must have matched the whole pattern. + // We do not need (can not do) any more checks on the value of 'matches' here + // -- see the comment for TryMatch. + for (int i = 0; i < n; i++) { + const int start = vec[2*(i+1)]; + const int limit = vec[2*(i+1)+1]; + if (!args[i]->Parse(text.data() + start, limit-start)) { + // TODO: Should we indicate what the error was? + return false; + } + } + + return true; +} + +bool RE::DoMatch(const StringPiece& text, + Anchor anchor, + int* consumed, + const Arg* const args[], + int n) const { + assert(n >= 0); + size_t const vecsize = (1 + n) * 3; // results + PCRE workspace + // (as for kVecSize) + int space[21]; // use stack allocation for small vecsize (common case) + int* vec = vecsize <= 21 ? space : new int[vecsize]; + bool retval = DoMatchImpl(text, anchor, consumed, args, n, vec, vecsize); + if (vec != space) delete [] vec; + return retval; +} + +bool RE::Rewrite(string *out, const StringPiece &rewrite, + const StringPiece &text, int *vec, int veclen) const { + for (const char *s = rewrite.data(), *end = s + rewrite.size(); + s < end; s++) { + int c = *s; + if (c == '\\') { + c = *++s; + if (isdigit(c)) { + int n = (c - '0'); + if (n >= veclen) { + //fprintf(stderr, requested group %d in regexp %.*s\n", + // n, rewrite.size(), rewrite.data()); + return false; + } + int start = vec[2 * n]; + if (start >= 0) + out->append(text.data() + start, vec[2 * n + 1] - start); + } else if (c == '\\') { + *out += '\\'; + } else { + //fprintf(stderr, "invalid rewrite pattern: %.*s\n", + // rewrite.size(), rewrite.data()); + return false; + } + } else { + *out += c; + } + } + return true; +} + +// Return the number of capturing subpatterns, or -1 if the +// regexp wasn't valid on construction. +int RE::NumberOfCapturingGroups() const { + if (re_partial_ == NULL) return -1; + + int result; + int pcre_retval = pcre_fullinfo(re_partial_, // The regular expression object + NULL, // We did not study the pattern + PCRE_INFO_CAPTURECOUNT, + &result); + assert(pcre_retval == 0); + return result; +} + +/***** Parsers for various types *****/ + +bool Arg::parse_null(const char* str, int n, void* dest) { + // We fail if somebody asked us to store into a non-NULL void* pointer + return (dest == NULL); +} + +bool Arg::parse_string(const char* str, int n, void* dest) { + if (dest == NULL) return true; + reinterpret_cast(dest)->assign(str, n); + return true; +} + +bool Arg::parse_stringpiece(const char* str, int n, void* dest) { + if (dest == NULL) return true; + reinterpret_cast(dest)->set(str, n); + return true; +} + +bool Arg::parse_char(const char* str, int n, void* dest) { + if (n != 1) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = str[0]; + return true; +} + +bool Arg::parse_uchar(const char* str, int n, void* dest) { + if (n != 1) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = str[0]; + return true; +} + +// Largest number spec that we are willing to parse +static const int kMaxNumberLength = 32; + +// REQUIRES "buf" must have length at least kMaxNumberLength+1 +// REQUIRES "n > 0" +// Copies "str" into "buf" and null-terminates if necessary. +// Returns one of: +// a. "str" if no termination is needed +// b. "buf" if the string was copied and null-terminated +// c. "" if the input was invalid and has no hope of being parsed +static const char* TerminateNumber(char* buf, const char* str, int n) { + if ((n > 0) && isspace(*str)) { + // We are less forgiving than the strtoxxx() routines and do not + // allow leading spaces. + return ""; + } + + // See if the character right after the input text may potentially + // look like a digit. + if (isdigit(str[n]) || + ((str[n] >= 'a') && (str[n] <= 'f')) || + ((str[n] >= 'A') && (str[n] <= 'F'))) { + if (n > kMaxNumberLength) return ""; // Input too big to be a valid number + memcpy(buf, str, n); + buf[n] = '\0'; + return buf; + } else { + // We can parse right out of the supplied string, so return it. + return str; + } +} + +bool Arg::parse_long_radix(const char* str, + int n, + void* dest, + int radix) { + if (n == 0) return false; + char buf[kMaxNumberLength+1]; + str = TerminateNumber(buf, str, n); + char* end; + errno = 0; + long r = strtol(str, &end, radix); + if (end != str + n) return false; // Leftover junk + if (errno) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = r; + return true; +} + +bool Arg::parse_ulong_radix(const char* str, + int n, + void* dest, + int radix) { + if (n == 0) return false; + char buf[kMaxNumberLength+1]; + str = TerminateNumber(buf, str, n); + if (str[0] == '-') return false; // strtoul() on a negative number?! + char* end; + errno = 0; + unsigned long r = strtoul(str, &end, radix); + if (end != str + n) return false; // Leftover junk + if (errno) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = r; + return true; +} + +bool Arg::parse_short_radix(const char* str, + int n, + void* dest, + int radix) { + long r; + if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse + if (r < SHRT_MIN || r > SHRT_MAX) return false; // Out of range + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = static_cast(r); + return true; +} + +bool Arg::parse_ushort_radix(const char* str, + int n, + void* dest, + int radix) { + unsigned long r; + if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse + if (r > USHRT_MAX) return false; // Out of range + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = static_cast(r); + return true; +} + +bool Arg::parse_int_radix(const char* str, + int n, + void* dest, + int radix) { + long r; + if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse + if (r < INT_MIN || r > INT_MAX) return false; // Out of range + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = r; + return true; +} + +bool Arg::parse_uint_radix(const char* str, + int n, + void* dest, + int radix) { + unsigned long r; + if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse + if (r > UINT_MAX) return false; // Out of range + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = r; + return true; +} + +bool Arg::parse_longlong_radix(const char* str, + int n, + void* dest, + int radix) { +#ifndef HAVE_LONG_LONG + return false; +#else + if (n == 0) return false; + char buf[kMaxNumberLength+1]; + str = TerminateNumber(buf, str, n); + char* end; + errno = 0; +#if defined HAVE_STRTOQ + long long r = strtoq(str, &end, radix); +#elif defined HAVE_STRTOLL + long long r = strtoll(str, &end, radix); +#elif defined HAVE__STRTOI64 + long long r = _strtoi64(str, &end, radix); +#elif defined HAVE_STRTOIMAX + long long r = strtoimax(str, &end, radix); +#else +#error parse_longlong_radix: cannot convert input to a long-long +#endif + if (end != str + n) return false; // Leftover junk + if (errno) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = r; + return true; +#endif /* HAVE_LONG_LONG */ +} + +bool Arg::parse_ulonglong_radix(const char* str, + int n, + void* dest, + int radix) { +#ifndef HAVE_UNSIGNED_LONG_LONG + return false; +#else + if (n == 0) return false; + char buf[kMaxNumberLength+1]; + str = TerminateNumber(buf, str, n); + if (str[0] == '-') return false; // strtoull() on a negative number?! + char* end; + errno = 0; +#if defined HAVE_STRTOQ + unsigned long long r = strtouq(str, &end, radix); +#elif defined HAVE_STRTOLL + unsigned long long r = strtoull(str, &end, radix); +#elif defined HAVE__STRTOI64 + unsigned long long r = _strtoui64(str, &end, radix); +#elif defined HAVE_STRTOIMAX + unsigned long long r = strtoumax(str, &end, radix); +#else +#error parse_ulonglong_radix: cannot convert input to a long-long +#endif + if (end != str + n) return false; // Leftover junk + if (errno) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = r; + return true; +#endif /* HAVE_UNSIGNED_LONG_LONG */ +} + +bool Arg::parse_double(const char* str, int n, void* dest) { + if (n == 0) return false; + static const int kMaxLength = 200; + char buf[kMaxLength]; + if (n >= kMaxLength) return false; + memcpy(buf, str, n); + buf[n] = '\0'; + errno = 0; + char* end; + double r = strtod(buf, &end); + if (end != buf + n) return false; // Leftover junk + if (errno) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = r; + return true; +} + +bool Arg::parse_float(const char* str, int n, void* dest) { + double r; + if (!parse_double(str, n, &r)) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = static_cast(r); + return true; +} + + +#define DEFINE_INTEGER_PARSERS(name) \ + bool Arg::parse_##name(const char* str, int n, void* dest) { \ + return parse_##name##_radix(str, n, dest, 10); \ + } \ + bool Arg::parse_##name##_hex(const char* str, int n, void* dest) { \ + return parse_##name##_radix(str, n, dest, 16); \ + } \ + bool Arg::parse_##name##_octal(const char* str, int n, void* dest) { \ + return parse_##name##_radix(str, n, dest, 8); \ + } \ + bool Arg::parse_##name##_cradix(const char* str, int n, void* dest) { \ + return parse_##name##_radix(str, n, dest, 0); \ + } + +DEFINE_INTEGER_PARSERS(short) /* */ +DEFINE_INTEGER_PARSERS(ushort) /* */ +DEFINE_INTEGER_PARSERS(int) /* Don't use semicolons after these */ +DEFINE_INTEGER_PARSERS(uint) /* statements because they can cause */ +DEFINE_INTEGER_PARSERS(long) /* compiler warnings if the checking */ +DEFINE_INTEGER_PARSERS(ulong) /* level is turned up high enough. */ +DEFINE_INTEGER_PARSERS(longlong) /* */ +DEFINE_INTEGER_PARSERS(ulonglong) /* */ + +#undef DEFINE_INTEGER_PARSERS + +} // namespace pcrecpp diff --git a/src/pcrecpp/pcrecpp.h b/src/pcrecpp/pcrecpp.h new file mode 100644 index 0000000..3e594b0 --- /dev/null +++ b/src/pcrecpp/pcrecpp.h @@ -0,0 +1,710 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Google Inc. 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 COPYRIGHT HOLDERS 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 COPYRIGHT +// OWNER 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. +// +// Author: Sanjay Ghemawat +// Support for PCRE_XXX modifiers added by Giuseppe Maxia, July 2005 + +#ifndef _PCRECPP_H +#define _PCRECPP_H + +// C++ interface to the pcre regular-expression library. RE supports +// Perl-style regular expressions (with extensions like \d, \w, \s, +// ...). +// +// ----------------------------------------------------------------------- +// REGEXP SYNTAX: +// +// This module is part of the pcre library and hence supports its syntax +// for regular expressions. +// +// The syntax is pretty similar to Perl's. For those not familiar +// with Perl's regular expressions, here are some examples of the most +// commonly used extensions: +// +// "hello (\\w+) world" -- \w matches a "word" character +// "version (\\d+)" -- \d matches a digit +// "hello\\s+world" -- \s matches any whitespace character +// "\\b(\\w+)\\b" -- \b matches empty string at a word boundary +// "(?i)hello" -- (?i) turns on case-insensitive matching +// "/\\*(.*?)\\*/" -- .*? matches . minimum no. of times possible +// +// ----------------------------------------------------------------------- +// MATCHING INTERFACE: +// +// The "FullMatch" operation checks that supplied text matches a +// supplied pattern exactly. +// +// Example: successful match +// pcrecpp::RE re("h.*o"); +// re.FullMatch("hello"); +// +// Example: unsuccessful match (requires full match): +// pcrecpp::RE re("e"); +// !re.FullMatch("hello"); +// +// Example: creating a temporary RE object: +// pcrecpp::RE("h.*o").FullMatch("hello"); +// +// You can pass in a "const char*" or a "string" for "text". The +// examples below tend to use a const char*. +// +// You can, as in the different examples above, store the RE object +// explicitly in a variable or use a temporary RE object. The +// examples below use one mode or the other arbitrarily. Either +// could correctly be used for any of these examples. +// +// ----------------------------------------------------------------------- +// MATCHING WITH SUB-STRING EXTRACTION: +// +// You can supply extra pointer arguments to extract matched subpieces. +// +// Example: extracts "ruby" into "s" and 1234 into "i" +// int i; +// string s; +// pcrecpp::RE re("(\\w+):(\\d+)"); +// re.FullMatch("ruby:1234", &s, &i); +// +// Example: does not try to extract any extra sub-patterns +// re.FullMatch("ruby:1234", &s); +// +// Example: does not try to extract into NULL +// re.FullMatch("ruby:1234", NULL, &i); +// +// Example: integer overflow causes failure +// !re.FullMatch("ruby:1234567891234", NULL, &i); +// +// Example: fails because there aren't enough sub-patterns: +// !pcrecpp::RE("\\w+:\\d+").FullMatch("ruby:1234", &s); +// +// Example: fails because string cannot be stored in integer +// !pcrecpp::RE("(.*)").FullMatch("ruby", &i); +// +// The provided pointer arguments can be pointers to any scalar numeric +// type, or one of +// string (matched piece is copied to string) +// StringPiece (StringPiece is mutated to point to matched piece) +// T (where "bool T::ParseFrom(const char*, int)" exists) +// NULL (the corresponding matched sub-pattern is not copied) +// +// CAVEAT: An optional sub-pattern that does not exist in the matched +// string is assigned the empty string. Therefore, the following will +// return false (because the empty string is not a valid number): +// int number; +// pcrecpp::RE::FullMatch("abc", "[a-z]+(\\d+)?", &number); +// +// ----------------------------------------------------------------------- +// DO_MATCH +// +// The matching interface supports at most 16 arguments per call. +// If you need more, consider using the more general interface +// pcrecpp::RE::DoMatch(). See pcrecpp.h for the signature for DoMatch. +// +// ----------------------------------------------------------------------- +// PARTIAL MATCHES +// +// You can use the "PartialMatch" operation when you want the pattern +// to match any substring of the text. +// +// Example: simple search for a string: +// pcrecpp::RE("ell").PartialMatch("hello"); +// +// Example: find first number in a string: +// int number; +// pcrecpp::RE re("(\\d+)"); +// re.PartialMatch("x*100 + 20", &number); +// assert(number == 100); +// +// ----------------------------------------------------------------------- +// UTF-8 AND THE MATCHING INTERFACE: +// +// By default, pattern and text are plain text, one byte per character. +// The UTF8 flag, passed to the constructor, causes both pattern +// and string to be treated as UTF-8 text, still a byte stream but +// potentially multiple bytes per character. In practice, the text +// is likelier to be UTF-8 than the pattern, but the match returned +// may depend on the UTF8 flag, so always use it when matching +// UTF8 text. E.g., "." will match one byte normally but with UTF8 +// set may match up to three bytes of a multi-byte character. +// +// Example: +// pcrecpp::RE_Options options; +// options.set_utf8(); +// pcrecpp::RE re(utf8_pattern, options); +// re.FullMatch(utf8_string); +// +// Example: using the convenience function UTF8(): +// pcrecpp::RE re(utf8_pattern, pcrecpp::UTF8()); +// re.FullMatch(utf8_string); +// +// NOTE: The UTF8 option is ignored if pcre was not configured with the +// --enable-utf8 flag. +// +// ----------------------------------------------------------------------- +// PASSING MODIFIERS TO THE REGULAR EXPRESSION ENGINE +// +// PCRE defines some modifiers to change the behavior of the regular +// expression engine. +// The C++ wrapper defines an auxiliary class, RE_Options, as a vehicle +// to pass such modifiers to a RE class. +// +// Currently, the following modifiers are supported +// +// modifier description Perl corresponding +// +// PCRE_CASELESS case insensitive match /i +// PCRE_MULTILINE multiple lines match /m +// PCRE_DOTALL dot matches newlines /s +// PCRE_DOLLAR_ENDONLY $ matches only at end N/A +// PCRE_EXTRA strict escape parsing N/A +// PCRE_EXTENDED ignore whitespaces /x +// PCRE_UTF8 handles UTF8 chars built-in +// PCRE_UNGREEDY reverses * and *? N/A +// PCRE_NO_AUTO_CAPTURE disables matching parens N/A (*) +// +// (For a full account on how each modifier works, please check the +// PCRE API reference manual). +// +// (*) Both Perl and PCRE allow non matching parentheses by means of the +// "?:" modifier within the pattern itself. e.g. (?:ab|cd) does not +// capture, while (ab|cd) does. +// +// For each modifier, there are two member functions whose name is made +// out of the modifier in lowercase, without the "PCRE_" prefix. For +// instance, PCRE_CASELESS is handled by +// bool caseless(), +// which returns true if the modifier is set, and +// RE_Options & set_caseless(bool), +// which sets or unsets the modifier. +// +// Moreover, PCRE_EXTRA_MATCH_LIMIT can be accessed through the +// set_match_limit() and match_limit() member functions. +// Setting match_limit to a non-zero value will limit the executation of +// pcre to keep it from doing bad things like blowing the stack or taking +// an eternity to return a result. A value of 5000 is good enough to stop +// stack blowup in a 2MB thread stack. Setting match_limit to zero will +// disable match limiting. Alternately, you can set match_limit_recursion() +// which uses PCRE_EXTRA_MATCH_LIMIT_RECURSION to limit how much pcre +// recurses. match_limit() caps the number of matches pcre does; +// match_limit_recrusion() caps the depth of recursion. +// +// Normally, to pass one or more modifiers to a RE class, you declare +// a RE_Options object, set the appropriate options, and pass this +// object to a RE constructor. Example: +// +// RE_options opt; +// opt.set_caseless(true); +// +// if (RE("HELLO", opt).PartialMatch("hello world")) ... +// +// RE_options has two constructors. The default constructor takes no +// arguments and creates a set of flags that are off by default. +// +// The optional parameter 'option_flags' is to facilitate transfer +// of legacy code from C programs. This lets you do +// RE(pattern, RE_Options(PCRE_CASELESS|PCRE_MULTILINE)).PartialMatch(str); +// +// But new code is better off doing +// RE(pattern, +// RE_Options().set_caseless(true).set_multiline(true)).PartialMatch(str); +// (See below) +// +// If you are going to pass one of the most used modifiers, there are some +// convenience functions that return a RE_Options class with the +// appropriate modifier already set: +// CASELESS(), UTF8(), MULTILINE(), DOTALL(), EXTENDED() +// +// If you need to set several options at once, and you don't want to go +// through the pains of declaring a RE_Options object and setting several +// options, there is a parallel method that give you such ability on the +// fly. You can concatenate several set_xxxxx member functions, since each +// of them returns a reference to its class object. e.g.: to pass +// PCRE_CASELESS, PCRE_EXTENDED, and PCRE_MULTILINE to a RE with one +// statement, you may write +// +// RE(" ^ xyz \\s+ .* blah$", RE_Options() +// .set_caseless(true) +// .set_extended(true) +// .set_multiline(true)).PartialMatch(sometext); +// +// ----------------------------------------------------------------------- +// SCANNING TEXT INCREMENTALLY +// +// The "Consume" operation may be useful if you want to repeatedly +// match regular expressions at the front of a string and skip over +// them as they match. This requires use of the "StringPiece" type, +// which represents a sub-range of a real string. Like RE, StringPiece +// is defined in the pcrecpp namespace. +// +// Example: read lines of the form "var = value" from a string. +// string contents = ...; // Fill string somehow +// pcrecpp::StringPiece input(contents); // Wrap in a StringPiece +// +// string var; +// int value; +// pcrecpp::RE re("(\\w+) = (\\d+)\n"); +// while (re.Consume(&input, &var, &value)) { +// ...; +// } +// +// Each successful call to "Consume" will set "var/value", and also +// advance "input" so it points past the matched text. +// +// The "FindAndConsume" operation is similar to "Consume" but does not +// anchor your match at the beginning of the string. For example, you +// could extract all words from a string by repeatedly calling +// pcrecpp::RE("(\\w+)").FindAndConsume(&input, &word) +// +// ----------------------------------------------------------------------- +// PARSING HEX/OCTAL/C-RADIX NUMBERS +// +// By default, if you pass a pointer to a numeric value, the +// corresponding text is interpreted as a base-10 number. You can +// instead wrap the pointer with a call to one of the operators Hex(), +// Octal(), or CRadix() to interpret the text in another base. The +// CRadix operator interprets C-style "0" (base-8) and "0x" (base-16) +// prefixes, but defaults to base-10. +// +// Example: +// int a, b, c, d; +// pcrecpp::RE re("(.*) (.*) (.*) (.*)"); +// re.FullMatch("100 40 0100 0x40", +// pcrecpp::Octal(&a), pcrecpp::Hex(&b), +// pcrecpp::CRadix(&c), pcrecpp::CRadix(&d)); +// will leave 64 in a, b, c, and d. +// +// ----------------------------------------------------------------------- +// REPLACING PARTS OF STRINGS +// +// You can replace the first match of "pattern" in "str" with +// "rewrite". Within "rewrite", backslash-escaped digits (\1 to \9) +// can be used to insert text matching corresponding parenthesized +// group from the pattern. \0 in "rewrite" refers to the entire +// matching text. E.g., +// +// string s = "yabba dabba doo"; +// pcrecpp::RE("b+").Replace("d", &s); +// +// will leave "s" containing "yada dabba doo". The result is true if +// the pattern matches and a replacement occurs, or false otherwise. +// +// GlobalReplace() is like Replace(), except that it replaces all +// occurrences of the pattern in the string with the rewrite. +// Replacements are not subject to re-matching. E.g., +// +// string s = "yabba dabba doo"; +// pcrecpp::RE("b+").GlobalReplace("d", &s); +// +// will leave "s" containing "yada dada doo". It returns the number +// of replacements made. +// +// Extract() is like Replace(), except that if the pattern matches, +// "rewrite" is copied into "out" (an additional argument) with +// substitutions. The non-matching portions of "text" are ignored. +// Returns true iff a match occurred and the extraction happened +// successfully. If no match occurs, the string is left unaffected. + + +#include +#include +#include // defines the Arg class +// This isn't technically needed here, but we include it +// anyway so folks who include pcrecpp.h don't have to. +#include + +namespace pcrecpp { + +#define PCRE_SET_OR_CLEAR(b, o) \ + if (b) all_options_ |= (o); else all_options_ &= ~(o); \ + return *this + +#define PCRE_IS_SET(o) \ + (all_options_ & o) == o + +/***** Compiling regular expressions: the RE class *****/ + +// RE_Options allow you to set options to be passed along to pcre, +// along with other options we put on top of pcre. +// Only 9 modifiers, plus match_limit and match_limit_recursion, +// are supported now. +class PCRECPP_EXP_DEFN RE_Options { + public: + // constructor + RE_Options() : match_limit_(0), match_limit_recursion_(0), all_options_(0) {} + + // alternative constructor. + // To facilitate transfer of legacy code from C programs + // + // This lets you do + // RE(pattern, RE_Options(PCRE_CASELESS|PCRE_MULTILINE)).PartialMatch(str); + // But new code is better off doing + // RE(pattern, + // RE_Options().set_caseless(true).set_multiline(true)).PartialMatch(str); + RE_Options(int option_flags) : match_limit_(0), match_limit_recursion_(0), + all_options_(option_flags) {} + // we're fine with the default destructor, copy constructor, etc. + + // accessors and mutators + int match_limit() const { return match_limit_; }; + RE_Options &set_match_limit(int limit) { + match_limit_ = limit; + return *this; + } + + int match_limit_recursion() const { return match_limit_recursion_; }; + RE_Options &set_match_limit_recursion(int limit) { + match_limit_recursion_ = limit; + return *this; + } + + bool caseless() const { + return PCRE_IS_SET(PCRE_CASELESS); + } + RE_Options &set_caseless(bool x) { + PCRE_SET_OR_CLEAR(x, PCRE_CASELESS); + } + + bool multiline() const { + return PCRE_IS_SET(PCRE_MULTILINE); + } + RE_Options &set_multiline(bool x) { + PCRE_SET_OR_CLEAR(x, PCRE_MULTILINE); + } + + bool dotall() const { + return PCRE_IS_SET(PCRE_DOTALL); + } + RE_Options &set_dotall(bool x) { + PCRE_SET_OR_CLEAR(x, PCRE_DOTALL); + } + + bool extended() const { + return PCRE_IS_SET(PCRE_EXTENDED); + } + RE_Options &set_extended(bool x) { + PCRE_SET_OR_CLEAR(x, PCRE_EXTENDED); + } + + bool dollar_endonly() const { + return PCRE_IS_SET(PCRE_DOLLAR_ENDONLY); + } + RE_Options &set_dollar_endonly(bool x) { + PCRE_SET_OR_CLEAR(x, PCRE_DOLLAR_ENDONLY); + } + + bool extra() const { + return PCRE_IS_SET(PCRE_EXTRA); + } + RE_Options &set_extra(bool x) { + PCRE_SET_OR_CLEAR(x, PCRE_EXTRA); + } + + bool ungreedy() const { + return PCRE_IS_SET(PCRE_UNGREEDY); + } + RE_Options &set_ungreedy(bool x) { + PCRE_SET_OR_CLEAR(x, PCRE_UNGREEDY); + } + + bool utf8() const { + return PCRE_IS_SET(PCRE_UTF8); + } + RE_Options &set_utf8(bool x) { + PCRE_SET_OR_CLEAR(x, PCRE_UTF8); + } + + bool no_auto_capture() const { + return PCRE_IS_SET(PCRE_NO_AUTO_CAPTURE); + } + RE_Options &set_no_auto_capture(bool x) { + PCRE_SET_OR_CLEAR(x, PCRE_NO_AUTO_CAPTURE); + } + + RE_Options &set_all_options(int opt) { + all_options_ = opt; + return *this; + } + int all_options() const { + return all_options_ ; + } + + // TODO: add other pcre flags + + private: + int match_limit_; + int match_limit_recursion_; + int all_options_; +}; + +// These functions return some common RE_Options +static inline RE_Options UTF8() { + return RE_Options().set_utf8(true); +} + +static inline RE_Options CASELESS() { + return RE_Options().set_caseless(true); +} +static inline RE_Options MULTILINE() { + return RE_Options().set_multiline(true); +} + +static inline RE_Options DOTALL() { + return RE_Options().set_dotall(true); +} + +static inline RE_Options EXTENDED() { + return RE_Options().set_extended(true); +} + +// Interface for regular expression matching. Also corresponds to a +// pre-compiled regular expression. An "RE" object is safe for +// concurrent use by multiple threads. +class PCRECPP_EXP_DEFN RE { + public: + // We provide implicit conversions from strings so that users can + // pass in a string or a "const char*" wherever an "RE" is expected. + RE(const string& pat) { Init(pat, NULL); } + RE(const string& pat, const RE_Options& option) { Init(pat, &option); } + RE(const char* pat) { Init(pat, NULL); } + RE(const char* pat, const RE_Options& option) { Init(pat, &option); } + RE(const unsigned char* pat) { + Init(reinterpret_cast(pat), NULL); + } + RE(const unsigned char* pat, const RE_Options& option) { + Init(reinterpret_cast(pat), &option); + } + + // Copy constructor & assignment - note that these are expensive + // because they recompile the expression. + RE(const RE& re) { Init(re.pattern_, &re.options_); } + const RE& operator=(const RE& re) { + if (this != &re) { + Cleanup(); + + // This is the code that originally came from Google + // Init(re.pattern_.c_str(), &re.options_); + + // This is the replacement from Ari Pollak + Init(re.pattern_, &re.options_); + } + return *this; + } + + + ~RE(); + + // The string specification for this RE. E.g. + // RE re("ab*c?d+"); + // re.pattern(); // "ab*c?d+" + const string& pattern() const { return pattern_; } + + // If RE could not be created properly, returns an error string. + // Else returns the empty string. + const string& error() const { return *error_; } + + /***** The useful part: the matching interface *****/ + + // This is provided so one can do pattern.ReplaceAll() just as + // easily as ReplaceAll(pattern-text, ....) + + bool FullMatch(const StringPiece& text, + const Arg& ptr1 = no_arg, + const Arg& ptr2 = no_arg, + const Arg& ptr3 = no_arg, + const Arg& ptr4 = no_arg, + const Arg& ptr5 = no_arg, + const Arg& ptr6 = no_arg, + const Arg& ptr7 = no_arg, + const Arg& ptr8 = no_arg, + const Arg& ptr9 = no_arg, + const Arg& ptr10 = no_arg, + const Arg& ptr11 = no_arg, + const Arg& ptr12 = no_arg, + const Arg& ptr13 = no_arg, + const Arg& ptr14 = no_arg, + const Arg& ptr15 = no_arg, + const Arg& ptr16 = no_arg) const; + + bool PartialMatch(const StringPiece& text, + const Arg& ptr1 = no_arg, + const Arg& ptr2 = no_arg, + const Arg& ptr3 = no_arg, + const Arg& ptr4 = no_arg, + const Arg& ptr5 = no_arg, + const Arg& ptr6 = no_arg, + const Arg& ptr7 = no_arg, + const Arg& ptr8 = no_arg, + const Arg& ptr9 = no_arg, + const Arg& ptr10 = no_arg, + const Arg& ptr11 = no_arg, + const Arg& ptr12 = no_arg, + const Arg& ptr13 = no_arg, + const Arg& ptr14 = no_arg, + const Arg& ptr15 = no_arg, + const Arg& ptr16 = no_arg) const; + + bool Consume(StringPiece* input, + const Arg& ptr1 = no_arg, + const Arg& ptr2 = no_arg, + const Arg& ptr3 = no_arg, + const Arg& ptr4 = no_arg, + const Arg& ptr5 = no_arg, + const Arg& ptr6 = no_arg, + const Arg& ptr7 = no_arg, + const Arg& ptr8 = no_arg, + const Arg& ptr9 = no_arg, + const Arg& ptr10 = no_arg, + const Arg& ptr11 = no_arg, + const Arg& ptr12 = no_arg, + const Arg& ptr13 = no_arg, + const Arg& ptr14 = no_arg, + const Arg& ptr15 = no_arg, + const Arg& ptr16 = no_arg) const; + + bool FindAndConsume(StringPiece* input, + const Arg& ptr1 = no_arg, + const Arg& ptr2 = no_arg, + const Arg& ptr3 = no_arg, + const Arg& ptr4 = no_arg, + const Arg& ptr5 = no_arg, + const Arg& ptr6 = no_arg, + const Arg& ptr7 = no_arg, + const Arg& ptr8 = no_arg, + const Arg& ptr9 = no_arg, + const Arg& ptr10 = no_arg, + const Arg& ptr11 = no_arg, + const Arg& ptr12 = no_arg, + const Arg& ptr13 = no_arg, + const Arg& ptr14 = no_arg, + const Arg& ptr15 = no_arg, + const Arg& ptr16 = no_arg) const; + + bool Replace(const StringPiece& rewrite, + string *str) const; + + int GlobalReplace(const StringPiece& rewrite, + string *str) const; + + bool Extract(const StringPiece &rewrite, + const StringPiece &text, + string *out) const; + + // Escapes all potentially meaningful regexp characters in + // 'unquoted'. The returned string, used as a regular expression, + // will exactly match the original string. For example, + // 1.5-2.0? + // may become: + // 1\.5\-2\.0\? + // Note QuoteMeta behaves the same as perl's QuoteMeta function, + // *except* that it escapes the NUL character (\0) as backslash + 0, + // rather than backslash + NUL. + static string QuoteMeta(const StringPiece& unquoted); + + + /***** Generic matching interface *****/ + + // Type of match (TODO: Should be restructured as part of RE_Options) + enum Anchor { + UNANCHORED, // No anchoring + ANCHOR_START, // Anchor at start only + ANCHOR_BOTH // Anchor at start and end + }; + + // General matching routine. Stores the length of the match in + // "*consumed" if successful. + bool DoMatch(const StringPiece& text, + Anchor anchor, + int* consumed, + const Arg* const* args, int n) const; + + // Return the number of capturing subpatterns, or -1 if the + // regexp wasn't valid on construction. + int NumberOfCapturingGroups() const; + + // The default value for an argument, to indicate the end of the argument + // list. This must be used only in optional argument defaults. It should NOT + // be passed explicitly. Some people have tried to use it like this: + // + // FullMatch(x, y, &z, no_arg, &w); + // + // This is a mistake, and will not work. + static Arg no_arg; + + private: + + void Init(const string& pattern, const RE_Options* options); + void Cleanup(); + + // Match against "text", filling in "vec" (up to "vecsize" * 2/3) with + // pairs of integers for the beginning and end positions of matched + // text. The first pair corresponds to the entire matched text; + // subsequent pairs correspond, in order, to parentheses-captured + // matches. Returns the number of pairs (one more than the number of + // the last subpattern with a match) if matching was successful + // and zero if the match failed. + // I.e. for RE("(foo)|(bar)|(baz)") it will return 2, 3, and 4 when matching + // against "foo", "bar", and "baz" respectively. + // When matching RE("(foo)|hello") against "hello", it will return 1. + // But the values for all subpattern are filled in into "vec". + int TryMatch(const StringPiece& text, + int startpos, + Anchor anchor, + bool empty_ok, + int *vec, + int vecsize) const; + + // Append the "rewrite" string, with backslash subsitutions from "text" + // and "vec", to string "out". + bool Rewrite(string *out, + const StringPiece& rewrite, + const StringPiece& text, + int *vec, + int veclen) const; + + // internal implementation for DoMatch + bool DoMatchImpl(const StringPiece& text, + Anchor anchor, + int* consumed, + const Arg* const args[], + int n, + int* vec, + int vecsize) const; + + // Compile the regexp for the specified anchoring mode + pcre* Compile(Anchor anchor); + + string pattern_; + RE_Options options_; + pcre* re_full_; // For full matches + pcre* re_partial_; // For partial matches + const string* error_; // Error indicator (or points to empty string) +}; + +} // namespace pcrecpp + +#endif /* _PCRECPP_H */ diff --git a/src/pcrecpp/pcrecpp_internal.h b/src/pcrecpp/pcrecpp_internal.h new file mode 100644 index 0000000..827f9e0 --- /dev/null +++ b/src/pcrecpp/pcrecpp_internal.h @@ -0,0 +1,71 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* +Copyright (c) 2005, Google Inc. +All rights reserved. + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the University of Cambridge 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. +----------------------------------------------------------------------------- +*/ + + +#ifndef PCRECPP_INTERNAL_H +#define PCRECPP_INTERNAL_H + +/* When compiling a DLL for Windows, the exported symbols have to be declared +using some MS magic. I found some useful information on this web page: +http://msdn2.microsoft.com/en-us/library/y4h7bcy6(VS.80).aspx. According to the +information there, using __declspec(dllexport) without "extern" we have a +definition; with "extern" we have a declaration. The settings here override the +setting in pcre.h. We use: + + PCRECPP_EXP_DECL for declarations + PCRECPP_EXP_DEFN for definitions of exported functions + +*/ + +#ifndef PCRECPP_EXP_DECL +# ifdef _WIN32 +# ifndef PCRE_STATIC +# define PCRECPP_EXP_DECL extern __declspec(dllexport) +# define PCRECPP_EXP_DEFN __declspec(dllexport) +# else +# define PCRECPP_EXP_DECL extern +# define PCRECPP_EXP_DEFN +# endif +# else +# define PCRECPP_EXP_DECL extern +# define PCRECPP_EXP_DEFN +# endif +#endif + +#endif /* PCRECPP_INTERNAL_H */ + +/* End of pcrecpp_internal.h */ diff --git a/src/pcrecpp/pcrecpparg.h b/src/pcrecpp/pcrecpparg.h new file mode 100644 index 0000000..20438a8 --- /dev/null +++ b/src/pcrecpp/pcrecpparg.h @@ -0,0 +1,175 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Google Inc. 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 COPYRIGHT HOLDERS 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 COPYRIGHT +// OWNER 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. +// +// Author: Sanjay Ghemawat + +#ifndef _PCRECPPARG_H +#define _PCRECPPARG_H + +#include // Custom +#include // for NULL +#include + +#include + +namespace pcrecpp { + +class StringPiece; + +// Hex/Octal/Binary? + +// Special class for parsing into objects that define a ParseFrom() method +template +class _RE_MatchObject { + public: + static inline bool Parse(const char* str, int n, void* dest) { + if (dest == NULL) return true; + T* object = reinterpret_cast(dest); + return object->ParseFrom(str, n); + } +}; + +class PCRECPP_EXP_DEFN Arg { + public: + // Empty constructor so we can declare arrays of Arg + Arg(); + + // Constructor specially designed for NULL arguments + Arg(void*); + + typedef bool (*Parser)(const char* str, int n, void* dest); + +// Type-specific parsers +#define PCRE_MAKE_PARSER(type,name) \ + Arg(type* p) : arg_(p), parser_(name) { } \ + Arg(type* p, Parser parser) : arg_(p), parser_(parser) { } + + + PCRE_MAKE_PARSER(char, parse_char); + PCRE_MAKE_PARSER(unsigned char, parse_uchar); + PCRE_MAKE_PARSER(short, parse_short); + PCRE_MAKE_PARSER(unsigned short, parse_ushort); + PCRE_MAKE_PARSER(int, parse_int); + PCRE_MAKE_PARSER(unsigned int, parse_uint); + PCRE_MAKE_PARSER(long, parse_long); + PCRE_MAKE_PARSER(unsigned long, parse_ulong); +#if 1 + PCRE_MAKE_PARSER(long long, parse_longlong); +#endif +#if 1 + PCRE_MAKE_PARSER(unsigned long long, parse_ulonglong); +#endif + PCRE_MAKE_PARSER(float, parse_float); + PCRE_MAKE_PARSER(double, parse_double); + PCRE_MAKE_PARSER(std::string, parse_string); + PCRE_MAKE_PARSER(StringPiece, parse_stringpiece); + +#undef PCRE_MAKE_PARSER + + // Generic constructor + template Arg(T*, Parser parser); + // Generic constructor template + template Arg(T* p) + : arg_(p), parser_(_RE_MatchObject::Parse) { + } + + // Parse the data + bool Parse(const char* str, int n) const; + + private: + void* arg_; + Parser parser_; + + static bool parse_null (const char* str, int n, void* dest); + static bool parse_char (const char* str, int n, void* dest); + static bool parse_uchar (const char* str, int n, void* dest); + static bool parse_float (const char* str, int n, void* dest); + static bool parse_double (const char* str, int n, void* dest); + static bool parse_string (const char* str, int n, void* dest); + static bool parse_stringpiece (const char* str, int n, void* dest); + +#define PCRE_DECLARE_INTEGER_PARSER(name) \ + private: \ + static bool parse_ ## name(const char* str, int n, void* dest); \ + static bool parse_ ## name ## _radix( \ + const char* str, int n, void* dest, int radix); \ + public: \ + static bool parse_ ## name ## _hex(const char* str, int n, void* dest); \ + static bool parse_ ## name ## _octal(const char* str, int n, void* dest); \ + static bool parse_ ## name ## _cradix(const char* str, int n, void* dest) + + PCRE_DECLARE_INTEGER_PARSER(short); + PCRE_DECLARE_INTEGER_PARSER(ushort); + PCRE_DECLARE_INTEGER_PARSER(int); + PCRE_DECLARE_INTEGER_PARSER(uint); + PCRE_DECLARE_INTEGER_PARSER(long); + PCRE_DECLARE_INTEGER_PARSER(ulong); + PCRE_DECLARE_INTEGER_PARSER(longlong); + PCRE_DECLARE_INTEGER_PARSER(ulonglong); + +#undef PCRE_DECLARE_INTEGER_PARSER +}; + +inline Arg::Arg() : arg_(NULL), parser_(parse_null) { } +inline Arg::Arg(void* p) : arg_(p), parser_(parse_null) { } + +inline bool Arg::Parse(const char* str, int n) const { + return (*parser_)(str, n, arg_); +} + +// This part of the parser, appropriate only for ints, deals with bases +#define MAKE_INTEGER_PARSER(type, name) \ + inline Arg Hex(type* ptr) { \ + return Arg(ptr, Arg::parse_ ## name ## _hex); } \ + inline Arg Octal(type* ptr) { \ + return Arg(ptr, Arg::parse_ ## name ## _octal); } \ + inline Arg CRadix(type* ptr) { \ + return Arg(ptr, Arg::parse_ ## name ## _cradix); } + +MAKE_INTEGER_PARSER(short, short) /* */ +MAKE_INTEGER_PARSER(unsigned short, ushort) /* */ +MAKE_INTEGER_PARSER(int, int) /* Don't use semicolons */ +MAKE_INTEGER_PARSER(unsigned int, uint) /* after these statement */ +MAKE_INTEGER_PARSER(long, long) /* because they can cause */ +MAKE_INTEGER_PARSER(unsigned long, ulong) /* compiler warnings if */ +#if 1 /* the checking level is */ +MAKE_INTEGER_PARSER(long long, longlong) /* turned up high enough. */ +#endif /* */ +#if 1 /* */ +MAKE_INTEGER_PARSER(unsigned long long, ulonglong) /* */ +#endif + +#undef PCRE_IS_SET +#undef PCRE_SET_OR_CLEAR +#undef MAKE_INTEGER_PARSER + +} // namespace pcrecpp + + +#endif /* _PCRECPPARG_H */ diff --git a/gsmartcontrol/src/rconfig/Makefile.am b/src/rconfig/Makefile.am similarity index 100% rename from gsmartcontrol/src/rconfig/Makefile.am rename to src/rconfig/Makefile.am diff --git a/gsmartcontrol/src/rconfig/rcautosave.h b/src/rconfig/rcautosave.h similarity index 100% rename from gsmartcontrol/src/rconfig/rcautosave.h rename to src/rconfig/rcautosave.h diff --git a/gsmartcontrol/src/rconfig/rcdump.h b/src/rconfig/rcdump.h similarity index 100% rename from gsmartcontrol/src/rconfig/rcdump.h rename to src/rconfig/rcdump.h diff --git a/gsmartcontrol/src/rconfig/rcloadsave.h b/src/rconfig/rcloadsave.h similarity index 100% rename from gsmartcontrol/src/rconfig/rcloadsave.h rename to src/rconfig/rcloadsave.h diff --git a/gsmartcontrol/src/rconfig/rcmain.h b/src/rconfig/rcmain.h similarity index 100% rename from gsmartcontrol/src/rconfig/rcmain.h rename to src/rconfig/rcmain.h diff --git a/gsmartcontrol/src/rconfig/rconfig.h b/src/rconfig/rconfig.h similarity index 100% rename from gsmartcontrol/src/rconfig/rconfig.h rename to src/rconfig/rconfig.h diff --git a/gsmartcontrol/src/rconfig/rconfig_mini.h b/src/rconfig/rconfig_mini.h similarity index 100% rename from gsmartcontrol/src/rconfig/rconfig_mini.h rename to src/rconfig/rconfig_mini.h diff --git a/gsmartcontrol/src/rconfig/rconfig_test.cpp b/src/rconfig/rconfig_test.cpp similarity index 100% rename from gsmartcontrol/src/rconfig/rconfig_test.cpp rename to src/rconfig/rconfig_test.cpp diff --git a/src/res/Makefile.am b/src/res/Makefile.am new file mode 100644 index 0000000..2c744aa --- /dev/null +++ b/src/res/Makefile.am @@ -0,0 +1,97 @@ + +# Disable implicit suffix rules for these extensions. +# The problem is that make sees the .glade.cpp or .ui.cpp files +# with their .glade prerequisites, and decides to rebuild the +# (already existing) .glade files by using .glade.cpp files. +# Implicit rules are to blame, so disable them for .glade. +SUFFIXES = .glade .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.glade.cpp gsc_about_dialog.ui.cpp \ + gsc_add_device_window.glade.cpp gsc_add_device_window.ui.cpp \ + gsc_executor_log_window.glade.cpp gsc_executor_log_window.ui.cpp \ + gsc_help_window.glade.cpp gsc_help_window.ui.cpp \ + gsc_info_window.glade.cpp gsc_info_window.ui.cpp \ + gsc_main_window.glade.cpp gsc_main_window.ui.cpp \ + gsc_preferences_window.glade.cpp gsc_preferences_window.ui.cpp \ + gsc_text_window.glade.cpp gsc_text_window.ui.cpp \ + AUTHORS.txt.cpp LICENSE_gsmartcontrol.txt.cpp README.txt.cpp + +# list all intermediate files here (.tmp_ui files are cleaned automatically by make, but it won't hurt) +mostlyclean-local: + rm -f *.glade.cpp *.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@ + + + +# # List all local resource files here. Note: noinst_DATA doesn't put them into distribution (why?) +# EXTRA_DIST = gsc_about_dialog.glade gsc_executor_log_window.glade \ +# gsc_help_window.glade gsc_info_window.glade gsc_main_window.glade \ +# gsc_preferences_window.glade gsc_text_window.glade +# +# Note: All .cpp files will be generated during "make dist" and bundled with +# the distribution. +# +# libres_a_SOURCES = AUTHORS.txt.cpp LICENSE_gsmartcontrol.txt.cpp README.txt.cpp +# +# +# if ENABLE_GTKBUILDER +# # list all .glade.cpp files here +# libres_a_SOURCES += gsc_about_dialog.ui.cpp gsc_executor_log_window.ui.cpp \ +# gsc_help_window.ui.cpp gsc_info_window.ui.cpp gsc_main_window.ui.cpp \ +# gsc_preferences_window.ui.cpp gsc_text_window.ui.cpp +# endif +# +# if ENABLE_LIBGLADE +# # list all .ui.cpp files here +# libres_a_SOURCES += gsc_about_dialog.glade.cpp gsc_executor_log_window.glade.cpp \ +# gsc_help_window.glade.cpp gsc_info_window.glade.cpp gsc_main_window.glade.cpp \ +# gsc_preferences_window.glade.cpp gsc_text_window.glade.cpp +# endif +# +# # Note: GNU patterns here. Can't convert them to suffix rules +# # because paths in targets don't seem to be supported by them. +# +# # Note: We use abs_top_srcdir (instead of top_srcdir) because without +# # it we get "file name too long" errors when compiling in this directory. +# # (automake bug?) +# +# # GtkBuilder support +# %.tmp_ui: $(abs_top_srcdir)/src/res/%.glade +# gtk-builder-convert $< $@ +# +# %.ui.cpp: %.tmp_ui +# $(top_srcdir)/file2csource.sh $< $@ $*_ui +# +# +# # Libglade support +# %.glade.cpp: $(abs_top_srcdir)/src/res/%.glade +# $(top_srcdir)/file2csource.sh $< $@ $*_ui +# +# # Common compiled-in files (resources) +# +# # This covers the files in top srcdir only. +# %.txt.cpp: $(abs_top_srcdir)/%.txt +# $(top_srcdir)/file2csource.sh $< $@ $*_txt +# + diff --git a/gsmartcontrol/src/res/gsc_about_dialog.ui b/src/res/gsc_about_dialog.glade similarity index 62% rename from gsmartcontrol/src/res/gsc_about_dialog.ui rename to src/res/gsc_about_dialog.glade index 38cdccd..bf70f69 100644 --- a/gsmartcontrol/src/res/gsc_about_dialog.ui +++ b/src/res/gsc_about_dialog.glade @@ -1,48 +1,40 @@ - - - - - - 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 + GTK_WIN_POS_CENTER_ON_PARENT True - dialog + GDK_WINDOW_TYPE_HINT_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 - - - + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_BUTTONBOX_END + + + False + GTK_PACK_END + + + - - + + diff --git a/gsmartcontrol/src/res/gsc_add_device_window.ui b/src/res/gsc_add_device_window.glade similarity index 55% rename from gsmartcontrol/src/res/gsc_add_device_window.ui rename to src/res/gsc_add_device_window.glade index 073cdd2..566c856 100644 --- a/gsmartcontrol/src/res/gsc_add_device_window.ui +++ b/src/res/gsc_add_device_window.glade @@ -1,229 +1,211 @@ - - - - - - False + + + + Add Device - GSmartControl True - + True - False 12 - vertical + GTK_ORIENTATION_VERTICAL 12 + GTK_ORIENTATION_VERTICAL - + True - False - vertical + GTK_ORIENTATION_VERTICAL + GTK_ORIENTATION_VERTICAL - + True - False - start + 0 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 + 3 + 2 12 + 6 - + + True + True + Additional smartctl parameters + ● + True + + + 1 + 2 + 2 + 3 + + + + + True + Additional smartctl parameters + 0 + Smartctl parameters: + + + 2 + 3 + GTK_FILL + + + + + True + 0 + Device _type: + True + + + 1 + 2 + GTK_FILL + + + + True - False 6 - + True True - ● + True + ● True - False - False - - - True - True - 0 - + - - _Browse... + True True True + _Browse... True - + 0 + False - True 1 - + 1 - 0 + 2 - + True - False - start - Device _type: - True - - - 0 - 1 - - - - - True - False - Additional smartctl parameters - start - Smartctl parameters: - - - 0 - 2 - - - - - True - False - True - + + True True - + ● + True + - + 1 + 2 1 + 2 - + True - True - Additional smartctl parameters - ● - True - False - False - - - 1 - 2 - - - - - True - False - start + 0 Device _name: True - + - 0 - 0 + GTK_FILL - + False - True 6 1 - + True - False - start + 0 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_BUTTONBOX_END - - gtk-cancel + True True True + gtk-cancel True - + 0 + False False - 0 - - gtk-ok + True True True True True + gtk-ok True - + 0 + False False 1 - + False - True - end + GTK_PACK_END 1 - + - - + + diff --git a/gsmartcontrol/src/res/gsc_executor_log_window.ui b/src/res/gsc_executor_log_window.glade similarity index 51% rename from gsmartcontrol/src/res/gsc_executor_log_window.ui rename to src/res/gsc_executor_log_window.glade index 7c1d599..2066b14 100644 --- a/gsmartcontrol/src/res/gsc_executor_log_window.ui +++ b/src/res/gsc_executor_log_window.glade @@ -1,272 +1,220 @@ - - - - - - False + + + + Execution Log - GSmartControl - 900 - 650 + 620 + 450 True - + True - False 12 - vertical 12 - + True True 180 - + True - False - vertical 6 - + True True - in + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN - + True True - Commands executed + Commands executed True - - - - + - - - True - True - 0 - + - + True - False True - + True - False - - - True - True - 0 - + - - gtk-clear + True True True - Clear command list + Clear command list + gtk-clear True - + 0 + - True - True 1 - + False - True 1 - + False True - + True - False 2 - + True - False - vertical 6 - + True - False 6 - + True - False - start + 0 Command: - + False - True - 0 - + True True - Command with parameters + Command with parameters False True True - False - False - + - True - True 1 - + False - True 3 - 0 - + True - False - start + 0 Output: - + False - True 1 - + True True - in + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN - + True True - Complete output of the command + 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 + Save currently shown output + _Save Current True - - - True - True - 0 - + 0 + - - Save _All + True True True - Save complete log of command execution (useful when reporting bugs) + Save complete log of command execution (useful when reporting bugs) + Save _All True - + 0 + - True - True 1 - + True - False - + - True - True 2 - - gtk-close + True True True - Close this window + Close this window + gtk-close True - + 0 + - True - True 3 - + False - True 1 - + - - + + diff --git a/src/res/gsc_help_window.glade b/src/res/gsc_help_window.glade new file mode 100644 index 0000000..dbc5a29 --- /dev/null +++ b/src/res/gsc_help_window.glade @@ -0,0 +1,135 @@ + + + + + Help - GSmartControl + 630 + 460 + + + True + 12 + 12 + + + True + True + 185 + + + True + 6 + + + True + 0.029999999329447746 + Topics + + + False + + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + Topics + False + + + + + 1 + + + + + False + True + + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + True + True + False + GTK_WRAP_WORD + 5 + 5 + + + + + True + True + + + + + + + True + True + + + True + + + + + True + + + 1 + + + + + True + + + 2 + + + + + True + True + True + True + True + gtk-close + True + 0 + + + False + 3 + + + + + False + 1 + + + + + + diff --git a/src/res/gsc_info_window.glade b/src/res/gsc_info_window.glade new file mode 100644 index 0000000..a410588 --- /dev/null +++ b/src/res/gsc_info_window.glade @@ -0,0 +1,863 @@ + + + + + Device Information - GSmartControl + GTK_WIN_POS_CENTER_ON_PARENT + 850 + 600 + True + + + True + 12 + 12 + + + True + 6 + + + True + 6 + 6 + + + True + + + + + + + + False + False + + + + + True + True + True + + + True + + + True + + + 1 + True + + + + + 1 + True + 0 + True + + + False + 1 + + + + + False + + + + + True + 8 + 2 + 12 + 6 + + + + + + + + + + + + + + + + + + + + + False + 1 + + + + + + + True + True + True + True + Drive identity and general health information + _Identity + True + True + + + False + tab + + + + + True + + + True + + + 1 + True + + + + + 1 + True + 0 + True + + + False + 1 + + + + + False + + + + + True + 3 + + + + + + False + 4 + 1 + + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + GTK_TREE_VIEW_GRID_LINES_BOTH + + + + + 2 + + + + + 1 + + + + + True + True + SMART attributes + Attri_butes + True + True + + + 1 + False + tab + + + + + True + + + True + + + 1 + True + + + + + 1 + True + 0 + True + + + False + 1 + + + + + False + + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + GTK_TREE_VIEW_GRID_LINES_BOTH + + + + + 1 + + + + + 2 + True + + + + + True + True + Drive capabilities + Ca_pabilities + True + True + + + 2 + False + tab + + + + + True + + + True + + + 1 + True + + + + + 1 + True + 0 + True + + + False + 1 + + + + + False + + + + + True + 3 + + + + + + False + 4 + 1 + + + + + True + + + True + True + 170 + True + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + GTK_TREE_VIEW_GRID_LINES_BOTH + + + + + False + True + + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + Complete error log information + False + 5 + 5 + + + + + True + True + + + + + + + 2 + + + + + 3 + True + + + + + True + True + SMART error log contains (usually five) most recent non-trivial errors the hard drive has encountered + _Error Log + True + True + + + 3 + False + tab + + + + + True + + + True + + + 1 + True + + + + + 1 + True + 0 + True + + + False + 1 + + + + + False + + + + + True + 3 + + + + + + False + 4 + 1 + + + + + True + + + True + True + 200 + True + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + GTK_TREE_VIEW_GRID_LINES_BOTH + + + + + False + True + + + + + True + 0 + GTK_SHADOW_NONE + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + Complete selective self-test log information + False + 5 + 5 + + + + + + + True + Selective self-test is a type of self-test which is performed only on selected ares of a drive + Selective self-test log + True + + + label_item + + + + + True + True + + + + + + + 2 + + + + + 4 + + + + + True + True + Self-test log contains information about (usually twenty) most recent manually performed SMART self-tests. +Selective self-test log is also presented here. + _Self-test Logs + True + True + + + 4 + False + tab + + + + + True + 11 + 6 + + + True + 15 + + + True + 0 + 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 + + + + + True + 4 + 2 + 6 + 6 + + + 6 + + + True + gtk-dialog-info + 5 + + + False + + + + + True + 0 + Test result text + True + 70 + + + 1 + + + + + 2 + 3 + 4 + 20 + + + + + True + Test progress + True + Test completion: 90%; ETA: 12 minutes + PANGO_ELLIPSIZE_END + + + 2 + 3 + 10 + + + + + True + True + True + Abort the test + gtk-stop + True + 0 + + + 1 + 2 + 2 + 3 + GTK_FILL + 10 + + + + + 85 + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + Test description + False + GTK_WRAP_WORD + 5 + 5 + False + + + + + 2 + 1 + 2 + + + + + True + True + True + Perform selected test + gtk-execute + True + 0 + + + 1 + 2 + GTK_FILL + + + + + True + 6 + + + True + 6 + + + True + 0 + <b>Test type:</b> + True + + + False + + + + + True + Choose a test type + These are dummy entries +Immediate Offline +Short +Extended +Conveyance + + + False + 1 + + + + + False + + + + + True + 6 + + + True + Estimated test duration on idle drive + <b>Estimated duration:</b> + True + + + False + + + + + True + Minimum test duration on idle drive + N/A + + + False + 1 + + + + + 10 + 1 + + + + + + + 1 + + + + + False + 6 + + + + + 5 + + + + + True + True + Perform self-tests on the hard drive + Perform _Tests + True + True + + + 5 + False + tab + + + + + 1 + + + + + + + True + 6 + True + + + True + True + True + Re-read all the information + gtk-refresh + True + 0 + + + + + + + True + True + True + View smartctl output + _View Output + True + 0 + + + 1 + + + + + True + True + True + Save the SMART information (smartctl output) to a text file + gtk-save-as + True + 0 + + + + 2 + + + + + True + + + 3 + + + + + True + True + True + Close this window + gtk-close + True + 0 + + + 4 + + + + + False + False + 1 + + + + + + diff --git a/src/res/gsc_main_window.glade b/src/res/gsc_main_window.glade new file mode 100644 index 0000000..d18bbb6 --- /dev/null +++ b/src/res/gsc_main_window.glade @@ -0,0 +1,236 @@ + + + + + GSmartControl + 580 + 400 + + + True + + + True + + + + + + False + False + + + + + True + 3 + + + True + 6 + + + True + Device, capacity, model (if available) + 6 + + + True + 0 + <b>Drive information:</b> + True + + + False + + + + + True + + + + + + 1 + + + + + + + True + Basic health check status as reported by the drive. +View drive details for more information. + 6 + + + True + 0 + <b>Basic health check:</b> + True + + + False + + + + + True + + + + + + 1 + + + + + 1 + + + + + True + True + True + True + 6 + + + True + 12 + + + True + 6 + + + True + Model family (if available in smartctl database) + 6 + + + True + 2 + + + True + 0 + <b>Model family:</b> + True + True + + + + + False + + + + + True + + + + + + 1 + + + + + + + True + True + enable smart (action-replaced text) + 0 + True + + + 1 + + + + + True + True + enable aodc (action-replaced text) + 0 + True + + + 2 + + + + + + + + + True + <b>Mo_re</b> + True + True + + + label_item + + + + + 2 + + + + + 4 + + + + + False + 1 + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_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 + 130 + 3 + 8 + 5 + 10 + + + + + 2 + + + + + + False + 3 + + + + + + diff --git a/gsmartcontrol/src/res/gsc_preferences_window.ui b/src/res/gsc_preferences_window.glade similarity index 55% rename from gsmartcontrol/src/res/gsc_preferences_window.ui rename to src/res/gsc_preferences_window.glade index 49e6f04..5be1c2e 100644 --- a/gsmartcontrol/src/res/gsc_preferences_window.ui +++ b/src/res/gsc_preferences_window.glade @@ -1,777 +1,635 @@ - - - - - - False + + + + - + True - False 12 - vertical 12 - + True - False - vertical - + True True True - + True - False 5 - vertical 6 - + True - False 0 - none + GTK_SHADOW_NONE - + True - False 12 - + True - False - vertical - + True - False - vertical 6 - - _Scan system for drives on startup + True True - False + _Scan system for drives on startup True + 0 True - - - True - True - 0 - + - - Show SMART-capable drives only + True True - False + Show SMART-capable drives only True + 0 True - + - True - True 1 - - Show device name under drive icon + True True - False + Show device name under drive icon True + 0 True - + - True - True 2 - - Show serial number under drive icon + True True - False + Show serial number under drive icon True + 0 True - + - True - True 3 - + - True - True 10 - 0 - + - + - - + + True - False <b>Program Settings</b> True - + + + label_item + - + False - True 5 - 0 - + True - False 0 - none + GTK_SHADOW_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 + If smartmontools is installed, use its smartctl by default + Loo_k for smartctl in smartmontools installation directory first True + 0 True - - - True - True - 0 - + - + True - False - 6 + 2 + 2 12 + 6 - + True - False 6 - + True True True - False - False - - - True - True - 0 - + - - _Browse... + True True True + _Browse... True - + 0 + - True - True 1 - + 1 - 0 + 2 - + True True - Global parameters for smartctl. These parameters will be used every time the progam invokes smartctl. Must be shell-escaped. + Global parameters for smartctl. These parameters will be used every time the progam invokes smartctl. Must be shell-escaped. True - False - False - + 1 + 2 1 + 2 - + True - False - Global parameters for smartctl. These parameters will be used every time the progam invokes smartctl. Must be shell-escaped. - start + Global parameters for smartctl. These parameters will be used every time the progam invokes smartctl. Must be shell-escaped. Smartctl parameters: True - + - 0 1 + 2 + GTK_FILL - + True - False - start + 1 S_martctl binary: True - + - 0 - 0 + GTK_FILL - + - True - True 1 - + - True - True 10 - 0 - + - + - - + + True - False <b>Smartctl Invocation</b> True - + + + label_item + - + False - True 5 1 - + - - + + True - False - 3 - 3 - True _General True - + False + tab - + True - False 5 - vertical 6 - + True - False 0 - none + GTK_SHADOW_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 + 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. + 0 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. + 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 - + + + label_item + - + False - True 6 - 0 - + True - False 0 - none + GTK_SHADOW_NONE - + True - False 12 - + True - False - vertical - + True - False 12 - + True - False - start + 0 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 + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN - + True True - Device list + Device list False - - - - + - - - True - True - 0 - + - + True - False 6 True - - gtk-remove + True True True - Remove selected entry from device list + Remove selected entry from device list + gtk-remove True - - - True - True - 0 - + 0 + - - gtk-add + True True True - Add a new entry to device list + Add a new entry to device list + gtk-add True - + 0 + - True - True 1 - + - False - True 1 - - - True - True - 0 - + - + True - False - vertical - + True - False - 6 + 4 + 2 12 + 6 - + True - False - Smartctl parameters to add (for example, "-T permissive" or "-d usbsunplus") - start + True + Smartctl parameters to add (for example, "-T permissive" or "-d usbsunplus") + + + 1 + 2 + 3 + 4 + + + + + True + Smartctl parameters to add (for example, "-T permissive" or "-d usbsunplus") + 1 _Parameters: True - + - 0 3 + 4 - + 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 + 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". + + + 1 + 2 + 2 + 3 + + + + + True + True + + + 1 + 2 + 1 + 2 + + + + + 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". + 1 _Type: True - + - 0 2 + 3 - + True - False - start + 1 De_vice: True - + - 0 1 + 2 - + 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 + 0 <b>Drive Properties:</b> True - + - 0 - 0 - 2 + 2 - + False - True - 0 - + - True - True 1 - + - True - True 6 1 - + - + - - + + True - False <b>Per-drive Smartctl Parameters</b> True - + + + label_item + - + False - True 6 1 - + 1 - - + + True - False - 6 - 6 - True _Drives True - 10 - + 1 False + tab - - - True - True - 0 - + - - - True - True - 0 - + - + True - False 6 True - - Reset a_ll + True True True - Reset all program settings to their defaults + Reset all program settings to their defaults + Reset a_ll True - - - True - True - 0 - + 0 + - + True - False - + - True - True 1 - - gtk-cancel + True True True + gtk-cancel True - + 0 + - True - True 2 - - gtk-ok + True True True True True True + gtk-ok True - + 0 + - True - True 3 - + False - True 1 - + - - + + diff --git a/src/res/gsc_text_window.glade b/src/res/gsc_text_window.glade new file mode 100644 index 0000000..195e8fa --- /dev/null +++ b/src/res/gsc_text_window.glade @@ -0,0 +1,98 @@ + + + + + GSmartControl + 600 + 430 + True + + + True + 12 + 12 + + + True + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + True + True + False + 5 + 5 + False + + + + + + + + + True + 6 + True + + + True + True + True + Saved displayed text as... + gtk-save-as + True + 0 + + + + + + True + + + 1 + + + + + True + + + 2 + + + + + True + True + True + True + True + Close this window + gtk-close + True + 0 + + + 3 + + + + + False + 1 + + + + + + diff --git a/gsmartcontrol/src/rmn/Makefile.am b/src/rmn/Makefile.am similarity index 100% rename from gsmartcontrol/src/rmn/Makefile.am rename to src/rmn/Makefile.am diff --git a/gsmartcontrol/src/rmn/resource_base.h b/src/rmn/resource_base.h similarity index 100% rename from gsmartcontrol/src/rmn/resource_base.h rename to src/rmn/resource_base.h diff --git a/gsmartcontrol/src/rmn/resource_data_any.h b/src/rmn/resource_data_any.h similarity index 100% rename from gsmartcontrol/src/rmn/resource_data_any.h rename to src/rmn/resource_data_any.h diff --git a/gsmartcontrol/src/rmn/resource_data_locking.h b/src/rmn/resource_data_locking.h similarity index 100% rename from gsmartcontrol/src/rmn/resource_data_locking.h rename to src/rmn/resource_data_locking.h diff --git a/gsmartcontrol/src/rmn/resource_data_one.h b/src/rmn/resource_data_one.h similarity index 100% rename from gsmartcontrol/src/rmn/resource_data_one.h rename to src/rmn/resource_data_one.h diff --git a/gsmartcontrol/src/rmn/resource_data_types.h b/src/rmn/resource_data_types.h similarity index 100% rename from gsmartcontrol/src/rmn/resource_data_types.h rename to src/rmn/resource_data_types.h diff --git a/gsmartcontrol/src/rmn/resource_exception.h b/src/rmn/resource_exception.h similarity index 100% rename from gsmartcontrol/src/rmn/resource_exception.h rename to src/rmn/resource_exception.h diff --git a/gsmartcontrol/src/rmn/resource_node.h b/src/rmn/resource_node.h similarity index 100% rename from gsmartcontrol/src/rmn/resource_node.h rename to src/rmn/resource_node.h diff --git a/gsmartcontrol/src/rmn/resource_node_dump.h b/src/rmn/resource_node_dump.h similarity index 100% rename from gsmartcontrol/src/rmn/resource_node_dump.h rename to src/rmn/resource_node_dump.h diff --git a/gsmartcontrol/src/rmn/resource_serialization.h b/src/rmn/resource_serialization.h similarity index 100% rename from gsmartcontrol/src/rmn/resource_serialization.h rename to src/rmn/resource_serialization.h diff --git a/gsmartcontrol/src/rmn/rmn.h b/src/rmn/rmn.h similarity index 100% rename from gsmartcontrol/src/rmn/rmn.h rename to src/rmn/rmn.h diff --git a/gsmartcontrol/src/rmn/rmn_test.cpp b/src/rmn/rmn_test.cpp similarity index 100% rename from gsmartcontrol/src/rmn/rmn_test.cpp rename to src/rmn/rmn_test.cpp diff --git a/gsmartcontrol/version.in b/version.in similarity index 100% rename from gsmartcontrol/version.in rename to version.in