Compare commits

..
4 Commits
Author SHA1 Message Date
Alexander Shaduri 77e3668173 Tagging 0.8.2:
Fixed gsmartcontrol_root.sh script to support distributions with no
        /usr/sbin in their users' paths (thanks to Erwan Velu).
    Added desktop auto-detection to gsmartcontrol_root.sh script. This allows
        us to use only one desktop file (thanks to Erwan Velu).
    Added debian package directory (named "debian.dist" for now to avoid
        control file conflicts with Build Service).
    Renamed gsmartcontrol_root.sh to gsmartcontrol-root, to make Debian happy.
    Added make targets for Windows packages (zip and NSIS).
    Fixed Windows-related issues (Vista is fully supported now).
    Fixed minor bugs:
        Smartctl parser is win32-locale-aware now.
        No more unnecessary parsing.
        No segfault on exit under Windows and Solaris.
        A friendlier message is displayed if smartctl was not found.
        No hilighted labels when switching tabs in Information window.
2010-12-26 19:34:23 +00:00
Alexander Shaduri 5c257ffdc4 Tagging 0.8.2. 2010-12-26 19:33:25 +00:00
Alexander Shaduri dc6297b0c9 Importing 0.8.1 2010-12-26 19:24:29 +00:00
Alexander Shaduri 3a181b6880 Imported 0.8.0rc4. 2010-12-26 18:54:11 +00:00
137 changed files with 5952 additions and 1617 deletions
+1 -1
View File
@@ -1 +1 @@
See NEWS file.
See the NEWS file.
+6
View File
@@ -18,12 +18,18 @@ If not, see <http://www.gnu.org/licenses/>.
See LICENSE_gpl2.txt and LICENSE_gpl3.txt for details.
All files which don't have any copyright notices, tests and examples are
covered under the Whatever License. See LICENSE_whatever.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.
This product includes icons from Crystal Project,
copyright 2006-2007 Everaldo Coelho.
http://www.everaldo.com/crystal, http://www.yellowicon.com .
+18 -6
View File
@@ -4,21 +4,33 @@ HZ Library License
The HZ library contains software licensed under several different licenses.
This is due to HZ including portions of other software.
All original code is licensed under the Zlib License. See LICENSE_zlib.txt for
details.
Portions of this software are copyright Regents of the University of California,
see LICENSE_bsd-ucb.txt for details.
All original code (except for the tests) is licensed under the Zlib License.
See LICENSE_zlib.txt for details.
All tests and examples are covered under the Whatever License. See
LICENSE_whatever.txt for details.
Portions of this software are copyright Regents of the University of
California, see LICENSE_bsd-ucb.txt for details.
Portions of this software are licensed under Boost Software License, see
LICENSE_boost_1_0.txt and individual source files for copyright information.
Each source file contains its copyright information.
If you intend to use HZ in your product, please note that you MUST include
these licenses somewhere in your distribution (simply redistributing the
license files or reproducing their texts in documentation and/or other supplied
materials is OK):
license files or reproducing their texts in documentation and/or other
supplied materials is OK):
* LICENSE_zlib.txt (source distributions only);
* LICENSE_bsd-ucb.txt (source and binary distributions);
* LICENSE_boost_1_0.txt (source distributions only).
If you use only portions of HZ, you may ignore this requirement for files you
don't use. For example, if you don't use any of the Boost-licensed files, you
are not required to distribute the Boost license.
+12
View File
@@ -0,0 +1,12 @@
Whatever License
THIS SOFTWARE IS PROVIDED 'AS-IS', WITHOUT ANY EXPRESS OR IMPLIED
WARRANTY. IN NO EVENT WILL THE AUTHORS BE HELD LIABLE FOR ANY DAMAGES
ARISING FROM THE USE OF THIS SOFTWARE.
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely.
+69 -2
View File
@@ -2,7 +2,7 @@
# Add autoconf.m4 directory to local macro search path
ACLOCAL_AMFLAGS = -I autoconf.m4
SUBDIRS = src data
SUBDIRS = data debian.dist src
# These files are actually needed for compilation.
@@ -22,6 +22,7 @@ dist_doc_DATA = AUTHORS.txt ChangeLog NEWS README.txt \
LICENSE_gsmartcontrol.txt \
LICENSE_hz.txt \
LICENSE_pcrecpp.txt \
LICENSE_whatever.txt \
LICENSE_zlib.txt
@@ -45,7 +46,6 @@ gch: global_macros.h.gch
gch-clean:
rm -f global_macros.h.gch
distclean-local: gch-clean
# rpm support
@@ -59,3 +59,70 @@ src-rpm: dist
# rpmbuild -ta $(distdir).tar.bz2
# NSIS installer support.
# Requires wine, installed NSIS, gtkmm dlls, pcre dlls, smartctl.exe, smartctl_console.exe.
# dos2unix on build machine.
# Execute only with win32 build present.
WIN_ZIP_NAME = $(PACKAGE)-$(VERSION)-win32
win-dist: win-dist-prepare nsis-dist-nocleanup win-zip-dist
win-dist-clean:
rm -rf gsmartcontrol-*.exe gsmartcontrol-*.zip nsis-dist $(WIN_ZIP_NAME)
win-dist-prepare: all win-dist-clean
$(MKDIR_P) nsis-dist
cp data/nsis/gsmartcontrol.nsi $(top_srcdir)/data/nsis/nsi_* nsis-dist/
cp $(top_srcdir)/data/gsmartcontrol.ico $(top_srcdir)/data/icon_*.png nsis-dist/
$(MKDIR_P) nsis-dist/doc
cp data/nsis/distribution.txt nsis-dist/doc/
cp $(top_srcdir)/AUTHORS.txt $(top_srcdir)/LICENSE_* $(top_srcdir)/README.txt nsis-dist/doc/
cp $(top_srcdir)/NEWS nsis-dist/doc/NEWS.txt
unix2dos nsis-dist/*.nsi nsis-dist/doc/*.txt
cp "@WIN32_ENV@"/libatkmm-*.dll "@WIN32_ENV@"/libcairomm-*.dll "@WIN32_ENV@"/libgdkmm-*.dll nsis-dist/
cp "@WIN32_ENV@"/libgiomm-*.dll "@WIN32_ENV@"/libglibmm-*.dll "@WIN32_ENV@"/libgtkmm-*.dll nsis-dist/
cp "@WIN32_ENV@"/libpangomm-*.dll "@WIN32_ENV@"/libsigc-*.dll "@WIN32_ENV@"/pcre3.dll nsis-dist/
cp "@WIN32_ENV@"/mingwm10.dll "@WIN32_ENV@"/smartctl.exe "@WIN32_ENV@"/smartctl_console.exe nsis-dist/
cp src/gsmartcontrol.exe nsis-dist/
$(STRIP) nsis-dist/*.exe
mv nsis-dist/mingwm10.dll nsis-dist/mingwm10.dll_
$(STRIP) --strip-debug --strip-unneeded nsis-dist/*.dll
mv nsis-dist/mingwm10.dll_ nsis-dist/mingwm10.dll
nsis-dist: nsis-dist-nocleanup nsis-cleanup
nsis-dist-nocleanup: win-dist-prepare
cd nsis-dist && @NSIS_EXEC@ gsmartcontrol.nsi
mv nsis-dist/$(PACKAGE)-$(VERSION).exe .
nsis-cleanup:
rm -rf nsis-dist
win-zip-dist: win-zip-dist-nocleanup win-zip-dist-cleanup
win-zip-dist-nocleanup: win-dist-prepare
mv nsis-dist $(WIN_ZIP_NAME)
rm -rf $(WIN_ZIP_NAME)/gsmartcontrol.ico $(WIN_ZIP_NAME)/nsi_* $(WIN_ZIP_NAME)/*.nsi
zip -9r $(WIN_ZIP_NAME).zip $(WIN_ZIP_NAME)
win-zip-dist-cleanup:
rm -rf $(WIN_ZIP_NAME)
distclean-local: gch-clean win-dist-clean
+40
View File
@@ -1,4 +1,44 @@
Version 0.8.2, released on 2008-12-10
Fixed gsmartcontrol_root.sh script to support distributions with no
/usr/sbin in their users' paths (thanks to Erwan Velu).
Added desktop auto-detection to gsmartcontrol_root.sh script. This allows
us to use only one desktop file (thanks to Erwan Velu).
Added debian package directory (named "debian.dist" for now to avoid
control file conflicts with Build Service).
Renamed gsmartcontrol_root.sh to gsmartcontrol-root, to make Debian happy.
Added make targets for Windows packages (zip and NSIS).
Fixed Windows-related issues (Vista is fully supported now).
Fixed minor bugs:
Smartctl parser is win32-locale-aware now.
No more unnecessary parsing.
No segfault on exit under Windows and Solaris.
A friendlier message is displayed if smartctl was not found.
No hilighted labels when switching tabs in Information window.
Version 0.8.1, released on 2008-11-11
Disabled Linux "by-id" drive detection - it's unreliable on some broken
systems.
Added some more attribute descriptions.
Our names for attributes override smartctls' now.
Added a proper "Add Device" dialog for Windows.
Added an icon and resource file for Windows.
Fixed minor bugs.
Version 0.8.0rc4, released on 2008-10-20
FreeBSD support (tested with DesktopBSD 1.6 (FreeBSD 6.3) / x86).
NetBSD support (tested with NetBSD 4.0.1 / x86).
OpenBSD support (tested with OpenBSD 4.3 / x86-64 / gcc-3.3.5).
Solaris/gcc support (tested with Solaris 10 / x86 / gcc-3.4.3 / blastwave).
Solaris/sunstudio support (tested with Solaris 10 / x86 / sunstudio12 /
sunfreeware).
Code to support Windows, Mac OS X, QNX.
Support of older pcre versions (at least 4.5, maybe older too).
Added gsmartcontrol_root.sh script to easily run gsmartcontrol as root.
Improved .desktop files.
License for tests and examples is legally more correct now.
Minor bugfixes.
Version 0.8.0rc3, released on 2008-10-08
Added support for udevless Linux distributions by providing a fallback
/proc/partitions-based drive detection method. The new method adds
+87 -36
View File
@@ -35,6 +35,8 @@ Perform SMART self-tests.
Ability to load smartctl output as a "virtual" device, which acts just like a
real (read-only) device.
Works on most smartctl-supported operating systems.
Extensive help information.
@@ -58,7 +60,7 @@ the drive is idle, and repair the defects, reallocating the data to more safe
areas.
While having SMART sounds really good, there are some nuances to consider. One
of the commond pitfalls is that it may create a false sense of security. That
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
@@ -86,8 +88,6 @@ these bugs and works around them.
Software Requirements
So far, only Linux systems are supported.
You need to have the following software installed:
* pcre - http://www.pcre.org .
@@ -99,16 +99,60 @@ You need to have the following software installed:
* Gtkmm, version 2.6 or higher - see http://www.gtkmm.org .
* libglademm, version 2.4 or higher - see http://www.gtkmm.org .
libglademm is not needed when using GTK 2.12 and Gtkmm 2.12.
Note: libglademm is not needed when using GTK 2.12 and Gtkmm 2.12.
Note that GTK+ 2.12 and Gtkmm 2.12 are HIGHLY recommended. While earlier
versions may work, they may produce ugly results and buggy behaviour.
versions may work, they may produce suboptimal results and buggy behaviour.
Libglademm is not needed when using GTK / Gtkmm 2.12.
Most of these packages are probably already provided by your distribution. For
example, to build this program on OpenSUSE, you just need to install
smartmontools, gtkmm2-devel and pcre-devel - the rest is installed
automatically by the package manager's dependency resolver.
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.
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 (XP or higher) - Tested with Windows XP and Vista. Windows 2000 does
not work for some unknown reason. 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. Note that until either smartctl gets ATA support under
Solaris or GSmartControl gets SCSI support, these configurations are
essentially useless.
* Mac OS X - 3rd-party testing by Fink project.
* 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.
@@ -120,9 +164,9 @@ 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. One option is to try OpenSUSE Build Service - it
provides ready-to-install packages for various popular Linux distributions
(OpenSUSE, Fedora, etc...). See
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
@@ -130,15 +174,16 @@ dependencies (see Software Requirements section). Then the usual
./configure; make; make install
will build and install it. Installation usually requires root privileges, but
you don't need to install the program in order to run it directly from the
compilation directory.
will build and install it. Installation usually requires adminstrative
privileges, but you don't need to install the program in order to run it
directly from the compilation directory.
Permission Problems
Short answer: you need to be root. In X11, use kdesu, gnomesu, sux or similar.
Short answer: you need to be root (Administrator in Windows).
In X11, use kdesu, gnomesu, sux, xdg-su or similar.
Long answer: read below.
@@ -146,29 +191,25 @@ 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 desktop and menu icons should show the "Please enter the root
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.
However, in case the described method is somehow inaccessible, you need to
manually invoke the program with kdesu, gnomesu, sux or similar programs. For
example,
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" command may also help, if
neither KDE or GNOME are available.
"kdesu" with "gnomesu" if using Gnome. The "sux" or "xdg-su" commands may also
help, if neither KDE or GNOME are available.
Another method would be to set the "setuid" flag on smartctl binary, for
example (run as root):
chmod 4755 `which smartctl`
This method is not recommended, however, and is considered a security risk.
Also, some systems may reset the change automatically, unless explicitly
instructed otherwise.
Please don't set the "setuid" flag on smartctl binary. It is considered a
security risk.
@@ -210,7 +251,7 @@ have X11 / Gtkmm running, so this is a low priority task.
* I only have ATA drives, so testing would be almost impossible.
Immediate Offline Tests are not supported. I haven't found a way to reliably
monitor them.
monitor them yet.
You may run several tests on different drives in parallel, but you will only
see the progress information of the last executing test. So, if you run tests
@@ -234,20 +275,30 @@ language and all.
Reporting Bugs
First, please collect the following information about your system. Without it
it may be very hard or impossible to fix the bug.
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
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.0).
* What version of GTK and Gtkmm you have installed. Finding this out is very
* 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 terminal emulator or Run dialog):
with -v option, e.g. (type the following in a terminal emulator or Run
dialog):
kdesu -- biocalcon -v
gsmartcontrol-root auto -v
Perform the steps needed to reproduce the bug, then go to
"Options -> View Execution Log", and click "Save All".
+19 -33
View File
@@ -16,61 +16,47 @@ local scope destruction also works if using stack variables.
A treeview (also iconview, combobox) from glade comes
with existing model, which should be unset
with existing model, which should be unset.
--------------------------------------------------------
GTKMM problems:
portability:
win32 32-bit: int: 4, long: 4, pointer: 4.
win32 64-bit: int: 4, long: 8, pointer: 8.
Lots of gtk_, GTK_, g_ in documentation.
no ComboBox::unset_model() and IconView::unset_model().
sparc: same as win32 (what about OS?).
--------------------------------------------------------
TODO:
if -q is passed, Execution log doesn't contain libdebug log. fix.
Drive detection - any cross-platform way to do it?
Patch pangomm for Pango::strip_markup().
+Make a test case of IconView positioning bug, file a bug report.
Patch gtkmm, glibmm, pangomm for static win32 builds.
FreeBSD: /dev/ad[0-3], partitions - /dev/ad[]
http://www.freebsd.org/doc/en/books/handbook/disks-naming.html
Non-existent devices give ENXIO (6, Device not configured).
In Linux ENXIO has name (6, No such device or address).
/dev/ad0s1b none swap sw 0 0
/dev/ad0s1a / ufs rw 1 1
/dev/acd0c /cdrom cd9660 ro,noauto 0 0
Do NOT open cd devices - it may cause hang on music cd, etc... .
In the slice case the drive will be added as /dev/da1s1e. This is read as:
SCSI disk, unit number 1 (second SCSI disk), slice 1 (PC BIOS partition 1),
and e BSD partition. In the dedicated case, the drive will be added simply as /dev/da1e.
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).
iphone:
/dev/disk0
/dev/disk0s1, /dev/disk0s2
desktop files: gnomesu / gtksu wrapper.
desktop_other - find suitable su.
OpenSuse Build Service:
fails with error when tarball already contains the debian directory.
----------- Feature List ----------
+Autodetect available devices (through /proc / /sys ?).
win32 support for autodetection (what about different smartctl drivers?).
+Autodetect available devices
+Select current device (from autodetected list; manually whitelist devices).
@@ -117,7 +103,7 @@ SCT Temperatures (do we use these?)
This configures temperature logging interval (1 min by default) and clears the history.
Tests:
+Tests:
Ability to detect a testing drive on boot, show its info with testing tab.
Run one-time Immediate Offline test (-t offline).
-16
View File
@@ -1,16 +0,0 @@
AC_DEFUN([AC_CXX_VAR_FUNC],
[AC_CACHE_CHECK(whether $CXX recognizes __func__,
ac_cv_cxx_var_func,
[AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[const char* s = __func__;]])],[ac_cv_cxx_var_func=yes],[ac_cv_cxx_var_func=no])
AC_LANG_POP([])
])
if test "$ac_cv_cxx_var_func" = yes; then
AC_DEFINE(HAVE_CXX__FUNC,,[Define if the C++ complier supports __func__])
fi
])
+34
View File
@@ -0,0 +1,34 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# APP_AUTO_CLEAR_FLAGS()
# Clear user-specified CFLAGS / CXXFLAGS / LIBS / LDFLAGS if requested.
# You should call this macro before any of AC_PROG_C(XX) checks to
# avoid checking with user-specified options.
AC_DEFUN([APP_AUTO_CLEAR_FLAGS], [
AC_ARG_ENABLE(user-flags, AS_HELP_STRING([--disable-user-flags],
[ignore user-supplied compiler flags (default: user flags are enabled)]),
[app_cv_compiler_user_flags=${enableval}], [app_cv_compiler_user_flags=yes])
AC_MSG_NOTICE([Enable user-specified compiler flags: $app_cv_compiler_user_flags])
# Reset user-supplied flags if requested.
if test "x$app_cv_compiler_user_flags" = "xno"; then
CFLAGS="";
CXXFLAGS="";
LIBS="";
LDFLAGS="";
fi
])
+141
View File
@@ -0,0 +1,141 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# 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])
# 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
AC_MSG_WARN([Neither GtkBuilder nor $app_cv_gld_reader_libglade_name found. The program will be unable to compile fully.])
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])
fi
if test "$app_cv_gld_reader" = "gtkbuilder"; then
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])
])
+254
View File
@@ -0,0 +1,254 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# APP_COMPILER_OPTIONS([flags_prefix])
# This macro enables various compiler flags (common warnings, optimization,
# debug, etc...), controllable through configure options.
# flags_prefixCFLAGS, flags_prefixCXXFLAGS and flags_prefixLDFLAGS
# will be initialized (or appended to) with the results.
# DEBUG and DEBUG_BUILD will be exported to config.h in case of debug builds.
# You must call AX_COMPILER_VENDOR, APP_DETECT_OS_KERNEL([target], ...)
# and APP_DETECT_OS_ENV([target], ...) before using this macro.
AC_DEFUN([APP_COMPILER_OPTIONS], [
# These requirements check compile-time presence, not that they were called
AC_REQUIRE([AX_COMPILER_VENDOR])
AC_REQUIRE([APP_DETECT_OS_ENV])
app_cv_compiler_options_cflags="";
app_cv_compiler_options_cxxflags="";
app_cv_compiler_options_ldflags="";
# ---- Common compiler options (warnings, etc...)
AC_ARG_ENABLE(common-options, AS_HELP_STRING([--enable-common-options=<compiler>|auto|none],
[enable useful compilation options (warnings, mostly). Accepted values are auto, none, gnu, intel, sun. (Default: auto)]),
[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
app_cv_compiler_common_options="$ax_cv_cxx_compiler_vendor";
fi
if test "x$app_cv_compiler_common_options" = "xno"; then
# for pretty message
app_cv_compiler_common_options="none";
fi
AC_MSG_NOTICE([Enable common compiler flags for: $app_cv_compiler_common_options])
if test "x$app_cv_compiler_common_options" != "xnone"; then
# gcc / gnu environment, mingw, cygwin.
if test "x$app_cv_compiler_common_options" = "xgnu"; then
if test "x$app_cv_target_os_env" = "xmingw" || test "x$app_cv_target_os_env" = "xcygwin"; then
# mingw gcc options:
# -mno-cygwin - generate non-cygwin executables in cygwin's mingw.
# -mwindows - hide console window. possibly suppresses any std output / error.
# -mconsole - opposite of -mwindows, default.
# -mms-bitfields - make structures compatible with msvc. recommended default for non-cygwin.
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -mms-bitfields -mwindows"
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -mms-bitfields -mwindows"
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -mms-bitfields -mwindows"
fi
# Note: Disabled -Wconversion, it was causing lots of silly warnings under x86-64.
app_cv_compiler_tmp_var="-Wall -Wcast-align -Wcast-qual \
-Wctor-dtor-privacy -Wfloat-equal -Wnon-virtual-dtor -Woverloaded-virtual \
-Wpointer-arith -Wshadow -Wsign-compare -Wsign-promo -Wundef -Wwrite-strings";
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags $app_cv_compiler_tmp_var";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags $app_cv_compiler_tmp_var";
# intel / gnu environment
elif test "x$app_cv_compiler_common_options" = "xintel" && test "x$app_cv_target_os_env" = "xgnu"; then
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -w1";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags \
-ftrapuv -fstack-security-check -early-template-check -static-libgcc -static-intel \
-Wcomment -Wdeprecated -Wextra-tokens -Wformat -Wmissing-prototypes -Wnon-virtual-dtor \
-Wpointer-arith -Wreturn-type -Wshadow -Wtrigraphs -Wuninitialized -w1";
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -static-libgcc -static-intel";
# suncc
elif test "x$app_cv_compiler_common_options" = "xsun"; then
# Enable useful language extensions (e.g. __func__ in C++). Details at
# http://docs.sun.com/app/docs/doc/819-5267/6n7c46drp?a=view
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags \
+w -errtags -erroff=notemsource,notused -features=extensions";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags \
+w -errtags -erroff=notemsource,notused -features=extensions";
fi
fi
# ---- Compiler options for debug builds
AC_ARG_ENABLE(debug-options, AS_HELP_STRING([--enable-debug-options=<compiler>|auto|none],
[enable debug build options. Accepted values are auto, none, gnu, intel, sun. (Default: none)]),
[app_cv_compiler_debug_options=${enableval}], [app_cv_compiler_debug_options=none])
# the default value is "yes" (if no value given after =), treat it as auto.
if test "x$app_cv_compiler_debug_options" = "xyes" || test "x$app_cv_compiler_debug_options" = "xauto"; then
app_cv_compiler_debug_options="$ax_cv_cxx_compiler_vendor";
fi
if test "x$app_cv_compiler_debug_options" = "xno"; then
# for pretty message
app_cv_compiler_debug_options="none";
fi
AC_MSG_NOTICE([Enable debug build flags for: $app_cv_compiler_debug_options])
if test "x$app_cv_compiler_debug_options" != "xnone"; then
# Define DEBUG and DEBUG_BUILD for debug builds (through config.h).
AC_DEFINE([DEBUG], [1], [Defined for debug builds])
AC_DEFINE([DEBUG_BUILD], [1], [Defined for debug builds])
# gcc, mingw
if test "x$app_cv_compiler_debug_options" = "xgnu"; then
if test "x$app_cv_target_os_env" = "xmingw" || test "x$app_cv_target_os_env" = "xcygwin"; then
# Enable console window for debug builds
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -mconsole"
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -mconsole"
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -mconsole"
fi
# We could put libstdc++ debug options here, but it generates binary-incompatible
# C++ code, which leads to runtime errors with e.g. libsigc++.
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -g3 -O0";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -g3 -O0";
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -g3 -O0";
# intel / gnu
elif test "x$app_cv_compiler_debug_options" = "xintel" && test "x$app_cv_target_os_env" = "xgnu"; then
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -g3 -O0";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -g3 -O0";
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -g3 -O0";
# suncc
elif test "x$app_cv_compiler_debug_options" = "xsun"; then
# There's no -O0, it's the default. Debug format is stabs by default, but can be switched
# to dwarf by -xdebugformat=dwarf.
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -xs -g";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -xs -g";
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -xs -g";
fi
fi
# ---- Compiler options for optimized builds
AC_ARG_ENABLE(optimize-options, AS_HELP_STRING([--enable-optimize-options=<compiler>|auto|none],
[enable optimized build options. Accepted values are auto, none, gnu, intel, sun. (Default: none)]),
[app_cv_compiler_optimize_options=${enableval}], [app_cv_compiler_optimize_options=none])
# the default value is "yes" (if no value given after =), treat it as auto.
if test "x$app_cv_compiler_optimize_options" = "xyes" || test "x$app_cv_compiler_optimize_options" = "xauto"; then
app_cv_compiler_optimize_options="$ax_cv_cxx_compiler_vendor";
fi
if test "x$app_cv_compiler_optimize_options" = "xno"; then
# for pretty message
app_cv_compiler_optimize_options="none";
fi
AC_MSG_NOTICE([Enable optimized build flags for: $app_cv_compiler_optimize_options])
if test "x$app_cv_compiler_optimize_options" != "xnone"; then
# gcc, mingw
if test "x$app_cv_compiler_optimize_options" = "xgnu"; then
# -mtune=generic is since gcc 4.0 iirc
if test "$app_cv_target_os_env" = "mingw" || test "$app_cv_target_os_env" = "cygwin"; then
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -g0 -O3 -s -march=i586";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -g0 -O3 -s -march=i586";
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -g0 -O3 -s -march=i586";
else
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
# intel / gnu
elif test "x$app_cv_compiler_optimize_options" = "xintel" && test "x$app_cv_target_os_env" = "xgnu"; then
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -g0 -O3 -s";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -g0 -O3 -s";
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -g0 -O3 -s";
# suncc
elif test "x$app_cv_compiler_optimize_options" = "xsun"; then
# There's no -O0, it's the default. Debug format is stabs by default, but can be switched
# to dwarf by -xdebugformat=dwarf.
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -xO5";
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -xO5";
app_cv_compiler_options_ldflags="$app_cv_compiler_options_ldflags -s";
fi
fi
# ---- Compiler options for GCC pch support
AC_ARG_ENABLE(gcc-pch,
AS_HELP_STRING([--enable-gcc-pch],[enable precompiled header support (pch make target) (GCC only) (default: disabled)
(use "make gch" to speed up the compilation, but don't enable it if you don't intend to use it - it will slow things down)]),
[app_cv_compiler_gcc_pch=${enableval}], [app_cv_compiler_gcc_pch=no])
AC_MSG_NOTICE([Enable GCC precompiled header usage: $app_cv_compiler_gcc_pch])
# Don't check the vendor here - it's disabled by default, so it's on the user.
if test "x$app_cv_compiler_gcc_pch" = "xyes"; then
app_cv_compiler_options_cflags="$app_cv_compiler_options_cflags -Winvalid-pch -DENABLE_PCH"
app_cv_compiler_options_cxxflags="$app_cv_compiler_options_cxxflags -Winvalid-pch -DENABLE_PCH"
fi
# ---- Export our variables
if test "x$$1[]CFLAGS" = "x"; then
$1[]CFLAGS="$app_cv_compiler_options_cflags";
else
$1[]CFLAGS="$$1[]CFLAGS $app_cv_compiler_options_cflags";
fi
if test "x$$1[]CXXFLAGS" = "x"; then
$1[]CXXFLAGS="$app_cv_compiler_options_cxxflags";
else
$1[]CXXFLAGS="$$1[]CXXFLAGS $app_cv_compiler_options_cxxflags";
fi
if test "x$$1[]LDFLAGS" = "x"; then
$1[]LDFLAGS="$app_cv_compiler_options_ldflags";
else
$1[]LDFLAGS="$$1[]LDFLAGS $app_cv_compiler_options_ldflags";
fi
AC_MSG_NOTICE([Compiler-specific build options:])
AC_MSG_NOTICE([CFLAGS: $app_cv_compiler_options_cflags])
AC_MSG_NOTICE([CXXFLAGS: $app_cv_compiler_options_cxxflags])
AC_MSG_NOTICE([LDFLAGS: $app_cv_compiler_options_ldflags])
])
+46
View File
@@ -0,0 +1,46 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# Check whether C++ compiler supports overloading between
# extern "C" function pointer and C++ function pointer. GCC and
# Intel treat them equally, so no overloads are needed (or accepted)
# there.
# SunCC accepts C++-to-C function pointer assignment in function
# parameters, but not in constructor parameters. To make such
# code work an additional C-function-pointer constructor must be
# specified. Then the constructor itself can make the conversion
# (with a compiler warning though).
# SunCC also disallows C-to-C++ function pointer specification
# for template parameters (I don't remember the exact details).
# If such support is found, HAVE_CXX_EXTERN_C_OVERLOAD is defined.
AC_DEFUN([APP_CXX_EXTERN_C_OVERLOAD], [
AC_CACHE_CHECK(whether $CXX accepts extern C function pointer overload, ac_cv_cxx_extern_c_overload, [
AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
extern "C" { typedef void (*c_func_t)(void); }
typedef void (*cpp_func_t)(void);
void f1(c_func_t f) { }
void f1(cpp_func_t f) { }
]], [[]])],
[ac_cv_cxx_extern_c_overload=yes], [ac_cv_cxx_extern_c_overload=no])
AC_LANG_POP([])
])
if test "x$ac_cv_cxx_extern_c_overload" = "xyes"; then
AC_DEFINE(HAVE_CXX_EXTERN_C_OVERLOAD, ,
[Define if the C++ complier accepts extern C function pointer overload])
fi
])
+44
View File
@@ -0,0 +1,44 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# Check whether C++ compiler supports C99 __func__.
AC_DEFUN([APP_CXX___FUNC__], [
AC_CACHE_CHECK(whether $CXX recognizes __func__, app_cv_cxx___func__, [
AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]],
[[const char* s = __func__;]])],
[app_cv_cxx___func__=yes], [app_cv_cxx___func__=no])
AC_LANG_POP([])
])
if test "x$app_cv_cxx___func__" = "xyes"; then
AC_DEFINE(HAVE_CXX___func__, ,
[Define if the C++ complier supports __func__])
fi
])
# Check whether C++ compiler supports __FUNCTION__.
AC_DEFUN([APP_CXX___FUNCTION__], [
AC_CACHE_CHECK(whether $CXX recognizes __FUNCTION__, app_cv_cxx___FUNCTION__, [
AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]],
[[const char* s = __FUNCTION__;]])],
[app_cv_cxx___FUNCTION__=yes], [app_cv_cxx___FUNCTION__=no])
AC_LANG_POP([])
])
if test "x$app_cv_cxx___FUNCTION__" = "xyes"; then
AC_DEFINE(HAVE_CXX___FUNCTION__, ,
[Define if the C++ complier supports __FUNCTION__])
fi
])
+323
View File
@@ -0,0 +1,323 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# APP_DETECT_OS_KERNEL(triplet_name, define_macro_prefix)
# Check target OS kernel.
# triplet_name is target, host or build.
# You must call AX_COMPILER_VENDOR before using this macro.
# If you call
# APP_DETECT_OS_KERNEL([target], [CONFIG_KERNEL])
# you will get CONFIG_KERNEL_LINUX C macro exported to config.h
# (on gcc/linux).
# If no define_macro_prefix is specified, no C macro prefix will be used.
# app_cv_target_os_kernel will be set to kernel name (lowercase).
# app_cv_target_os_kernel_macro will be set to kernel name (all caps).
# TARGET_OS_KERNEL_LINUX automake conditional will be set to true.
# Note: Some operating systems / compilers have built-in defines.
# For gcc, check with
# $ gcc -dM -E - < /dev/null
AC_DEFUN([APP_DETECT_OS_KERNEL], [
# These requirements check compile-time presence, not that they were called
AC_REQUIRE([AX_COMPILER_VENDOR])
AC_MSG_CHECKING([for $1 OS kernel])
app_cv_[]$1[]_os_kernel="";
app_cv_[]$1[]_os_kernel_macro="";
# If vendor is not recognized, it's set to "unknown".
# NOTE: This works only on target compilers!
if test "x${ax_cv_cxx_compiler_vendor}" = "x"; then
# msg_result is needed to print the checking... message.
AC_MSG_RESULT([error])
AC_MSG_ERROR([[ax_cv_cxx_compiler_vendor] not set.])
fi
# "build" - the type of system on which the package is being configured and compiled.
# "host" - the type of system on which the package runs. Specifying it enables the
# cross-compilation mode. "target" defaults to this too.
if test "x$1" != "xbuild" && test "x$1" != "xhost" && test "x$1" != "xtarget"; then
# msg_result is needed to print the checking... message.
AC_MSG_RESULT([error])
AC_MSG_ERROR([[Triplet name must be build, host or target.]])
fi
case "$$1" in
*linux*)
# gcc defines __linux__ on linux. Other defines include: __linux, __unix__, __gnu_linux__,
# linux, unix, __i386__, i386.
app_cv_[]$1[]_os_kernel="linux";
app_cv_[]$1[]_os_kernel_macro="LINUX";
;;
*mingw* | *cygwin*)
# mingw defines _WIN32. msvc defines _WIN32 (built-in), and WIN32 via windows.h.
# Other mingw defines include: __WINNT, __WINNT__, __WIN32__, __i386, _X86_,
# i386, __i386__, WIN32, __MINGW32__, WINNT (all equal to 1).
app_cv_[]$1[]_os_kernel="windows";
app_cv_[]$1[]_os_kernel_macro="WINDOWS";
;;
# This includes debian gnu/kfreebsd. Dragonfly is checked separately.
*freebsd*)
# gcc built-in defines (to check for freebsd kernel):
# defined(__FreeBSD__) || defined(__FreeBSD_kernel__).
# See http://glibc-bsd.alioth.debian.org/porting/PORTING .
app_cv_[]$1[]_os_kernel="freebsd";
app_cv_[]$1[]_os_kernel_macro="FREEBSD";
;;
*dragonfly*)
app_cv_[]$1[]_os_kernel="dragonfly";
app_cv_[]$1[]_os_kernel_macro="DRAGONFLY";
;;
*openbsd*)
app_cv_[]$1[]_os_kernel="openbsd";
app_cv_[]$1[]_os_kernel_macro="OPENBSD";
;;
# This includes debian gnu/netbsd.
*netbsd*)
app_cv_[]$1[]_os_kernel="netbsd";
app_cv_[]$1[]_os_kernel_macro="NETBSD";
;;
*solaris*)
app_cv_[]$1[]_os_kernel="solaris";
app_cv_[]$1[]_os_kernel_macro="SOLARIS";
;;
*darwin*)
app_cv_[]$1[]_os_kernel="darwin";
app_cv_[]$1[]_os_kernel_macro="DARWIN";
;;
*qnx*)
app_cv_[]$1[]_os_kernel="qnx";
app_cv_[]$1[]_os_kernel_macro="QNX";
;;
*)
app_cv_[]$1[]_os_kernel="unknown";
app_cv_[]$1[]_os_kernel_macro="UNKNOWN";
;;
esac
# Assign this - we may use this in configure itself.
if test "x$2" != "x"; then
app_cv_[]$1[]_os_kernel_macro="$2[]_${app_cv_[]$1[]_os_kernel_macro}";
fi
# config.h macros (yes, we need this many. otherwise config.h will be incorrect).
if test "x${app_cv_[]$1[]_os_kernel}" = "xlinux"; then
AC_DEFINE($2[]LINUX, 1, [$1 OS kernel])
fi
if test "x${app_cv_[]$1[]_os_kernel}" = "xwindows"; then
AC_DEFINE($2[]WINDOWS, 1, [$1 OS kernel])
fi
if test "x${app_cv_[]$1[]_os_kernel}" = "xfreebsd"; then
AC_DEFINE($2[]FREEBSD, 1, [$1 OS kernel])
fi
if test "x${app_cv_[]$1[]_os_kernel}" = "xdragonfly"; then
AC_DEFINE($2[]DRAGONFLY, 1, [$1 OS kernel])
fi
if test "x${app_cv_[]$1[]_os_kernel}" = "xopenbsd"; then
AC_DEFINE($2[]OPENBSD, 1, [$1 OS kernel])
fi
if test "x${app_cv_[]$1[]_os_kernel}" = "xnetbsd"; then
AC_DEFINE($2[]NETBSD, 1, [$1 OS kernel])
fi
if test "x${app_cv_[]$1[]_os_kernel}" = "xsolaris"; then
AC_DEFINE($2[]SOLARIS, 1, [$1 OS kernel])
fi
if test "x${app_cv_[]$1[]_os_kernel}" = "xdarwin"; then
AC_DEFINE($2[]DARWIN, 1, [$1 OS kernel])
fi
if test "x${app_cv_[]$1[]_os_kernel}" = "xqnx"; then
AC_DEFINE($2[]QNX, 1, [$1 OS kernel])
fi
if test "x${app_cv_[]$1[]_os_kernel}" = "xunknown"; then
AC_DEFINE($2[]UNKNOWN, 1, [$1 OS kernel])
fi
# Export to Makefile.am's.
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_LINUX, [test "x${app_cv_[]$1[]_os_kernel}" = "xlinux"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_WINDOWS, [test "x${app_cv_[]$1[]_os_kernel}" = "xwindows"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_FREEBSD, [test "x${app_cv_[]$1[]_os_kernel}" = "xfreebsd"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_DRAGONFLY, [test "x${app_cv_[]$1[]_os_kernel}" = "xdragonfly"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_OPENBSD, [test "x${app_cv_[]$1[]_os_kernel}" = "xopenbsd"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_NETBSD, [test "x${app_cv_[]$1[]_os_kernel}" = "xnetbsd"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_SOLARIS, [test "x${app_cv_[]$1[]_os_kernel}" = "xsolaris"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_DARWIN, [test "x${app_cv_[]$1[]_os_kernel}" = "xdarwin"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_QNX, [test "x${app_cv_[]$1[]_os_kernel}" = "xqnx"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_KERNEL_UNKNOWN, [test "x${app_cv_[]$1[]_os_kernel}" = "xunknown"])
AC_MSG_RESULT([${app_cv_[]$1[]_os_kernel}])
])
# APP_DETECT_OS_ENV(triplet_name, define_macro_prefix)
# Check target OS userland environment (mainly glibc).
# triplet_name is target, host or build.
# You must call AX_COMPILER_VENDOR before using this macro.
# If you call
# APP_DETECT_OS_ENV([target], [COMPILER], [CONFIG_OS_ENV])
# you will get CONFIG_OS_ENV_LINUX C macro exported to config.h
# (on gcc/linux).
# If no define_macro_prefix is specified, no C macro prefix will be used.
# app_cv_target_os_env will be set to env name (lowercase).
# app_cv_target_os_env_macro will be set to env name (all caps).
# TARGET_OS_ENV_LINUX automake conditional will be set to true.
AC_DEFUN([APP_DETECT_OS_ENV], [
# These requirements check compile-time presence, not that they were called
AC_REQUIRE([AX_COMPILER_VENDOR])
AC_MSG_CHECKING([for $1 OS userland environment])
app_cv_[]$1[]_os_env="";
app_cv_[]$1[]_os_env_macro="";
# If vendor is not recognized, it's set to "unknown".
# NOTE: This works only on target compilers!
if test "x${ax_cv_cxx_compiler_vendor}" = "x"; then
# msg_result is needed to print the checking... message.
AC_MSG_RESULT([error])
AC_MSG_ERROR([[ax_cv_cxx_compiler_vendor] not set.])
fi
# "build" - the type of system on which the package is being configured and compiled.
# "host" - the type of system on which the package runs. Specifying it enables the
# cross-compilation mode. "target" defaults to this too.
if test "x$1" != "xbuild" && test "x$1" != "xhost" && test "x$1" != "xtarget"; then
# msg_result is needed to print the checking... message.
AC_MSG_RESULT([error])
AC_MSG_ERROR([[Triplet name must be build, host or target.]])
fi
case "$$1" in
*cygwin*)
app_cv_[]$1[]_os_env="cygwin";
app_cv_[]$1[]_os_env_macro="CYGWIN";
;;
*mingw*)
app_cv_[]$1[]_os_env="mingw";
app_cv_[]$1[]_os_env_macro="MINGW";
;;
# This includes gnu/linux, debian gnu/kfreebsd, debian gnu/netbsd...
*-gnu | gnu*)
app_cv_[]$1[]_os_env="gnu";
app_cv_[]$1[]_os_env_macro="GNU";
;;
# Freebsd libc
*freebsd*)
app_cv_[]$1[]_os_env="freebsd";
app_cv_[]$1[]_os_env_macro="FREEBSD";
;;
*dragonfly*)
app_cv_[]$1[]_os_env="dragonfly";
app_cv_[]$1[]_os_env_macro="DRAGONFLY";
;;
*openbsd*)
app_cv_[]$1[]_os_env="openbsd";
app_cv_[]$1[]_os_env_macro="OPENBSD";
;;
*netbsd*)
app_cv_[]$1[]_os_env="netbsd";
app_cv_[]$1[]_os_env_macro="NETBSD";
;;
*solaris*)
app_cv_[]$1[]_os_env="solaris";
app_cv_[]$1[]_os_env_macro="SOLARIS";
;;
*darwin*)
app_cv_[]$1[]_os_env="darwin";
app_cv_[]$1[]_os_env_macro="DARWIN";
;;
*qnx*)
app_cv_[]$1[]_os_env="qnx";
app_cv_[]$1[]_os_env_macro="QNX";
;;
*)
app_cv_[]$1[]_os_env="unknown";
app_cv_[]$1[]_os_env_macro="UNKNOWN";
;;
esac
# Assign this even if not using flags - we may use this in configure itself.
if test "x$2" != "x"; then
app_cv_[]$1[]_os_env_macro="$2[]_${app_cv_[]$1[]_os_env_macro}";
fi
# config.h macros (yes, we need this many. otherwise config.h will be incorrect).
if test "x${app_cv_[]$1[]_os_env}" = "xcygwin"; then
AC_DEFINE($2[]CYGWIN, 1, [$1 OS userspace environment])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xmingw"; then
AC_DEFINE($2[]MINGW, 1, [$1 OS userspace environment])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xgnu"; then
AC_DEFINE($2[]GNU, 1, [$1 OS userspace environment])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xfreebsd"; then
AC_DEFINE($2[]FREEBSD, 1, [$1 OS userspace environment])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xdragonfly"; then
AC_DEFINE($2[]DRAGONFLY, 1, [$1 OS userspace environment])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xopenbsd"; then
AC_DEFINE($2[]OPENBSD, 1, [$1 OS userspace environment])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xnetbsd"; then
AC_DEFINE($2[]NETBSD, 1, [$1 OS userspace environment])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xsolaris"; then
AC_DEFINE($2[]SOLARIS, 1, [$1 OS userspace environment])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xdarwin"; then
AC_DEFINE($2[]DARWIN, 1, [$1 OS userspace environment])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xqnx"; then
AC_DEFINE($2[]QNX, 1, [$1 OS userspace environment])
fi
if test "x${app_cv_[]$1[]_os_env}" = "xunknown"; then
AC_DEFINE($2[]UNKNOWN, 1, [$1 OS userspace environment])
fi
# Export to Makefile.am's.
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_CYGWIN, [test "x${app_cv_[]$1[]_os_env}" = "xcygwin"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_MINGW, [test "x${app_cv_[]$1[]_os_env}" = "xmingw"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_GNU, [test "x${app_cv_[]$1[]_os_env}" = "xgnu"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_FREEBSD, [test "x${app_cv_[]$1[]_os_env}" = "xfreebsd"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_DRAGONFLY, [test "x${app_cv_[]$1[]_os_env}" = "xdragonfly"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_OPENBSD, [test "x${app_cv_[]$1[]_os_env}" = "xopenbsd"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_NETBSD, [test "x${app_cv_[]$1[]_os_env}" = "xnetbsd"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_SOLARIS, [test "x${app_cv_[]$1[]_os_env}" = "xsolaris"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_DARWIN, [test "x${app_cv_[]$1[]_os_env}" = "xdarwin"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_QNX, [test "x${app_cv_[]$1[]_os_env}" = "xqnx"])
AM_CONDITIONAL(m4_toupper($1)[]_OS_ENV_UNKNOWN, [test "x${app_cv_[]$1[]_os_env}" = "xunknown"])
AC_MSG_RESULT([${app_cv_[]$1[]_os_env}])
])
+171
View File
@@ -0,0 +1,171 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# APP_GET_MT_FLAGS(flags_prefix[, action-if-not-found])
# This macro detects compiler flags which are needed to compile a multi-threaded
# (possibly pthreads-based) applications on target OS. flags_prefixCFLAGS,
# flags_prefixCXXFLAGS and flags_prefixLIBS will be initialized (or appended to)
# with the correct flags.
# You must call AX_COMPILER_VENDOR, APP_DETECT_OS_KERNEL([target], ...)
# and APP_DETECT_OS_ENV([target], ...) before using this macro.
AC_DEFUN([APP_GET_MT_FLAGS], [
# These requirements check compile-time presence, not that they were called
AC_REQUIRE([AX_COMPILER_VENDOR])
AC_REQUIRE([APP_DETECT_OS_KERNEL])
AC_REQUIRE([APP_DETECT_OS_ENV])
AC_MSG_CHECKING([for target thread support flags])
app_cv_target_thread_cflags="";
app_cv_target_thread_cxxflags="";
app_cv_target_thread_libs="";
app_cv_target_thread_found="no";
# If vendor is not recognized, it's set to "unknown".
# NOTE: This works only on target compilers!
if test "x${ax_cv_cxx_compiler_vendor}" = "x"; then
# msg_result is needed to print the checking... message.
AC_MSG_RESULT([error])
AC_MSG_ERROR([[ax_cv_cxx_compiler_vendor] not set.])
fi
if test "x${app_cv_target_os_kernel}" = "x"; then
# msg_result is needed to print the checking... message.
AC_MSG_RESULT([error])
AC_MSG_ERROR([[app_cv_target_os_kernel] not set.])
fi
if test "x${app_cv_target_os_env}" = "x"; then
# msg_result is needed to print the checking... message.
AC_MSG_RESULT([error])
AC_MSG_ERROR([[app_cv_target_os_env] not set.])
fi
# Compilers use at least 3 defines to indicate thread-safe support to standard libs:
# _THREAD_SAFE, _MT, _REENTRANT.
# We use the ones that are required for detected platform.
case "$app_cv_target_os_kernel" in
linux)
# glibc manual says that defining either _REENTRANT or _THREAD_SAFE
# enables some thread-safe implementations of glibc functions.
if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "intel" \
|| test "$ax_cv_cxx_compiler_vendor" = "pathscale"; then
app_cv_target_thread_cflags="-pthread -D_MT -D_THREAD_SAFE"
app_cv_target_thread_cxxflags="-pthread -D_MT -D_THREAD_SAFE"
app_cv_target_thread_libs="-pthread"
app_cv_target_thread_found="yes";
elif test "$ax_cv_cxx_compiler_vendor" = "sun"; then
# suncc needs -lpthread on posix but not solaris.
# -mt defines _REENTRANT too. Don't use -lthread.
app_cv_target_thread_cflags="-mt"
app_cv_target_thread_cxxflags="-mt"
app_cv_target_thread_libs="-mt -lpthread"
app_cv_target_thread_found="yes";
fi
;;
windows)
# NOTE: Not sure about cygwin.
# NOTE: This only enables multithreaded code generation. For linking
# with pthreads-win32 you need additional flags.
# gcc man page: -mthreads: Support thread-safe exception handling on Mingw32.
# -mthreads defines -D_MT, and links with -lmingwthrd.
if test "$ax_cv_cxx_compiler_vendor" = "gnu"; then
app_cv_target_thread_cflags="-mthreads -D_THREAD_SAFE"
app_cv_target_thread_cxxflags="-mthreads -D_THREAD_SAFE"
app_cv_target_thread_libs="-mthreads"
app_cv_target_thread_found="yes"
fi
;;
freebsd | dragonfly | netbsd | openbsd)
# NOTE: Not sure about debian gnu/kfreebsd and gnu/netbsd.
# Freebsd had -kthread, but it has been discontinued (afaik).
# For freebsd -pthread already replaces libc with libc_r.
if test "$ax_cv_cxx_compiler_vendor" = "gnu"; then
app_cv_target_thread_cflags="-pthread -D_MT -D_THREAD_SAFE"
app_cv_target_thread_cxxflags="-pthread -D_MT -D_THREAD_SAFE"
app_cv_target_thread_libs="-pthread"
app_cv_target_thread_found="yes";
fi
;;
solaris)
if test "$ax_cv_cxx_compiler_vendor" = "gnu"; then
# gnu documentation mentions -threads (and its aliases, -pthreads and -pthread)
app_cv_target_thread_cflags="-pthreads -D_MT -D_THREAD_SAFE"
app_cv_target_thread_cxxflags="-pthreads -D_MT -D_THREAD_SAFE"
app_cv_target_thread_libs="-pthreads"
app_cv_target_thread_found="yes"
elif test "$ax_cv_cxx_compiler_vendor" = "sun"; then
# -mt defines _REENTRANT too. Don't use -lthread.
# http://docs.sun.com/app/docs/doc/816-5175/pthreads-5?a=view
# http://docs.sun.com/app/docs/doc/816-5137/compile-74765?a=view
# There are two threading libraries, selectable via different switches.
app_cv_target_thread_cflags="-mt"
app_cv_target_thread_cxxflags="-mt"
app_cv_target_thread_libs="-mt"
app_cv_target_thread_found="yes"
fi
;;
darwin)
if test "$ax_cv_cxx_compiler_vendor" = "gnu"; then
# Darwin produces mt code by default, and -pthread is an error.
# gcc/darwin defines __APPLE__. __UNIX__ is not defined.
# -D_THREAD_SAFE
app_cv_target_thread_found="yes"
fi
;;
qnx)
if test "$ax_cv_cxx_compiler_vendor" = "gnu"; then
# afaik, gcc/qnx produces mt code by default (at least on newer qnx releases),
# so no flags are needed.
app_cv_target_thread_found="yes"
fi
;;
esac
if test "$app_cv_target_thread_found" = "no"; then
AC_MSG_WARN([Cannot detect compiler thread support. Set CFLAGS, CXXFLAGS and LIBS manually.])
m4_ifvaln([$2],[$2])dnl
else
AC_MSG_RESULT([CFLAGS: $app_cv_target_thread_cflags; CXXFLAGS: $app_cv_target_thread_cxxflags; LIBS: $app_cv_target_thread_libs])
if test "x$$1[]CFLAGS" = "x"; then
$1[]CFLAGS="$app_cv_target_thread_cflags";
else
$1[]CFLAGS="$$1[]CFLAGS $app_cv_target_thread_cflags";
fi
if test "x$$1[]CXXFLAGS" = "x"; then
$1[]CXXFLAGS="$app_cv_target_thread_cxxflags";
else
$1[]CXXFLAGS="$$1[]CXXFLAGS $app_cv_target_thread_cxxflags";
fi
if test "x$$1[]LIBS" = "x"; then
$1[]LIBS="$app_cv_target_thread_libs";
else
$1[]LIBS="$$1[]LIBS $app_cv_target_thread_libs";
fi
fi
])
+101
View File
@@ -0,0 +1,101 @@
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# APP_USE_SYSTEM_EXTENSIONS([flags_prefix])
# This macro detects compiler flags which are needed to enable
# system-specific library extensions. For example, on GNU platforms
# it's -D_GNU_SOURCE, on solaris it's -D__EXTENSIONS__, etc...
# Basically, it's the same as AC_USE_SYSTEM_EXTENSIONS,
# but uses compiler flag approach instead of config.h.
# You must call AX_COMPILER_VENDOR
# and APP_DETECT_OS_ENV([target], ...) before using this macro.
# More info:
# http://www.gnu.org/software/autoconf/manual/autoconf.html#Posix-Variants
# http://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html
# http://gcc.gnu.org/onlinedocs/libstdc++/faq.html#faq.predefined
# http://docs.sun.com/app/docs/doc/817-3946/6mjgmt4o8?l=en&a=view
AC_DEFUN([APP_USE_SYSTEM_EXTENSIONS], [
# These requirements check compile-time presence, not that they were called
AC_REQUIRE([AX_COMPILER_VENDOR])
AC_REQUIRE([APP_DETECT_OS_ENV])
AC_MSG_CHECKING([for target system extension flags])
app_cv_target_extension_cflags="";
app_cv_target_extension_cxxflags="";
app_cv_target_extension_libs="";
# If vendor is not recognized, it's set to "unknown".
# NOTE: This works only on target compilers!
if test "x${ax_cv_cxx_compiler_vendor}" = "x"; then
# msg_result is needed to print the checking... message.
AC_MSG_RESULT([error])
AC_MSG_ERROR([[ax_cv_cxx_compiler_vendor] not set.])
fi
if test "x${app_cv_target_os_kernel}" = "x"; then
# msg_result is needed to print the checking... message.
AC_MSG_RESULT([error])
AC_MSG_ERROR([[app_cv_target_os_kernel] not set.])
fi
if test "x${app_cv_target_os_env}" = "x"; then
# msg_result is needed to print the checking... message.
AC_MSG_RESULT([error])
AC_MSG_ERROR([[app_cv_target_os_env] not set.])
fi
case "$app_cv_target_os_env" in
gnu)
# glibc all-extensions macro: _GNU_SOURCE.
# gcc automatically defines it in later versions.
# suncc needs the flag explicitly.
# http://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html
app_cv_target_extension_cflags="-D_GNU_SOURCE"
app_cv_target_extension_cxxflags="-D_GNU_SOURCE"
;;
solaris)
# The problem with solaris is that it has too many incompatible
# feature test macros, and there is no enable-all macro. Autoconf
# mistakenly assumes that __EXTENSIONS__ is the one, but
# it should be defined _in addition_ to feature test macros.
# To support some posix macros, additional link objects are also required.
# http://docs.sun.com/app/docs/doc/816-5175/standards-5?a=view
;;
esac
AC_MSG_RESULT([CFLAGS: $app_cv_target_extension_cflags; CXXFLAGS: $app_cv_target_extension_cxxflags; LIBS: $app_cv_target_extension_libs])
if test "x$$1[]CFLAGS" = "x"; then
$1[]CFLAGS="$app_cv_target_extension_cflags";
else
$1[]CFLAGS="$$1[]CFLAGS $app_cv_target_extension_cflags";
fi
if test "x$$1[]CXXFLAGS" = "x"; then
$1[]CXXFLAGS="$app_cv_target_extension_cxxflags";
else
$1[]CXXFLAGS="$$1[]CXXFLAGS $app_cv_target_extension_cxxflags";
fi
if test "x$$1[]LIBS" = "x"; then
$1[]LIBS="$app_cv_target_extension_libs";
else
$1[]LIBS="$$1[]LIBS $app_cv_target_extension_libs";
fi
])
+1 -1
View File
@@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
stamp="../gsmartcontrol-`date +%F`"
echo $stamp
+161 -276
View File
@@ -1,13 +1,16 @@
AC_INIT([GSmartControl], [0.8.0rc3], [], [gsmartcontrol])
AC_INIT([GSmartControl], 0.8.2, [], [gsmartcontrol])
AC_CONFIG_SRCDIR([configure.ac])
AC_CONFIG_HEADERS([config.h])
AC_CANONICAL_TARGET
# disable portability warnings because of pattern rules in src/res.
AM_INIT_AUTOMAKE([foreign no-dist-gzip dist-bzip2 -Wall,no-portability])
AM_INIT_AUTOMAKE([foreign no-dist-gzip dist-bzip2 -Wall])
# Clear *FLAGS, LIBS, LDFLAGS if requested through configure option.
APP_AUTO_CLEAR_FLAGS()
# C compiler, C preprocessor
AC_PROG_CC
@@ -24,46 +27,51 @@ AC_PROG_CXX
#AC_LANG([C])
AC_LANG([C++])
# to be able to generate static libs
# Needed for generating static libs
AC_CHECK_TOOL(RANLIB, ranlib)
# Win32 resource file compilation
AC_CHECK_TOOL(WINDRES, windres)
# Win32 external dll stripping (already detected by autoconf)
# AC_CHECK_TOOL(STRIP, strip)
# -------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------------
# Various compiler features
# ------------- Detect compiler, OS, environment. Enable system features.
# detect compiler vendor (sets $ax_cv_cxx_compiler_vendor)
AX_COMPILER_VENDOR
# detect RTTI (defines HAVE_RTTI)
AC_CXX_RTTI
# Detect target OS kernel and userspace and export to config.h
# (also define automake conditionals).
APP_DETECT_OS_KERNEL([target], [CONFIG_KERNEL_])
APP_DETECT_OS_ENV([target], [CONFIG_OS_ENV_])
# detect exception support (defines HAVE_EXCEPTIONS)
AC_CXX_EXCEPTIONS
# Detect common (warning, debug, optimize) compiler flags and
# export them to C(XX)FLAGS, LDFLAGS.
# We don't use prefix because this way the flags will be used for tests,
# which is important for some compatibility flags (e.g. __func__ visibility, etc...).
APP_COMPILER_OPTIONS([])
# detect abi::__cxa_demangle (defines HAVE_GCC_ABI_DEMANGLE)
AX_CXX_GCC_ABI_DEMANGLE
# Thread support for known configurations.
APP_GET_MT_FLAGS([])
# detect __gnu_cxx::__verbose_terminate_handler (defines HAVE_VERBOSE_TERMINATE_HANDLER.)
AC_CXX_VERBOSE_TERMINATE_HANDLER
# Enable _GNU_SOURCE, etc...
APP_USE_SYSTEM_EXTENSIONS([])
# detect __func__ support (defines HAVE_CXX__FUNC)
AC_CXX_VAR_FUNC
# -------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------------
# Portability stuff
# arrange large file support (define _FILE_OFFSET_BITS 64, etc...)
# Arrange large file support (define _FILE_OFFSET_BITS 64, etc...)
AC_SYS_LARGEFILE
# ------------- Correct incomplete standard libraries
# define int8_t and friends if the standard doesn't have them
AC_TYPE_INT8_T
AC_TYPE_UINT8_T
@@ -79,6 +87,33 @@ AC_TYPE_UINTMAX_T
AC_TYPE_LONG_LONG_INT
AC_TYPE_UNSIGNED_LONG_LONG_INT
# ------------- Language / Standard Library checks
# detect RTTI (defines HAVE_RTTI)
AC_CXX_RTTI
# detect exception support (defines HAVE_EXCEPTIONS)
AC_CXX_EXCEPTIONS
# detect abi::__cxa_demangle (defines HAVE_GCC_ABI_DEMANGLE)
AX_CXX_GCC_ABI_DEMANGLE
# detect __gnu_cxx::__verbose_terminate_handler (defines HAVE_VERBOSE_TERMINATE_HANDLER.)
AC_CXX_VERBOSE_TERMINATE_HANDLER
# detect __func__ support (defines HAVE_CXX___func__)
APP_CXX___FUNC__
# detect __FUNCTION__ support (defines HAVE_CXX___FUNCTION__)
APP_CXX___FUNCTION__
# detect if the compiler accepts extern "C" function pointer overloads
# (defines HAVE_CXX_EXTERN_C_OVERLOAD)
APP_CXX_EXTERN_C_OVERLOAD
# This may be needed for pch support
AC_CHECK_HEADERS([stdc++.h])
@@ -87,9 +122,9 @@ 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 unsupported by headers (e.g. sun studio on linux). In that case,
# but not enabled by default (e.g. by glibc feature macros). In that case,
# defining them still leads to undefined symbols at link time, so we don't do it.
# header / link check:
# Header check is the best way to determine availability:
AC_CHECK_DECLS([strtof, strtold], [], [], [[#include <stdlib.h>]])
# link check only:
# AC_CHECK_FUNCS([strtof strtold])
@@ -100,39 +135,10 @@ AC_CHECK_DECLS([strtof, strtold], [], [], [[#include <stdlib.h>]])
# -------------------------------------------------------------------------------------
# "build" - the type of system on which the package is being configured and compiled.
ac_windows_build="no"
ac_linux_build="no"
case "${host}" in
*mingw*)
ac_windows_build="yes"
;;
*linux*)
ac_linux_build="yes"
;;
esac
# Non-standard feature checks
# export to Makefile.am-s
AM_CONDITIONAL([WINDOWS_BUILD], [test "$ac_windows_build" = "yes"])
AM_CONDITIONAL([LINUX_BUILD], [test "$ac_linux_build" = "yes"])
# "host" - the type of system on which the package runs. Specifying it enables the
# cross-compilation mode. "target" defaults to this too.
ac_windows_target="no"
ac_linux_target="no"
case "${target}" in
*mingw*)
ac_windows_target="yes"
;;
*linux*)
ac_linux_target="yes"
;;
esac
# export to Makefile.am-s
AM_CONDITIONAL([WINDOWS_TARGET], [test "$ac_windows_target" = "yes"])
AM_CONDITIONAL([LINUX_TARGET], [test "$ac_linux_target" = "yes"])
# getrawpartition() for netbsd and openbsd in -lutil.
AC_CHECK_LIB(util, getrawpartition, [ LIBS="$LIBS -lutil" ])
# -------------------------------------------------------------------------------------
@@ -140,124 +146,11 @@ AM_CONDITIONAL([LINUX_TARGET], [test "$ac_linux_target" = "yes"])
# -------------------------------------------------------------------------------------
COMPILER_CXXFLAGS=""
COMPILER_LDFLAGS=""
# reset original user-supplied *FLAGS
RESET_ORIGINAL="no"
AC_ARG_ENABLE(default-gcc-options,
AS_HELP_STRING([--enable-default-gcc-options],[enable default compilation options (warnings, mostly) (GCC only) (default: disabled)]),
[default_gcc_options=${enableval}], [default_gcc_options=no])
if test "$default_gcc_options" = "yes"; then
if test "$ac_windows_target" = "yes"; then
COMPILER_CXXFLAGS="$COMPILER_CXXFLAGS -mms-bitfields -mwindows"
COMPILER_LDFLAGS="$COMPILER_CXXFLAGS -mms-bitfields"
fi
COMPILER_CXXFLAGS="$COMPILER_CXXFLAGS \
-Wall -Wcast-align -Wcast-qual -Wconversion \
-Wctor-dtor-privacy -Wfloat-equal -Wnon-virtual-dtor -Woverloaded-virtual \
-Wpointer-arith -Wshadow -Wsign-compare -Wsign-promo -Wundef -Wwrite-strings"
RESET_ORIGINAL="yes";
fi
AC_ARG_ENABLE(gcc-debug,
AS_HELP_STRING([--enable-gcc-debug],[enable debug build (GCC only) (default: disabled)]),
[gcc_debug=${enableval}], [gcc_debug=no])
if test "$gcc_debug" = "yes"; then
COMPILER_CXXFLAGS="$COMPILER_CXXFLAGS -g3 -O0 -DDEBUG -DDEBUG_BUILD"
COMPILER_LDFLAGS="$COMPILER_LDFLAGS -g3 -O0"
RESET_ORIGINAL="yes";
fi
AC_ARG_ENABLE(gcc-optimize,
AS_HELP_STRING([--enable-gcc-optimize],[enable optimized build (GCC only) (default: disabled)]),
[gcc_optimize=${enableval}], [gcc_optimize=no])
if test "$gcc_optimize" = "yes"; then
# Note: Do NOT put CPU flags here - they make the program non-portable.
if test "$ac_windows_target" = "yes"; then
COMPILER_CXXFLAGS="$COMPILER_CXXFLAGS -g0 -O3 -s"
COMPILER_LDFLAGS="$COMPILER_LDFLAGS -s"
else
COMPILER_CXXFLAGS="$COMPILER_CXXFLAGS -g0 -O3 -s -mtune=generic"
COMPILER_LDFLAGS="$COMPILER_LDFLAGS -s -mtune=generic"
fi
RESET_ORIGINAL="yes";
fi
AC_ARG_ENABLE(gcc_pch,
AS_HELP_STRING([--enable-gcc-pch],[enable precompiled header support (pch make target) (GCC only) (default: disabled)
(use "make gch" to speed up the compilation)]),
[gcc_pch=${enableval}], [gcc_pch=no])
if test "$gcc_pch" = "yes"; then
COMPILER_CXXFLAGS="$COMPILER_CXXFLAGS -Winvalid-pch -DENABLE_PCH"
fi
# define LINUX for linux target
if test "$ac_linux_target" = "yes"; then
COMPILER_CXXFLAGS="$COMPILER_CXXFLAGS -DLINUX"
fi
# Reset user-supplied flags if one of the above options is present.
# We append COMPILER_* counterparts in the end, to override any
# previous options.
if test "$RESET_ORIGINAL" = "yes"; then
CFLAGS=""
CXXFLAGS=""
LIBS=""
LDFLAGS=""
fi
# -------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------------
# Enable compiler thread switches for known platforms.
AC_MSG_NOTICE([checking thread support flags...])
THREAD_CFLAGS=""
THREAD_LIBS=""
THREAD_FOUND="no"
if test "$ac_windows_target" = "yes"; then
if test "$ax_cv_cxx_compiler_vendor" = "gnu"; then
THREAD_CFLAGS="-mthreads -D_THREAD_SAFE"
THREAD_LIBS="-mthreads"
THREAD_FOUND="yes"
fi
elif test "$ac_linux_target" = "yes"; then
if test "$ax_cv_cxx_compiler_vendor" = "gnu" || test "$ax_cv_cxx_compiler_vendor" = "intel"; then
THREAD_CFLAGS="-pthread -D_MT -D_THREAD_SAFE"
THREAD_LIBS="-pthread"
THREAD_FOUND="yes"
elif test "$ax_cv_cxx_compiler_vendor" = "sun"; then
THREAD_CFLAGS="-mt"
THREAD_LIBS="-mt"
THREAD_FOUND="yes"
fi
fi
if test "$THREAD_FOUND" = "no"; then
AC_MSG_NOTICE([Cannot detect compiler thread support. Set CXXFLAGS and LDFLAGS manually.])
else
AC_MSG_NOTICE([CFLAGS: $THREAD_CFLAGS; LIBS: $THREAD_LIBS])
CXXFLAGS="$CXXFLAGS $THREAD_CFLAGS"
LIBS="$LIBS $THREAD_LIBS"
fi
# enable / disable compilation of tests
AC_ARG_ENABLE([tests], AS_HELP_STRING([--enable-tests],
[enable compilation of test programs (default: no)]), [with_tests=${enableval}], [with_tests=no])
# export to automake.am-s
AM_CONDITIONAL([ENABLE_TESTS], [test "$with_tests" = "yes"])
# -------------------------------------------------------------------------------------
@@ -272,8 +165,8 @@ pkg_modules="gtkmm-2.4 >= 2.6.0 gthread-2.0 >= 2.0"
gtkmm_found="no"
PKG_CHECK_MODULES([GTKMM], [$pkg_modules], [
AC_DEFINE([ENABLE_GLIB], [], [Whether GLib is installed])
AC_DEFINE([ENABLE_GLIBMM], [], [Whether GLibmm is installed])
AC_DEFINE([ENABLE_GLIB], [1], [Whether GLib is installed])
AC_DEFINE([ENABLE_GLIBMM], [1], [Whether GLibmm is installed])
gtkmm_found="yes"
], [
AC_MSG_WARN([$GTKMM_PKG_ERRORS])
@@ -290,68 +183,15 @@ PKG_CHECK_MODULES([GTKMM], [$pkg_modules], [
CXXFLAGS="$GTKMM_CFLAGS $CXXFLAGS"
LIBS="$GTKMM_LIBS $LIBS"
# needed for in-code checks (AC_DEFINE() takes care of these above, no need of them here)
# CXXFLAGS="$CXXFLAGS -DENABLE_GLIB -DENABLE_GLIBMM"
# Detect libglade / gtkbuilder support
# -------------------------------------------------------------------------------------
APP_CHOOSE_GLD_READER([C++], [LIBGLADE_])
# -------------------------------------------------------------------------------------
gld_found=no; # equals to "gtkbuilder" or "libglademm" after tests
#if test "gtkmm_found" = "yes"; then
# Check whether we should use Gtk::Builder (gtkmm >= 2.12) or libglademm.
AC_ARG_ENABLE(libglademm, AS_HELP_STRING([--enable-libglademm],
[use libglademm instead of GtkBuilder (default: auto)]), [with_libglademm=${enableval}], [with_libglademm=auto])
# try gtkbuilder first
if test "$with_libglademm" = "no" || test "$with_libglademm" = "auto"; then
PKG_CHECK_EXISTS([gtkmm-2.4 >= 2.12.0], [gld_found=gtkbuilder])
fi
if test "$with_libglademm" = "no" && test "$gld_found" = "no"; then
AC_MSG_ERROR([GtkBuilder support not found (you need gtkmm version 2.12 or higher). Try using libglademm instead.])
fi
# libglade specified, or auto and no gtkbuilder. gtkmm2.4-compatible libglade is >= 2.4.0.
if test "$gld_found" = "no"; then
PKG_CHECK_MODULES([LIBGLADEMM], [libglademm-2.4 >= 2.4.0], [gld_found="libglademm"],
[AC_MSG_WARN([$LIBGLADEMM_PKG_ERRORS])])
fi
if test "$with_libglademm" = "yes" && test "$gld_found" = "no"; then
AC_MSG_ERROR([Libglademm not found. Try building with GtkBuilder support (you need gtkmm version 2.12 or higher).])
fi
# still not found.
if test "$gld_found" = "no"; then
AC_MSG_WARN([Neither GtkBuilder nor libglademm found. The program will be unable to compile fully.])
fi
AC_MSG_NOTICE([Glade file support through: $gld_found])
# merge libglademm flags
if test "$gld_found" = "libglademm"; then
CXXFLAGS="$LIBGLADEMM_CFLAGS $CXXFLAGS"
LIBS="$LIBGLADEMM_LIBS $LIBS"
fi
#fi
# export to automake.am-s
AM_CONDITIONAL([ENABLE_LIBGLADEMM], [test "$gld_found" = "libglademm"])
AM_CONDITIONAL([ENABLE_GTKBUILDER], [test "$gld_found" = "gtkbuilder"])
# export to config.h
if test "$gld_found" = "libglademm"; then
AC_DEFINE(ENABLE_LIBGLADEMM, 1, [Use libglademm instead of GtkBuilder])
fi
if test "$gld_found" = "gtkbuilder"; then
AC_DEFINE(ENABLE_GTKBUILDER, 1, [Use GtkBuilder instead of libglademm])
if test "$app_cv_gld_reader" = "libglade"; then
CFLAGS="$CFLAGS $LIBGLADE_CFLAGS"
CXXFLAGS="$CXXFLAGS $LIBGLADE_CXXFLAGS"
LIBS="$LIBS $LIBGLADE_LIBS"
fi
@@ -360,13 +200,27 @@ fi
# -------------------------------------------------------------------------------------
pkg_modules="libpcre"
PCRE_CFLAGS="";
PCRE_LIBS="";
PKG_CHECK_MODULES(PCRE, [$pkg_modules])
# try pkg-config first
PKG_CHECK_MODULES(PCRE, [libpcre], [pcre_found=yes], [pcre_found=no])
#CFLAGS="$PCRE_CFLAGS $CFLAGS"
CXXFLAGS="$PCRE_CFLAGS $CXXFLAGS"
LIBS="$PCRE_LIBS $LIBS"
# if not found, try pcre-config (solaris had it but not pkg-config).
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`"
else
# 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 $PCRE_CFLAGS"
CXXFLAGS="$CXXFLAGS $PCRE_CFLAGS"
LIBS="$LIBS $PCRE_LIBS"
# -------------------------------------------------------------------------------------
@@ -374,11 +228,36 @@ LIBS="$PCRE_LIBS $LIBS"
# -------------------------------------------------------------------------------------
# enable / disable compilation of tests
AC_ARG_ENABLE([tests], AS_HELP_STRING([--enable-tests],
[enable compilation of test programs (default: no)]), [with_tests=${enableval}], [with_tests=no])
# export to automake.am-s
AM_CONDITIONAL([ENABLE_TESTS], [test "$with_tests" = "yes"])
# Win32 distribution support (NSIS, zip)
AC_ARG_WITH([nsis], [AS_HELP_STRING([--with-nsis],
[specify makensis.exe location to support making NSIS packages]
[ (default: ~/".wine/drive_c/Program Files/NSIS/makensis.exe")])],
[with_nsis=${withval}], [with_nsis=auto])
AC_ARG_ENABLE([nsis-wine], [AS_HELP_STRING([--enable-nsis-wine],
[use wine to run makensis.exe when making NSIS packages (default: yes)])],
[with_nsis_wine=${enableval}], [with_nsis_wine=yes])
AC_ARG_WITH([win32-env], [AS_HELP_STRING([--with-win32-env],
[specify location of win32 dlls when making win32 packages (default: /target/bin)])],
[with_win32_env=${withval}], [win_win32_env=auto])
if test "x$with_nsis" = "xauto" || test "x$with_nsis" = "x"; then
with_nsis="~/\".wine/drive_c/Program Files/NSIS/makensis.exe\""
fi
if test "x$with_win32_env" = "xauto" || test "x$with_win32_env" = "x"; then
with_win32_env="/target/bin"
fi
NSIS_EXEC="$with_nsis";
if test "x$with_nsis_wine" != "xno"; then
NSIS_EXEC="wine $with_nsis";
fi
WIN32_ENV="$with_win32_env";
AC_SUBST(NSIS_EXEC)
AC_SUBST(WIN32_ENV)
# -------------------------------------------------------------------------------------
@@ -401,7 +280,7 @@ RES_DIST="" # only files in "res" directory, EXTRA_DIST
RES_TARGETS="" # entry for each file in makefile
RES_GLD_EXT="glade"
if test "$gld_found" = "gtkbuilder"; then
if test "$app_cv_gld_reader" = "gtkbuilder"; then
RES_GLD_EXT="ui"
fi
@@ -468,7 +347,8 @@ _AM_SUBST_NOTMAKE([RES_TARGETS])
# -------------------------------------------------------------------------------------
# Export some configure variables as defines for the source code
# Export some configure variables as defines for the source code.
# Note: Autoconf manual says that AC_DEFINE won't work here.
ADDITIONAL_FLAGS="-DPACKAGE_PKGDATA_DIR=\"\\\"\$(pkgdatadir)\\\"\" \
-DPACKAGE_SYSCONF_DIR=\"\\\"\$(sysconfdir)\\\"\" \
@@ -478,10 +358,6 @@ ADDITIONAL_FLAGS="-DPACKAGE_PKGDATA_DIR=\"\\\"\$(pkgdatadir)\\\"\" \
CXXFLAGS="$CXXFLAGS $ADDITIONAL_FLAGS"
# -------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------------
# Top src dir should always be in include path, before other includes.
# Add global configuration file. Note: It must be searched in top_builddir
# first (that's why -I is the first flag), for precompiled headers to work.
@@ -489,15 +365,20 @@ CXXFLAGS="$CXXFLAGS $ADDITIONAL_FLAGS"
# Add pcrecpp too, because its files use #<file> on each other.
ADDITIONAL_INCLUDES="-I\$(top_builddir) -I\$(top_srcdir)/src -I\$(top_srcdir)/src/pcrecpp"
# -include doesn't work with sun and pgi, but works with intel and gcc.
# we don't whitelist intel/gcc, because there are some gcc-frontend-based
# compilers we don't want to break (pathscale, gccfss (gcc frontend / sun backend), etc...).
if test "$ax_cv_cxx_compiler_vendor" != "sun" && test "$ax_cv_cxx_compiler_vendor" != "portland"; then
# -include works with gcc, intel and pathscale, but doesn't work with sun and pgi.
# Note: some gcc-frontend-based compilers may have to be added here
# (e.g. gccfss (gcc frontend / sun backend), etc...).
if test "$ax_cv_cxx_compiler_vendor" = "gnu" || 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"
else # sun, pgi, others...
# This forces the file to be included manually.
ADDITIONAL_INCLUDES="$ADDITIONAL_INCLUDES -DHZ_NO_COMPILER_AUTOINCLUDE"
fi
#CFLAGS="$ADDITIONAL_INCLUDES $CFLAGS"
# our flags must be before all the others
#CFLAGS="$ADDITIONAL_INCLUDES $CFLAGS"
CXXFLAGS="$ADDITIONAL_INCLUDES $CXXFLAGS"
@@ -506,23 +387,12 @@ CXXFLAGS="$ADDITIONAL_INCLUDES $CXXFLAGS"
# -------------------------------------------------------------------------------------
# Append compiler flags to CXXFLAGS, so that they override
# whatever the user has set.
CXXFLAGS="$CXXFLAGS $COMPILER_CXXFLAGS"
LIBS="$LIBS $COMPILER_LDFLAGS"
# final setup
# This macro should be defined by ./configure automatically.
# However, sometimes it's not (e.g. win32, why?), so we define it manually.
CXXFLAGS="$CXXFLAGS -DHAVE_CONFIG_H"
# -------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------------
# final setup
#CFLAGS="$CFLAGS $USER_INCLUDES"
#CXXFLAGS="$CXXFLAGS $USER_INCLUDES"
#LIBS="$LIBS $USER_LDFLAGS"
@@ -535,6 +405,8 @@ AC_SUBST(LIBS)
AC_SUBST(LDFLAGS)
# -------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------------
@@ -551,11 +423,13 @@ AC_SUBST(LDFLAGS)
# print'em out for debug purposes
# print them out for debug purposes
AC_MSG_NOTICE([
CXXFLAGS:
$CXXFLAGS
LDFLAGS:
$LDFLAGS
LIBS:
$LIBS
])
@@ -566,19 +440,30 @@ $LIBS
AC_SUBST(AUTODIRS)
# ----------------------------------------------------
# -------------------------------------------------------------------------------------
AC_CONFIG_FILES([ data/gsmartcontrol_gnome.desktop data/gsmartcontrol_kde.desktop \
data/gsmartcontrol_other.desktop \
# -------------------------------------------------------------------------------------
AC_CONFIG_FILES([ data/gsmartcontrol.desktop \
data/nsis/distribution.txt data/nsis/gsmartcontrol.nsi \
debian.dist/changelog \
src/gsc_winres.rc \
gsmartcontrol.spec version compilation_flags ])
AC_CONFIG_FILES([data/gsmartcontrol-root], [chmod +x data/gsmartcontrol-root])
# these are all the makefiles to generate
AC_CONFIG_FILES([Makefile src/Makefile src/applib/Makefile src/res/Makefile src/hz/Makefile \
src/libdebug/Makefile src/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/48/Makefile data/64/Makefile data/128/Makefile data/256/Makefile data/nsis/Makefile \
debian.dist/Makefile])
AC_OUTPUT
+4
View File
@@ -0,0 +1,4 @@
appicondir = $(datadir)/icons/hicolor/256x256/apps
appicon_DATA = gsmartcontrol.png
EXTRA_DIST = $(appicon_DATA)
Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

+15 -7
View File
@@ -1,4 +1,5 @@
SUBDIRS = 16 22 24 32 48 64 128
SUBDIRS = 16 22 24 32 48 64 128 256 nsis
gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor
@@ -14,7 +15,7 @@ update-icon-cache:
fi
# installs to /usr/share/appname
# Application data, installs to /usr/share/appname
appdatadir = $(pkgdatadir)
appdata_DATA = icon_cddvd.png icon_hdd.png
@@ -22,15 +23,22 @@ appdata_DATA = icon_cddvd.png icon_hdd.png
# pkgdatadir = $(datadir)/gsmartcontrol
# pkgdata_DATA =
# Desktop files
desktopdir = $(datadir)/applications
desktop_DATA = gsmartcontrol_gnome.desktop gsmartcontrol_kde.desktop \
gsmartcontrol_other.desktop
desktop_DATA = gsmartcontrol.desktop
# Same as 48/gsmartcontrol.png
# Application pixmap (fallback icon for desktop files).
# Same as 48/gsmartcontrol.png.
# Also, xpm for debian menu, 32x32.
pixmapsdir = $(datadir)/pixmaps
pixmaps_DATA = 48/gsmartcontrol.png
pixmaps_DATA = 48/gsmartcontrol.png gsmartcontrol.xpm
EXTRA_DIST = $(appdata_DATA) $(pixmaps_DATA)
# Put everything inside the distribution
EXTRA_DIST = $(appdata_DATA) gsmartcontrol.ico gsmartcontrol.xpm
bin_SCRIPTS = gsmartcontrol-root
CLEANFILES = $(bin_SCRIPTS)
+111
View File
@@ -0,0 +1,111 @@
#!/bin/bash
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# Run gsmartcontrol with root, asking for root password first.
EXEC_BIN="@prefix@/bin/gsmartcontrol";
prog_name="gsmartcontrol"
DESKTOP="$1";
# This works for --help too
if [ "$DESKTOP" = "" ]; then
DESKTOP="auto"; # default
elif [ "$DESKTOP" != "auto" ] && [ "$DESKTOP" != "kde" ] && \
[ "$DESKTOP" != "gnome" ] && [ "$DESKTOP" != "other" ]; then
echo "Usage: $0 [<auto|kde|gnome|other> [<${prog_name}_options>] ]";
exit 1;
fi
shift; # remove $1
# Auto-detect current desktop if auto was specified.
if [ "$DESKTOP" = "auto" ]; then
# KDE_SESSION_UID is present on kde3 and kde4.
# Note that it may be empty (but still set)
if [ "${KDE_SESSION_UID+set}" = "set" ]; then
DESKTOP="kde";
# same with gnome
elif [ "${GNOME_DESKTOP_SESSION_ID+set}" = "set" ]; then
DESKTOP="gnome";
else
DESKTOP="other";
fi
fi
# echo $DESKTOP;
# They're basically the same, only the order is different.
# sux requires xterm to ask for the password.
# xdg-su is basically like this script, except worse :)
gnome_sus="gnomesu gksu kdesu xdg-su sux";
kde_sus="kdesu gnomesu gksu xdg-su sux";
other_sus="$gnome_sus";
candidates="";
found_su=""
if [ "$DESKTOP" = "gnome" ]; then
candidates="$gnome_sus";
elif [ "$DESKTOP" = "kde" ]; then
candidates="$kde_sus";
elif [ "$DESKTOP" = "other" ]; then
candidates="$other_sus";
fi
for subin in $candidates; do
which $subin &>/dev/null
if [ $? -eq 0 ]; then
found_su="$subin";
break;
fi
done
if [ "$found_su" = "" ]; then
xmessage "Error launching ${prog_name}: No suitable su mechanism found.
Try installing kdesu, gnomesu, gksu or sux first.";
exit 1;
fi
# gnomesu and gksu (but not kdesu, not sure about others) fail to adopt
# root's PATH. Since the user's PATH may not contain /usr/sbin (with smartctl)
# on some distributions (e.g. mandriva), add it manually. We also add
# /usr/local/sbin, since that's the default location of custom-compiled smartctl.
# Add these directories _before_ existing PATH, so that the user is not
# tricked into running it from some other path (we're running as root with
# the user's env after all).
export PATH="/usr/sbin:/usr/local/sbin:$PATH"
# echo $found_su;
# Examples:
# gnomesu -c 'gsmartcontrol --no-scan'
# kdesu -c 'gsmartcontrol --no-scan'
# xterm -e sux -c 'gsmartcontrol --no-scan' # sux asks for password in a terminal
full_cmd=""
if [ "$found_su" = "sux" ]; then
full_cmd="xterm -e sux -c '$EXEC_BIN $@'"
elif [ "$found_su" = "gksu" ]; then
full_cmd="$found_su '$EXEC_BIN $@'"
else # gnomesu, kdesu, xdg-su
full_cmd="$found_su -c '$EXEC_BIN $@'"
fi
eval $full_cmd
@@ -1,14 +1,10 @@
[Desktop Entry]
# Desktop file for kde only.
OnlyShowIn=KDE;
# desktop file version
Version=1.0
Type=Application
Categories=System;GTK;
# Categories=Settings;HardwareSettings;
Categories=System;Monitor;
Name=GSmartControl
# short description, usually shown in parentheses after Name
@@ -26,4 +22,8 @@ Icon=gsmartcontrol
#X-KDE-SubstituteUID=true
#X-KDE-RootOnly=true
Exec=kdesu "@prefix@/bin/gsmartcontrol"
# Run with root permissions.
Exec="@prefix@/bin/gsmartcontrol-root"
# Build service rpmlint gives errors without this
X-SuSE-translate=false
Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

+394
View File
@@ -0,0 +1,394 @@
/* XPM */
static char * gsmartcontrol_xpm[] = {
"32 32 359 2",
" c None",
". c #ABAAAA",
"+ c #CCCCCC",
"@ c #C5C4C4",
"# c #C2C1C1",
"$ c #D2D1D1",
"% c #C8C7C7",
"& c #C8C7C8",
"* c #C9C8C8",
"= c #CAC8C9",
"- c #CAC9C9",
"; c #C8C6C7",
"> c #C7C6C6",
", c #CFCDCE",
"' c #D1D0D0",
") c #C6C5C6",
"! c #CFCECE",
"~ c #B9B7B7",
"{ c #D4D4D4",
"] c #DCDBDC",
"^ c #D8D7D7",
"/ c #D6D5D5",
"( c #D6D5D6",
"_ c #D7D6D6",
": c #D9D8D8",
"< c #DBDADB",
"[ c #DAD9DA",
"} c #D7D7D7",
"| c #D6D6D6",
"1 c #DAD9D9",
"2 c #E2E1E1",
"3 c #B0AFAF",
"4 c #D3D2D2",
"5 c #CDCCCC",
"6 c #CDCDCD",
"7 c #CECDCD",
"8 c #CECECE",
"9 c #CFCFCF",
"0 c #D0CFCF",
"a c #C0BFBF",
"b c #9C9A9B",
"c c #6E6B6B",
"d c #807E7F",
"e c #C8C8C8",
"f c #CAC9CA",
"g c #CACACA",
"h c #CBCACA",
"i c #CCCBCB",
"j c #CCCBCC",
"k c #D0CECF",
"l c #C3C2C2",
"m c #A09E9F",
"n c #B5B4B4",
"o c #AFADAE",
"p c #787576",
"q c #8D8B8B",
"r c #D4D3D3",
"s c #C7C6C7",
"t c #C7C7C7",
"u c #C9C8C9",
"v c #C9C9C9",
"w c #CACAC9",
"x c #BFBEBE",
"y c #A3A1A2",
"z c #A09D9E",
"A c #B4B2B2",
"B c #B6B4B4",
"C c #827F80",
"D c #969595",
"E c #C4C3C3",
"F c #C4C4C4",
"G c #C5C5C5",
"H c #C6C5C5",
"I c #C6C6C6",
"J c #C9C7C8",
"K c #B9B7B8",
"L c #A5A4A4",
"M c #A4A3A3",
"N c #A7A5A5",
"O c #B3B1B2",
"P c #BFBDBD",
"Q c #8D8A8B",
"R c #A09F9F",
"S c #DDDCDC",
"T c #C0C0C0",
"U c #C3C1C2",
"V c #C3C2C3",
"W c #C3C3C3",
"X c #C4C3C4",
"Y c #B2B1B1",
"Z c #A8A7A7",
"` c #ABA9A9",
" . c #CFCECF",
".. c #C2C0C1",
"+. c #989697",
"@. c #A8A6A7",
"#. c #DEDDDD",
"$. c #DEDDDE",
"%. c #C1C0C0",
"&. c #C2C1C2",
"*. c #B1B0B0",
"=. c #ADACAC",
"-. c #AFAEAE",
";. c #BDBCBC",
">. c #B3B2B2",
",. c #B4B3B4",
"'. c #B8B8B8",
"). c #D5D4D4",
"!. c #C0BFBE",
"~. c #C1C0C1",
"{. c #C3C1C1",
"]. c #C4C4C3",
"^. c #B2B0B1",
"/. c #B3B2B3",
"(. c #B4B3B3",
"_. c #A1A0A0",
":. c #B9B8B8",
"<. c #C4C2C3",
"[. c #BDBBBC",
"}. c #B5B4B3",
"|. c #B6B4B5",
"1. c #B7B5B6",
"2. c #B7B6B7",
"3. c #B8B7B7",
"4. c #B8B7B8",
"5. c #BCBABB",
"6. c #BBBABA",
"7. c #BFBEBF",
"8. c #C1C1C1",
"9. c #C1BFC0",
"0. c #BCBBBB",
"a. c #BEBCBD",
"b. c #BEBDBD",
"c. c #9B999A",
"d. c #9D9B9B",
"e. c #C2C2C2",
"f. c #D4D3D4",
"g. c #CECDCE",
"h. c #C7C5C5",
"i. c #A4A2A3",
"j. c #A19F9F",
"k. c #C6C4C5",
"l. c #DCDCDC",
"m. c #D0CFD0",
"n. c #CECCCD",
"o. c #A2A1A1",
"p. c #939292",
"q. c #E1E0E0",
"r. c #E3E3E3",
"s. c #B3B3B3",
"t. c #CCCACB",
"u. c #D2D2D2",
"v. c #D5D3D4",
"w. c #A7A6A6",
"x. c #959494",
"y. c #E3E2E2",
"z. c #DCDBDB",
"A. c #CBC9CA",
"B. c #E1E0E1",
"C. c #DFDEDE",
"D. c #CECCCC",
"E. c #CDCCCD",
"F. c #D2D0D0",
"G. c #D2D0D1",
"H. c #B5B4B5",
"I. c #B1AFB0",
"J. c #EDECEC",
"K. c #D1CFCF",
"L. c #CDCBCC",
"M. c #D5D5D5",
"N. c #DCDADA",
"O. c #C5C4C5",
"P. c #D2D1D2",
"Q. c #D5D4D5",
"R. c #DADADA",
"S. c #DBDADA",
"T. c #DDDCDD",
"U. c #D7D6D7",
"V. c #E8E7E7",
"W. c #D8D8D8",
"X. c #D8D6D6",
"Y. c #D9D8D9",
"Z. c #DBDBDB",
"`. c #DDDDDD",
" + c #474747",
".+ c #ECEBEB",
"++ c #F3F3F3",
"@+ c #E4E3E3",
"#+ c #E0DFDF",
"$+ c #E5E5E5",
"%+ c #EFEFEF",
"&+ c #EBEAEB",
"*+ c #E6E5E5",
"=+ c #E0E0E0",
"-+ c #E4E4E4",
";+ c #E5E4E4",
">+ c #ECECEC",
",+ c #666666",
"'+ c #585757",
")+ c #D9D9DA",
"!+ c #E2E2E2",
"~+ c #E6E5E6",
"{+ c #E1E1E1",
"]+ c #E6E6E6",
"^+ c #E7E7E7",
"/+ c #898989",
"(+ c #767575",
"_+ c #E0DFE0",
":+ c #E9E9E9",
"<+ c #BDBDBD",
"[+ c #B6B6B6",
"}+ c #B0B0B0",
"|+ c #ACACAC",
"1+ c #ACABAC",
"2+ c #A7A7A7",
"3+ c #DFDFDF",
"4+ c #E5E5E4",
"5+ c #E9E8E8",
"6+ c #E8E8E8",
"7+ c #E3E2E3",
"8+ c #D7D5D5",
"9+ c #8E8D8D",
"0+ c #807F7F",
"a+ c #494848",
"b+ c #5A5758",
"c+ c #605E5F",
"d+ c #646262",
"e+ c #555354",
"f+ c #1F1F1F",
"g+ c #282626",
"h+ c #323031",
"i+ c #3D3C3C",
"j+ c #494748",
"k+ c #555353",
"l+ c #5A5959",
"m+ c #3E3D3D",
"n+ c #434242",
"o+ c #444343",
"p+ c #414041",
"q+ c #3E3E3E",
"r+ c #4A4949",
"s+ c #706D6E",
"t+ c #6B6969",
"u+ c #6A6869",
"v+ c #696469",
"w+ c #656065",
"x+ c #595757",
"y+ c #A5A5A5",
"z+ c #767676",
"A+ c #8A8888",
"B+ c #4B4849",
"C+ c #5C5A5A",
"D+ c #686565",
"E+ c #524F50",
"F+ c #000000",
"G+ c #090607",
"H+ c #171415",
"I+ c #262324",
"J+ c #363334",
"K+ c #454343",
"L+ c #4A4748",
"M+ c #212020",
"N+ c #2A292A",
"O+ c #2C2B2B",
"P+ c #272626",
"Q+ c #242424",
"R+ c #242323",
"S+ c #3C3B3B",
"T+ c #726F70",
"U+ c #6C696A",
"V+ c #6B676A",
"W+ c #7A916C",
"X+ c #7B926B",
"Y+ c #605F60",
"Z+ c #9C9B9C",
"`+ c #606060",
" @ c #8F8D8D",
".@ c #494747",
"+@ c #595657",
"@@ c #615E5F",
"#@ c #656162",
"$@ c #525050",
"%@ c #020101",
"&@ c #0C0A0A",
"*@ c #1A1818",
"=@ c #282525",
"-@ c #454243",
";@ c #494647",
">@ c #2A2929",
",@ c #393838",
"'@ c #363536",
")@ c #323131",
"!@ c #2B292A",
"~@ c #282727",
"{@ c #444243",
"]@ c #696666",
"^@ c #666268",
"/@ c #709D48",
"(@ c #7BAD4A",
"_@ c #68686A",
":@ c #8F8E8F",
"<@ c #4E4D4E",
"[@ c #8E8C8D",
"}@ c #484546",
"|@ c #555253",
"1@ c #5D595A",
"2@ c #5F5B5C",
"3@ c #514F50",
"4@ c #0F0D0D",
"5@ c #211E1F",
"6@ c #312E2F",
"7@ c #413D3E",
"8@ c #222121",
"9@ c #2E2D2D",
"0@ c #312F30",
"a@ c #262425",
"b@ c #464344",
"c@ c #696667",
"d@ c #666263",
"e@ c #5F5E5F",
"f@ c #5A5B5B",
"g@ c #797577",
"h@ c #7A7A7A",
"i@ c #4C4A4A",
"j@ c #2A2728",
"k@ c #2C292A",
"l@ c #302D2E",
"m@ c #312F2F",
"n@ c #2E2C2C",
"o@ c #100F0F",
"p@ c #110F10",
"q@ c #141212",
"r@ c #1B1919",
"s@ c #242222",
"t@ c #2D2B2B",
"u@ c #322F30",
"v@ c #2F2D2D",
"w@ c #2B2829",
"x@ c #292728",
"y@ c #363233",
"z@ c #332F30",
"A@ c #312D2F",
"B@ c #2A2627",
"C@ c #424040",
"D@ c #454444",
"E@ c #737373",
"F@ c #828282",
"G@ c #939293",
"H@ c #9C9C9C",
"I@ c #A3A3A3",
"J@ c #A9A9A9",
"K@ c #ADADAD",
"L@ c #B2B2B2",
"M@ c #B5B5B5",
"N@ c #A4A4A4",
"O@ c #959595",
"P@ c #8C8C8C",
"Q@ c #707070",
"R@ c #FFFFFF",
" ",
" . + @ # $ % & * = - ; > * - - - * , ' ) ! ~ ",
" = { ' ] ^ { / / ( _ : < [ ^ ^ } ^ | 1 2 } 3 ",
" - 4 ' ' 5 6 6 6 7 7 8 9 ! ! 9 9 0 0 $ 5 a b c ",
" d 4 [ * e - - f g h h h i i j + 5 5 k l m n o p ",
" q _ r @ > > s s t % % * * u v - w 5 x y z A B C ",
" D 1 7 l E F @ @ G H H I > > > J * K L M N O P Q ",
" R S % T U U l V W E X F @ @ > E Y Z ` > .* ..+. ",
" @.#.$.W %.# # # &.l l V E G T *.=.-.*.5 ;.>.,.Z ",
" n '.).[ !.T ~.# ..{.U l ].x Y ^.>.A /.(.x ;.O _. ",
" :.^.<.h a %.%.# # # &.U [.}.|.1.2.3.4.K K a 5.N ",
" 6.i H 7.%.8.# &.# l * % E 9.[.5.0.[.;.a.b.;., %.c. ",
" d.> 0 a # &.e.V V % ^ f.j g - % %.....&.l V l g.h.i. ",
" j./ i # E E @ k.H l.g.%.# l E H <.b.; > % % % m.n.o. ",
" p.q.7 @ ) > % % - r.) > % * * g f s.5 t.i i i u.v.w. ",
" x.y.z.> u - g A.- B.C.D.E.! 0 7 (.6.$ ' ' F.G.).^ H. ",
" I.7 J.K.L.7 7 ! ! m.S 1 m.- %.3.8./ ).M./ | _ : N.; ",
" O., F.f.P.4 4 4 f.f.r / Q.4 4 ^ R.: : 1 [ S.< S T.U. ",
" 4 V.| ).| _ W._ X.^ : Y.1 S.Z.z.] S S `.#.C.C.2 V.q. + ",
" S..+++@+`.#+$+%+&+*+2 #+=+q.y.-+-+-+;+$+$+*+>+++Z.*+,+ ",
" '+S.# $ >+r.m.e.# 6 )+!+~+*+@+B.=+{+-+]+]+^+]+^+j 5 y./+ ",
" (+b.3 _+! m.#+:+q.r > <+[+}+|+3 '.b.8.> e 5 7 ! ^+j 1+2+ ",
" x.] ! r / ^ W.R.S 3+2 y.@+4+5+6+^+~+*+;+7+#+C.#.S.8+: V ",
" 9+0+a+b+c+d+e+f+g+h+i+j+k+l+m+n+o+n+p+q+r+s+t+u+v+w+x+y+ ",
" z+A+B+C+d+D+E+F+G+H+I+J+K+L+M+N+O+P+Q+R+S+T+U+V+W+X+Y+Z+ ",
" `+ @.@+@@@#@$@%@&@*@=@J+-@;@>@,@'@)@!@~@{@c ]@^@/@(@_@:@ ",
" <@[@}@|@1@2@3@F+%@4@5@6@7@-@8@9@9@0@~@a@b@c@#@d@e@f@g@h@ ",
" i@j@k@l@m@n@o@p@q@r@s@t@u@!@l@v@v@w@x@l@y@z@6@A@B@C@D@ ",
" E@F@G@H@I@J@K@}+L@M@M@M@s.}+|+N@H@O@P@F@Q@ ",
" R@R@R@R@R@R@R@R@R@ ",
" ",
" "};
-26
View File
@@ -1,26 +0,0 @@
[Desktop Entry]
# Desktop file for gnome only.
OnlyShowIn=GNOME;
# desktop file version
Version=1.0
Type=Application
Categories=System;GTK;
# Categories=Settings;HardwareSettings;
Name=GSmartControl
# short description, usually shown in parentheses after Name
GenericName=Hard Disk Health Inspection
# tooltip
Comment=Monitor and control SMART data on hard disks
# If it's a name only, it looks for "name.[png|xpm]" file in
# $XDG_DATA_DIRS/icons and /usr/share/pixmaps.
# An absolute file path is also supported.
Icon=gsmartcontrol
# Run with root permissions. Some desktop files used gksu,
# but it doesn't seem available on my system.
Exec=gnomesu -c "@prefix@/bin/gsmartcontrol"
-28
View File
@@ -1,28 +0,0 @@
[Desktop Entry]
# Desktop file for non-gnome and non-kde only.
NotShowIn=GNOME;KDE;
# desktop file version
Version=1.0
Type=Application
Categories=System;GTK;
# Categories=Settings;HardwareSettings;
Name=GSmartControl
# short description, usually shown in parentheses after Name
GenericName=Hard Disk Health Inspection
# tooltip
Comment=Monitor and control SMART data on hard disks
# If it's a name only, it looks for "name.[png|xpm]" file in
# $XDG_DATA_DIRS/icons and /usr/share/pixmaps.
# An absolute file path is also supported.
Icon=gsmartcontrol
# Run with root permissions using gnomesu (from libgnomesu).
# There is also kdesu (kdebase3-runtime, kdebase4-runtime), in different paths.
# There is also xdg-su, which doesn't work at all on my machine.
Exec=gnomesu -c "@prefix@/bin/gsmartcontrol"
# Exec=kdesu -u root "@prefix@/bin/gsmartcontrol"
+2
View File
@@ -0,0 +1,2 @@
EXTRA_DIST = distribution.txt.in gsmartcontrol.nsi.in nsi_install.ico nsi_uninstall.ico
+28
View File
@@ -0,0 +1,28 @@
GSmartControl - Hard disk drive 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.berlios.de
and
http://developer.berlios.de/project/showfiles.php?group_id=10075
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.14.4-2008-11-11
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.
* Gtkmm (http://www.gtkmm.org/) dlls.
* PCRE (http://www.pcre.org/) dll.
* Smartctl binaries - smartctl.exe (compiled without console output),
smartctl_console.exe (compiled with console output), version 5.38.
http://smartmontools.sf.net/ .
+293
View File
@@ -0,0 +1,293 @@
; NSIS2 Script
; Compatible with NSIS 2.40
!define PRODUCT_VERSION "@VERSION@"
!define PRODUCT_NAME "GSmartControl"
!define PRODUCT_NAME_SMALL "gsmartcontrol"
!define PRODUCT_PUBLISHER "Alexander Shaduri"
!define PRODUCT_WEB_SITE "http://gsmartcontrol.berlios.de"
;!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"
; --------------- General Settings
; This compressor gives us the best results
SetCompressor /SOLID lzma
; Do a CRC check before installing
CRCCheck On
; This is used in titles
Name "${PRODUCT_NAME}" ; ${PRODUCT_VERSION}
; Output File Name
OutFile "${PRODUCT_NAME_SMALL}-${PRODUCT_VERSION}.exe"
;The Default Installation Directory
; try to install to the same directory as runtime.
InstallDir "$PROGRAMFILES\${PRODUCT_NAME_SMALL}"
; If already installed, try here
InstallDirRegKey HKLM "SOFTWARE\${PRODUCT_NAME}" "InstallationDirectory"
ShowInstDetails show
ShowUnInstDetails show
; --------------------- MUI INTERFACE
; MUI 2.0 compatible install
!include "MUI2.nsh"
;Backgound Colors. uncomment to enable fullscreen.
; BGGradient 0000FF 000000 FFFFFF
; MUI Settings
!define MUI_ABORTWARNING
!define MUI_ICON "nsi_install.ico"
!define MUI_UNICON "nsi_uninstall.ico"
;Things that need to be extracted on first (keep these lines before any File command!)
;Only useful for BZIP2 compression
!insertmacro MUI_RESERVEFILE_LANGDLL
;!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS
; Language Selection Dialog Settings
!define MUI_LANGDLL_REGISTRY_ROOT "${PRODUCT_UNINST_ROOT_KEY}"
!define MUI_LANGDLL_REGISTRY_KEY "${PRODUCT_UNINST_KEY}"
!define MUI_LANGDLL_REGISTRY_VALUENAME "NSIS:Language"
; Pages to show during installation
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "doc\distribution.txt"
;!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
;!define MUI_FINISHPAGE_RUN "$INSTDIR\gsmartcontrol.exe"
;!define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\Example.file"
;!define MUI_FINISHPAGE_RUN_NOTCHECKED
!define MUI_FINISHPAGE_NOAUTOCLOSE
!define MUI_FINISHPAGE_NOREBOOTSUPPORT
!insertmacro MUI_PAGE_FINISH
; Uninstaller page
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
; Language files
!insertmacro MUI_LANGUAGE "English"
; --------------- END MUI
LangString TEXT_IO_TITLE ${LANG_ENGLISH} "GSmartControl"
; --------------- START INSTALLATION
Function .onInit
!insertmacro MUI_LANGDLL_DISPLAY
Push 2 ;F ;22 = number of languages, F = change font
;Language selection
LangDLL::LangDialog "Installer Language" "Please select a language."
Pop $LANGUAGE
StrCmp $LANGUAGE "cancel" 0 +2
Abort
; detect previous installation
ReadRegStr $R0 HKLM "${PRODUCT_UNINST_KEY}" "UninstallString"
StrCmp $R0 "" no_prev
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \
"${PRODUCT_NAME} is already installed. $\n$\nClick 'OK' to remove the \
previous version or 'Cancel' to continue anyway." \
IDOK uninst_product
; Abort
goto no_prev ; if cancel was pressed, continue as if no old installation.
;Run the uninstaller
uninst_product:
ClearErrors
ExecWait '$R0 _?=$INSTDIR' ;Do not copy the uninstaller to a temp file
IfErrors cannot_uninst_product
;You can either use Delete /REBOOTOK in the uninstaller or add some code
;here to remove the uninstaller. Use a registry key to check
;whether the user has chosen to uninstall. If you are using an uninstaller
;components page, make sure all sections are uninstalled.
cannot_uninst_product:
no_prev: ; no old installation found, all ok
; detect GTK installation (any of available versions)
ReadRegStr $R0 HKLM "SOFTWARE\GTK\2.0" "DllPath"
StrCmp $R0 "" no_gtk
goto 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." \
IDOK have_gtk
Abort
have_gtk:
; do nothing
FunctionEnd
; ----------------- INSTALLATINO TYPES
; InstType "Recommended"
InstType "Full"
;InstType "Minimal"
Section "Main" SecMain
SectionIn 1
SetOutPath "$INSTDIR"
SetOverwrite Off ; don't overwrite the config file
; File "gsmartcontrol.conf"
SetOverwrite On
File gsmartcontrol.exe
File gsmartcontrol.ico
File icon_*.png
File *.dll
File smartctl.exe
File smartctl_console.exe
File /r doc
; set a special path for this exe, as GTK may not be in a global path.
ReadRegStr $R3 HKLM "SOFTWARE\GTK\2.0" "DllPath"
WriteRegStr HKLM "${REGISTRY_APP_PATHS}\gsmartcontrol.exe" "Path" "$R3"
;Add Shortcuts
CreateShortCut "$SMPROGRAMS\GSmartControl.lnk" "$INSTDIR\gsmartcontrol.exe" "" "$INSTDIR\gsmartcontrol.ico" 0
SectionEnd
; ------------------ POST INSTALL
!define TEMP $R0
Section -post
WriteRegStr HKLM "SOFTWARE\${PRODUCT_NAME}" "InstallationDirectory" "$INSTDIR"
WriteRegStr HKLM "SOFTWARE\${PRODUCT_NAME}" "Vendor" "Alexander Shaduri"
WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "DisplayName" "${PRODUCT_NAME}"
WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\gsmartcontrol_uninst.exe"
; We don't need this, MUI takes care for us
WriteRegStr HKCU "Software\${PRODUCT_NAME}" "Installer Language" $LANGUAGE
; uninstall shortcut
; CreateDirectory "$SMPROGRAMS\GSmartControl"
; CreateShortCut "$SMPROGRAMS\GSmartControl\Uninstall GSmartControl.lnk" "$INSTDIR\gsmartcontrol_uninst.exe" "" ""
; write out uninstaller
WriteUninstaller "$INSTDIR\gsmartcontrol_uninst.exe"
SectionEnd ; post
; ---------------- UNINSTALL
Function un.onUninstSuccess
HideWindow
MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) was successfully removed from your computer."
FunctionEnd
Section Uninstall
SetAutoClose false
; add delete commands to delete whatever files/registry keys/etc you installed here.
Delete "$INSTDIR\gsmartcontrol_uninst.exe"
DeleteRegKey HKLM "SOFTWARE\${PRODUCT_NAME}"
DeleteRegKey HKLM "${PRODUCT_UNINST_KEY}"
Delete "$INSTDIR\gsmartcontrol.exe"
Delete "$INSTDIR\gsmartcontrol.ico"
Delete "$INSTDIR\icon_*.png"
Delete "$INSTDIR\*.dll"
Delete "$INSTDIR\smartctl.exe"
Delete "$INSTDIR\smartctl_console.exe"
RMDir /r "$INSTDIR\doc"
RMDir "$INSTDIR" ; only if empty
DeleteRegKey HKLM "${REGISTRY_APP_PATHS}\gsmartcontrol.exe"
Delete "$SMPROGRAMS\GSmartControl.lnk"
; Delete "$SMPROGRAMS\GSmartControl\Uninstall GSmartControl.lnk"
SectionEnd ; end of uninstall section
; eof
Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

+7
View File
@@ -0,0 +1,7 @@
noinst_DATA = changelog.in compat control copyright docs menu postinst \
postrm rules
# include the generated file too
EXTRA_DIST=changelog.in changelog compat control copyright docs menu postinst \
postrm rules
+5
View File
@@ -0,0 +1,5 @@
gsmartcontrol (0.8.2+nmu1) unstable; urgency=low
* Permanently Initial Release.
-- Alexander Shaduri <ashaduriREMOVETHIS@gmail.com> Sat, 15 Nov 2008 00:12:04 +0400
+5
View File
@@ -0,0 +1,5 @@
gsmartcontrol (@VERSION@+nmu1) unstable; urgency=low
* Permanently Initial Release.
-- Alexander Shaduri <ashaduriREMOVETHIS@gmail.com> Sat, 15 Nov 2008 00:12:04 +0400
+1
View File
@@ -0,0 +1 @@
5
+22
View File
@@ -0,0 +1,22 @@
Source: gsmartcontrol
Section: utils
Priority: extra
Homepage: http://gsmartcontrol.berlios.de/
Maintainer: Alexander Shaduri <ashaduriREMOVETHIS@gmail.com>
# technically, we don't need libglademm if gtkmm >= 2.12, but it won't get
# into Depends if it's not needed, so let's leave it here.
Build-Depends: debhelper (>= 5), autotools-dev,
libpcre3-dev, libgtkmm-2.4-dev (>= 2.6.0), libglademm-2.4-dev
Standards-Version: 3.7.3
Package: gsmartcontrol
Architecture: any
#Depends: ${shlibs:Depends}, ${misc:Depends}, smartmontools
Depends: ${shlibs:Depends}, smartmontools
Description: Hard disk drive 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 drives. It allows you to inspect the
drive's SMART data to determine its health, as well as run various tests on
it.
+32
View File
@@ -0,0 +1,32 @@
This package was debianized by Alexander Shaduri <ashaduri 'at' gmail.com> on
Sat, 15 Nov 2008 00:12:04 +0400.
It was downloaded from <http://gsmartcontrol.berlios.de/>
Copyright:
Copyright (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License:
This product is multi-licensed under GNU GPL versions 2 and 3.
You are free to choose which one you use.
This program is free software: you can redistribute it and/or modify it under
the terms of either version 2 or (at your option) version 3 of the GNU General
Public License as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public Licenses for more details.
You should have received copies of these licenses along with this program.
If not, see <http://www.gnu.org/licenses/>.
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 Alexander Shaduri <ashaduri 'at' gmail.com> and
is licensed under the GPLv2 and GPLv3, see above.
+12
View File
@@ -0,0 +1,12 @@
AUTHORS.txt
LICENSE_boost_1_0.txt
LICENSE_bsd-ucb.txt
LICENSE_gpl2.txt
LICENSE_gpl3.txt
LICENSE_gsmartcontrol.txt
LICENSE_hz.txt
LICENSE_pcrecpp.txt
LICENSE_whatever.txt
LICENSE_zlib.txt
NEWS
README.txt
+4
View File
@@ -0,0 +1,4 @@
?package(gsmartcontrol):needs="X11" section="Applications/System/Hardware"\
title="GSmartControl" command="/usr/bin/gsmartcontrol-root"\
longtitle="Hard Disk Health Inspection"\
icon="/usr/share/pixmaps/gsmartcontrol.xpm"
+14
View File
@@ -0,0 +1,14 @@
#!/bin/sh
set -e
if test -x /usr/bin/update-menus; then update-menus; fi
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0
+14
View File
@@ -0,0 +1,14 @@
#!/bin/sh
set -e
if test -x /usr/bin/update-menus; then update-menus; fi
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0
+98
View File
@@ -0,0 +1,98 @@
#!/usr/bin/make -f
# -*- makefile -*-
# Sample debian/rules that uses debhelper.
# This file was originally written by Joey Hess and Craig Small.
# As a special exception, when this file is copied by dh-make into a
# dh-make output file, you may use that output file without restriction.
# This special exception was added by Craig Small in version 0.37 of dh-make.
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
# These are used for cross-compiling and for saving the configure script
# from having to guess our platform (since we know it already)
DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
config.status: configure
dh_testdir
# Add here commands to configure the package.
ifneq "$(wildcard /usr/share/misc/config.sub)" ""
cp -f /usr/share/misc/config.sub config.sub
endif
ifneq "$(wildcard /usr/share/misc/config.guess)" ""
cp -f /usr/share/misc/config.guess config.guess
endif
./configure --enable-optimize-options=auto --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info CFLAGS="$(CFLAGS)" LDFLAGS="-Wl,-z,defs"
build: build-stamp
build-stamp: config.status
dh_testdir
# Add here commands to compile the package.
$(MAKE)
#docbook-to-man debian/gsmartcontrol.sgml > gsmartcontrol.1
touch $@
clean:
dh_testdir
dh_testroot
rm -f build-stamp
# Add here commands to clean up after the build process.
-$(MAKE) clean
dh_clean
install: build
dh_testdir
dh_testroot
dh_clean -k
dh_installdirs
# Add here commands to install the package into debian/gsmartcontrol.
$(MAKE) DESTDIR=$(CURDIR)/debian/gsmartcontrol install-strip
# Build architecture-independent files here.
binary-indep: build install
# We have nothing to do by default.
# Build architecture-dependent files here.
binary-arch: build install
dh_testdir
dh_testroot
dh_installchangelogs ChangeLog
dh_installdocs
# dh_installexamples
# dh_install
dh_installmenu
# dh_installdebconf
# dh_installlogrotate
# dh_installemacsen
# dh_installpam
# dh_installmime
# dh_python
# dh_installinit
# dh_installcron
# dh_installinfo
# dh_installman
dh_link
dh_strip
dh_compress
dh_fixperms
# dh_perl
# dh_makeshlibs
dh_installdeb
dh_shlibdeps
dh_gencontrol
dh_md5sums
dh_builddeb
binary: binary-indep binary-arch
.PHONY: build clean binary-indep binary-arch binary install
+5 -1
View File
@@ -1,10 +1,14 @@
#!/bin/sh
#!/bin/bash
############################################################################
# Copyright:
# (C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
# License: See LICENSE_zlib.txt file
############################################################################
# We use #!/bin/bash of #!/bin/sh because freebsd (tested with 6.3) in its
# infinite wisdom has csh-like /bin/sh (even though the SUS says it
# must be bourne-compatible).
if [ "$1" == "" ] | [ "$2" == "" ] | [ "$3" == "" ]; then
echo "Usage: $0 <input_file> <cpp_file> <symbol_name>";
+31 -14
View File
@@ -66,7 +66,7 @@
<cflags/>
<cxxflags/>
<envvars/>
<configargs>--enable-default-gcc-options --enable-gcc-optimize --enable-gcc-pch --enable-tests</configargs>
<configargs>--disable-user-flags --enable-optimize-options --enable-tests</configargs>
<topsourcedir/>
<cppflags/>
<ldflags/>
@@ -76,7 +76,7 @@
<f77flags/>
</optimized>
<debuggy>
<configargs>--enable-default-gcc-options --enable-gcc-debug --enable-tests</configargs>
<configargs>--disable-user-flags --enable-debug-options --enable-tests</configargs>
<builddir>debuggy</builddir>
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
@@ -94,24 +94,24 @@
</debuggy>
<icc>
<envvars/>
<configargs>--disable-default-gcc-options --enable-tests</configargs>
<configargs>--disable-user-flags --enable-tests</configargs>
<builddir>icc</builddir>
<topsourcedir/>
<cppflags/>
<ldflags>-s -static-libgcc -static-intel</ldflags>
<ldflags/>
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
<f77compiler>kdevg77options</f77compiler>
<ccompilerbinary>icc</ccompilerbinary>
<cxxcompilerbinary>icpc</cxxcompilerbinary>
<f77compilerbinary/>
<cflags>-w1</cflags>
<cxxflags>-g0 -O3 -m32 -ftrapuv -fstack-security-check -early-template-check -static-libgcc -static-intel -Wcomment -Wdeprecated -Wextra-tokens -Wformat -Wmissing-prototypes -Wnon-virtual-dtor -Wpointer-arith -Wreturn-type -Wshadow -Wtrigraphs -Wuninitialized -w1</cxxflags>
<cflags/>
<cxxflags/>
<f77flags/>
</icc>
<win32>
<envvars/>
<configargs>--enable-default-gcc-options --enable-gcc-optimize --enable-gcc-pch --enable-tests --target=i386-mingw32msvc --host=i386-mingw32msvc --build=i686-linux PKG_CONFIG_PATH=/target/lib/pkgconfig PKG_CONFIG_LIBDIR=/target/lib PATH=&quot;/opt/cross-tools/bin:/target/bin:$PATH&quot;</configargs>
<configargs>--disable-user-flags --enable-optimize-options --enable-tests --target=i386-mingw32msvc --host=i386-mingw32msvc --build=i686-linux PKG_CONFIG_PATH=/target/lib/pkgconfig PKG_CONFIG_LIBDIR=/target/lib PATH=&quot;/opt/cross-tools/bin:/target/bin:$PATH&quot;</configargs>
<builddir>win32</builddir>
<topsourcedir/>
<cppflags/>
@@ -119,8 +119,8 @@
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
<f77compiler>kdevg77options</f77compiler>
<ccompilerbinary>&quot;i386-mingw32msvc-gcc -mms-bitfields&quot;</ccompilerbinary>
<cxxcompilerbinary>&quot;i386-mingw32msvc-g++ -mms-bitfields&quot;</cxxcompilerbinary>
<ccompilerbinary>i386-mingw32msvc-gcc</ccompilerbinary>
<cxxcompilerbinary>i386-mingw32msvc-g++</cxxcompilerbinary>
<f77compilerbinary/>
<cflags/>
<cxxflags/>
@@ -130,7 +130,7 @@
<envvars>
<envvar value="/opt/sun/sunstudio12/bin:$PATH" name="PATH" />
</envvars>
<configargs>--disable-default-gcc-options --enable-tests</configargs>
<configargs>--enable-tests</configargs>
<builddir>suncc</builddir>
<topsourcedir/>
<cppflags/>
@@ -141,8 +141,8 @@
<ccompilerbinary>suncc</ccompilerbinary>
<cxxcompilerbinary>sunCC</cxxcompilerbinary>
<f77compilerbinary/>
<cflags>-DDEBUG -DDEBUG_BUILD</cflags>
<cxxflags>+w -errtags -erroff=notemsource,notused -features=extensions -DHZ_FORCE_GLOBAL_MACROS -DDEBUG -DDEBUG_BUILD -DGLIBMM_HAVE_SUN_REVERSE_ITERATOR -DSIGC_HAVE_SUN_REVERSE_ITERATOR</cxxflags>
<cflags>-errwarn=%all</cflags>
<cxxflags>-DGLIBMM_HAVE_SUN_REVERSE_ITERATOR -DSIGC_HAVE_SUN_REVERSE_ITERATOR</cxxflags>
<f77flags/>
</suncc>
<portland>
@@ -151,7 +151,7 @@
<builddir>portland</builddir>
<topsourcedir/>
<cppflags/>
<ldflags/>
<ldflags>-s</ldflags>
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
<f77compiler>kdevg77options</f77compiler>
@@ -159,9 +159,26 @@
<cxxcompilerbinary>pgCC</cxxcompilerbinary>
<f77compilerbinary/>
<cflags>-Minform=warn -Bstatic_pgi</cflags>
<cxxflags>-Minform=warn -Bstatic_pgi -noswitcherror -define_macro=HZ_FORCE_GLOBAL_MACROS</cxxflags>
<cxxflags>-Minform=warn -Bstatic_pgi -noswitcherror -define_macro=HZ_NO_COMPILER_AUTOINCLUDE</cxxflags>
<f77flags/>
</portland>
<win32-debug>
<envvars/>
<configargs>--disable-user-flags --enable-debug-options --enable-tests --target=i386-mingw32msvc --host=i386-mingw32msvc --build=i686-linux PKG_CONFIG_PATH=/target/lib/pkgconfig PKG_CONFIG_LIBDIR=/target/lib PATH=&quot;/opt/cross-tools/bin:/target/bin:$PATH&quot;</configargs>
<builddir>win32-debug</builddir>
<topsourcedir/>
<cppflags/>
<ldflags/>
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
<f77compiler>kdevg77options</f77compiler>
<ccompilerbinary>i386-mingw32msvc-gcc</ccompilerbinary>
<cxxcompilerbinary>i386-mingw32msvc-g++</cxxcompilerbinary>
<f77compilerbinary/>
<cflags/>
<cxxflags/>
<f77flags/>
</win32-debug>
</configurations>
<make>
<envvars>
+45 -21
View File
@@ -2,30 +2,44 @@ Name: gsmartcontrol
Version: @VERSION@
Release: 1as
License: GPLv2, GPLv3
Vendor: Alexander Shaduri <ashaduri 'at' gmail.com>
Url: http://gsmartcontrol.berlios.de
Vendor: Alexander Shaduri <ashaduri 'at' gmail.com>
Packager: Alexander Shaduri <ashaduri 'at' gmail.com>
Group: Hardware/Other
Source: gsmartcontrol-%{version}.tar.bz2
BuildRoot: %{_tmppath}/%{name}-%{version}-build
Summary: GSmartControl - Hard Disk Health Inspection Tool
%if 0%{?mandriva_version}
Group: System/Kernel and hardware
%else
Group: Hardware/Other
%endif
# Support build service rpmlint. Disabled - the build service
# stopped recognizing the flag for some architectures.
# %if 0%{?opensuse_bs} && 0%{?suse_version} >= 1010
# Support: rpmlint-bs
# %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.
# SUSE / OpenSUSE. SLES also defines the correct suse_version.
%if 0%{?suse_version}
%if %suse_version >= 1030
%if 0%{?%suse_version} >= 1030
Requires: smartmontools, pcre, gtkmm2 >= 2.12.0
BuildRequires: gcc-c++ libstdc++-devel pcre-devel, gtkmm2-devel >= 2.12.0
BuildRequires: gcc-c++, libstdc++-devel, pcre-devel, gtkmm2-devel >= 2.12.0
%else
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
BuildRequires: gcc-c++, libstdc++-devel, pcre-devel, gtkmm2-devel >= 2.6.0, libglademm-devel >= 2.4.0
%endif
%endif
@@ -37,10 +51,10 @@ BuildRequires: gcc-c++ libstdc++-devel pcre-devel, gtkmm2-devel >= 2.6.0, libgla
# 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
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
BuildRequires: gcc-c++, pcre-devel, gtkmm24-devel >= 2.6.0, libglademm24-devel >= 2.4.0
%endif
%endif
@@ -50,11 +64,11 @@ BuildRequires: gcc-c++ pcre-devel, gtkmm24-devel >= 2.6.0, libglademm24-devel >=
%if 0%{?mandriva_version}
%if 0%{?mandriva_version} >= 2008
Requires: smartmontools, libpcre0, libgtkmm2.4_1 >= 2.12.0
BuildRequires: gcc-c++ gcc-cpp libpcre-devel, libgtkmm2.4-devel >= 2.12.0
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, libgtkmm2.4_1 >= 2.6.0, libglademm2.4_1 >= 2.4.0
BuildRequires: gcc-c++ gcc-cpp libpcre-devel, libgtkmm2.4-devel >= 2.6.0, libglademm2.4_1-devel >= 2.4.0
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
@@ -70,25 +84,31 @@ SMART data to determine its health, as well as run various tests on it.
%setup -q
%configure
# Don't use configure parameters - let rpm set them.
# --prefix=/usr --enable-default-gcc-options --enable-gcc-optimize
%configure --enable-optimize-options=auto
%build
# Setup for parallel builds
# Set up for parallel builds
numprocs=`egrep -c ^cpu[0-9]+ /proc/stat || :`
if [ "$numprocs" = "0" ]; then
numprocs=1
fi
make -j$numprocs
#make
# make
%install
# %makeinstall
make DESTDIR=%buildroot install
make DESTDIR=%buildroot install-strip
%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
@@ -99,18 +119,22 @@ rm -rf %buildroot
%defattr(-,root,root)
%attr(0755,root,root) %{_bindir}/*
#%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/*
# %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/*
%doc %{_datadir}/doc/gsmartcontrol
%{_datadir}/gsmartcontrol
%{_datadir}/gsmartcontrol/*
%{_datadir}/applications/*
# %{_datadir}/gsmartcontrol/*
%{_datadir}/applications/*.desktop
%{_datadir}/icons/*
%{_datadir}/pixmaps/*
%changelog
* Fri Nov 14 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
- Updated .spec file to incorporate changes from the official Mandriva package.
* Mon Oct 06 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
- Updated .spec file to support OpenSUSE < 10.3, Fedora and friends,
Mandriva (for OpenSUSE Build Service).
+19 -2
View File
@@ -1,13 +1,29 @@
AM_CPPFLAGS = $(all_includes)
METASOURCES = AUTO
SUBDIRS = libdebug hz rmn rconfig pcrecpp applib res
if TARGET_OS_KERNEL_WINDOWS
WINRES_OBJ = gsc_winres_win32.o
endif
# use different name for object file to avoid make auto-rules
gsc_winres_win32.o: $(top_srcdir)/data/gsmartcontrol.ico gsc_winres.rc
cp -f $(top_srcdir)/data/gsmartcontrol.ico .
$(WINDRES) gsc_winres.rc gsc_winres_win32.o
mostlyclean-local:
rm -f gsmartcontrol.ico
noinst_DATA = gsc_winres.rc.in
bin_PROGRAMS = gsmartcontrol
SUBDIRS = libdebug hz rmn rconfig pcrecpp applib res
gsmartcontrol_LDADD = $(top_builddir)/src/applib/libapplib.a \
$(top_builddir)/src/pcrecpp/libpcrecpp.a $(top_builddir)/src/libdebug/libdebug.a \
$(top_builddir)/src/res/libres.a
$(top_builddir)/src/res/libres.a $(WINRES_OBJ)
noinst_HEADERS = global_macros.h gsc_about_dialog.h gsc_executor_error_dialog.h \
gsc_executor_log_window.h gsc_help_window.h gsc_info_window.h gsc_init.h gsc_main_window.h \
@@ -15,3 +31,4 @@ noinst_HEADERS = global_macros.h gsc_about_dialog.h gsc_executor_error_dialog.h
gsmartcontrol_SOURCES = gsc_about_dialog.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
+19 -13
View File
@@ -1,21 +1,21 @@
AM_CPPFLAGS = $(all_includes)
METASOURCES = AUTO
noinst_LIBRARIES = libapplib.a
libapplib_a_SOURCES = app_gtkmm_utils.cpp cmdex.cpp cmdex_sync.cpp \
cmdex_sync_gui.cpp gui_utils.cpp selftest.cpp smartctl_parser.cpp storage_detector.cpp \
storage_device.cpp storage_property.cpp storage_property_descr.cpp
noinst_HEADERS = app_gtkmm_features.h app_gtkmm_utils.h app_pcrecpp.h \
app_ui_res_utils.h cmdex.h cmdex_sync.h cmdex_sync_gui.h gui_utils.h selftest.h smartctl_executor.h \
smartctl_executor_gui.h smartctl_parser.h storage_detector.h storage_device.h \
storage_property.h storage_property_colors.h storage_property_descr.h storage_settings.h \
wrapping_label.h
libapplib_a_SOURCES = app_gtkmm_utils.cpp app_pango_utils.cpp cmdex.cpp \
cmdex_sync.cpp cmdex_sync_gui.cpp gui_utils.cpp selftest.cpp smartctl_parser.cpp \
storage_detector.cpp storage_device.cpp storage_property.cpp storage_property_descr.cpp
noinst_HEADERS = app_gtkmm_features.h app_gtkmm_utils.h app_pango_utils.h \
app_pcrecpp.h app_ui_res_utils.h cmdex.h cmdex_sync.h cmdex_sync_gui.h gui_utils.h \
selftest.h smartctl_executor.h smartctl_executor_gui.h smartctl_parser.h \
storage_detector.h storage_device.h storage_property.h storage_property_colors.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)
storage_detector_test_SOURCES = storage_detector_test.cpp
storage_detector_test_LDADD = libapplib.a \
$(top_builddir)/src/pcrecpp/libpcrecpp.a $(top_builddir)/src/libdebug/libdebug.a
app_ui_res_utils_test_SOURCES = app_ui_res_utils_test.cpp app_ui_res_utils_test_data.cpp
app_ui_res_utils_test_LDADD = libapplib.a \
$(top_builddir)/src/libdebug/libdebug.a
smartctl_executor_test_SOURCES = smartctl_executor_test.cpp
smartctl_executor_test_LDADD = libapplib.a \
@@ -25,6 +25,12 @@ smartctl_parser_test_SOURCES = smartctl_parser_test.cpp
smartctl_parser_test_LDADD = libapplib.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/pcrecpp/libpcrecpp.a $(top_builddir)/src/libdebug/libdebug.a
spawn_test_SOURCES = spawn_test.cpp
# Build them first
SUBDIRS=../libdebug ../pcrecpp
@@ -32,8 +38,8 @@ SUBDIRS=../libdebug ../pcrecpp
# we don't list them in a separate variable because otherwise kdevelop won't see them.
noinst_PROGRAMS =
if ENABLE_TESTS
noinst_PROGRAMS += smartctl_executor_test smartctl_parser_test \
storage_detector_test
noinst_PROGRAMS += app_ui_res_utils_test smartctl_executor_test smartctl_parser_test \
storage_detector_test spawn_test
endif
tests: $(noinst_PROGRAMS)
+4 -9
View File
@@ -14,6 +14,7 @@
#include <gtk/gtk.h> // gtk_tooltips_*
#endif
#include "app_pango_utils.h" // app_pango_strip_markup()
#include "app_gtkmm_utils.h"
@@ -67,16 +68,10 @@ void app_gtkmm_set_widget_tooltip(Gtk::Widget& widget,
if (tooltips) {
if (use_markup) {
// strip markup
gchar* stripped = 0;
if (pango_parse_markup(tooltip_text.c_str(), -1, 0, NULL, &stripped, NULL, NULL) && stripped) {
gtk_tooltips_set_tip(tooltips, widget.gobj(), stripped, "");
Glib::ustring stripped;
if (app_pango_strip_markup(tooltip_text, stripped)) {
gtk_tooltips_set_tip(tooltips, widget.gobj(), stripped.c_str(), "");
}
g_free(stripped);
// alternate method, check if it works (AttrList has bool() since gtkmm 2.10):
// char dummy = 0;
// Glib::ustring stripped;
// if (Pango::AttrList(tooltip_text, 0, stripped, dummy).gobj()) ...
} else {
gtk_tooltips_set_tip(tooltips, widget.gobj(), tooltip_text.c_str(), "");
-1
View File
@@ -22,7 +22,6 @@
// Note: This works only if the column has custom widget set.
Gtk::Widget* app_gtkmm_get_column_header(Gtk::TreeViewColumn& column);
+50
View File
@@ -0,0 +1,50 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#include <pango/pango.h> // pango_parse_markup()
#include "app_pango_utils.h"
// Returns true if markup was successfully stripped
bool app_pango_strip_markup(const Glib::ustring& str, Glib::ustring& stripped)
{
gchar* gstripped = 0;
bool ok = false;
if (pango_parse_markup(str.c_str(), -1, 0, NULL, &gstripped, NULL, NULL) && gstripped) {
stripped = gstripped;
ok = true;
}
g_free(gstripped);
// alternative method, check if it works (AttrList has bool() cast since gtkmm 2.10):
// char dummy = 0;
// if (Pango::AttrList(str, 0, stripped, dummy).gobj()) {
// return true;
// }
return ok;
}
bool app_pango_strip_markup(const std::string& str, std::string& stripped)
{
gchar* gstripped = 0;
bool ok = false;
if (pango_parse_markup(str.c_str(), -1, 0, NULL, &gstripped, NULL, NULL) && gstripped) {
stripped = gstripped;
ok = true;
}
g_free(gstripped);
return ok;
}
+25
View File
@@ -0,0 +1,25 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#ifndef APP_PANGO_UTILS_H
#define APP_PANGO_UTILS_H
#include <string>
#include <glibmm.h> // ustring
// strip markup of a string. returns true on success.
bool app_pango_strip_markup(const Glib::ustring& str, Glib::ustring& stripped);
bool app_pango_strip_markup(const std::string& str, std::string& stripped);
#endif
+6
View File
@@ -194,6 +194,12 @@ inline bool app_pcre_replace_once(const pcrecpp::RE& re, const std::string& repl
// Escape a string to be used as a regular expression
inline std::string app_pcre_escape(const std::string& str)
{
return pcrecpp::RE::QuoteMeta(str);
}
+11 -9
View File
@@ -10,7 +10,9 @@
#include <string>
#include <gtkmm/window.h>
#if defined ENABLE_LIBGLADEMM
#include "hz/hz_config.h" // feature test macros (ENABLE_*)
#if defined ENABLE_LIBGLADE
#include <libglademm.h>
#elif defined ENABLE_GTKBUILDER
#include <gtkmm/builder.h>
@@ -42,7 +44,7 @@
// Or, if you're using compiled-in buffers, it will make them available.
#if defined ENABLE_LIBGLADEMM
#if defined ENABLE_LIBGLADE
#define APP_UI_RES_DATA_INIT(res_name) \
HZ_RES_DATA_INIT_NAMED(res_name##_ui, #res_name ".glade", UIResDataBase); \
@@ -51,7 +53,7 @@
{ \
UIResDataBase::root_name = #res_name; /* we need original name here, not with _ui */ \
} \
};
}
#elif defined ENABLE_GTKBUILDER
@@ -63,7 +65,7 @@
{ \
UIResDataBase::root_name = #res_name; /* we need original name here, not with _ui */ \
} \
};
}
#else // none, just avoid errors
@@ -75,14 +77,14 @@
{ \
UIResDataBase::root_name = #res_name; /* we need original name here, not with _ui */ \
} \
};
}
#endif
#if defined ENABLE_LIBGLADEMM
#if defined ENABLE_LIBGLADE
typedef Glib::RefPtr<Gnome::Glade::Xml> app_ui_res_ref_t;
@@ -215,7 +217,7 @@ class AppUIResWidget : public WidgetType, public hz::InstanceManager<Child, Mult
typedef hz::InstanceManager<Child, MultiInstance> instance_class;
#if defined ENABLE_LIBGLADEMM
#if defined ENABLE_LIBGLADE
friend class Gnome::Glade::Xml; // allow construction through libglade
#elif defined ENABLE_GTKBUILDER
friend class Gtk::Builder; // allow construction through gtkbuilder
@@ -230,7 +232,7 @@ class AppUIResWidget : public WidgetType, public hz::InstanceManager<Child, Mult
return hz::InstanceManager<Child, MultiInstance>::get_single_instance();
std::string error;
#if defined ENABLE_LIBGLADEMM
#if defined ENABLE_LIBGLADE
app_ui_res_ref_t ui;
#elif defined ENABLE_GTKBUILDER
app_ui_res_ref_t ui = Gtk::Builder::create();
@@ -297,7 +299,7 @@ class AppUIResWidget : public WidgetType, public hz::InstanceManager<Child, Mult
Glib::Object* lookup_object(const Glib::ustring& name)
{
#if defined ENABLE_LIBGLADEMM
#if defined ENABLE_LIBGLADE
return static_cast<Glib::Object*>(lookup_widget(name)); // all glade objects are widgets, it's an upcast
#elif defined ENABLE_GTKBUILDER
return ref_ui_->get_object(name).operator->(); // silly RefPtr doesn't have get().
+99
View File
@@ -0,0 +1,99 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_whatever.txt
***************************************************************************/
#include <iostream>
#include <gtkmm/box.h>
#include <gtkmm/button.h>
#include <gtkmm/label.h>
#include <gtkmm/window.h>
#include <gtkmm/main.h>
#include "app_ui_res_utils.h"
// #include "hz/instance_manager.h"
class AppUiResTestWindow;
// class AppUiResTestWindow : public Gtk::Window
// class AppUiResTestWindow : public hz::InstanceManager<AppUiResTestWindow, false>, public Gtk::Window
class AppUiResTestWindow : public AppUIResWidget<AppUiResTestWindow, false>
{
public:
// name of glade/ui file without a .glade/.ui extension and quotes
APP_UI_RES_DATA_INIT(app_ui_res_test_window);
enum action_t {
action_quit
};
// AppUiResTestWindow()
AppUiResTestWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui)
: AppUIResWidget<AppUiResTestWindow, false>(gtkcobj, ref_ui)
{
Gtk::Box* vbox = Gtk::manage(new Gtk::VBox(false, 5));
add(*vbox);
Gtk::Button* button = Gtk::manage(new Gtk::Button("Clicky"));
vbox->pack_start(*button, Gtk::PACK_SHRINK);
// button->signal_clicked().connect(sigc::mem_fun(*this, &AppUiResTestWindow::on_button_clicked));
button->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &AppUiResTestWindow::on_button_clicked2), action_quit));
Gtk::Label* label = Gtk::manage(new Gtk::Label("test"));
vbox->pack_start(*label, Gtk::PACK_SHRINK);
this->signal_delete_event().connect(sigc::mem_fun(*this, &AppUiResTestWindow::on_delete_event));
show_all();
}
virtual ~AppUiResTestWindow()
{ }
private:
void on_button_clicked()
{
std::cerr << "AppUiResTestWindow::on_button_clicked()\n";
}
void on_button_clicked2(action_t action_type)
{
std::cerr << "AppUiResTestWindow::on_button_clicked2()\n";
}
bool on_delete_event(GdkEventAny* e)
{
Gtk::Main::quit();
return true; // action handled
}
};
int main(int argc, char *argv[])
{
Gtk::Main m(&argc, &argv);
// AppUiResTestWindow* app = new AppUiResTestWindow();
AppUiResTestWindow::create();
m.run();
// delete app;
AppUiResTestWindow::destroy();
return(0);
}
+582
View File
@@ -0,0 +1,582 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_whatever.txt
***************************************************************************/
#include "hz/hz_config.h" // feature test macros (ENABLE_*)
// A simple empty window definition
#if defined 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
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
+191 -166
View File
@@ -8,19 +8,16 @@
#include <cerrno> // errno (not std::errno, it may be a macro)
#ifdef _WIN32
#include <io.h> // close()
// #include <io.h> // close()
#else
#include <sys/wait.h> // waitpid()'s W* macros
#include <unistd.h> // close()
// #include <unistd.h> // close()
#endif
#include <glibmm/shell.h>
#include <glibmm/miscutils.h> // get_current_dir()
#include <glibmm/convert.h>
#include "hz/tls.h"
#include "hz/tls_policy_glib.h"
#include "hz/debug.h"
#include "hz/string_num.h" // hz::number_to_string()
#include "cmdex.h"
@@ -41,40 +38,40 @@ extern "C" {
// NOTE: These functions are called in a separate threads!
inline void command_executor_child_watch_handler(GPid arg_pid, int waitpid_status, gpointer data)
inline void cmdex_child_watch_handler(GPid arg_pid, int waitpid_status, gpointer data)
{
Cmdex::on_child_watch_handler(arg_pid, waitpid_status, data);
}
inline gboolean command_executor_on_channel_io_stdout_as_available(GIOChannel* source, GIOCondition cond, gpointer data)
inline gboolean cmdex_on_channel_io_stdout_as_available(GIOChannel* source, GIOCondition cond, gpointer data)
{
return Cmdex::on_channel_io_as_available(source, cond, static_cast<Cmdex*>(data), Cmdex::channel_type_stdout);
}
inline gboolean command_executor_on_channel_io_stderr_as_available(GIOChannel* source, GIOCondition cond, gpointer data)
inline gboolean cmdex_on_channel_io_stderr_as_available(GIOChannel* source, GIOCondition cond, gpointer data)
{
return Cmdex::on_channel_io_as_available(source, cond, static_cast<Cmdex*>(data), Cmdex::channel_type_stderr);
}
inline gboolean command_executor_on_channel_io_stdout_buffered(GIOChannel* source, GIOCondition cond, gpointer data)
inline gboolean cmdex_on_channel_io_stdout_buffered(GIOChannel* source, GIOCondition cond, gpointer data)
{
return Cmdex::on_channel_io_buffered(source, cond, static_cast<Cmdex*>(data), Cmdex::channel_type_stdout);
}
inline gboolean command_executor_on_channel_io_stderr_buffered(GIOChannel* source, GIOCondition cond, gpointer data)
inline gboolean cmdex_on_channel_io_stderr_buffered(GIOChannel* source, GIOCondition cond, gpointer data)
{
return Cmdex::on_channel_io_buffered(source, cond, static_cast<Cmdex*>(data), Cmdex::channel_type_stderr);
}
inline void command_executor_on_buffered_source_destroy(gpointer data);
inline void cmdex_on_buffered_source_destroy(gpointer data);
// this is executed in another thread!
inline gboolean command_executor_on_term_timeout(gpointer data)
inline gboolean cmdex_on_term_timeout(gpointer data)
{
DBG_FUNCTION_ENTER_MSG;
Cmdex* self = static_cast<Cmdex*>(data);
@@ -84,7 +81,7 @@ extern "C" {
// this is executed in another thread!
inline gboolean command_executor_on_kill_timeout(gpointer data)
inline gboolean cmdex_on_kill_timeout(gpointer data)
{
DBG_FUNCTION_ENTER_MSG;
Cmdex* self = static_cast<Cmdex*>(data);
@@ -104,114 +101,152 @@ bool Cmdex::execute()
{
ScopedLock locker(object_mutex_);
if (this->running || this->stopped_cleanup_needed(false))
if (this->running_ || this->stopped_cleanup_needed(false))
return false;
cleanup_members();
clear_errors(false); // don't lock
str_stdout.clear();
str_stderr.clear();
str_stdout_.clear();
str_stderr_.clear();
std::string cmd = command_exec + " " + command_args;
std::deque<std::string> child_argv;
std::string cmd = command_exec_ + " " + command_args_;
try {
child_argv = Glib::shell_parse_argv(cmd);
}
catch (Glib::ShellError& e) {
push_error(Error<Glib::ShellError>("gshell", ErrorLevel::error, e, e.what()), false);
// Make command vector
int argcp = 0; // number of args
gchar** argvp = 0; // args vector
GError* shell_error = 0;
if (!g_shell_parse_argv(cmd.c_str(), &argcp, &argvp, &shell_error)) {
push_error(Error<void>("gshell", ErrorLevel::error, shell_error->message), false);
g_error_free(shell_error);
g_strfreev(argvp);
return false;
}
// Reset the locale - otherwise it may mangle the output.
bool old_lang_set = false;
std::string old_lang = Glib::getenv("LANG", old_lang_set);
// Set the locale for a child to Classic - otherwise it may mangle the output.
// LANG is posix-only, so it has no effect on win32.
// Unfortunately, I was unable to find a way to execute a child with a different
// locale in win32. Locale seems to be non-inheritable, so setting it here won't help.
#ifndef _WIN32
// TODO: make this controllable.
const gchar* old_lang_gchar = g_getenv("LANG");
bool old_lang_set = (old_lang_gchar);
std::string old_lang = (old_lang_gchar ? old_lang_gchar : "");
if (old_lang_set)
Glib::setenv("LANG", "C");
g_setenv("LANG", "C", true);
#endif
try {
// execute the command
debug_out_info("app", DBG_FUNC_MSG << "Executing \"" << cmd << "\".\n");
Glib::spawn_async_with_pipes(Glib::get_current_dir(), child_argv,
Glib::SPAWN_SEARCH_PATH | Glib::SPAWN_DO_NOT_REAP_CHILD,
sigc::slot<void>(), &this->pid,
0, &fd_stdout, &fd_stderr);
debug_out_info("app", DBG_FUNC_MSG << "Executing \"" << cmd << "\".\n");
if (argvp) {
debug_out_dump("app", DBG_FUNC_MSG << "Dumping argvp:\n");
gchar** elem = argvp;
while (*elem) {
debug_out_dump("app", *elem << "\n");
++elem;
}
}
catch (Glib::SpawnError& e) {
push_error(Error<Glib::SpawnError>("gspawn", ErrorLevel::error, e, e.what()), false);
// Execute the command
gchar* curr_dir = g_get_current_dir();
GError* spawn_error = 0;
if (!g_spawn_async_with_pipes(curr_dir, argvp, NULL,
GSpawnFlags(G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD),
NULL, NULL, // child setup function
&this->pid_, 0, &fd_stdout_, &fd_stderr_, &spawn_error)) {
push_error(Error<void>("gspawn", ErrorLevel::error, spawn_error->message), false);
g_error_free(spawn_error);
g_free(curr_dir);
g_strfreev(argvp);
#ifndef _WIN32
if (old_lang_set)
Glib::setenv("LANG", old_lang);
g_setenv("LANG", old_lang.c_str(), true);
#endif
return false;
}
g_free(curr_dir);
g_strfreev(argvp);
#ifndef _WIN32
if (old_lang_set)
Glib::setenv("LANG", old_lang);
g_setenv("LANG", old_lang.c_str(), true);
#endif
this->timer.start(); // start the timer
g_timer_start(timer_); // start the timer
#ifdef _WIN32
channel_stdout = Glib::IOChannel::create_from_win32_fd(fd_stdout);
channel_stderr = Glib::IOChannel::create_from_win32_fd(fd_stderr);
channel_stdout_ = g_io_channel_win32_new_fd(fd_stdout_);
channel_stderr_ = g_io_channel_win32_new_fd(fd_stderr_);
#else
channel_stdout = Glib::IOChannel::create_from_fd(fd_stdout);
channel_stderr = Glib::IOChannel::create_from_fd(fd_stderr);
channel_stdout_ = g_io_channel_unix_new(fd_stdout_);
channel_stderr_ = g_io_channel_unix_new(fd_stderr_);
#endif
// The internal encoding is always UTF8. To read command output correctly, use
// "" for binary data, or set io encoding to current locale.
// If using locales, call Glib::locale_to_utf8() or Glib::convert() afterwards.
// If using locales, call g_locale_to_utf8() or g_convert() afterwards.
// std::string charset;
// Glib::get_charset(charset);
try {
channel_stdout->set_encoding(""); // binary IO
channel_stderr->set_encoding(""); // binary IO
// blocking writes if the pipe is full helps for small-pipe systems (see man 7 pipe).
GIOFlags channel_flags = GIOFlags(~G_IO_FLAG_NONBLOCK);
// blocking writes if the pipe is full helps for small-pipe systems (see man 7 pipe).
Glib::IOFlags flags = ~Glib::IO_FLAG_NONBLOCK;
channel_stdout->set_flags(flags);
channel_stderr->set_flags(flags);
channel_stdout->set_buffer_size(channel_stdout_buffer_size);
channel_stderr->set_buffer_size(channel_stderr_buffer_size);
// Note about GError's here:
// What do we do? The command is already running, so let's ignore these
// errors - it's better to get a slightly mangled buffer than to abort the
// command in the mid-run.
if (channel_stdout_) {
// Since we invoke shutdown() manually before unref(), this would cause
// a double-shutdown.
// g_io_channel_set_close_on_unref(channel_stdout_, true); // close() on fd
g_io_channel_set_encoding(channel_stdout_, NULL, 0); // binary IO
g_io_channel_set_flags(channel_stdout_, channel_flags, 0);
g_io_channel_set_buffer_size(channel_stdout_, channel_stdout_buffer_size_);
}
catch (Glib::IOChannelError& e) {
// What do we do? The command is already running, so let's ignore these
// errors - it's better to get a slightly mangled buffer than to abort the
// command in the mid-run.
if (channel_stderr_) {
// g_io_channel_set_close_on_unref(channel_stderr_, true); // close() on fd
g_io_channel_set_encoding(channel_stderr_, NULL, 0); // binary IO
g_io_channel_set_flags(channel_stderr_, channel_flags, 0);
g_io_channel_set_buffer_size(channel_stderr_, channel_stderr_buffer_size_);
}
// Note: glib callbacks _are_ invoked in separate threads,
// so proper locking is necessary.
// This is different from gtk callbacks, which are directly invoked from main loop
// in the same thread.
// this doesn't really work
// Glib::signal_io().connect(sigc::bind(sigc::ptr_fun(&io_handler), this), channel_stdout, cond);
GIOCondition cond = GIOCondition(G_IO_IN | G_IO_PRI | G_IO_HUP | G_IO_ERR | G_IO_NVAL);
gint io_thread_priority = G_PRIORITY_HIGH; // channel reader callback thread must read enough so that the loss is minimal
if (this->stdout_make_str_as_available) {
this->event_source_id_stdout = g_io_add_watch_full(channel_stdout->gobj(), io_thread_priority, cond,
&command_executor_on_channel_io_stdout_as_available, this, NULL);
if (this->stdout_make_str_as_available_) {
this->event_source_id_stdout_ = g_io_add_watch_full(channel_stdout_, io_thread_priority, cond,
&cmdex_on_channel_io_stdout_as_available, this, NULL);
} else {
this->event_source_id_stdout = g_io_add_watch_full(channel_stdout->gobj(), io_thread_priority, cond,
&command_executor_on_channel_io_stdout_buffered, this, command_executor_on_buffered_source_destroy);
this->event_source_id_stdout_ = g_io_add_watch_full(channel_stdout_, io_thread_priority, cond,
&cmdex_on_channel_io_stdout_buffered, this, cmdex_on_buffered_source_destroy);
}
if (this->stderr_make_str_as_available) {
this->event_source_id_stderr = g_io_add_watch_full(channel_stderr->gobj(), io_thread_priority, cond,
&command_executor_on_channel_io_stderr_as_available, this, NULL);
if (this->stderr_make_str_as_available_) {
this->event_source_id_stderr_ = g_io_add_watch_full(channel_stderr_, io_thread_priority, cond,
&cmdex_on_channel_io_stderr_as_available, this, NULL);
} else {
this->event_source_id_stderr = g_io_add_watch_full(channel_stderr->gobj(), io_thread_priority, cond,
&command_executor_on_channel_io_stderr_buffered, this, command_executor_on_buffered_source_destroy);
this->event_source_id_stderr_ = g_io_add_watch_full(channel_stderr_, io_thread_priority, cond,
&cmdex_on_channel_io_stderr_buffered, this, cmdex_on_buffered_source_destroy);
}
@@ -220,11 +255,11 @@ bool Cmdex::execute()
// The watch function is protected by mutex and won't be called before execute() exits.
// This is AFTER the channels so that it doesn't get called before the channels are attached (is this needed?).
// (the child stops being a zombie as soon as wait*() exits and this handler is called).
g_child_watch_add(static_cast<GPid>(this->pid), &command_executor_child_watch_handler, this);
g_child_watch_add(this->pid_, &cmdex_child_watch_handler, this);
this->running = true; // process is running now.
this->running_ = true; // process is running now.
// actually, it may be already stopped, but the watcher function is blocked
// until we exit, so the actions are serialized.
@@ -239,15 +274,15 @@ bool Cmdex::try_stop(hz::signal_t sig)
{
ScopedLock locker(object_mutex_);
if (!this->running || this->pid <= 0)
if (!this->running_ || this->pid_ <= 0)
return false;
// other variants: SIGHUP(1) (terminal closed), SIGINT(2) (Ctrl-C),
// SIGKILL(9) (kill).
// Note that SIGKILL cannot be trapped by any process.
if (process_signal_send(this->pid, sig) == 0) { // success
this->kill_signal_sent = static_cast<int>(sig); // just the number to compare later.
if (process_signal_send(this->pid_, sig) == 0) { // success
this->kill_signal_sent_ = static_cast<int>(sig); // just the number to compare later.
return true; // the rest is done by a handler
}
@@ -265,16 +300,16 @@ void Cmdex::set_stop_timeouts(int term_timeout_msec, int kill_timeout_msec)
DBG_ASSERT(term_timeout_msec == 0 || kill_timeout_msec == 0 || kill_timeout_msec > term_timeout_msec);
ScopedLock locker(object_mutex_); // member access / serialization lock
if (!this->running) // process not running
if (!this->running_) // process not running
return;
unset_stop_timeouts(false);
if (term_timeout_msec != 0)
event_source_id_term = g_timeout_add(term_timeout_msec, &command_executor_on_term_timeout, this);
event_source_id_term = g_timeout_add(term_timeout_msec, &cmdex_on_term_timeout, this);
if (kill_timeout_msec != 0)
event_source_id_kill = g_timeout_add(kill_timeout_msec, &command_executor_on_kill_timeout, this);
event_source_id_kill = g_timeout_add(kill_timeout_msec, &cmdex_on_kill_timeout, this);
}
@@ -307,7 +342,7 @@ void Cmdex::stopped_cleanup()
// DBG_FUNCTION_ENTER_MSG;
ScopedLock locker(object_mutex_); // member access / serialization lock
if (this->running || !this->stopped_cleanup_needed(false)) // huh?
if (this->running_ || !this->stopped_cleanup_needed(false)) // huh?
return;
// remove stop timeout callbacks
@@ -315,22 +350,23 @@ void Cmdex::stopped_cleanup()
// various statuses (see waitpid (2)):
if (WIFEXITED(waitpid_status)) { // exited normally
int exit_status = WEXITSTATUS(waitpid_status);
if (WIFEXITED(waitpid_status_)) { // exited normally
int exit_status = WEXITSTATUS(waitpid_status_);
if (exit_status != 0) {
// translate the exit_code into a message
Glib::ustring msg = signal_exit_status_translator.emit(exit_status);
std::string msg = (translator_func_ ? translator_func_(exit_status, translator_func_data_)
: "[no translator function, exit code: " + hz::number_to_string(exit_status));
push_error(Error<int>("exit", ErrorLevel::warn, exit_status, msg), false);
}
} else {
if (WIFSIGNALED(waitpid_status)) { // exited by signal
int sig_num = WTERMSIG(waitpid_status);
if (WIFSIGNALED(waitpid_status_)) { // exited by signal
int sig_num = WTERMSIG(waitpid_status_);
// If it's not our signal, treat as error.
// Note: they will never match under win32
if (sig_num != this->kill_signal_sent) {
if (sig_num != this->kill_signal_sent_) {
push_error(Error<int>("signal", ErrorLevel::error, sig_num), false);
} else { // it's our signal, treat as warning
push_error(Error<int>("signal", ErrorLevel::warn, sig_num), false);
@@ -338,11 +374,11 @@ void Cmdex::stopped_cleanup()
}
}
Glib::spawn_close_pid(this->pid); // needed to avoid zombies
g_spawn_close_pid(this->pid_); // needed to avoid zombies
cleanup_members();
this->running = false;
this->running_ = false;
}
@@ -357,41 +393,45 @@ void Cmdex::on_child_watch_handler(GPid arg_pid, int waitpid_status, gpointer da
self->lock();
self->timer.stop(); // stop the timer
g_timer_stop(self->timer_); // stop the timer
// wait until the IO channels are closed
// while (!(self->event_source_stdout_closed && self->event_source_stderr_closed)) {
// self->unlock();
// Glib::usleep(75*1000); // 75ms
// g_usleep(75*1000); // 75ms
// self->lock();
// }
self->waitpid_status = waitpid_status;
self->child_watch_handler_called = true;
self->running = false; // process is not running anymore
self->waitpid_status_ = waitpid_status;
self->child_watch_handler_called_ = true;
self->running_ = false; // process is not running anymore
// Remove fd IO callbacks. They may actually be removed already.
// This will force calling the iochannel callback (they may not be called
// otherwise at all if there was no output).
if (self->event_source_id_stdout) {
GSource* source_stdout = g_main_context_find_source_by_id(NULL, self->event_source_id_stdout);
if (self->event_source_id_stdout_) {
GSource* source_stdout = g_main_context_find_source_by_id(NULL, self->event_source_id_stdout_);
if (source_stdout)
g_source_destroy(source_stdout);
}
if (self->event_source_id_stderr) {
GSource* source_stderr = g_main_context_find_source_by_id(NULL, self->event_source_id_stderr);
if (self->event_source_id_stderr_) {
GSource* source_stderr = g_main_context_find_source_by_id(NULL, self->event_source_id_stderr_);
if (source_stderr)
g_source_destroy(source_stderr);
}
// Cose std pipes
close(self->fd_stdout);
close(self->fd_stderr);
// Close std pipes.
// The channel closes them now.
// close(self->fd_stdout_);
// close(self->fd_stderr_);
self->signal_exited.emit(); // ALL slots are called in non-main thread!
// Note: This callback is called in non-main thread!
if (self->exited_callback_)
self->exited_callback_(self->exited_callback_data_);
self->unlock();
// DBG_FUNCTION_EXIT_MSG;
@@ -421,45 +461,42 @@ gboolean Cmdex::on_channel_io_as_available(GIOChannel* source,
// read the bytes one by one. without this, a buffered iochannel hangs while waiting for data.
// we don't use unbuffered iochannels - they may lose data on program exit.
const gsize count = 1;
char buf[count] = {0};
gchar buf[count] = {0};
Cmdex::ScopedLock locker(self->object_mutex_);
Glib::RefPtr<Glib::IOChannel> channel;
GIOChannel* channel = 0;
std::string* output_str = 0;
if (type == channel_type_stdout) {
channel = self->channel_stdout;
output_str = &self->str_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;
channel = self->channel_stderr_;
output_str = &self->str_stderr_;
}
try { // read() may throw something
// while there's anything to read, read it
do {
gsize bytes_read = 0;
Glib::IOStatus status = channel->read(buf, count, bytes_read);
if (bytes_read)
output_str->append(buf, bytes_read);
// while there's anything to read, read it
do {
GError* channel_error = 0;
gsize bytes_read = 0;
GIOStatus status = g_io_channel_read_chars(channel, buf, count, &bytes_read, &channel_error);
if (bytes_read)
output_str->append(buf, bytes_read);
// IO_STATUS_NORMAL and IO_STATUS_AGAIN (resource unavailable) are continuable.
if (status == Glib::IO_STATUS_ERROR || status == Glib::IO_STATUS_EOF) {
continue_events = false;
break;
}
} while (channel->get_buffer_condition() & Glib::IO_IN);
if (channel_error) {
self->push_error(Error<void>("giochannel", ErrorLevel::error, channel_error->message), false);
break; // stop on next invocation (is this correct?)
}
// IO_STATUS_NORMAL and IO_STATUS_AGAIN (resource unavailable) are continuable.
if (status == G_IO_STATUS_ERROR || status == G_IO_STATUS_EOF) {
continue_events = false;
break;
}
} while (g_io_channel_get_buffer_condition(channel) & G_IO_IN);
}
catch (Glib::IOChannelError& e) {
self->push_error(Error<Glib::IOChannelError>("giochannel", ErrorLevel::error, e, e.what()), false);
}
catch (Glib::ConvertError& e) { // no conversion in binary mode, shouldn't happen
self->push_error(Error<void>("custom", ErrorLevel::error,
"Cmdex::on_channel_io_as_available(): Glib::ConvertError thrown."), false);
}
// false if the source should be removed, true otherwise.
return continue_events;
@@ -487,7 +524,7 @@ namespace {
// This callback is executed from in the same thread as on_channel_io_buffered,
// so we get the same variable (tests show cleared s_final_str, but is this really
// so? this function is executed during on_child_watch_handler (if spawn-terminated)).
inline void command_executor_on_buffered_source_destroy(gpointer data)
inline void cmdex_on_buffered_source_destroy(gpointer data)
{
// DBG_FUNCTION_ENTER_MSG;
s_final_str.reset(); // .reset(), not ->clear - we don't want memory leaks in stopped threads.
@@ -520,57 +557,45 @@ gboolean Cmdex::on_channel_io_buffered(GIOChannel* source,
const gsize count = 1;
static char buf[count] = {0};
// Glib::RefPtr<Glib::IOChannel> channel = Glib::wrap(source); // this segfaults on large data (?)
// while there's anything to read, read it
GError* error = 0;
do {
GError* channel_error = 0;
gsize bytes_read = 0;
GIOStatus status = g_io_channel_read_chars(source, buf, count, &bytes_read, &error);
GIOStatus status = g_io_channel_read_chars(source, buf, count, &bytes_read, &channel_error);
if (bytes_read)
s_final_str->append(buf, bytes_read);
if (channel_error) {
Cmdex::ScopedLock locker(self->object_mutex_);
self->push_error(Error<void>("giochannel", ErrorLevel::error, channel_error->message), false);
break; // stop on next invocation (is this correct?)
}
// IO_STATUS_NORMAL and IO_STATUS_AGAIN (resource unavailable) are continuable.
if (status == G_IO_STATUS_ERROR || status == G_IO_STATUS_EOF) {
continue_events = false;
break;
}
if (error)
break;
} while (g_io_channel_get_buffer_condition(source) & G_IO_IN);
if (!continue_events || error) { // these conditions need locking
if (!continue_events) { // need locking
Cmdex::ScopedLock locker(self->object_mutex_);
if (error) {
try {
Glib::Error::throw_exception(error); // convert GError to exception for easy storage.
}
catch (Glib::IOChannelError& e) {
self->push_error(Error<Glib::IOChannelError>("giochannel", ErrorLevel::error, e, e.what()), false);
}
catch (Glib::ConvertError& e) { // no conversion in binary mode, shouldn't happen
self->push_error(Error<void>("custom", ErrorLevel::error,
"Cmdex::on_channel_io_buffered(): Glib::ConvertError thrown."), false);
}
if (type == channel_type_stdout) {
// set the source to be closed (the actual closing is done when we return false).
// this is needed for the caller to wait until all the channels are stopped.
self->str_stdout_ = *s_final_str;
} else if (type == channel_type_stderr) {
self->str_stderr_ = *s_final_str;
}
if (!continue_events) {
if (type == channel_type_stdout) {
// set the source to be closed (the actual closing is done when we return false).
// this is needed for the caller to wait until all the channels are stopped.
self->str_stdout = *s_final_str;
} else if (type == channel_type_stderr) {
self->str_stderr = *s_final_str;
}
// glib may re-use this thread, so do a TLS cleanup.
// also, manual cleanup is needed for policies which don't support automatic one.
// source_destroy callback already does this, but it won't hurt.
s_final_str.reset(); // this will delete the previous contents.
}
// glib may re-use this thread, so do a TLS cleanup.
// also, manual cleanup is needed for policies which don't support automatic one.
// source_destroy callback already does this, but it won't hurt.
s_final_str.reset(); // this will delete the previous contents.
}
// DBG_FUNCTION_EXIT_MSG;
+103 -78
View File
@@ -8,12 +8,7 @@
#define APP_CMDEX_H
#include <string>
#include <sigc++/sigc++.h>
#include <glibmm/iochannel.h>
#include <glibmm/thread.h>
#include <glibmm/timer.h> // Timer
#include <glibmm/spawn.h> // Pid
#include <glib.h> // G*, g_*
#include <glib.h>
#include "hz/process_signal.h" // hz::SIGNAL_*, hz::process_signal_send (cpp), win32's W* (cpp)
#include "hz/error_holder.h"
@@ -41,7 +36,7 @@ from the _main_ thread.
// inheriting from sigc::trackable automatically disconnects this class's
// slots upon destruction.
class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib>, public sigc::trackable {
class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib> {
public:
@@ -51,24 +46,26 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib>, public sigc::trackable
typedef hz::ErrorHolder<hz::SyncPolicyGlib>::ErrorScopedLock ScopedLock;
typedef hz::ErrorHolder<hz::SyncPolicyGlib>::ErrorLockPolicy LockPolicy;
typedef std::string (*exit_status_translator_func_t)(int, void*);
typedef void (*exited_callback_func_t)(void*);
Cmdex(
//const sigc::slot<Glib::ustring, int>& slot_exit_status_translator,
const sigc::slot<void>& slot_exited = sigc::slot<void>()
// exit_status_translator_func_t translator_func,
exited_callback_func_t exited_cb = NULL, void* exited_cb_data = NULL
)
: object_mutex_(error_object_mutex_),
running(false), kill_signal_sent(0), child_watch_handler_called(false),
pid(0), waitpid_status(0),
running_(false), kill_signal_sent_(0), child_watch_handler_called_(false),
pid_(0), waitpid_status_(0),
timer_(g_timer_new()),
event_source_id_term(0), event_source_id_kill(0),
fd_stdout(0), fd_stderr(0),
channel_stdout_buffer_size(100 * 1024), channel_stderr_buffer_size(10 * 1024), // 100K and 10K
event_source_id_stdout(0), event_source_id_stderr(0),
stdout_make_str_as_available(false), stderr_make_str_as_available(true)
{
// signal_exit_status_translator.connect(slot_exit_status_translator);
if (!slot_exited.empty())
signal_exited.connect(slot_exited);
}
fd_stdout_(0), fd_stderr_(0), channel_stdout_(0), channel_stderr_(0),
channel_stdout_buffer_size_(100 * 1024), channel_stderr_buffer_size_(10 * 1024), // 100K and 10K
event_source_id_stdout_(0), event_source_id_stderr_(0),
stdout_make_str_as_available_(false), stderr_make_str_as_available_(true),
translator_func_(0), translator_func_data_(0),
exited_callback_(exited_cb), exited_callback_data_(exited_cb_data)
{ }
@@ -79,14 +76,22 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib>, public sigc::trackable
// This will help if object is destroyed after the command has exited, but before
// stopped_cleanup() has been called.
stopped_cleanup();
g_timer_destroy(timer_);
// no need to destroy the channels - stopped_cleanup() calls
// cleanup_members(), which deletes them.
}
// Call before execute.
void set_command(const std::string& command_exec_, const std::string& command_args_)
// Note: The command and the arguments _must_ be shell-escaped.
// Use g_shell_quote() or Glib::shell_quote(). Note that each argument
// must be escaped separately.
void set_command(const std::string& command_exec, const std::string& command_args)
{
command_exec = command_exec_;
command_args = command_args_;
command_exec_ = command_exec;
command_args_ = command_args;
}
@@ -130,7 +135,7 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib>, public sigc::trackable
bool stopped_cleanup_needed(bool do_lock = true)
{
ScopedLock locker(object_mutex_, do_lock);
return (child_watch_handler_called);
return (child_watch_handler_called_);
}
@@ -140,7 +145,7 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib>, public sigc::trackable
bool is_running() const
{
ScopedLock locker(object_mutex_);
return running;
return running_;
}
@@ -162,9 +167,9 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib>, public sigc::trackable
{
ScopedLock locker(object_mutex_);
if (stdout_buffer_size)
channel_stdout_buffer_size = stdout_buffer_size; // 100K by default
channel_stdout_buffer_size_ = stdout_buffer_size; // 100K by default
if (stderr_buffer_size)
channel_stderr_buffer_size = stderr_buffer_size; // 10K by default
channel_stderr_buffer_size_ = stderr_buffer_size; // 10K by default
}
@@ -179,25 +184,25 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib>, public sigc::trackable
void set_str_available(bool stdout_str_as_available = false, bool stderr_str_as_available = true)
{
ScopedLock locker(object_mutex_);
stdout_make_str_as_available = stdout_str_as_available;
stderr_make_str_as_available = stderr_str_as_available;
stdout_make_str_as_available_ = stdout_str_as_available;
stderr_make_str_as_available_ = stderr_str_as_available;
}
// If stdout_make_str_as_available is false, call this after stopped_cleanup(),
// If stdout_make_str_as_available_ is false, call this after stopped_cleanup(),
// before next execute(). If it's true, you may call this before the command has
// stopped, but it will decrease performance significantly.
std::string get_stdout_str(bool clear_existing = false)
{
ScopedLock locker(object_mutex_);
// debug_out_dump("app", str_stdout);
// debug_out_dump("app", str_stdout_);
if (clear_existing) {
std::string ret = str_stdout;
str_stdout.clear();
std::string ret = str_stdout_;
str_stdout_.clear();
return ret;
}
return str_stdout;
return str_stdout_;
}
@@ -206,33 +211,42 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib>, public sigc::trackable
{
ScopedLock locker(object_mutex_);
if (clear_existing) {
std::string ret = str_stderr;
str_stderr.clear();
std::string ret = str_stderr_;
str_stderr_.clear();
return ret;
}
return str_stderr;
return str_stderr_;
}
// Return execution time, in seconds. Call after execute().
double get_execution_time()
{
ScopedLock locker(object_mutex_);
gulong microsec = 0;
return g_timer_elapsed(timer_, &microsec);
}
// don't return sigc::connection - it's better for thread-safety this way.
// this will disconnect earlier slots. Call before execute().
void set_exit_status_translator(const sigc::slot<Glib::ustring, int>& slot_exit_status_translator)
void set_exit_status_translator(exit_status_translator_func_t func, void* user_data)
{
ScopedLock locker(object_mutex_);
signal_exit_status_translator.slots().erase(signal_exit_status_translator.slots().begin(), signal_exit_status_translator.slots().end());
signal_exit_status_translator.connect(slot_exit_status_translator);
translator_func_ = func;
translator_func_data_ = user_data;
}
// Returns sigc::connection; you must use lock() / unlock() before modifying it.
// NOTE: The slot will be called in non-main thread! (the call is enclosed
// NOTE: The callback will be called in non-main thread! (the call is enclosed
// in lock/unlock).
// Don't use this unless absolutely necessary, use stopped_cleanup_needed()
// polling instead.
sigc::connection connect_signal_exited(const sigc::slot<void>& slot_exited)
void set_exited_callback(exited_callback_func_t func, void* user_data)
{
ScopedLock locker(object_mutex_);
return signal_exited.connect(slot_exited);
exited_callback_ = func;
exited_callback_data_ = user_data;
}
@@ -291,16 +305,25 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib>, public sigc::trackable
void cleanup_members()
{
// ScopedLock locker(object_mutex_);
kill_signal_sent = 0;
child_watch_handler_called = false;
pid = 0;
waitpid_status = 0;
channel_stdout.clear();
channel_stderr.clear();
event_source_id_stdout = 0;
event_source_id_stderr = 0;
fd_stdout = 0;
fd_stderr = 0;
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;
}
}
@@ -308,51 +331,53 @@ class Cmdex : public hz::ErrorHolder<hz::SyncPolicyGlib>, public sigc::trackable
// default command and its args. std::strings, not ustrings.
std::string command_exec; // binary name. NOT affected by cleanup().
std::string command_args; // args that always go with binary. NOT affected by cleanup().
std::string command_exec_; // binary name. NOT affected by cleanup().
std::string command_args_; // args that always go with binary. NOT affected by cleanup().
bool running; // child process is running now. NOT affected by cleanup().
int kill_signal_sent; // command has been sent this signal to terminate
bool child_watch_handler_called; // true after child_watch_handler callback, before stopped_cleanup().
bool running_; // child process is running now. NOT affected by cleanup().
int kill_signal_sent_; // command has been sent this signal to terminate
bool child_watch_handler_called_; // true after child_watch_handler callback, before stopped_cleanup().
Glib::Pid pid; // int in Unix, pointer in win32
int waitpid_status; // after the command is stopped, before cleanup, this will be available.
GPid pid_; // int in Unix, pointer in win32
int waitpid_status_; // after the command is stopped, before cleanup, this will be available.
Glib::Timer timer; // keep track of elapsed time since command execution. not used by this class, but may be handy.
GTimer* timer_; // keep track of elapsed time since command execution. not used by this class, but may be handy.
int event_source_id_term;
int event_source_id_kill;
int fd_stdout;
int fd_stderr;
int fd_stdout_;
int fd_stderr_;
Glib::RefPtr<Glib::IOChannel> channel_stdout;
Glib::RefPtr<Glib::IOChannel> channel_stderr;
GIOChannel* channel_stdout_;
GIOChannel* channel_stderr_;
int channel_stdout_buffer_size; // NOT affected by cleanup().
int channel_stderr_buffer_size; // NOT affected by cleanup().
int channel_stdout_buffer_size_; // NOT affected by cleanup().
int channel_stderr_buffer_size_; // NOT affected by cleanup().
int event_source_id_stdout;
int event_source_id_stderr;
int event_source_id_stdout_;
int event_source_id_stderr_;
int stdout_make_str_as_available; // NOT affected by cleanup().
int stderr_make_str_as_available; // NOT affected by cleanup().
int stdout_make_str_as_available_; // NOT affected by cleanup().
int stderr_make_str_as_available_; // NOT affected by cleanup().
std::string str_stdout; // NOT affected by cleanup().
std::string str_stderr; // NOT affected by cleanup().
std::string str_stdout_; // NOT affected by cleanup().
std::string str_stderr_; // NOT affected by cleanup().
// signals
// convert command exit status to message string
sigc::signal<Glib::ustring, int> signal_exit_status_translator; // NOT affected by cleanup().
// command exited signal
sigc::signal<void> signal_exited; // NOT affected by cleanup().
exit_status_translator_func_t translator_func_; // NOT affected by cleanup().
void* translator_func_data_;
// "command exited" signal callback.
// NOTE: This will be called in separate thread!
exited_callback_func_t exited_callback_; // NOT affected by cleanup().
void* exited_callback_data_;
};
+2 -2
View File
@@ -4,7 +4,7 @@
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#include <glibmm/timer.h> // usleep()
#include <glib.h> // g_usleep()
#include "cmdex_sync.h"
@@ -93,7 +93,7 @@ bool CmdexSync::execute()
g_main_context_iteration(NULL, false);
}
Glib::usleep(50*1000); // 50 msec. avoids 100% CPU usage.
g_usleep(50*1000); // 50 msec. avoids 100% CPU usage.
}
// command exited, do a cleanup.
+6 -6
View File
@@ -8,7 +8,7 @@
#define APP_CMDEX_SYNC_H
#include <sigc++/sigc++.h>
#include <glibmm/ustring.h> // ustring
#include <string>
#include "hz/error.h"
#include "hz/intrusive_ptr.h"
@@ -196,9 +196,9 @@ class CmdexSync : public hz::intrusive_ptr_referenced, public sigc::trackable {
return cmdex_.get_stderr_str(clear_existing);
}
void set_exit_status_translator(const sigc::slot<Glib::ustring, int>& slot_exit_status_translator)
void set_exit_status_translator(Cmdex::exit_status_translator_func_t func, void* user_data)
{
cmdex_.set_exit_status_translator(slot_exit_status_translator);
cmdex_.set_exit_status_translator(func, user_data);
}
@@ -209,7 +209,7 @@ class CmdexSync : public hz::intrusive_ptr_referenced, public sigc::trackable {
return error_msg_;
}
void set_running_msg(const Glib::ustring& msg)
void set_running_msg(const std::string& msg)
{
running_msg_ = msg;
}
@@ -264,7 +264,7 @@ class CmdexSync : public hz::intrusive_ptr_referenced, public sigc::trackable {
}
Glib::ustring get_running_msg() const
std::string get_running_msg() const
{
return running_msg_;
}
@@ -282,7 +282,7 @@ class CmdexSync : public hz::intrusive_ptr_referenced, public sigc::trackable {
std::string command_name_;
std::string command_args_;
Glib::ustring running_msg_; // message to show in the dialogs, etc...
std::string running_msg_; // message to show in the dialogs, etc...
int forced_kill_timeout_msec;
+93
View File
@@ -5,7 +5,13 @@
***************************************************************************/
#include <gtkmm/messagedialog.h>
#include <gtkmm/dialog.h>
#include <gtkmm/label.h>
#include <gtkmm/entry.h>
#include <gtkmm/stock.h>
#include <gtkmm/box.h>
#include "app_pango_utils.h" // app_pango_strip_markup()
#include "gui_utils.h"
@@ -75,5 +81,92 @@ void gui_show_info_dialog(const std::string& message, const std::string& sec_mes
// Returns false if Cancel was clicked. Puts the user-entered string into result otherwise.
bool gui_show_text_entry_dialog(const std::string& title, const std::string& message,
std::string& result, const std::string& default_str, Gtk::Window* parent)
{
return gui_show_text_entry_dialog(title, message, "", result, default_str, parent, false);
}
bool gui_show_text_entry_dialog(const std::string& title, const std::string& message, const std::string& sec_message,
std::string& result, const std::string& default_str, Gtk::Window* parent, bool sec_msg_markup)
{
int response = 0;
std::string input_str;
{ // the dialog hides at the end of scope
Gtk::Dialog dialog(title, true); // modal
dialog.set_resizable(false);
dialog.set_skip_taskbar_hint(true);
dialog.set_border_width(5);
if (parent) {
dialog.set_transient_for(*parent);
dialog.set_position(Gtk::WIN_POS_CENTER_ON_PARENT);
} else {
dialog.set_position(Gtk::WIN_POS_MOUSE);
}
Gtk::Label main_label;
main_label.set_markup("<big><b>" + Glib::Markup::escape_text(message)
+ (sec_message.empty() ? "\n" : "") + "</b></big>");
main_label.set_line_wrap(true);
main_label.set_selectable(true);
main_label.set_alignment(0.0, 0.0);
Gtk::Label sec_label;
if (sec_msg_markup) {
sec_label.set_markup(sec_message);
} else {
sec_label.set_text(sec_message);
}
sec_label.set_line_wrap(true);
sec_label.set_selectable(true);
sec_label.set_alignment(0.0, 0.0);
Gtk::Entry input_entry;
input_entry.set_activates_default(true);
Gtk::VBox vbox;
vbox.set_spacing(12);
vbox.pack_start(main_label, false, false, 0);
vbox.pack_start(sec_label, true, true, 0);
vbox.pack_start(input_entry, true, true, 0);
vbox.show_all();
dialog.get_action_area()->set_border_width(5);
dialog.get_action_area()->set_spacing(6);
dialog.get_vbox()->set_spacing(14); // as in MessageDialog
dialog.get_vbox()->pack_start(vbox, false, false, 0);
dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
Gtk::Button ok_button(Gtk::Stock::OK);
ok_button.set_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
response = dialog.run(); // blocks until the dialog is closed
input_str = input_entry.get_text();
}
if (response == Gtk::RESPONSE_OK) {
result = input_str;
return true;
}
return false;
}
+10
View File
@@ -14,6 +14,8 @@
// These functions won't return until the dialogs are closed.
// Message must not contain any markup.
void gui_show_error_dialog(const std::string& message, Gtk::Window* parent = 0);
void gui_show_error_dialog(const std::string& message, const std::string& sec_message,
@@ -32,6 +34,14 @@ void gui_show_info_dialog(const std::string& message, const std::string& sec_mes
Gtk::Window* parent = 0, bool sec_msg_markup = false);
// Returns false if Cancel was clicked. Puts the user-entered string into result otherwise.
bool gui_show_text_entry_dialog(const std::string& title, const std::string& message,
std::string& result, const std::string& default_str, Gtk::Window* parent = 0);
bool gui_show_text_entry_dialog(const std::string& title, const std::string& message, const std::string& sec_message,
std::string& result, const std::string& default_str, Gtk::Window* parent = 0, bool sec_msg_markup = false);
+6 -5
View File
@@ -103,7 +103,7 @@ std::string SelfTest::start(hz::intrusive_ptr<CmdexSync> smartctl_ex)
return "Invalid test specified";
std::string output;
std::string error_msg = drive_->execute_smartctl("--test=" + test_param, smartctl_ex, output);
std::string error_msg = drive_->execute_smartctl("-t " + test_param, smartctl_ex, output); // --test=
if (!error_msg.empty()) // checks for empty output too
return error_msg;
@@ -163,7 +163,7 @@ std::string SelfTest::force_stop(hz::intrusive_ptr<CmdexSync> smartctl_ex)
// To abort non-captive short, long and conveyance tests, use "--abort".
std::string output;
std::string error_msg = drive_->execute_smartctl("--abort", smartctl_ex, output);
std::string error_msg = drive_->execute_smartctl("-X", smartctl_ex, output); // --abort
if (!error_msg.empty()) // checks for empty output too
return error_msg;
@@ -202,8 +202,8 @@ std::string SelfTest::update(hz::intrusive_ptr<CmdexSync> smartctl_ex)
return "Invalid drive given.";
std::string output;
// std::string error_msg = drive_->execute_smartctl("--log=selftest", smartctl_ex, output);
std::string error_msg = drive_->execute_smartctl("--capabilities", smartctl_ex, output);
// std::string error_msg = drive_->execute_smartctl("-l selftest", smartctl_ex, output); // --log=
std::string error_msg = drive_->execute_smartctl("-c", smartctl_ex, output); // --capabilities
if (!error_msg.empty()) // checks for empty output too
return error_msg;
@@ -262,7 +262,8 @@ std::string SelfTest::update(hz::intrusive_ptr<CmdexSync> smartctl_ex)
poll_in_seconds_ = std::max(int64_t(1*60), int64_t(gran / 10.)); // that's 2 min for 180min extended test
debug_out_dump("app", DBG_FUNC_MSG << "total: " << total << ", gran: " << gran
<< ", poll_in: " << poll_in_seconds_ << ", remaining: " << get_remaining_seconds() << ".\n");
<< ", poll in: " << poll_in_seconds_ << ", remaining secs: " << get_remaining_seconds()
<< ", remaining %: " << int(remaining_percent_) << ", last seen %: " << int(last_seen_percent_) << ".\n");
}
} else {
+1
View File
@@ -9,6 +9,7 @@
#include <string>
#include <stdint.h>
#include <glibmm/timer.h>
#include "hz/intrusive_ptr.h"
+3 -7
View File
@@ -7,14 +7,11 @@
#ifndef SMARTCTL_EXECUTOR_H
#define SMARTCTL_EXECUTOR_H
#include <sigc++/sigc++.h>
#include "cmdex.h"
#include "cmdex_sync.h"
// Executor inherits from sigc::trackable
template<class ExecutorSync>
class SmartctlExecutorGeneric : public ExecutorSync {
@@ -42,8 +39,7 @@ class SmartctlExecutorGeneric : public ExecutorSync {
void construct()
{
ExecutorSync::cmdex_.set_exit_status_translator(sigc::mem_fun(this,
&SmartctlExecutorGeneric::translate_exit_status));
ExecutorSync::cmdex_.set_exit_status_translator(&SmartctlExecutorGeneric::translate_exit_status, NULL);
this->set_error_header("An error occurred while executing smartctl:\n\n");
}
@@ -60,7 +56,7 @@ class SmartctlExecutorGeneric : public ExecutorSync {
};
Glib::ustring translate_exit_status(int status)
static std::string translate_exit_status(int status, void* user_data)
{
const char* table[] = {
"Command line did not parse.",
@@ -73,7 +69,7 @@ class SmartctlExecutorGeneric : public ExecutorSync {
"The device self-test log contains records of errors."
};
Glib::ustring str;
std::string str;
// check every bit
for (unsigned int i = 0; i <= 7; i++) {
+8 -5
View File
@@ -1,13 +1,14 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: Public Domain
License: See LICENSE_whatever.txt
***************************************************************************/
#include <iostream>
#include <gtkmm/main.h>
#include "smartctl_executor_gui.h"
// #include "smartctl_executor_gui.h"
#include "smartctl_executor.h"
@@ -16,12 +17,14 @@ int main(int argc, char** argv)
Glib::thread_init();
Gtk::Main m(argc, argv);
// NOTE: Don't use long options (e.g. --info). Use short ones (e.g. -i),
// because long options may be unsupported on some platforms.
// SmartctlExecutor ex("smartctl", "--info /dev/sda");
// SmartctlExecutor ex("smartctl", "-i /dev/sda");
// SmartctlExecutorGui ex("ls", "-l --color=no -R /dev");
// SmartctlExecutorGui ex("lsa", "-1 --color=no /sys/block");
SmartctlExecutorGui ex("../../../0test_binary.sh", "");
// SmartctlExecutor ex("../../../0test_binary.sh", "");
// SmartctlExecutorGui ex("../../../0test_binary.sh", "");
SmartctlExecutor ex("../../../0test_binary.sh", "");
ex.execute();
+46 -7
View File
@@ -5,6 +5,7 @@
***************************************************************************/
#include <stdint.h> // uint64_t
#include <clocale> // setlocale, localeconv
#include "hz/string_algo.h" // string_*
#include "hz/string_num.h" // string_is_numeric, number_to_string
@@ -249,10 +250,45 @@ bool SmartctlParser::check_version(const std::string& version_str)
// Note: this property is present since 5.33.
std::string SmartctlParser::parse_byte_size(const std::string& str, uint64_t& bytes, bool extended)
{
// e.g. "500,107,862,016" bytes or "80'060'424'192 bytes" or "80 026 361 856 bytes"
// (locale-dependent?). added '.'-separated too, just in case.
std::string s = hz::string_replace_copy(hz::string_replace_copy(hz::string_trim_copy(str), ",", ""), "'", "");
s = hz::string_replace_copy(hz::string_replace_copy(hz::string_replace_copy(s, " bytes", ""), " ", ""), ".", "");
// E.g. "500,107,862,016" bytes or "80'060'424'192 bytes" or "80 026 361 856 bytes".
// French locale inserts 0xA0 as a separator (non-breaking space, _not_ a valid utf8 char).
// Added '.'-separated too, just in case.
// Smartctl uses system locale's thousands_sep explicitly.
// When launching smartctl, we use LANG=C for it, but it works only on POSIX.
// Also, loading smartctl output files from different locales doesn't really work.
debug_out_dump("app", "Size reported as: " << str << "\n");
std::vector<std::string> to_replace;
to_replace.push_back(" ");
to_replace.push_back("'");
to_replace.push_back(",");
to_replace.push_back(".");
to_replace.push_back(std::string(1, 0xa0));
#ifdef _WIN32
// if current locale is C, then probably we didn't change it at application
// startup, so set it now (temporarily). Otherwise, just use the current locale's
// thousands separator.
std::string old_locale = std::setlocale(LC_ALL, NULL);
bool locale_is_c = (old_locale == "C");
if (locale_is_c) {
std::setlocale(LC_ALL, ""); // set system locale
}
struct lconv* lc = std::localeconv();
if (lc && lc->thousands_sep && *(lc->thousands_sep)) {
to_replace.push_back(lc->thousands_sep);
}
if (locale_is_c) {
std::setlocale(LC_ALL, "C"); // restore it
}
#endif
to_replace.push_back("bytes");
std::string s = hz::string_replace_array_copy(hz::string_trim_copy(str), to_replace, "");
uint64_t v = 0;
if (hz::string_is_numeric(s, v, false)) {
@@ -1295,9 +1331,9 @@ bool SmartctlParser::parse_section_data_subsection_selftest_log(const std::strin
// individual entries
{
// split by columns (minimum 2 spaces between them)
// num, type, status, remaining, hours, lba
pcrecpp::RE re("^(#[ \\t]*([0-9]+)[ \\t]*(\\S.*) [ \\t]*(\\S.*) [ \\t]*([0-9]+%) [ \\t]*([0-9]+) [ \\t]*(\\S.*))$", re_options);
// split by columns.
// num, type, status, remaining, hours, lba (optional).
pcrecpp::RE re("^(#[ \\t]*([0-9]+)[ \\t]+(\\S+(?: \\S+)*) [ \\t]*(\\S.*) [ \\t]*([0-9]+%) [ \\t]*([0-9]+)[ \\t]*((?: [ \\t]*\\S.*)?))$", re_options);
std::string line, num, type, status_str, remaining, hours, lba;
pcrecpp::StringPiece input(sub); // position tracker
@@ -1316,6 +1352,9 @@ bool SmartctlParser::parse_section_data_subsection_selftest_log(const std::strin
p.value_selftest_entry.type = hz::string_trim_copy(type);
p.value_selftest_entry.lba_of_first_error = hz::string_trim_copy(lba);
// old smartctls didn't print anything for lba if none, but newer ones print "-". normalize.
if (p.value_selftest_entry.lba_of_first_error == "")
p.value_selftest_entry.lba_of_first_error = "-";
hz::string_trim(status_str);
StorageSelftestEntry::status_t status = StorageSelftestEntry::status_unknown;
+1 -1
View File
@@ -1,7 +1,7 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: Public Domain
License: See LICENSE_whatever.txt
***************************************************************************/
#undef HZ_USE_LIBDEBUG
+83
View File
@@ -0,0 +1,83 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_whatever.txt
***************************************************************************/
#include <iostream>
#include <glibmm.h>
#include <glib.h>
int main(int argc, char** argv)
{
Glib::thread_init();
GPid pid;
int fd_stdout = 0, fd_stderr = 0;
std::string cmd = "iexplore";
// std::vector<std::string> child_argv = Glib::shell_parse_argv(cmd);
gchar* curr_dir = g_get_current_dir();
int argcp = 0; // number of args
gchar** argvp = 0; // args vector
GError* shell_error = 0;
g_shell_parse_argv(cmd.c_str(), &argcp, &argvp, &shell_error);
GError* spawn_error = 0;
g_spawn_async_with_pipes(curr_dir, argvp, NULL,
GSpawnFlags(G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD),
NULL, NULL, // child setup function
&pid, 0, &fd_stdout, &fd_stderr, &spawn_error);
g_free(curr_dir);
g_strfreev(argvp);
#ifdef _WIN32
GIOChannel* channel_stdout = g_io_channel_win32_new_fd(fd_stdout);
GIOChannel* channel_stderr = g_io_channel_win32_new_fd(fd_stderr);
#else
GIOChannel* channel_stdout = g_io_channel_unix_new(fd_stdout);
GIOChannel* channel_stderr = g_io_channel_unix_new(fd_stderr);
#endif
// blocking writes if the pipe is full helps for small-pipe systems (see man 7 pipe).
GIOFlags flags = GIOFlags(~G_IO_FLAG_NONBLOCK);
if (channel_stdout) {
g_io_channel_set_encoding(channel_stdout, NULL, 0); // binary IO
g_io_channel_set_flags(channel_stdout, flags, 0);
g_io_channel_set_buffer_size(channel_stdout, 10000);
}
if (channel_stderr) {
g_io_channel_set_encoding(channel_stderr, NULL, 0); // binary IO
g_io_channel_set_flags(channel_stderr, flags, 0);
g_io_channel_set_buffer_size(channel_stderr, 10000);
}
g_main_loop_run(g_main_loop_new(NULL, false));
return 0;
}
+515 -220
View File
@@ -4,267 +4,535 @@
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#include "hz/hz_config.h" // CONFIG_*
#include <ios> // std::boolalpha
// #include <glibmm/fileutils.h> // Dir
#include <algorithm> // std::find
#include <cstdio> // std::fgets()
#include <cerrno> // ENXIO
#if defined CONFIG_KERNEL_OPENBSD || defined CONFIG_KERNEL_NETBSD
#include <util.h> // getrawpartition()
#endif
#if defined CONFIG_KERNEL_WIN32
#include <windows.h>
#endif
#include "hz/debug.h"
#include "hz/fs_dir.h"
#include "hz/fs_file.h"
#include "hz/string_sprintf.h"
#include "hz/local_algo.h" // hz::shell_sort()
#include "rconfig/rconfig_mini.h"
#include "app_pcrecpp.h"
#include "smartctl_executor.h"
#include "storage_detector.h"
namespace {
// inline std::string detect_drives_linux_sys_block(std::vector<StorageDeviceRefPtr>& drives)
// {
// hz::Dir dir("/sys/block");
// }
// Note: Since 3ware RAID controllers need additional flags on most (all?) OSes,
// and they may ignore the actual devices names, I have no idea how to support them.
#if defined CONFIG_KERNEL_LINUX
// Linux 2.6 with udev. Scan /dev/disk/by-id - the directory entries there
// are symlinks to respective /dev devices. Some devices have multiple
// links pointing to them, so unique filter is needed.
/*
Sample listing:
------------------------------------------------------------
# ls -1 /dev/disk/by-id
ata-ST31000340AS_9QJ0FFG7
ata-ST31000340AS_9QJ0FFG7-part1
ata-ST31000340AS_9QJ0FFG7-part2
ata-ST3500630AS_9QG0R38D
ata-ST3500630AS_9QG0R38D-part1
scsi-SATA_ST31000340AS_9QJ0FFG7
scsi-SATA_ST31000340AS_9QJ0FFG7-part1
scsi-SATA_ST31000340AS_9QJ0FFG7-part2
scsi-SATA_ST3500630AS_9QG0R38D
scsi-SATA_ST3500630AS_9QG0R38D-part1
*/
inline std::string detect_drives_linux_udev_byid(std::vector<std::string>& devices)
{
debug_out_info("app", DBG_FUNC_MSG << "Detecting through device scan directory...\n");
// Linux 2.6 with udev. Scan /dev/disk/by-id - the directory entries there
// are symlinks to respective /dev devices. Some devices have multiple
// links pointing to them, so unique filter is needed.
/*
Sample listing:
------------------------------------------------------------
# ls -1 /dev/disk/by-id
ata-ST31000340AS_9QJ0FFG7
ata-ST31000340AS_9QJ0FFG7-part1
ata-ST31000340AS_9QJ0FFG7-part2
ata-ST3500630AS_9QG0R38D
ata-ST3500630AS_9QG0R38D-part1
scsi-SATA_ST31000340AS_9QJ0FFG7
scsi-SATA_ST31000340AS_9QJ0FFG7-part1
scsi-SATA_ST31000340AS_9QJ0FFG7-part2
scsi-SATA_ST3500630AS_9QG0R38D
scsi-SATA_ST3500630AS_9QG0R38D-part1
*/
inline std::string detect_drives_linux_udev_byid(std::vector<std::string>& devices)
{
debug_out_info("app", DBG_FUNC_MSG << "Detecting through device scan directory /dev/disk/by-id...\n");
std::string dev_dir; // this defaults to "/dev/disk/by-id"
if (!rconfig::get_data("system/linux_udev_byid_path", dev_dir) || dev_dir.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Device scan directory path is not set.\n");
return "Device scan directory path is not set.";
}
hz::Dir dir(dev_dir);
std::vector<std::string> all_devices;
if (!dir.entry_list(all_devices, false)) { // this outputs to debug too.
std::string error_msg = dir.get_error_utf8();
if (!dir.exists()) {
debug_out_warn("app", DBG_FUNC_MSG << "Device scan directory doesn't exist.\n");
} else {
debug_out_error("app", DBG_FUNC_MSG << "Cannot list directory entries.\n");
std::string dev_dir; // this defaults to "/dev/disk/by-id"
if (!rconfig::get_data("system/linux_udev_byid_path", dev_dir) || dev_dir.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Device scan directory path is not set.\n");
return "Device scan directory path is not set.";
}
return error_msg;
}
std::vector<std::string> blacklist;
blacklist.push_back("/-part[0-9]+$/");
hz::Dir dir(dev_dir);
// filter-out the ones with "partN" in them
for (unsigned int i = 0; i < all_devices.size(); ++i) {
std::string entry = all_devices[i];
if (entry == "." || entry == "..")
continue;
// platform blacklist
bool blacked = false;
for (std::vector<std::string>::const_iterator iter = blacklist.begin(); iter != blacklist.end(); ++iter) {
if (app_pcre_match(*iter, entry)) {
blacked = true;
break;
std::vector<std::string> all_devices;
if (!dir.entry_list(all_devices, false)) { // this outputs to debug too.
std::string error_msg = dir.get_error_utf8();
if (!dir.exists()) {
debug_out_warn("app", DBG_FUNC_MSG << "Device scan directory doesn't exist.\n");
} else {
debug_out_error("app", DBG_FUNC_MSG << "Cannot list directory entries.\n");
}
}
if (blacked)
continue;
hz::FsPath path = dev_dir + hz::DIR_SEPARATOR_S + all_devices[i];
// those are usually relative links, so find out where they are pointing to.
std::string link_dest;
if (path.get_link_destination(link_dest)) {
path = (hz::path_is_absolute(link_dest) ? link_dest : (dev_dir + hz::DIR_SEPARATOR_S + link_dest));
path.compress(); // compress in case there are ../-s.
return error_msg;
}
// skip number-ended devices, in case we couldn't filter out the scan directory correctly
// (it's user-settable after all).
// if (app_pcre_match("/[0-9]+$/", path.str()))
// continue;
std::vector<std::string> blacklist;
blacklist.push_back("/-part[0-9]+$/");
if (std::find(devices.begin(), devices.end(), path.str()) == devices.end()) // there may be duplicates
devices.push_back(path.str());
}
// filter-out the ones with "partN" in them
for (unsigned int i = 0; i < all_devices.size(); ++i) {
std::string entry = all_devices[i];
if (entry == "." || entry == "..")
continue;
return std::string();
}
// Procfs files don't support SEEK_END or ftello() (I think). Anyway, they can't
// be read through hz::File::get_contents, so use this function instead.
inline bool read_proc_partitions_file(hz::File& file, std::vector<std::string>& lines)
{
if (!file.open("rb")) // closed automatically
return false; // the error message is in File itself.
FILE* fp = file.get_handle();
if (!fp)
return false;
char line[256];
while (std::fgets(line, sizeof(line), fp) != NULL) {
if (*line != '\0')
lines.push_back(line);
}
return true;
}
// Linux (tested with 2.4 and 2.6) /proc/partitions. Parses the file, appends /dev to each entry.
// Note that file format changed from 2.4 to 2.6 (some statistics fields moved to another file).
/*
Sample 1 (2.4, devfs, with statistics):
------------------------------------------------------------
# cat /proc/partitions
major minor #blocks name rio rmerge rsect ruse wio wmerge wsect wuse running use aveq
3 0 60051600 ide/host0/bus0/target0/lun0/disc 72159 136746 1668720 467190 383039 658435 8342136 1659840 -429 17038808 22703194
3 1 1638598 ide/host0/bus0/target0/lun0/part1 0 0 0 0 0 0 0 0 0 0 0
3 2 1028160 ide/host0/bus0/target0/lun0/part2 0 0 0 0 0 0 0 0 0 0 0
3 3 3092512 ide/host0/bus0/target0/lun0/part3 0 0 0 0 0 0 0 0 0 0 0
3 4 1 ide/host0/bus0/target0/lun0/part4 0 0 0 0 0 0 0 0 0 0 0
3 5 514048 ide/host0/bus0/target0/lun0/part5 1458 7877 74680 9070 2140 18285 166272 42490 0 10100 52000
------------------------------------------------------------
Sample 2 (2.6):
------------------------------------------------------------
# cat /proc/partitions
major minor #blocks name
1 0 4096 ram0
8 0 156290904 sda
8 1 39070048 sda1
8 2 7560 sda2
8 3 1 sda3
------------------------------------------------------------
Sample 3 (2.6 on nokia n8xx, spaces may be missing):
------------------------------------------------------------
# cat /proc/partitions
major minor #blocks name
31 0 128 mtdblock0
31 1 384 mtdblock1
31 2 2048 mtdblock2
31 3 2048 mtdblock3
31 4 257536 mtdblock4
254 0 3932160 mmcblk0
254 1 3928064 mmcblk0p1
254 8 1966080 mmcblk1
254 9 2007032 mmcblk1p1
*/
inline std::string detect_drives_linux_proc_partitions(std::vector<std::string>& devices)
{
debug_out_info("app", DBG_FUNC_MSG << "Detecting through partitions file...\n");
std::string parts_file;
if (!rconfig::get_data("system/linux_proc_partitions_path", parts_file) || parts_file.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Partitions file path is not set.\n");
return "Partitions file path is not set.";
}
hz::File file(parts_file);
// std::string contents;
std::vector<std::string> lines;
// if (!f.get_contents(contents)) {
if (!read_proc_partitions_file(file, lines)) { // this outputs to debug too
std::string error_msg = file.get_error_utf8(); // save before calling other file functions
if (!file.exists()) {
debug_out_warn("app", DBG_FUNC_MSG << "Partitions file doesn't exist.\n");
} else {
debug_out_error("app", DBG_FUNC_MSG << "Partitions file exists but cannot be read.\n");
}
return error_msg;
}
// debug_out_dump("app", DBG_FUNC_MSG << "Dumping partitions file:\n" << contents << "\n");
// hz::string_split(contents, '\n', lines, true);
std::vector<std::string> blacklist;
// fixme: not sure about how partitions are visible with twa0.
blacklist.push_back("/d[a-z][0-9]+$/"); // sda1, hdb2 - partitions. twa0 and twe1 are drives, not partitions.
blacklist.push_back("/ram[0-9]+$/");
blacklist.push_back("/loop[0-9]*$/");
blacklist.push_back("/part[0-9]+$/");
blacklist.push_back("/p[0-9]+$/");
for (unsigned int i = 0; i < lines.size(); ++i) {
std::string line = hz::string_trim_copy(lines[i]);
if (line.empty() || app_pcre_match("/^major/", line)) // file header
continue;
std::string dev;
if (!app_pcre_match("/^[ \\t]*[^ \\t\\n]+[ \\t]+[^ \\t\\n]+[ \\t]+[^ \\t\\n]+[ \\t]+([^ \\t\\n]+)/", line, &dev) || dev.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Cannot parse line \"" << line << "\".\n");
continue;
}
// platform blacklist
bool blacked = false;
for (std::vector<std::string>::const_iterator iter = blacklist.begin(); iter != blacklist.end(); ++iter) {
if (app_pcre_match(*iter, dev)) {
blacked = true;
break;
// platform blacklist
bool blacked = false;
for (std::vector<std::string>::const_iterator iter = blacklist.begin(); iter != blacklist.end(); ++iter) {
if (app_pcre_match(*iter, entry)) {
blacked = true;
break;
}
}
}
if (blacked)
continue;
if (blacked)
continue;
std::string path = "/dev/" + dev;
if (std::find(devices.begin(), devices.end(), path) == devices.end()) // there may be duplicates
devices.push_back(path);
hz::FsPath path = dev_dir + hz::DIR_SEPARATOR_S + all_devices[i];
// those are usually relative links, so find out where they are pointing to.
std::string link_dest;
if (path.get_link_destination(link_dest)) {
path = (hz::path_is_absolute(link_dest) ? link_dest : (dev_dir + hz::DIR_SEPARATOR_S + link_dest));
path.compress(); // compress in case there are ../-s.
}
// skip number-ended devices, in case we couldn't filter out the scan directory correctly
// (it's user-settable after all).
// if (app_pcre_match("/[0-9]+$/", path.str()))
// continue;
if (std::find(devices.begin(), devices.end(), path.str()) == devices.end()) // there may be duplicates
devices.push_back(path.str());
}
return std::string();
}
return std::string();
}
/*
// FreeBSD, etc... .
// Linux devices are not listed because /proc/partitions should always exist there.
// Procfs files don't support SEEK_END or ftello() (I think). Anyway, they can't
// be read through hz::File::get_contents, so use this function instead.
inline bool read_proc_partitions_file(hz::File& file, std::vector<std::string>& lines)
{
if (!file.open("rb")) // closed automatically
return false; // the error message is in File itself.
inline std::string detect_drives_dev(std::vector<std::string>& devices)
{
FILE* fp = file.get_handle();
if (!fp)
return false;
return std::string();
}
char line[256];
while (std::fgets(line, sizeof(line), fp) != NULL) {
if (*line != '\0')
lines.push_back(line);
}
return true;
}
inline std::string detect_drives_win32(std::vector<std::string>& devices)
{
// Linux (tested with 2.4 and 2.6) /proc/partitions. Parses the file, appends /dev to each entry.
// Note that file format changed from 2.4 to 2.6 (some statistics fields moved to another file).
// No /proc/partitions on at least freebsd, solaris and osx, afaik.
/*
Sample 1 (2.4, devfs, with statistics):
------------------------------------------------------------
# cat /proc/partitions
major minor #blocks name rio rmerge rsect ruse wio wmerge wsect wuse running use aveq
return std::string();
}
*/
3 0 60051600 ide/host0/bus0/target0/lun0/disc 72159 136746 1668720 467190 383039 658435 8342136 1659840 -429 17038808 22703194
3 1 1638598 ide/host0/bus0/target0/lun0/part1 0 0 0 0 0 0 0 0 0 0 0
3 2 1028160 ide/host0/bus0/target0/lun0/part2 0 0 0 0 0 0 0 0 0 0 0
3 3 3092512 ide/host0/bus0/target0/lun0/part3 0 0 0 0 0 0 0 0 0 0 0
3 4 1 ide/host0/bus0/target0/lun0/part4 0 0 0 0 0 0 0 0 0 0 0
3 5 514048 ide/host0/bus0/target0/lun0/part5 1458 7877 74680 9070 2140 18285 166272 42490 0 10100 52000
------------------------------------------------------------
Sample 2 (2.6):
------------------------------------------------------------
# cat /proc/partitions
major minor #blocks name
1 0 4096 ram0
8 0 156290904 sda
8 1 39070048 sda1
8 2 7560 sda2
8 3 1 sda3
------------------------------------------------------------
Sample 3 (2.6 on nokia n8xx, spaces may be missing):
------------------------------------------------------------
# cat /proc/partitions
major minor #blocks name
31 0 128 mtdblock0
31 1 384 mtdblock1
31 2 2048 mtdblock2
31 3 2048 mtdblock3
31 4 257536 mtdblock4
254 0 3932160 mmcblk0
254 1 3928064 mmcblk0p1
254 8 1966080 mmcblk1
254 9 2007032 mmcblk1p1
*/
inline std::string detect_drives_linux_proc_partitions(std::vector<std::string>& devices)
{
debug_out_info("app", DBG_FUNC_MSG << "Detecting through /proc/partitions...\n");
std::string parts_file;
if (!rconfig::get_data("system/linux_proc_partitions_path", parts_file) || parts_file.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Partitions file path is not set.\n");
return "Partitions file path is not set.";
}
hz::File file(parts_file);
// std::string contents;
std::vector<std::string> lines;
// if (!f.get_contents(contents)) {
if (!read_proc_partitions_file(file, lines)) { // this outputs to debug too
std::string error_msg = file.get_error_utf8(); // save before calling other file functions
if (!file.exists()) {
debug_out_warn("app", DBG_FUNC_MSG << "Partitions file doesn't exist.\n");
} else {
debug_out_error("app", DBG_FUNC_MSG << "Partitions file exists but cannot be read.\n");
}
return error_msg;
}
// debug_out_dump("app", DBG_FUNC_MSG << "Dumping partitions file:\n" << contents << "\n");
// hz::string_split(contents, '\n', lines, true);
std::vector<std::string> blacklist;
// fixme: not sure about how partitions are visible with twa0.
blacklist.push_back("/d[a-z][0-9]+$/"); // sda1, hdb2 - partitions. twa0 and twe1 are drives, not partitions.
blacklist.push_back("/ram[0-9]+$/"); // ramdisks?
blacklist.push_back("/loop[0-9]*$/"); // not sure if loop devices go there, but anyway...
blacklist.push_back("/part[0-9]+$/"); // devfs had them
blacklist.push_back("/p[0-9]+$/"); // partitions are usually marked this way
}
for (unsigned int i = 0; i < lines.size(); ++i) {
std::string line = hz::string_trim_copy(lines[i]);
if (line.empty() || app_pcre_match("/^major/", line)) // file header
continue;
std::string dev;
if (!app_pcre_match("/^[ \\t]*[^ \\t\\n]+[ \\t]+[^ \\t\\n]+[ \\t]+[^ \\t\\n]+[ \\t]+([^ \\t\\n]+)/", line, &dev) || dev.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Cannot parse line \"" << line << "\".\n");
continue;
}
// platform blacklist
bool blacked = false;
for (std::vector<std::string>::const_iterator iter = blacklist.begin(); iter != blacklist.end(); ++iter) {
if (app_pcre_match(*iter, dev)) {
blacked = true;
break;
}
}
if (blacked)
continue;
std::string path = "/dev/" + dev;
if (std::find(devices.begin(), devices.end(), path) == devices.end()) // there may be duplicates
devices.push_back(path);
}
return std::string();
}
#elif defined CONFIG_KERNEL_WINDOWS
// smartctl accepts various variants, the most straight being pdN,
// (or /dev/pdN, /dev being optional) where N comes from
// "\\.\PhysicalDriveN" (winnt only).
inline std::string detect_drives_win32(std::vector<std::string>& devices)
{
for (int drive_num = 0; ; ++drive_num) {
std::string name = hz::string_sprintf("\\\\.\\PhysicalDrive%d", drive_num);
// If the drive is openable, then it's there.
// NOTE: Administrative privileges are required to open it.
// Yes, CreateFile() is open, not create. Yes, it's silly (ah, win32...).
HANDLE h = CreateFile(name.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
// The numbers seem to be consecutive, so break on first invalid.
if (h == INVALID_HANDLE_VALUE)
break;
CloseHandle(h);
devices.push_back(hz::string_sprintf("pd%d", drive_num));
}
return std::string();
}
#else // FreeBSD, Solaris, etc... .
inline std::string detect_drives_dev(std::vector<std::string>& devices)
{
debug_out_info("app", DBG_FUNC_MSG << "Detecting through /dev...\n");
std::string sdev_config_path;
#ifdef CONFIG_KERNEL_SOLARIS
sdev_config_path = "system/solaris_dev_path";
#else // other unixes
sdev_config_path = "system/unix_sdev_path";
#endif
std::string dev_dir; // defaults to /dev for freebsd, /dev/rdsk for solaris.
if (!rconfig::get_data(sdev_config_path, dev_dir) || dev_dir.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Device directory path is not set.\n");
return "Device directory path is not set.";
}
hz::Dir dir(dev_dir);
std::vector<std::string> all_devices;
if (!dir.entry_list(all_devices, false, hz::DirSortNone())) { // this outputs to debug too.
std::string error_msg = dir.get_error_utf8();
if (!dir.exists()) {
debug_out_warn("app", DBG_FUNC_MSG << "Device directory doesn't exist.\n");
} else {
debug_out_error("app", DBG_FUNC_MSG << "Cannot list directory entries.\n");
}
return error_msg;
}
// platform whitelist
std::vector<std::string> whitelist;
#if defined CONFIG_KERNEL_FREEBSD || defined CONFIG_KERNEL_DRAGONFLY
// FreeBSD example device names:
// * /dev/ad0 - ide disk 0. /dev/ad0a - non-slice dos partition 1.
// * /dev/da1s1e - scsi disk 1 (second disk), slice 1 (aka dos partition 1),
// bsd partition e (multiple bsd partitions may be inside one dos partition).
// * /dev/acd0 - first cdrom. /dev/acd0c - ?.
// Info: http://www.freebsd.org/doc/en/books/handbook/disks-naming.html
// Of two machines I had access to, freebsd 6.3 had only real devices,
// while freebsd 4.10 had lots of dummy ones (ad1, ad2, ad3, da0, da1, da2, da3, sa0, ...).
whitelist.push_back("/^ad[0-9]+$/"); // adN without suffix - fbsd ide
whitelist.push_back("/^da[0-9]+$/"); // daN without suffix - fbsd scsi, usb
// whitelist.push_back("/ ^sa[0-9]+$/"); // saN without suffix - fbsd scsi tape
// whitelist.push_back("/^ast[0-9]+$/"); // astN without suffix - fbsd ide tape
whitelist.push_back("/^aacd[0-9]+$/"); // fbsd adaptec raid
whitelist.push_back("/^mlxd[0-9]+$/"); // fbsd mylex raid
whitelist.push_back("/^mlyd[0-9]+$/"); // fbsd mylex raid
whitelist.push_back("/^amrd[0-9]+$/"); // fbsd AMI raid
whitelist.push_back("/^idad[0-9]+$/"); // fbsd compaq raid
whitelist.push_back("/^twed[0-9]+$/"); // fbsd 3ware raid
// these are checked by smartctl, but they are not mentioned in freebsd docs:
whitelist.push_back("/^tw[ae][0-9]+$/"); // fbsd 3ware raid
// unused: acd - ide cdrom, cd - scsi cdrom, mcd - mitsumi cdrom, scd - sony cdrom,
// fd - floppy, fla - diskonchip flash.
#elif defined CONFIG_KERNEL_SOLARIS
// /dev/rdsk contains "raw" physical char devices, as opposed to
// "filesystem" block devices in /dev/dsk. smartctl needs rdsk.
// x86:
// /dev/rdsk/c0t0d0p0:1
// Where c0 is the controller number.
// t0 is the target (SCSI ID number) (omit for ATAPI)
// d0 is always 0 for SCSI, the drive # for ATAPI
// p0 is the partition (p0 is the entire disk, or p1 - p4)
// :1 is the logical drive (c - z or 1 - 24) (that is, logical partitions inside extended partition)
// /dev/rdsk/c0d0p2:1
// where p2 means the extended partition (type 0x05) is partition 2 (out of 1-4) and
// ":1" is the 2nd extended partition (:0 would be the first extended partition). -- not sure about this!
// p0 whole physical disk
// p1 - p4 Four primary fdisk partitions
// p5 - p30 26 logical drives in extended DOS partition (not implemented by Solaris)
// s0 - s15 16 slices in the Solaris FDISK partition (SPARC has only 8)
// On Solaris, by convention, s2 is the whole Solaris partition.
// SPARC: There are no *p* devices on sparc afaik, only slices. By convention,
// s2 is used as a "whole" disk there (but not with EFI partitions!).
// So, c0d0s2 is the whole disk there.
// x86 has both c0d0s2 and c0d0p2, where s2 is a whole solaris partition.
// NOTE: It seems that smartctl searches for s0 (aka root partition) in the end. I'm not sure
// what implications this has for x86, but we replicate this behaviour.
// Note: Cdroms are ATAPI, so they pose as SCSI, as opposed to IDE hds.
// TODO: No idea how to implement /dev/rmt (scsi tape devices),
// I have no files in that directory.
whitelist.push_back("/^c[0-9]+(?:t[0-9]+)?d[0-9]+s0$/");
#elif defined CONFIG_KERNEL_OPENBSD || defined CONFIG_KERNEL_NETBSD
// OpenBSD / NetBSD have /dev/wdNc for IDE/ATA, /dev/sdNc for SCSI disk,
// /dev/stNc for scsi tape. N is [0-9]+. "c" means "whole disk" (not sure about
// different architectures though). There are no "sdN" devices, only "sdNP".
// Another manual says that wd0d would be a whole disk, while wd0c is its
// bsd part only (on x86) (only on netbsd?). Anyway, getrawpartition() gives
// us the letter we need.
// There is no additional level of names for BSD subpartitions (unlike freebsd).
// cd0a is cdrom.
// Dummy devices are present.
// Note: This detection may take a while (probably due to open() check).
char whole_part = 'a' + getrawpartition(); // from bsd's util.h
whitelist.push_back(hz::string_sprintf("/^wd[0-9]+%c$/", whole_part));
whitelist.push_back(hz::string_sprintf("/^sd[0-9]+%c$/", whole_part));
whitelist.push_back(hz::string_sprintf("/^st[0-9]+%c$/", whole_part));
#elif defined CONFIG_KERNEL_DARWIN
// Darwin has /dev/disk0, /dev/disk0s1, etc...
// Only real devices are present, so no need for additional checks.
whitelist.push_back("/^disk[0-9]+$/");
#elif defined CONFIG_KERNEL_QNX
// QNX has /dev/hd0, /dev/hd0t78 (partition?).
// Afaik, IDE and SCSI have the same prefix. fd for floppy, cd for cdrom.
// Not sure about the tapes.
// Only real devices are present, so no need for additional checks.
whitelist.push_back("/^hd[0-9]+$/");
#endif // unix platforms
std::vector<std::string> matched_devices;
for (unsigned int i = 0; i < all_devices.size(); ++i) {
std::string entry = all_devices[i];
if (entry == "." || entry == "..")
continue;
bool matched = false;
for (std::vector<std::string>::const_iterator iter = whitelist.begin(); iter != whitelist.end(); ++iter) {
if (app_pcre_match(*iter, entry)) {
matched = true;
break;
}
}
if (!matched)
continue;
hz::FsPath path = dev_dir + hz::DIR_SEPARATOR_S + all_devices[i];
// In case these are links, trace them to originals.
// We don't replace /dev files with real devices - it leads to really bad paths (pci ids for solaris, etc...).
std::string link_dest;
hz::FsPath real_dev_path;
if (path.get_link_destination(link_dest)) {
real_dev_path = (hz::path_is_absolute(link_dest) ? link_dest : (dev_dir + hz::DIR_SEPARATOR_S + link_dest));
real_dev_path.compress(); // compress in case there are ../-s.
// solaris has dangling links for non-existant devices, so filter them out.
if (!real_dev_path.exists())
continue;
}
matched_devices.push_back(path.str());
}
// List ones who need dummy device filtering
#if defined CONFIG_KERNEL_FREEBSD || defined CONFIG_KERNEL_DRAGONFLY \
|| defined CONFIG_KERNEL_OPENBSD || defined CONFIG_KERNEL_NETBSD
// Since we may have encountered dummy devices, we should check
// if they really exist. Unfortunately, this involves opening them, which
// is not good with some media (e.g. it may hang on audio cd (freebsd, maybe others), etc...).
// That's why we don't whitelist cd devices. Let's hope usb and others are
// ok with this. Dummy devices give errno ENXIO (6, Device not configured) on *bsd.
// In Linux ENXIO is (6, No such device or address).
// Don't do this on solaris - we can't distinguish between cdroms and hds there.
// If there are less than 4 devices, they are probably not dummy (newer freebsd).
bool open_needed = (matched_devices.size() >= 4);
if (open_needed) {
debug_out_info("app", DBG_FUNC_MSG << "Number of matched devices is "
<< matched_devices.size() << ", will try to filter non-existent ones out.\n");
} else {
debug_out_info("app", DBG_FUNC_MSG << "Number of matched devices is "
<< matched_devices.size() << ", no need for filtering them out.\n");
}
for (unsigned int i = 0; i < matched_devices.size(); ++i) {
hz::File dev_file(matched_devices[i]);
// we check errno because OS may deny us for some other reason on valid devices.
if (open_needed && !dev_file.open("rb") && dev_file.get_errno() == ENXIO) {
debug_out_dump("app", DBG_FUNC_MSG << "Device \"" << dev_file.str() << "\" failed to open, ignoring.\n");
continue;
}
if (open_needed)
debug_out_dump("app", DBG_FUNC_MSG << "Device \"" << dev_file.str() << "\" opened successfully, adding to device list.\n");
devices.push_back(dev_file.str());
}
#else // not *BSD
for (unsigned int i = 0; i < matched_devices.size(); ++i) {
devices.push_back(matched_devices[i]);
}
#endif
hz::shell_sort(devices.begin(), devices.end());
return std::string();
}
#endif // platforms
} // anon. ns
@@ -279,6 +547,13 @@ std::string StorageDetector::detect(std::vector<StorageDeviceRefPtr>& drives)
// Try each one and move to next if it fails.
#if defined CONFIG_KERNEL_LINUX
// Disable by-id detection - it's unreliable on broken systems.
// For example, on Ubuntu 8.04, /dev/disk/by-id contains two device
// links for two drives, but both point to the same sdb (instead of
// sda and sdb). Plus, there are no "*-partN" files (not that we need them).
/*
if (!found) {
error_msg = detect_drives_linux_udev_byid(devices); // linux udev
// we check for devices vector emptiness because it could be a dummy directory
@@ -287,21 +562,35 @@ std::string StorageDetector::detect(std::vector<StorageDeviceRefPtr>& drives)
found = true;
}
}
*/
if (!found) {
error_msg = detect_drives_linux_proc_partitions(devices); // linux /proc/partitions as fallback.
if (error_msg.empty() && !devices.empty()) {
found = true;
}
}
/*
#elif defined CONFIG_KERNEL_WINDOWS
if (!found) {
error_msg = detect_drives_win32(devices); // win32
if (error_msg.empty() && !devices.empty()) {
found = true;
}
}
#else // freebsd, etc...
if (!found) {
error_msg = detect_drives_dev(devices); // bsd, etc... . scans /dev.
if (error_msg.empty() && !devices.empty()) {
found = true;
}
}
*/
#endif
if (!found) {
debug_out_warn("app", DBG_FUNC_MSG << "Cannot detect drives: None of the drive detection methods returned any drives.\n");
return error_msg; // last error message should be ok.
@@ -353,6 +642,12 @@ std::string StorageDetector::fetch_basic_data(std::vector<StorageDeviceRefPtr>&
fetch_data_errors_.clear();
fetch_data_error_outputs_.clear();
// If it doesn't exist, create a default one. Even though it will be auto-created later,
// we need it here to get its errors afterwards.
if (!smartctl_ex)
smartctl_ex = new SmartctlExecutor(); // will be auto-deleted
for (unsigned int i = 0; i < drives.size(); ++i) {
StorageDeviceRefPtr drive = drives[i];
debug_out_info("app", "Retrieving basic information about the device...\n");
+5 -2
View File
@@ -1,18 +1,21 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: Public Domain
License: See LICENSE_whatever.txt
***************************************************************************/
#include <iostream>
#include "storage_device.h"
#include "storage_detector.h"
#include "gsc_settings.h" // in src directory
int main()
{
// These settings contain device search paths, smartctl binary, etc...
init_default_settings();
std::vector<StorageDeviceRefPtr> drives;
// std::vector<std::string> match_patterns;
std::vector<std::string> blacklist_patterns; // additional parameters
+43 -22
View File
@@ -4,6 +4,8 @@
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#include <glibmm.h> // Glib::shell_quote()
#include "rconfig/rconfig_mini.h"
#include "hz/string_algo.h" // string_trim_copy
#include "hz/fs_path.h" // FsPath
@@ -29,19 +31,21 @@ std::string StorageDevice::fetch_basic_data_and_parse(hz::intrusive_ptr<CmdexSyn
std::string output;
// We don't use "--all" - it may cause really screwed up the output (tests, etc...).
// This looks just like "--info" only on non-smart devices.
std::string error_msg = execute_smartctl("--info --health --capabilities", smartctl_ex, output);
std::string error_msg = execute_smartctl("-i -H -c", smartctl_ex, output); // --info --health --capabilities
if (!error_msg.empty())
return error_msg;
this->info_output_ = output;
return this->parse_basic_data();
// Set some properties too - they are needed for e.g. AODC status, etc...
return this->parse_basic_data(true);
}
// note: this will clear the non-basic properties!
std::string StorageDevice::parse_basic_data(bool emit_signal)
std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_signal)
{
this->clear_fetched(false); // clear everything fetched before, except outputs
@@ -52,9 +56,9 @@ std::string StorageDevice::parse_basic_data(bool emit_signal)
std::string version = SmartctlParser::parse_version(this->info_output_);
if (version.empty()) // is this smartctl data at all?
return "Cannot extract smartctl version information.";
return "Cannot get smartctl version information.";
// detect type. note: we can't distinguish between sata and scsi.
// detect type. note: we can't distinguish between sata and scsi (on linux, for -d ata switch).
if (app_pcre_match("/this device: CD\\/DVD/mi", info_output_)) {
this->set_type(type_cddvd);
@@ -72,7 +76,8 @@ std::string StorageDevice::parse_basic_data(bool emit_signal)
// Don't put complete messages here - they change across smartctl versions.
if (app_pcre_match("/^SMART support is:[ \\t]*Unavailable/mi", info_output_) // cdroms output this
|| app_pcre_match("/Device does not support SMART/mi", info_output_)) { // usb flash drives output this
|| app_pcre_match("/Device does not support SMART/mi", info_output_) // usb flash drives, non-smart hds
|| app_pcre_match("/Device Read Identity Failed \\(not an ATA\\/ATAPI device\\)/mi", info_output_)) { // solaris scsi, unsupported by smartctl
smart_supported_ = false;
smart_enabled_ = false;
@@ -112,12 +117,13 @@ std::string StorageDevice::parse_basic_data(bool emit_signal)
// try to parse the properties. ignore its errors - we already got what we came for.
SmartctlParser ps;
if (ps.parse_full(this->info_output_)) { // try to parse it
this->set_properties(ps.get_properties()); // copy to our drive, overwriting old data
if (do_set_properties) {
SmartctlParser ps;
if (ps.parse_full(this->info_output_)) { // try to parse it
this->set_properties(ps.get_properties()); // copy to our drive, overwriting old data
}
}
if (emit_signal)
signal_changed.emit(this); // notify listeners
@@ -135,7 +141,13 @@ std::string StorageDevice::fetch_data_and_parse(hz::intrusive_ptr<CmdexSync> sma
this->clear_fetched(); // clear everything fetched before, including outputs
std::string output;
std::string error_msg = execute_smartctl("--all", smartctl_ex, output);
// std::string error_msg = execute_smartctl("-a", smartctl_ex, output); // --all
// instead of -a, we use all the individual options -a encompasses, so that
// an addition to default -a output won't affect us.
// IDE equivalent of -a:
std::string error_msg = execute_smartctl("-H -i -c -A -l error -l selftest -l selective", smartctl_ex, output);
// SCSI equivalent of -a:
// std::string error_msg = execute_smartctl("-H -i -A -l error -l selftest", smartctl_ex, output);
if (!error_msg.empty())
return error_msg;
@@ -153,13 +165,15 @@ std::string StorageDevice::parse_data()
this->clear_fetched(false); // clear everything fetched before, except outputs
SmartctlParser ps;
if (ps.parse_full(this->full_output_)) { // try to parse it
if (ps.parse_full(this->full_output_)) { // try to parse it (parse only, set the properties after basic parsing).
// refresh basic info too
this->info_output_ = ps.get_data_full(); // put data including version information
// note: this will clear the non-basic properties!
this->parse_basic_data(false); // don't emit signal, we're not complete yet.
// this will parse some info that is already parsed by SmartctlParser::parse_full(),
// but this one sets the StorageDevice class members, not properties.
this->parse_basic_data(false, false); // don't emit signal, we're not complete yet.
// set the full properties
this->set_fully_parsed(true);
@@ -179,7 +193,7 @@ std::string StorageDevice::parse_data()
// proper parsing failed. try to at least extract info section
this->info_output_ = this->full_output_; // complete output here. sometimes it's only the info section
if (!this->parse_basic_data().empty()) { // this will emit signal_changed
if (!this->parse_basic_data(true).empty()) { // will add some properties too. this will emit signal_changed.
return ps.get_error_msg(); // return full parser's error messages - they are more detailed.
}
@@ -210,7 +224,8 @@ A mandatory SMART command failed: exiting. To continue, add one or more '-T perm
*/
std::string output;
std::string error_msg = execute_smartctl((b ? "--smart=on --saveauto=on" : "--smart=off"), smartctl_ex, output);
// --smart=on --saveauto=on, --smart=off
std::string error_msg = execute_smartctl((b ? "-s on -S on" : "-s off"), smartctl_ex, output);
if (!error_msg.empty())
return error_msg;
@@ -245,7 +260,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_smartctl((b ? "--offlineauto=on" : "--offlineauto=off"), smartctl_ex, output);
// --offlineauto=on, --offlineauto=off
std::string error_msg = execute_smartctl((b ? "-o on" : "-o off"), smartctl_ex, output);
if (!error_msg.empty())
return error_msg;
@@ -408,11 +424,16 @@ std::string StorageDevice::execute_smartctl(const std::string& command_options,
}
std::string device = get_device();
std::string::size_type pos = device.rfind('/'); // find basename
if (pos == std::string::npos) {
debug_out_error("app", DBG_FUNC_MSG << "Invalid device name \"" << device << "\".\n");
return "Invalid device name specified.";
#ifndef _WIN32 // win32 doesn't have slashes in devices names
{
std::string::size_type pos = device.rfind('/'); // find basename
if (pos == std::string::npos) {
debug_out_error("app", DBG_FUNC_MSG << "Invalid device name \"" << device << "\".\n");
return "Invalid device name specified.";
}
}
#endif
if (!smartctl_ex) // if it doesn't exist, create a default one
smartctl_ex = new SmartctlExecutor(); // will be auto-deleted
@@ -436,8 +457,8 @@ std::string StorageDevice::execute_smartctl(const std::string& command_options,
device_specific_options += " ";
smartctl_ex->set_command(smartctl_binary,
smartctl_def_options + device_specific_options + command_options + " " + device);
smartctl_ex->set_command(Glib::shell_quote(smartctl_binary),
smartctl_def_options + device_specific_options + command_options + " " + Glib::shell_quote(device));
if (!smartctl_ex->execute() || !smartctl_ex->get_error_msg().empty()) {
+3 -3
View File
@@ -30,7 +30,7 @@ class StorageDevice : public hz::intrusive_ptr_referenced {
// to display the correct icon
enum type_t {
type_unknown, // unknown. will be autodetected by smartctl
type_cddvd, // unsupported by smartctl
type_cddvd // unsupported by smartctl
// type_pata, // old pata
// we can't differentiate between these.
@@ -55,7 +55,7 @@ class StorageDevice : public hz::intrusive_ptr_referenced {
status_enabled, // smart, aodc
status_disabled, // smart, aodc
status_unsupported, // smart, aodc
status_unknown, // aodc - supported but unknown if it's enabled or not.
status_unknown // aodc - supported but unknown if it's enabled or not.
};
static std::string get_status_name(status_t status, bool use_yesno = false)
@@ -155,7 +155,7 @@ class StorageDevice : public hz::intrusive_ptr_referenced {
// detects type, smart support, smart status (on / off).
// note: this will clear the non-basic properties!
std::string parse_basic_data(bool emit_signal = true);
std::string parse_basic_data(bool do_set_properties = true, bool emit_signal = true);
// execute smartctl --all (all sections), get output, parse it, fill properties.
+9 -9
View File
@@ -45,7 +45,7 @@ class StorageAttribute {
enum attr_t {
attr_type_unknown,
attr_type_prefail, // reported: Pre-fail
attr_type_oldage, // reported: Old_age
attr_type_oldage // reported: Old_age
};
static std::string get_attr_type_name(attr_t a)
@@ -62,7 +62,7 @@ class StorageAttribute {
enum update_t {
update_type_unknown,
update_type_always, // reported: Always
update_type_offline, // reported: Offline
update_type_offline // reported: Offline
};
static std::string get_update_type_name(update_t a)
@@ -80,7 +80,7 @@ class StorageAttribute {
fail_time_unknown,
fail_time_none, // reported: -
fail_time_past, // reported: In_the_past
fail_time_now, // reported: FAILING_NOW
fail_time_now // reported: FAILING_NOW
};
static std::string get_fail_time_name(fail_time_t a)
@@ -108,8 +108,8 @@ class StorageAttribute {
attr_t attr_type;
update_t update_type;
fail_time_t when_failed;
std::string raw_value;
int64_t raw_value_int; // same as raw_value, but parsed as int.
std::string raw_value; // as presented by smartctl (formatted).
int64_t raw_value_int; // same as raw_value, but parsed as int. original value is 6 bytes I think.
};
@@ -245,7 +245,7 @@ class StorageProperty {
value_type_capability, // for "capabilities" subsection (non-time-interval blocks only)
value_type_attribute, // for "attributes" subsection
value_type_error_block, // for "error_log" subsection
value_type_selftest_entry, // for "selftest_log" subsection
value_type_selftest_entry // for "selftest_log" subsection
};
static std::string get_value_type_name(value_type_t type)
@@ -270,7 +270,7 @@ class StorageProperty {
section_unknown, // use when searching in all sections
section_info, // Short info (--info)
section_data, // SMART DATA
section_internal, // internal application-specific data
section_internal // internal application-specific data
};
static std::string get_section_name(section_t s)
@@ -293,7 +293,7 @@ class StorageProperty {
subsection_attributes, // Attributes (-A, --attributes). These need decoding.
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
subsection_selective_selftest_log // Selective self-test log and settings
};
static std::string get_subsection_name(subsection_t s)
@@ -315,7 +315,7 @@ class StorageProperty {
warning_none,
warning_notice, // a known attribute is somewhat disturbing, but no smart error
warning_warn, // smart warning is raised by old-age attribute
warning_alert, // smart warning is raised by pre-fail attribute, and similar errors
warning_alert // smart warning is raised by pre-fail attribute, and similar errors
};
+70 -20
View File
@@ -4,6 +4,9 @@
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#include "hz/string_algo.h" // string_replace_copy
#include "applib/app_pcrecpp.h"
#include "storage_property_descr.h"
@@ -15,6 +18,7 @@ namespace {
return (p.generic_name.empty() ? (p.reported_name == name) : (p.generic_name == name));
}
inline bool auto_set(StorageProperty& p, const std::string& name, const char* descr)
{
if (name_match(p, name)) {
@@ -25,18 +29,49 @@ namespace {
}
// pass -1 as attr_id to match any attribute property
inline bool attr_match(StorageProperty& p, int32_t attr_id)
{
return (p.value_type == StorageProperty::value_type_attribute && p.value_attribute.id == attr_id);
return (p.value_type == StorageProperty::value_type_attribute &&
(p.value_attribute.id == attr_id || attr_id == -1));
}
inline bool auto_set_attr(StorageProperty& p, int32_t attr_id, const char* attr_name, const char* descr)
{
if (attr_match(p, attr_id)) {
p.set_description(std::string("<b>") + attr_name + "</b>\n" + descr);
return true;
if (!attr_match(p, attr_id))
return false;
bool has_descr = bool(descr);
if (!has_descr) {
p.set_description("No description is available for this attribute");
}
return false;
std::string smartctl_name = hz::string_replace_copy(p.reported_name, '_', ' ');
if (attr_id == -1) { // not in our database
// use smartctl-reported name. replace "_"'s with spaces.
p.readable_name = smartctl_name;
if (has_descr)
p.set_description(descr); // "no description" tooltip
} else {
bool known_by_smartctl = !app_pcre_match("/Unknown_Attribute/i", p.reported_name);
bool smartctl_is_same = app_pcre_match("/^" + app_pcre_escape(smartctl_name) + "$/i", attr_name);
p.readable_name = attr_name;
if (has_descr) {
p.set_description(std::string("<b>") + attr_name + "</b>"
+ ((known_by_smartctl && !smartctl_is_same) ?
("\n<small>Reported by smartctl as <b>\"" + smartctl_name + "\"</b></small>\n") : "")
+ "\n" + descr);
}
}
return true;
}
}
@@ -85,10 +120,10 @@ bool storage_property_autoset_description(StorageProperty& p)
// Reallocated Sector Count
|| auto_set_attr(p, 5, "Reallocated Sector Count", "Number of reallocated sectors (Raw value). Non-zero Raw value indicates a disk surface failure. "
"\n\nWhen a drive encounters a surface error, it marks that sector as &quot;unstable&quot; (also known as &quot;pending reallocation&quot;). "
"If the sector is successfully read from or written to at some later point, it is unmarked. However, if the sector continues to be inaccessible, "
"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 &quot;bad blocks&quot; "
"on modern drives - if you do, it means they could not be remapped yet, or the drive is out of reserved area.")
"on modern drives - if you do, it means that either they have not been remapped yet, or the drive is out of reserved area.")
// Read Channel Margin
|| auto_set_attr(p, 6, "Read Channel Margin", "Margin of a channel while reading data. The function of this attribute is not specified.")
// Seek Error Rate
@@ -105,8 +140,12 @@ bool storage_property_autoset_description(StorageProperty& p)
|| auto_set_attr(p, 12, "Power Cycle Count", "Number of complete power start/stop cycles of a drive.")
// Soft Read Error Rate (same as 201 ?) (description sounds lame, fix?)
|| auto_set_attr(p, 13, "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.")
// End to End Error (description?)
|| auto_set_attr(p, 184, "End to End Error", NULL)
// Reported Uncorrectable (seagate). anyone knows what it means? (no guesses please)
|| auto_set_attr(p, 187, "Reported Uncorrectable", "Exact meaning of this attribute is vendor-specific.")
|| auto_set_attr(p, 187, "Reported Uncorrectable", NULL)
// Command Timeout (description?)
|| auto_set_attr(p, 188, "Command Timeout", NULL)
// High Fly Writes (description?)
|| auto_set_attr(p, 189, "High Fly Writes", "Number of times the recording head is flying outside its normal operating range.")
// Airflow Temperature (WD Caviar (may be 50 less), Samsung). Temperature or (100 - temp.) on Seagate/Maxtor.
@@ -114,45 +153,45 @@ bool storage_property_autoset_description(StorageProperty& p)
// G-sense error rate (same as 221?)
|| auto_set_attr(p, 191, "G-Sense Error Rate", "Number of errors resulting from externally-induced shock and vibration (Raw value). May indicate incorrect installation.")
// Power-Off Retract Cycle, Fujitsu: Emergency Retract Cycle Count
|| auto_set_attr(p, 192, "Power-Off Retract Cycle / Emergency Retract Cycle Count", "Number of times the heads were loaded off the media (during power-offs or emergency conditions).")
|| auto_set_attr(p, 192, "Emergency Retract Cycle Count", "Number of times the heads were loaded off the media (during power-offs or emergency conditions).")
// Load/Unload Cycle
|| auto_set_attr(p, 193, "Load/Unload Cycle", "Number of load/unload cycles into Landing Zone position.")
// Temperature Celsius (same as 231). This is the most common one. Some Samsungs: 10xTemp.
|| auto_set_attr(p, 194, "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).")
// Hardware ECC Recovered, Fujitsu: ECC_On_The_Fly_Count
|| auto_set_attr(p, 195, "Hardware ECC Recovered / ECC On The Fly Count", "Number of ECC on the fly errors (Raw value). Users are advised to ignore this attribute.")
|| auto_set_attr(p, 195, "Hardware ECC Recovered", "Number of ECC on the fly errors (Raw value). Users are advised to ignore this attribute.")
// Reallocation Event Count
|| auto_set_attr(p, 196, "Reallocation Event Count", "Number of reallocation (remap) operations. Raw value <i>should</i> show the total number of attempts (both successful and unsuccessful) to reallocate sectors. An increase in Raw value indicates a disk surface failure. "
"\n\nWhen a drive encounters a surface error, it marks that sector as &quot;unstable&quot; (also known as &quot;pending reallocation&quot;). "
"If the sector is successfully read from or written to at some later point, it is unmarked. However, if the sector continues to be inaccessible, "
"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 &quot;bad blocks&quot; "
"on modern drives - if you do, it means they could not be remapped yet, or the drive is out of reserved area.")
"on modern drives - if you do, it means that either they have not been remapped yet, or the drive is out of reserved area.")
// Current Pending Sector Count
|| auto_set_attr(p, 197, "Current Pending Sector Count", "Number of &quot;unstable&quot; (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\nWhen a drive encounters a surface error, it marks that sector as &quot;unstable&quot; (also known as &quot;pending reallocation&quot;). "
"If the sector is successfully read from or written to at some later point, it is unmarked. However, if the sector continues to be inaccessible, "
"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 &quot;bad blocks&quot; "
"on modern drives - if you do, it means they could not be remapped yet, or the drive is out of reserved area.")
"on modern drives - if you do, it means that either they have not been remapped yet, or the drive is out of reserved area.")
// Offline Uncorrectable, Fujitsu: Off-line_Scan_UNC_Sector_Ct
|| auto_set_attr(p, 198, "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\nWhen a drive encounters a surface error, it marks that sector as &quot;unstable&quot; (also known as &quot;pending reallocation&quot;). "
"If the sector is successfully read from or written to at some later point, it is unmarked. However, if the sector continues to be inaccessible, "
"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 &quot;bad blocks&quot; "
"on modern drives - if you do, it means they could not be remapped yet, or the drive is out of reserved area.")
"on modern drives - if you do, it means that either they have not been remapped yet, or the drive is out of reserved area.")
// UDMA CRC Error Count
|| auto_set_attr(p, 199, "UDMA CRC Error Count", "Number of errors in data transfer via the interface cable in UDMA mode, as determined by ICRC (Interface Cyclic Redundancy Check) (Raw value).")
// Fujitsu: Write Error Rate, WD: Multi-Zone Error Rate. (maybe head flying height too (?))
|| auto_set_attr(p, 200, "Write Error Count / Multi-Zone Error Rate", "Number of errors found when writing to sectors (Raw value). The higher the value, the worse the disk surface condition and/or mechanical subsystem is.")
|| auto_set_attr(p, 200, "Write Error Count", "Number of errors found when writing to sectors (Raw value). The higher the value, the worse the disk surface condition and/or mechanical subsystem is.")
// Soft Read Error Rate / Maxtor: Off Track Errors / Fujitsu: Detected TA Count (description?)
|| auto_set_attr(p, 201, "Soft Read Error Rate / Off Track Errors", "Uncorrected read errors reported to the operating system (Raw value). If the value is non-zero, you should back up your data.")
|| auto_set_attr(p, 201, "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.")
// Data Address Mark Errors / TA Increase Count (same as 227?)
|| auto_set_attr(p, 202, "TA Increase Count / Data Address Mark Errors", "This attribute may mean several different things. TA Increase Count: Number of attempts to compensate for platter speed variations. Data Address Mark Errors: Frequency of the Data Address Mark errors.")
// Run Out Cancel. (description ?), Maxtor: ECC Errors
|| auto_set_attr(p, 203, "Run Out Cancel / ECC Errors", "Number of ECC errors.")
|| auto_set_attr(p, 203, "ECC Errors", "Number of ECC errors.")
// Shock_Count_Write_Opern (description?), Maxtor: Soft ECC Correction
|| auto_set_attr(p, 204, "Soft ECC Correction", "Number of errors corrected by software ECC.")
// Thermal Asperity Rate (TAR)
@@ -165,6 +204,12 @@ bool storage_property_autoset_description(StorageProperty& p)
|| auto_set_attr(p, 208, "Spin Buzz", "Number of buzz routines to spin up the drive.")
// Offline Seek Performance (description?)
|| auto_set_attr(p, 209, "Offline Seek Performance", "Seek performance during Offline Data Collection operations")
// Vibration During Write. wikipedia says 211, but it's wrong. (description?)
|| auto_set_attr(p, 210, "Vibration During Write", NULL)
// Vibration During Read. (description?)
|| auto_set_attr(p, 211, "Vibration During Read", NULL)
// Shock During Write (description?)
|| auto_set_attr(p, 212, "Shock During Write", NULL)
// Disk Shift / Temperature Celsius (temperature again? which drives?)
|| auto_set_attr(p, 220, "Disk Shift", "Shift of disks towards spindle. Shift of disks is possible as a result of a strong shock or a fall, or for other reasons.")
// G-sense error rate
@@ -184,13 +229,18 @@ bool storage_property_autoset_description(StorageProperty& p)
// Power-Off Retract Count
|| auto_set_attr(p, 228, "Power-off Retract Count", "Number of times the magnetic armature was retracted automatically as a result of cutting power.")
// GMR Head Amplitude (IBM)
|| auto_set_attr(p, 229, "GMR Head Amplitude", "Amplitude of heads trembling (GMR-head) in running mode.")
|| auto_set_attr(p, 230, "GMR Head Amplitude", "Amplitude of heads trembling (GMR-head) in running mode.")
// Temperature (Some drives)
|| auto_set_attr(p, 231, "Temperature", "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).")
// Head Flying Hours
|| auto_set_attr(p, 240, "Head Flying Hours", "Time while head is positioning.")
// Read Error Retry Rate (description?)
|| auto_set_attr(p, 250, "Read Error Retry Rate", "Number of read retries.")
// Free Fall Protection (seagate laptop drives)
|| auto_set_attr(p, 254, "Free Fall Protection", "Number of \"Free Fall Events\" detected.")
// all unknown attributes (not in our DB)
|| auto_set_attr(p, -1, "", NULL)
;
+38 -4
View File
@@ -85,6 +85,19 @@ GscExecutorLogWindow::GscExecutorLogWindow(BaseObjectType* gtkcobj, const app_ui
}
// Hide command text entry in win32.
// Setting text on this entry segfaults under win32, (utf8 conversion doesn't
// help, not that it should). Seems to be connected to non-english locale.
// Surprisingly, the treeview column text still works.
// Any solutions?
#ifdef _WIN32
Gtk::HBox* command_hbox = this->lookup_widget<Gtk::HBox*>("command_hbox");
if (command_hbox)
command_hbox->hide();
#endif
// ---------------
// Connect to CmdexSync signal
@@ -225,7 +238,7 @@ void GscExecutorLogWindow::on_window_save_all_button_clicked()
exss << "\n\n\n------------------------- EXECTION LOG -------------------------\n\n\n";
for (unsigned int i = 0; i < entries.size(); ++i) {
exss << "\n\n\n------------------------- EXECUTED COMMAND " << i << " -------------------------\n\n";
exss << "\n\n\n------------------------- EXECUTED COMMAND " << (i+1) << " -------------------------\n\n";
exss << "\n---------------" << "Command" << "---------------\n";
exss << entries[i]->command << "\n";
exss << "\n---------------" << "Parameters" << "---------------\n";
@@ -304,12 +317,29 @@ void GscExecutorLogWindow::on_tree_selection_changed()
CmdexSyncCommandInfoRefPtr entry = row[col_entry];
Gtk::TextView* output_textview = this->lookup_widget<Gtk::TextView*>("output_textview");
Gtk::Entry* command_entry = this->lookup_widget<Gtk::Entry*>("command_entry");
if (output_textview) {
Glib::RefPtr<Gtk::TextBuffer> buffer = output_textview->get_buffer();
if (buffer) {
buffer->set_text(entry->std_output);
// Under win32, we can't execute smartctl under C locale. Smartctl
// uses locale information only for thousands separator in User Capacity.
// We can parse that, but we need to insert that text into a textarea
// widget, which may error out on invalid utf8 char.
// So, we convert the whole output to utf8, hoping that the result is ok.
// Note that a separator converted to utf8 may be a different sequence
// of chars, so we parse it as original charset, but insert into a textarea
// as utf8.
std::string buf_text = entry->std_output;
#ifdef _WIN32
try {
buf_text = Glib::locale_to_utf8(buf_text);
} catch (Glib::ConvertError& e) {
buf_text = ""; // inserting invalid utf8 may trigger a segfault, so empty better.
}
#endif
buffer->set_text(buf_text);
Glib::RefPtr<Gtk::TextTag> tag;
Glib::RefPtr<Gtk::TextTagTable> table = buffer->get_tag_table();
@@ -323,9 +353,13 @@ void GscExecutorLogWindow::on_tree_selection_changed()
}
}
#ifndef _WIN32
Gtk::Entry* command_entry = this->lookup_widget<Gtk::Entry*>("command_entry");
if (command_entry) {
command_entry->set_text(entry->command + " " + entry->parameters);
std::string cmd_text = entry->command + " " + entry->parameters;
command_entry->set_text(cmd_text);
}
#endif
Gtk::Button* window_save_current_button = this->lookup_widget<Gtk::Button*>("window_save_current_button");
if (window_save_current_button)
+3 -3
View File
@@ -82,13 +82,13 @@ GscHelpWindow::GscHelpWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& re
std::string readme = hz::string_any_to_unix_copy(ReadmeTextResData().get_string());
// split by double-newlines
// Paragraphs are delimited by 3 empty lines
std::vector<std::string> topics;
hz::string_split(readme, "\n\n\n", topics, true); // skip empty
hz::string_split(readme, "\n\n\n\n", topics, true); // skip empty
// add to treeview and textview
// Add to treeview and textview
Gtk::TextView* content = this->lookup_widget<Gtk::TextView*>("content_textview");
+36 -16
View File
@@ -12,7 +12,7 @@
#include "hz/down_cast.h"
#include "hz/string_num.h" // number_to_string
#include "hz/string_format.h" // string_sprintf
#include "hz/string_algo.h" // string_join, string_replace_copy
#include "hz/string_algo.h" // string_join
#include "hz/fs_file.h" // hz::File
#include "hz/format_unit.h" // format_time_length
#include "rconfig/rconfig_mini.h" // rconfig::*
@@ -281,12 +281,13 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests
return;
}
} else {
std::string error_msg = drive->parse_data();
if (!error_msg.empty()) {
gui_show_error_dialog("Cannot interpret SMART data", error_msg, this);
return;
}
// it's already parsed
// } else {
// std::string error_msg = drive->parse_data();
// if (!error_msg.empty()) {
// gui_show_error_dialog("Cannot interpret SMART data", error_msg, this);
// return;
// }
}
}
@@ -309,16 +310,21 @@ 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_label = 0;
if ((note_label = lookup_widget("capabilities_tab_vbox")) != 0)
if ((note_label = lookup_widget("capabilities_tab_vbox")) != 0) {
if (smart_enabled) { note_label->show(); } else { note_label->hide(); }
if ((note_label = lookup_widget("attributes_tab_vbox")) != 0)
}
if ((note_label = lookup_widget("attributes_tab_vbox")) != 0) {
if (smart_enabled) { note_label->show(); } else { note_label->hide(); }
if ((note_label = lookup_widget("error_log_tab_vbox")) != 0)
}
if ((note_label = lookup_widget("error_log_tab_vbox")) != 0) {
if (smart_enabled) { note_label->show(); } else { note_label->hide(); }
if ((note_label = lookup_widget("selftest_log_tab_vbox")) != 0)
}
if ((note_label = lookup_widget("selftest_log_tab_vbox")) != 0) {
if (smart_enabled) { note_label->show(); } else { note_label->hide(); }
if ((note_label = lookup_widget("test_tab_vbox")) != 0)
}
if ((note_label = lookup_widget("test_tab_vbox")) != 0) {
if (smart_enabled) { note_label->show(); } else { note_label->hide(); }
}
}
@@ -649,7 +655,7 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests
Gtk::TreeRow row = *(list_store->append());
row[col_id] = iter->value_attribute.id;
row[col_name] = hz::string_replace_copy(iter->readable_name, '_', ' '); // names have unnecessary underscores
row[col_name] = iter->readable_name;
row[col_flag_value] = iter->value_attribute.flag; // it's a string, not int.
row[col_value] = hz::number_to_string(iter->value_attribute.value);
row[col_worst] = hz::number_to_string(iter->value_attribute.worst);
@@ -1019,7 +1025,7 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests
row = *(test_combo_model->append());
row[test_combo_col_name] = SelfTest::get_test_name(SelfTest::type_long);
row[test_combo_col_description] =
"Extended self-test examines complete disk surface and performs various test routines "
"Extended self-test examines complete disk surface and performs various test routines"
" built into the drive. Its result is reported in the Self-test Log.";
row[test_combo_col_self_test] = test_long;
}
@@ -1246,7 +1252,17 @@ void GscInfoWindow::on_view_output_button_clicked()
{
GscTextWindow<SmartctlOutputInstance>* win = GscTextWindow<SmartctlOutputInstance>::create();
// make save visible and enable monospace font
win->set_text("Smartctl Output", this->drive->get_full_output(), true, true);
std::string buf_text = this->drive->get_full_output();
// We receive locale'd thousands separators in win32, so convert them.
#ifdef _WIN32
try {
buf_text = Glib::locale_to_utf8(buf_text);
} catch (Glib::ConvertError& e) {
buf_text = ""; // inserting invalid utf8 may trigger a segfault, so empty better.
}
#endif
win->set_text("Smartctl Output", buf_text, true, true);
std::string filename = drive->get_save_filename();
if (!filename.empty())
@@ -1467,6 +1483,10 @@ void GscInfoWindow::test_loop()
} else {
result_msg = "<b>Test result: </b>" + StorageSelftestEntry::get_status_name(status) + ".";
// It may not reach 100% somehow, so do it manually.
if (test_completion_progressbar)
test_completion_progressbar->set_fraction(1.); // yes, we're at 100% anyway (at least logically).
}
}
@@ -1591,7 +1611,7 @@ 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.
#if !APP_GTKMM_CHECK_VERSION(2, 10, 0)
#if APP_GTKMM_CHECK_VERSION(2, 10, 0)
this->set_deletable(!test_active); // since 2.10
#endif
}
+46 -15
View File
@@ -5,7 +5,9 @@
***************************************************************************/
#include <string>
#include <locale> // std::locale
// #include <locale> // std::locale
#include <clocale> // std::setlocale
// #include <locale.h> // _configthreadlocale (win32)
#include <cstdio> // std::printf
#include <vector>
#include <sstream>
@@ -19,12 +21,13 @@
// #include <gtkmm.h>
// #include <iostream>
#include "hz/hz_config.h" // ENABLE_GLIB, VERSION, DEBUG_BUILD
#include "libdebug/libdebug.h" // include full libdebug here (to add domains, etc...)
#include "rconfig/rconfig.h" // include full rconfig here
#include "hz/data_file.h" // data_file_add_search_directory
#include "hz/fs_tools.h" // get_home_dir
#include "hz/fs_path.h" // FsPath
#include "hz/hz_config.h" // ENABLE_GLIB
#include "hz/string_algo.h" // string_join
#include "gsc_main_window.h"
@@ -200,7 +203,11 @@ bool app_init_and_loop(int& argc, char**& argv)
{
// glib needs this for command line args. It will be reset by Gtk::Main later.
std::locale::global(std::locale("")); // set locale to system LANG
// This aborts on freebsd with "locale::facet::_S_create_c_locale name not valid",
// so use the C equivalent.
// std::locale::global(std::locale("")); // set locale to system LANG
std::setlocale(LC_ALL, "");
// initialize GThread (for mutexes, etc... to work). Must be called before any other glib function.
@@ -213,6 +220,13 @@ bool app_init_and_loop(int& argc, char**& argv)
return true;
}
// Note: parsing gtk option context is initializes gtk, so
// gtk_disable_setlocale() won't work here.
if (!args.arg_locale) {
std::setlocale(LC_ALL, "C"); // set classic locale. otherwise we're already in user locale.
}
if (args.arg_version) {
// show version information and exit
app_print_version_info();
@@ -268,8 +282,21 @@ bool app_init_and_loop(int& argc, char**& argv)
app_init_config();
debug_out_info("app", "Current locale: " << std::setlocale(LC_ALL, NULL) << "\n");
// Initialize GTK+
Gtk::Main m(argc, argv, args.arg_locale);
// Gtk::Main m(argc, argv, args.arg_locale);
Gtk::Main m(argc, argv);
#ifdef _WIN32
// Now that all program-specific locale setup has been performed,
// make sure the future locale changes affect only current thread.
// Not available on mingw, so disable for now.
// _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
#endif
// Redirect all GTK+/Glib and related messages to libdebug
@@ -305,21 +332,25 @@ bool app_init_and_loop(int& argc, char**& argv)
// set default icon for all windows.
// set_default_icon_name is available since 2.12 in gtkmm, but since 2.6 in gtk.
// we load it via icontheme to provide multi-size version.
GtkIconTheme* default_icon_theme = gtk_icon_theme_get_default();
#ifndef _WIN32 // win32 version has its icon compiled-in.
{
// we load it via icontheme to provide multi-size version.
GtkIconTheme* default_icon_theme = gtk_icon_theme_get_default();
// application-installed, /usr/share/icons/<theme_name>/apps/<size>
if (gtk_icon_theme_has_icon(default_icon_theme, "gsmartcontrol")) {
gtk_window_set_default_icon_name("gsmartcontrol");
// application-installed, /usr/share/icons/<theme_name>/apps/<size>
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_icon_theme_has_icon(default_icon_theme, "gnome-dev-harddisk")) {
gtk_window_set_default_icon_name("gnome-dev-harddisk");
// try the gnome icon, it's higher quality / resolution
} 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 built-in, always available
} else {
gtk_window_set_default_icon_name("gtk-harddisk");
}
}
#endif
// Export some command line arguments to rmn
+177 -128
View File
@@ -62,7 +62,10 @@ GscMainWindow::GscMainWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& re
do {
std::string smartctl_binary, smartctl_def_options;
rconfig::get_data("system/smartctl_binary", smartctl_binary);
rconfig::get_data("system/smartctl_options", smartctl_def_options);
// Don't use default options here - they are used when invoked
// with a device option.
// rconfig::get_data("system/smartctl_options", smartctl_def_options);
if (smartctl_binary.empty()) {
error_msg = "Smartctl binary is not specified in configuration.";
@@ -70,14 +73,15 @@ GscMainWindow::GscMainWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& re
break;
}
if (!smartctl_def_options.empty())
smartctl_def_options += " ";
// if (!smartctl_def_options.empty())
// smartctl_def_options += " ";
SmartctlExecutorGui ex;
ex.create_running_dialog(this);
ex.set_running_msg("Checking if smartctl is executable...");
ex.set_command(smartctl_binary, smartctl_def_options + "--version");
// ex.set_command(Glib::shell_quote(smartctl_binary), smartctl_def_options + "-V"); // --version
ex.set_command(Glib::shell_quote(smartctl_binary), "-V"); // --version
if (!ex.execute() || !ex.get_error_msg().empty()) {
error_msg = ex.get_error_msg();
@@ -97,40 +101,49 @@ GscMainWindow::GscMainWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& re
} while (false);
if (!error_msg.empty()) {
bool smartctl_valid = error_msg.empty();
if (!smartctl_valid) {
gsc_executor_error_dialog_show("There was an error while executing smartctl",
error_msg + "\n\n<i>Please specify the correct smartctl binary in Preferences.</i>",
this, show_output_button, true);
return;
}
// Scan
populate_iconview();
populate_iconview(smartctl_valid);
}
void GscMainWindow::populate_iconview()
void GscMainWindow::populate_iconview(bool smartctl_valid)
{
if (rconfig::get_data<bool>("gui/scan_on_startup") // config option
if (!smartctl_valid) {
iconview->empty_view_message = GscMainWindowIconView::message_no_smartctl;
iconview->clear_all(); // the message won't be shown without invalidating the region.
while (Gtk::Main::events_pending()) // give expose event the time it needs
Gtk::Main::iteration();
} else if (rconfig::get_data<bool>("gui/scan_on_startup") // config option
&& !rconfig::get_data<bool>("/runtime/gui/force_no_scan_on_startup")) { // command-line option
rescan_devices(); // scan for devices and fill the iconview
} else {
iconview->empty_view_message = GscMainWindowIconView::message_scan_disabled;
iconview->clear_all(); // the message won't be shown without invalidating the region.
while (Gtk::Main::events_pending()) // give expose event the time it needs
Gtk::Main::iteration();
}
// Add command-line-requested devices and virtual drives.
std::vector<std::string> load_devices;
if (rconfig::get_data("/runtime/gui/add_devices_on_startup", load_devices)) {
for (unsigned int i = 0; i < load_devices.size(); ++i) {
if (!load_devices[i].empty()) {
add_device(load_devices[i]);
if (smartctl_valid) {
std::vector<std::string> load_devices;
if (rconfig::get_data("/runtime/gui/add_devices_on_startup", load_devices)) {
for (unsigned int i = 0; i < load_devices.size(); ++i) {
if (!load_devices[i].empty()) {
add_device(load_devices[i]);
}
}
}
}
@@ -234,8 +247,8 @@ bool GscMainWindow::create_widgets()
actiongroup_main->add(Gtk::Action::create("file_menu", "_File"));
action = Gtk::Action::create(APP_ACTION_NAME(action_quit), Gtk::Stock::QUIT);
actiongroup_main->add(action, Gtk::AccelKey("<control>Q"),
sigc::bind(sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_quit), action));
actiongroup_main->add((action_map[action_quit] = action), Gtk::AccelKey("<control>Q"),
sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_quit));
actiongroup_main->add(Gtk::Action::create("device_menu", "_Device"));
@@ -245,88 +258,88 @@ bool GscMainWindow::create_widgets()
#else
action = Gtk::Action::create(APP_ACTION_NAME(action_view_details), "_View details", "View detailed information");
#endif
actiongroup_device->add(action, Gtk::AccelKey("<control>V"),
sigc::bind(sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_view_details), action));
actiongroup_device->add((action_map[action_view_details] = action), Gtk::AccelKey("<control>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).");
action->connect_proxy(*lookup_widget<Gtk::CheckButton*>("status_smart_enabled_check"));
actiongroup_device->add(action, Gtk::AccelKey("<control>M"),
sigc::bind(sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_enable_smart), action));
actiongroup_device->add((action_map[action_enable_smart] = action), Gtk::AccelKey("<control>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");
action->connect_proxy(*lookup_widget<Gtk::CheckButton*>("status_aodc_enabled_check"));
actiongroup_device->add(action, Gtk::AccelKey("<control>F"),
sigc::bind(sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_enable_aodc), action));
actiongroup_device->add((action_map[action_enable_aodc] = action), Gtk::AccelKey("<control>F"),
sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_enable_aodc));
action = Gtk::Action::create(APP_ACTION_NAME(action_reread_device_data), Gtk::Stock::REFRESH, "R_e-read Data",
"Re-read basic SMART data");
actiongroup_device->add(action, Gtk::AccelKey("<control>E"),
sigc::bind(sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_reread_device_data), action));
actiongroup_device->add((action_map[action_reread_device_data] = action), Gtk::AccelKey("<control>E"),
sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_reread_device_data));
action = Gtk::Action::create(APP_ACTION_NAME(action_perform_tests), "Perform _Tests...",
"Perform various self-tests on the drive");
actiongroup_device->add(action, Gtk::AccelKey("<control>T"),
sigc::bind(sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_perform_tests), action));
actiongroup_device->add((action_map[action_perform_tests] = action), Gtk::AccelKey("<control>T"),
sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_perform_tests));
action = Gtk::Action::create(APP_ACTION_NAME(action_remove_device), Gtk::Stock::REMOVE, "Re_move Added Device",
"Remove previously added device");
actiongroup_device->add(action, Gtk::AccelKey("<control>W"),
sigc::bind(sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_remove_device), action));
actiongroup_device->add((action_map[action_remove_device] = action), Gtk::AccelKey("<control>W"),
sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_remove_device));
action = Gtk::Action::create(APP_ACTION_NAME(action_remove_virtual_device), Gtk::Stock::REMOVE, "Re_move Virtual Device",
"Remove previously loaded virtual device");
actiongroup_device->add(action, Gtk::AccelKey("Delete"),
sigc::bind(sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_remove_virtual_device), action));
actiongroup_device->add((action_map[action_remove_virtual_device] = action), Gtk::AccelKey("Delete"),
sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_remove_virtual_device));
// ---
action = Gtk::Action::create(APP_ACTION_NAME(action_add_device), Gtk::Stock::OPEN, "_Add Device...",
"Manually add device to device list");
actiongroup_main->add(action, Gtk::AccelKey("<control>D"),
sigc::bind(sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_add_device), action));
actiongroup_main->add((action_map[action_add_device] = action), Gtk::AccelKey("<control>D"),
sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_add_device));
action = Gtk::Action::create(APP_ACTION_NAME(action_load_virtual), Gtk::Stock::OPEN, "L_oad Smartctl Output as Virtual Device...",
"Load smartctl output from a text file as a read-only virtual device");
actiongroup_main->add(action, Gtk::AccelKey("<control>O"),
sigc::bind(sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_load_virtual), action));
actiongroup_main->add((action_map[action_load_virtual] = action), Gtk::AccelKey("<control>O"),
sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_load_virtual));
action = Gtk::Action::create(APP_ACTION_NAME(action_rescan_devices), Gtk::Stock::REFRESH, "_Re-scan Device List",
"Re-scan device list");
actiongroup_main->add(action, Gtk::AccelKey("<control>R"),
sigc::bind(sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_rescan_devices), action));
actiongroup_main->add((action_map[action_rescan_devices] = action), Gtk::AccelKey("<control>R"),
sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_rescan_devices));
actiongroup_main->add(Gtk::Action::create("options_menu", "_Options"));
action = Gtk::Action::create(APP_ACTION_NAME(action_executor_log), "View Execution Log");
actiongroup_main->add(action,
sigc::bind(sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_executor_log), action));
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_preferences), Gtk::Stock::PREFERENCES);
actiongroup_main->add(action, Gtk::AccelKey("<alt>P"),
sigc::bind(sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_preferences), action));
actiongroup_main->add((action_map[action_preferences] = action), Gtk::AccelKey("<alt>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_general_help), Gtk::Stock::HELP, "A_ll Help Topics");
actiongroup_main->add(action, Gtk::AccelKey("F1"),
sigc::bind(sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_general_help), action));
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_permission_problems), "Resolving _Permission Problems");
actiongroup_main->add(action,
sigc::bind(sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_permission_problems), action));
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,
sigc::bind(sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_how_to_enable_smart), action));
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,
sigc::bind(sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_report_bug), action));
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,
sigc::bind(sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_about), action));
actiongroup_main->add((action_map[action_about] = action),
sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_about));
@@ -411,11 +424,25 @@ bool GscMainWindow::create_widgets()
void GscMainWindow::on_action_activated(Glib::RefPtr<Gtk::Action> action, GscMainWindow::action_t action_type)
// NOTE: Do NOT bind the Glib::RefPtr<Gtk::Action> parameter to this function.
// Doing so causes valgrind errors on window destroy (and Send Report dialogs on win32).
// Probably a gtkmm bug. Use action maps or Gtk::Action* instead.
void GscMainWindow::on_action_activated(GscMainWindow::action_t action_type)
{
if (!this->action_handling_enabled_) // check if we should do something
return;
if (action_map.find(action_type) == action_map.end()) {
debug_out_error("app", DBG_FUNC_MSG << "Invalid action activated: " << static_cast<int>(action_type) << ".\n");
return;
}
Glib::RefPtr<Gtk::Action> action = action_map[action_type];
if (!action) {
debug_out_error("app", DBG_FUNC_MSG << "Action is NULL for action type " << static_cast<int>(action_type) << ".\n");
return;
}
debug_out_info("app", DBG_FUNC_MSG << "Action activated: \"" << action->get_name() << "\"\n");
switch (action_type) {
@@ -634,6 +661,7 @@ void GscMainWindow::on_action_enable_aodc_toggled(Gtk::ToggleAction* action)
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
dialog.set_position(Gtk::WIN_POS_CENTER_ON_PARENT);
@@ -739,92 +767,87 @@ void GscMainWindow::set_drive_menu_status(StorageDeviceRefPtr drive)
// bogus toggle actions, etc...
this->action_handling_enabled_ = false;
// if no drive is selected or if a test is being run on selected drive, disallow.
bool device_active = (drive && !drive->get_test_is_active());
do { // for quick skipping
actiongroup_device->set_sensitive(device_active);
// if no drive is selected or if a test is being run on selected drive, disallow.
if (!drive || drive->get_test_is_active()) {
actiongroup_device->set_sensitive(false);
break; // nothing else to do here
}
// smart toggle status
{
Gtk::ToggleAction* action = hz::down_cast<Gtk::ToggleAction*>(
actiongroup_device->get_action(APP_ACTION_NAME(action_enable_smart)).operator->());
if (action) {
StorageDevice::status_t status = StorageDevice::status_unsupported;
if (drive)
status = drive->get_smart_status();
// make everything sensitive, then disable one by one
actiongroup_device->set_sensitive(true);
if (status == StorageDevice::status_unsupported) {
action->set_active(false);
action->set_sensitive(false);
} else { // smart supported
action->set_sensitive(true);
action->set_active(status == StorageDevice::status_enabled);
bool is_virtual = (drive && drive->get_is_virtual());
StorageDevice::status_t smart_status = StorageDevice::status_unsupported;
StorageDevice::status_t aodc_status = StorageDevice::status_unsupported;
if (drive && !is_virtual) {
smart_status = drive->get_smart_status();
aodc_status = drive->get_aodc_status();
}
// Sensitivity and visibility manipulation.
// Do this first, then do the enable / disable stuff.
{
Glib::RefPtr<Gtk::Action> action;
if ((action = actiongroup_device->get_action(APP_ACTION_NAME(action_perform_tests))))
action->set_sensitive(smart_status == StorageDevice::status_enabled);
if ((action = actiongroup_device->get_action(APP_ACTION_NAME(action_reread_device_data))))
action->set_visible(drive && !is_virtual);
if ((action = actiongroup_device->get_action(APP_ACTION_NAME(action_remove_device)))) {
action->set_visible(drive && drive->get_is_manually_added());
// action->set_sensitive(drive && drive->get_is_manually_added());
}
if ((action = actiongroup_device->get_action(APP_ACTION_NAME(action_remove_virtual_device))))
action->set_visible(drive && is_virtual);
if ((action = actiongroup_device->get_action(APP_ACTION_NAME(action_enable_smart))))
action->set_sensitive(smart_status != StorageDevice::status_unsupported);
if ((action = actiongroup_device->get_action(APP_ACTION_NAME(action_enable_aodc))))
action->set_sensitive(smart_status != StorageDevice::status_unsupported);
}
// smart toggle status
{
Gtk::ToggleAction* action = hz::down_cast<Gtk::ToggleAction*>(
actiongroup_device->get_action(APP_ACTION_NAME(action_enable_smart)).operator->());
if (action) {
action->set_active(smart_status == StorageDevice::status_enabled);
}
}
}
// aodc toggle status
{
Gtk::ToggleAction* action = hz::down_cast<Gtk::ToggleAction*>(
actiongroup_device->get_action(APP_ACTION_NAME(action_enable_aodc)).operator->());
if (action) {
StorageDevice::status_t status = StorageDevice::status_unsupported;
if (drive)
status = drive->get_aodc_status();
// aodc toggle status
{
Gtk::ToggleAction* action = hz::down_cast<Gtk::ToggleAction*>(
actiongroup_device->get_action(APP_ACTION_NAME(action_enable_aodc)).operator->());
// add some more accelerators (in addition to existing ones)
Gtk::CheckMenuItem* dev_odc_item = hz::down_cast<Gtk::CheckMenuItem*>(ui_manager->get_widget(
"/main_menubar/device_menu/" APP_ACTION_NAME(action_enable_aodc)));
Gtk::CheckMenuItem* popup_odc_item = hz::down_cast<Gtk::CheckMenuItem*>(ui_manager->get_widget(
"/device_popup/" APP_ACTION_NAME(action_enable_aodc)));
Gtk::CheckButton* status_aodc_check = lookup_widget<Gtk::CheckButton*>("status_aodc_enabled_check");
if (action) {
Gtk::CheckMenuItem* dev_odc_item = hz::down_cast<Gtk::CheckMenuItem*>(ui_manager->get_widget(
"/main_menubar/device_menu/" APP_ACTION_NAME(action_enable_aodc)));
Gtk::CheckMenuItem* popup_odc_item = hz::down_cast<Gtk::CheckMenuItem*>(ui_manager->get_widget(
"/device_popup/" APP_ACTION_NAME(action_enable_aodc)));
Gtk::CheckButton* status_aodc_check = lookup_widget<Gtk::CheckButton*>("status_aodc_enabled_check");
// true if supported, but unknown whether it's enabled or not.
if (dev_odc_item)
dev_odc_item->set_inconsistent(status == StorageDevice::status_unknown);
if (popup_odc_item)
popup_odc_item->set_inconsistent(status == StorageDevice::status_unknown);
if (status_aodc_check)
status_aodc_check->set_inconsistent(status == StorageDevice::status_unknown);
// true if supported, but unknown whether it's enabled or not.
if (dev_odc_item)
dev_odc_item->set_inconsistent(aodc_status == StorageDevice::status_unknown);
if (popup_odc_item)
popup_odc_item->set_inconsistent(aodc_status == StorageDevice::status_unknown);
if (status_aodc_check)
status_aodc_check->set_inconsistent(aodc_status == StorageDevice::status_unknown);
if (status == StorageDevice::status_unsupported) {
action->set_active(false);
action->set_sensitive(false);
} else if (status == StorageDevice::status_unknown) {
action->set_sensitive(true);
action->set_active(false); // doesn't really matter what state
} else { // supported, known status
action->set_sensitive(true);
action->set_active(status == StorageDevice::status_enabled);
// for unknown it doesn't really matter what state it's in.
action->set_active(aodc_status == StorageDevice::status_enabled);
}
}
}
} while (false);
// hide some entries in virtual and vice versa
{
bool normal = (!drive || !drive->get_is_virtual()); // hide virtual stuff for non-drives, it's pretty long.
Glib::RefPtr<Gtk::Action> action;
if ((action = actiongroup_device->get_action(APP_ACTION_NAME(action_perform_tests))))
action->set_sensitive(normal);
if ((action = actiongroup_device->get_action(APP_ACTION_NAME(action_reread_device_data))))
action->set_visible(normal);
if ((action = actiongroup_device->get_action(APP_ACTION_NAME(action_remove_device)))) {
action->set_visible(drive && drive->get_is_manually_added());
// action->set_sensitive(drive && drive->get_is_manually_added());
}
if ((action = actiongroup_device->get_action(APP_ACTION_NAME(action_remove_virtual_device))))
action->set_visible(!normal);
if ((action = actiongroup_device->get_action(APP_ACTION_NAME(action_enable_smart))))
action->set_sensitive(normal);
if ((action = actiongroup_device->get_action(APP_ACTION_NAME(action_enable_aodc))))
action->set_sensitive(normal);
}
// re-enable action handling
this->action_handling_enabled_ = true;
@@ -928,7 +951,9 @@ void GscMainWindow::rescan_devices()
iconview->empty_view_message = GscMainWindowIconView::message_scanning;
iconview->clear_all(); // clear previous icons
iconview->clear_all(); // clear previous icons, invalidate region to update the message.
while (Gtk::Main::events_pending()) // give expose event the time it needs
Gtk::Main::iteration();
this->drives.clear();
@@ -989,6 +1014,7 @@ void GscMainWindow::rescan_devices()
bool GscMainWindow::add_device(const std::string& file)
{
#ifndef _WIN32 // win32 doesn't have device files, so skip the check
hz::File f(file);
if (!f.exists()) {
std::string msg = f.get_error_utf8(); // only errors are reported
@@ -996,6 +1022,7 @@ bool GscMainWindow::add_device(const std::string& file)
(msg.empty() ? ("Device \"" + file + "\" doesn't exist.") : msg), this);
return false;
}
#endif
StorageDeviceRefPtr d(new StorageDevice(file));
d->set_is_manually_added(true);
@@ -1143,11 +1170,30 @@ GscInfoWindow* GscMainWindow::show_device_info_window(StorageDeviceRefPtr drive)
void GscMainWindow::show_add_device_chooser()
{
#ifdef _WIN32
// A simple text entry dialog, since there are no device files on win32.
static std::string last_str;
if (last_str.empty())
last_str = "pd0";
std::string dev;
if (gui_show_text_entry_dialog("Add Device", "Enter a device name",
"For example, pd0 means the first physical drive",
dev, last_str, this, false)) {
last_str = dev; // safe for the future
this->add_device(dev);
}
#else // non-win32:
// Show a file chooser
static std::string last_dir;
#ifndef _WIN32
if (last_dir.empty())
last_dir = "/dev";
#endif
Gtk::FileChooserDialog dialog(*this, "Choose Device File...",
Gtk::FILE_CHOOSER_ACTION_OPEN);
@@ -1181,6 +1227,9 @@ void GscMainWindow::show_add_device_chooser()
debug_out_error("app", DBG_FUNC_MSG << "Unknown dialog response code: " << result << ".\n");
break;
}
#endif // non-win32
}
+6 -3
View File
@@ -7,6 +7,7 @@
#ifndef GSC_MAIN_WINDOW_H
#define GSC_MAIN_WINDOW_H
#include <map>
#include <gtkmm/window.h>
#include <gtkmm/menu.h>
#include <gtkmm/uimanager.h>
@@ -97,7 +98,7 @@ class GscMainWindow : public AppUIResWidget<GscMainWindow, false> {
action_permission_problems,
action_how_to_enable_smart,
action_report_bug,
action_about,
action_about
};
@@ -105,7 +106,7 @@ class GscMainWindow : public AppUIResWidget<GscMainWindow, false> {
bool create_widgets(); // iconview, gtkuimanager stuff (menus), custom labels
void populate_iconview(); // scan and populate iconview widget with drive icons
void populate_iconview(bool smartctl_valid); // scan and populate iconview widget with drive icons
void show_add_device_chooser();
@@ -121,6 +122,7 @@ class GscMainWindow : public AppUIResWidget<GscMainWindow, false> {
Glib::RefPtr<Gtk::ActionGroup> actiongroup_main;
Glib::RefPtr<Gtk::ActionGroup> actiongroup_device;
bool action_handling_enabled_;
std::map<action_t, Glib::RefPtr<Gtk::Action> > action_map; // used by on_action_activated().
WrappingLabel* name_label;
WrappingLabel* health_label;
@@ -139,7 +141,8 @@ class GscMainWindow : public AppUIResWidget<GscMainWindow, false> {
// ---------- other callbacks
void on_action_activated(Glib::RefPtr<Gtk::Action> action, action_t action_type);
// void on_action_activated(Glib::RefPtr<Gtk::Action> action, action_t action_type);
void on_action_activated(action_t action_type);
void on_action_enable_smart_toggled(Gtk::ToggleAction* action);
+2
View File
@@ -38,6 +38,7 @@ class GscMainWindowIconView : public Gtk::IconView {
message_scan_disabled,
message_scanning,
message_no_drives_found,
message_no_smartctl
};
@@ -147,6 +148,7 @@ class GscMainWindowIconView : public Gtk::IconView {
case message_scan_disabled: msg = "Automatic scanning is disabled.\nPress Ctrl+R to scan manually."; break;
case message_scanning: msg = "Scanning system, please wait..."; break;
case message_no_drives_found: msg = "No drives found."; break;
case message_no_smartctl: msg = "Please specify the correct smartctl binary in\nPreferences and press Ctrl-R to re-scan."; break;
default: msg = "[error - invalid message]";
}
+4 -2
View File
@@ -34,8 +34,10 @@ inline void init_default_settings()
rconfig::set_default_data("system/smartctl_options", ""); // default options on ALL commands
rconfig::set_default_data("system/smartctl_device_options", ""); // dev1:val1;dev2:val2;... format, each bin2ascii-encoded.
rconfig::set_default_data("system/linux_udev_byid_path", "/dev/disk/by-id"); // hard disk device links here
rconfig::set_default_data("system/linux_proc_partitions_path", "/proc/partitions"); // file in /proc/partitions format
rconfig::set_default_data("system/linux_udev_byid_path", "/dev/disk/by-id"); // linux hard disk device links here
rconfig::set_default_data("system/linux_proc_partitions_path", "/proc/partitions"); // file in linux /proc/partitions format
rconfig::set_default_data("system/solaris_dev_path", "/dev/rdsk"); // path to /dev/rdsk for solaris.
rconfig::set_default_data("system/unix_sdev_path", "/dev"); // path to /dev. used by other unices
// rconfig::set_default_data("system/device_match_patterns", ""); // semicolon-separated PCRE patterns
rconfig::set_default_data("system/device_blacklist_patterns", ""); // semicolon-separated PCRE patterns
+29
View File
@@ -0,0 +1,29 @@
//#include <winver.h>
biocalcon ICON "gsmartcontrol.ico"
1 VERSIONINFO
FILEVERSION 0,0,0,0
PRODUCTVERSION 0,0,0,0
// FILEOS VOS__WINDOWS32
// FILETYPE VFT_FONT
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "04090025"
BEGIN
VALUE "FileDescription", "GSmartControl - Hard disk drive health inspection tool"
VALUE "FileVersion", "@VERSION@"
VALUE "InternalName", "gsmartcontrol.exe"
VALUE "LegalCopyright", "Copyright (C) 2008 Alexander Shaduri"
VALUE "OriginalFilename", "gsmartcontrol.exe"
VALUE "ProductName", "GSmartControl"
VALUE "ProductVersion", "@VERSION@"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 1033, 37
END
END
+2 -1
View File
@@ -20,6 +20,7 @@ down_cast_test_SOURCES = down_cast_test.cpp
errno_string_test_SOURCES = errno_string_test.cpp
format_unit_test_SOURCES = format_unit_test.cpp
fs_dir_test_SOURCES = fs_dir_test.cpp
fs_file_test_SOURCES = fs_file_test.cpp
fs_path_test_SOURCES = fs_path_test.cpp
string_algo_test_SOURCES = string_algo_test.cpp
string_format_test_SOURCES = string_format_test.cpp
@@ -34,7 +35,7 @@ tls_test_SOURCES = tls_test.cpp
noinst_PROGRAMS =
if ENABLE_TESTS
noinst_PROGRAMS += any_type_test bin2ascii_encoder_test down_cast_test errno_string_test \
format_unit_test fs_dir_test fs_path_test string_algo_test string_num_test string_format_test \
format_unit_test fs_dir_test fs_file_test fs_path_test string_algo_test string_num_test string_format_test \
string_wcmatch_test sync_test sync_multilock_test tls_test
endif
+1 -1
View File
@@ -1,7 +1,7 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: Public Domain
License: See LICENSE_whatever.txt
***************************************************************************/
// disable libdebug, we don't link to it
+5 -2
View File
@@ -98,7 +98,8 @@ T ascii_strtoi(const char* nptr, char** endptr, int base)
const char* s = nptr;
typename hz::type_make_unsigned<T>::type acc = 0;
typename hz::type_make_unsigned<T>::type cutoff = 0;
int any = 0, cutlim = 0;
typename hz::type_make_signed<T>::type cutlim = 0;
int any = 0;
char c = 0;
bool neg = false;
@@ -188,7 +189,8 @@ T ascii_strtoi(const char* nptr, char** endptr, int base)
if (c >= base) {
break;
}
if ((any < 0) || acc > cutoff || (acc == cutoff && c > cutlim)) {
if ((any < 0) || acc > cutoff || (acc == cutoff
&& static_cast<typename hz::type_make_signed<T>::type>(c) > cutlim)) {
any = -1;
} else {
any = 1;
@@ -256,6 +258,7 @@ namespace internal {
errno = ERANGE;
// infinity is defined as builtin hugeval in limits. HUGE_VALF seems to be C99 only.
return std::numeric_limits<float>::infinity();
}
// negative overflow
if (val < -static_cast<double>(std::numeric_limits<float>::max())) {
+1 -1
View File
@@ -1,7 +1,7 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: Public Domain
License: See LICENSE_whatever.txt
***************************************************************************/
// disable libdebug, we don't link to it
+60 -13
View File
@@ -9,6 +9,12 @@
#include "hz_config.h" // feature macros
#include <cstdio> // std::fprintf(), std::vfprintf()
#ifndef __GNUC__
#include <cstdarg> // std::va_start, va_list macro and friends
#endif
/*
#include <cassert>
@@ -139,31 +145,72 @@
std::cerr << "<fatal> [" << (domain) << "] " << output
// The ## part is needed to avoid requirement of at least one argument after "format".
#define debug_print_dump(domain, format, ...) \
std::fprintf(stderr, (std::string("<dump> [") + (domain) + "] " + format).c_str(), ## __VA_ARGS__)
#ifdef __GNUC__
#define debug_print_info(domain, format, ...) \
std::fprintf(stderr, (std::string("<info> [") + (domain) + "] " + format).c_str(), ## __VA_ARGS__)
// The "trim trailing comma" and "##" extensions are GNU features (works with intel too).
// The ## part is needed to avoid requirement of at least one argument after "format".
#define debug_print_warn(domain, format, ...) \
std::fprintf(stderr, (std::string("<warn> [") + (domain) + "] " + format).c_str(), ## __VA_ARGS__)
#define debug_print_dump(domain, format, ...) \
std::fprintf(stderr, (std::string("<dump> [") + (domain) + "] " + format).c_str(), ## __VA_ARGS__)
#define debug_print_error(domain, format, ...) \
std::fprintf(stderr, (std::string("<error> [") + (domain) + "] " + format).c_str(), ## __VA_ARGS__)
#define debug_print_info(domain, format, ...) \
std::fprintf(stderr, (std::string("<info> [") + (domain) + "] " + format).c_str(), ## __VA_ARGS__)
#define debug_print_warn(domain, format, ...) \
std::fprintf(stderr, (std::string("<warn> [") + (domain) + "] " + format).c_str(), ## __VA_ARGS__)
#define debug_print_error(domain, format, ...) \
std::fprintf(stderr, (std::string("<error> [") + (domain) + "] " + format).c_str(), ## __VA_ARGS__)
#define debug_print_fatal(domain, format, ...) \
std::fprintf(stderr, (std::string("<fatal> [") + (domain) + "] " + format).c_str(), ## __VA_ARGS__)
#else // non-gcc compilers:
namespace hz {
namespace internal {
inline void debug_print_impl(const std::string& header, const char* format, ...)
{
std::va_list ap;
va_start(ap, format);
std::vfprintf(stderr, (header + format).c_str(), ap);
va_end(ap);
}
}
}
#define debug_print_dump(domain, ...) \
hz::internal::debug_print_impl(std::string("<dump> [") + (domain) + "] ", __VA_ARGS__)
#define debug_print_info(domain, ...) \
hz::internal::debug_print_impl(std::string("<info> [") + (domain) + "] ", __VA_ARGS__)
#define debug_print_warn(domain, ...) \
hz::internal::debug_print_impl(std::string("<warn> [") + (domain) + "] ", __VA_ARGS__)
#define debug_print_error(domain, ...) \
hz::internal::debug_print_impl(std::string("<error> [") + (domain) + "] ", __VA_ARGS__)
#define debug_print_fatal(domain, ...) \
hz::internal::debug_print_impl(std::string("<fatal> [") + (domain) + "] ", __VA_ARGS__)
#endif
#define debug_print_fatal(domain, format, ...) \
std::fprintf(stderr, (std::string("<fatal> [") + (domain) + "] " + format).c_str(), ## __VA_ARGS__)
#define DBG_FILE __FILE__
#define DBG_LINE __LINE__
#ifdef HAVE_CXX__FUNC
#if defined HAVE_CXX___func__
#define DBG_FUNC_NAME __func__
#else
#elif defined HAVE_CXX___FUNCTION__
#define DBG_FUNC_NAME __FUNCTION__
#else
#define DBG_FUNC_NAME "unknown"
#endif
#ifdef __GNUC__
+2 -2
View File
@@ -1,7 +1,7 @@
/**************************************************************************
Copyright:
(C) 2003 - 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: Public Domain
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_whatever.txt
***************************************************************************/
// disable libdebug, we don't link to it
+1 -1
View File
@@ -1,7 +1,7 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: Public Domain
License: See LICENSE_whatever.txt
***************************************************************************/
// disable libdebug, we don't link to it
+4 -6
View File
@@ -16,9 +16,9 @@
# include <typeinfo> // std::type_info
#endif
#if defined ENABLE_GLIBMM
# include <glibmm/error.h> // for Glib::Error specialization
#endif
// #if defined ENABLE_GLIBMM
// # include <glibmm/error.h> // for Glib::Error specialization
// #endif
#include "debug.h" // DBG_ASSERT
#include "errno_string.h" // hz::errno_string
@@ -35,8 +35,6 @@
// utf8 string messages and Glib::Error specialization). Note that this
// will also enable GLIB.
// Note that __GXX_RTTI (not used here) is available for gcc >= 4.3.
namespace hz {
@@ -55,7 +53,7 @@ struct ErrorLevel {
info = 1 << 1, // default
warn = 1 << 2,
error = 1 << 3,
fatal = 1 << 4,
fatal = 1 << 4
};
};
+3 -2
View File
@@ -38,13 +38,14 @@
// Note: The exceptions MUST have a what() member function
// for this to work.
// Do NOT put ex into parentheses. gcc-3.3 gives syntax errors about that (huh?).
#define THROW_FATAL(ex) \
throw (ex)
throw ex
// same as above, but no abort() in case of no-exceptions
#define THROW_WARN(ex) \
throw (ex)
throw ex
#else // no-exceptions alternative:
+1 -1
View File
@@ -1,7 +1,7 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: Public Domain
License: See LICENSE_whatever.txt
***************************************************************************/
// disable libdebug, we don't link to it
+177 -46
View File
@@ -10,6 +10,7 @@
#include "hz_config.h" // feature macros
#include <string>
#include <vector>
#include <sys/types.h> // dirent needs this
#include <dirent.h>
@@ -131,9 +132,23 @@ namespace internal {
// No filtering - leave all entries.
struct DirFilterNone {
bool operator() (const std::string& dir, const std::string& entry_name)
// whether to use operator() with FsPaths instead of strings.
bool use_path_objects()
{
return true; // no filtering
return false;
}
// no filtering
bool operator() (const std::string& entry_name)
{
return true;
}
// dummy
bool operator() (FsPath& path)
{
return true;
}
};
@@ -157,18 +172,27 @@ struct DirFilterByFlags {
DirFilterByFlags(int flags) : flags_(flags)
{ }
bool operator() (const std::string& dir, const std::string& entry_name)
// whether to use operator() with FsPaths instead of strings.
bool use_path_objects()
{
FsPath p(dir);
p.append(entry_name);
return true;
}
if ((flags_ & DIR_LEAVE_FILE) && p.is_file())
// dummy
bool operator() (const std::string& entry_name)
{
return false;
}
bool operator() (FsPath& path)
{
if ((flags_ & DIR_LEAVE_FILE) && path.is_file())
return true;
if ((flags_ & DIR_LEAVE_DIR) && p.is_dir())
if ((flags_ & DIR_LEAVE_DIR) && path.is_dir())
return true;
if ((flags_ & DIR_LEAVE_REGULAR) && p.is_regular())
if ((flags_ & DIR_LEAVE_REGULAR) && path.is_regular())
return true;
if ((flags_ & DIR_LEAVE_SYMLINK) && p.is_symlink())
if ((flags_ & DIR_LEAVE_SYMLINK) && path.is_symlink())
return true;
return false;
@@ -187,11 +211,23 @@ struct DirFilterWc {
DirFilterWc(const std::string pattern) : pattern_(pattern)
{ }
bool operator() (const std::string& dir, const std::string& entry_name)
// whether to use operator() with FsPaths instead of strings.
bool use_path_objects()
{
return false;
}
bool operator() (const std::string& entry_name)
{
return hz::string_wcmatch(pattern_, entry_name);
}
// dummy
bool operator() (FsPath& path)
{
return true;
}
private:
std::string pattern_;
};
@@ -216,11 +252,23 @@ struct DirSortNone {
void set_dir(const std::string& dir)
{ }
// "less" function
// whether to use operator() with FsPaths instead of strings.
bool use_path_objects()
{
return false;
}
// "less" function using entry names
bool operator() (const std::string& entry_name1, const std::string& entry_name2)
{
return true; // always return "less", which should cause no sorting.
}
// "less" function using path objects
bool operator() (FsPath& path1, FsPath& path2)
{
return true; // always return "less", which should cause no sorting.
}
};
@@ -228,6 +276,7 @@ struct DirSortNone {
// Base class for various sorters
template<class Child>
struct DirSortBase {
DirSortBase(int flag) : flag_(flag)
{ }
@@ -237,20 +286,22 @@ struct DirSortBase {
dir_ = dir;
}
// "less" function
// for DIR_SORT_MIXED only!
bool operator() (const std::string& entry_name1, const std::string& entry_name2)
{
if (flag_ == DIR_SORT_MIXED)
return static_cast<Child*>(this)->compare(entry_name1, entry_name2);
return static_cast<Child*>(this)->compare(entry_name1, entry_name2);
}
FsPath p1(dir_), p2(dir_);
p1.append(entry_name1);
p2.append(entry_name2);
bool e1_dir = p1.is_dir(), e2_dir = p2.is_dir();
// "less" function using path objects
bool operator() (FsPath& path1, FsPath& path2)
{
bool e1_dir = path1.is_dir(), e2_dir = path2.is_dir();
if (e1_dir == e2_dir) // same type
return static_cast<Child*>(this)->compare(entry_name1, entry_name2);
return static_cast<Child*>(this)->compare(path1, path2);
if (flag_ == DIR_SORT_DIRS_FIRST)
return e1_dir; // (e1 < e2) if e1 is dir
@@ -261,24 +312,38 @@ struct DirSortBase {
return true; // won't reach this
}
protected:
int flag_;
std::string dir_;
};
// Alphanumeric sorting
struct DirSortAlpha : public DirSortBase<DirSortAlpha> {
DirSortAlpha(int flag = DIR_SORT_DIRS_FIRST) : DirSortBase<DirSortAlpha>(flag)
{ }
// whether to use operator() with FsPaths instead of strings.
bool use_path_objects()
{
// for mixed comparison we don't need to construct the FsPaths.
return (flag_ != DIR_SORT_MIXED);
}
// "less" function (called by parent)
bool compare(const std::string& entry_name1, const std::string& entry_name2)
{
return entry_name1 < entry_name2;
}
// "less" function (called by parent)
bool compare(FsPath& path1, FsPath& path2)
{
return path1.str() < path2.str();
}
};
@@ -288,16 +353,24 @@ struct DirSortMTime : public DirSortBase<DirSortMTime> {
DirSortMTime(int flag = DIR_SORT_DIRS_FIRST) : DirSortBase<DirSortMTime>(flag)
{ }
// "less" function (called by parent)
// whether to use operator() with FsPaths instead of strings.
bool use_path_objects()
{
return true;
}
// dummy
bool compare(const std::string& entry_name1, const std::string& entry_name2)
{
FsPath p1(dir_), p2(dir_);
p1.append(entry_name1);
p2.append(entry_name2);
return true;
}
// "less" function (called by parent)
bool compare(FsPath& path1, FsPath& path2)
{
time_t e1_ts = 0, e2_ts = 0;
if (!p1.get_last_modified(e1_ts) || !p2.get_last_modified(e2_ts) || (e1_ts == e2_ts))
entry_name1 < entry_name2; // error or similar timestamps, fall back to this.
if (!path1.get_last_modified(e1_ts) || !path2.get_last_modified(e2_ts) || (e1_ts == e2_ts))
path1.str() < path2.str(); // error or similar timestamps, fall back to this.
return e1_ts < e2_ts;
}
@@ -339,6 +412,18 @@ class Dir : public FsPath {
}
private:
// Between move semantics (confusing and error-prone) and denying copying,
// I choose to deny.
Dir(const Dir& other); // copy constructor. needed to override Dir(const FsPath&).
Dir& operator= (const Dir& other);
public:
/*
// Copy constructor. Move semantics are implemented -
// the handle ownership is transferred exclusively.
Dir(Dir& other) : dir_(NULL), entry_(NULL)
@@ -348,7 +433,7 @@ class Dir : public FsPath {
// Move semantics, as with copy constructor.
inline Dir& operator= (Dir& other);
*/
// Destructor which invokes close() if needed.
~Dir()
@@ -448,9 +533,11 @@ class Dir : public FsPath {
private:
Dir(const Dir& other); // don't allow it. allow only from non-const.
// for move semantics:
const Dir& operator=(const Dir& other); // don't allow it. allow only from non-const.
// Dir(const Dir& other); // don't allow it. allow only from non-const.
// const Dir& operator=(const Dir& other); // don't allow it. allow only from non-const.
handle_type dir_; // DIR handle
@@ -529,7 +616,7 @@ namespace internal {
// --------------------------------------------
/*
// move semantics, as with copy constructor.
inline Dir& Dir::operator= (Dir& other)
{
@@ -544,7 +631,7 @@ inline Dir& Dir::operator= (Dir& other)
return *this;
}
*/
inline bool Dir::open()
@@ -702,38 +789,82 @@ bool Dir::entry_list(Container& put_here, bool put_with_path, SortFunctor sort_f
clear_error();
if (!dir_) { // open if needed
if (!open())
if (!open()) // this sets the error if needed
return false;
}
// if (!dir_) {
// set_error(HZ__("Error while reading directory entry: Directory is not open.");
// return false;
// }
entry_reset();
Container v;
bool filter_using_paths = filter_func.use_path_objects();
bool sort_using_paths = sort_func.use_path_objects();
typedef std::vector<FsPath> entry_list_path_list_t;
typedef std::vector<std::string> entry_list_string_list_t;
entry_list_path_list_t path_results; // for FsPaths
entry_list_string_list_t string_results; // for strings
while (entry_next()) {
if (bad())
continue;
std::string name = entry_get_name();
if (!bad() && filter_func(path_, name))
v.push_back(name);
if (bad())
continue;
if (filter_using_paths) {
FsPath p(path_);
p.append(name);
if (filter_func(p)) {
if (sort_using_paths) {
path_results.push_back(p);
} else {
string_results.push_back(name);
}
}
} else { // string-based filtering
if (filter_func(name)) {
if (sort_using_paths) {
FsPath p(path_);
p.append(name);
path_results.push_back(p);
} else {
string_results.push_back(name);
}
}
}
}
sort_func.set_dir(path_);
shell_sort(v.begin(), v.end(), sort_func);
for(typename Container::const_iterator iter = v.begin(); iter != v.end(); ++iter) {
if (put_with_path) {
FsPath p(path_);
p.append(*iter);
put_here.push_back(p.str());
if (sort_using_paths) {
hz::shell_sort(path_results.begin(), path_results.end(), sort_func);
} else {
put_here.push_back(*iter);
for (entry_list_path_list_t::const_iterator iter = path_results.begin(); iter != path_results.end(); ++iter) {
if (put_with_path) {
put_here.push_back(iter->str());
} else {
put_here.push_back(iter->get_basename());
}
}
} else {
hz::shell_sort(string_results.begin(), string_results.end(), sort_func);
for (entry_list_string_list_t::const_iterator iter = string_results.begin(); iter != string_results.end(); ++iter) {
if (put_with_path) {
FsPath p(path_);
p.append(*iter);
put_here.push_back(p.str());
} else {
put_here.push_back(*iter);
}
}
}
+8 -7
View File
@@ -75,18 +75,19 @@ class FsErrorHolder {
// Errno string is in libc locale charset or utf8 (if using glib).
gchar* loc_errno = g_locale_from_utf8(hz::errno_string(error_errno_).c_str(), -1, NULL, NULL, NULL);
std::string loc_errno_str = (loc_errno ? loc_errno : HZ__("[Errno charset conversion error]"));
std::string loc_errno_str = (loc_errno ? loc_errno
: (HZ__("[charset conv error] ") + error_errno_ + hz::errno_string(error_errno_)));
g_free(loc_errno);
gchar* p1_utf8 = g_filename_display_name(error_path1_.c_str()); // fs -> utf8. always non-null.
gchar* p1_locale = g_locale_from_utf8(error_path1_.c_str(), -1, NULL, NULL, NULL); // utf8 -> locale
std::string p1 = (p1_locale ? p1_locale : HZ__("[Path charset conversion error]"));
gchar* p1_locale = g_locale_from_utf8(p1_utf8, -1, NULL, NULL, NULL); // utf8 -> locale
std::string p1 = (p1_locale ? p1_locale : (HZ__("[charset conv error] ") + error_path1_));
g_free(p1_utf8);
g_free(p1_locale);
gchar* p2_utf8 = g_filename_display_name(error_path2_.c_str()); // fs -> utf8. always non-null.
gchar* p2_locale = g_locale_from_utf8(error_path2_.c_str(), -1, NULL, NULL, NULL); // utf8 -> locale
std::string p2 = (p2_locale ? p2_locale : HZ__("[Path charset conversion error]"));
gchar* p2_locale = g_locale_from_utf8(p2_utf8, -1, NULL, NULL, NULL); // utf8 -> locale
std::string p2 = (p2_locale ? p2_locale : (HZ__("[charset conv error] ") + error_path2_));
g_free(p2_utf8);
g_free(p2_locale);
@@ -108,11 +109,11 @@ class FsErrorHolder {
// unlike g_filename_to_utf8, this always returns something. (available since glib 2.6).
gchar* p1_utf8 = g_filename_display_name(error_path1_.c_str());
std::string p1 = (p1_utf8 ? p1_utf8 : HZ__("[Path charset conversion error]"));
std::string p1 = (p1_utf8 ? p1_utf8 : (HZ__("[charset conv error] ") + error_path1_));
g_free(p1_utf8);
gchar* p2_utf8 = g_filename_display_name(error_path2_.c_str());
std::string p2 = (p2_utf8 ? p2_utf8 : HZ__("[Path charset conversion error]"));
std::string p2 = (p2_utf8 ? p2_utf8 : (HZ__("[charset conv error] ") + error_path2_));
g_free(p2_utf8);
std::string msg = error_format_;
+98 -27
View File
@@ -103,6 +103,19 @@ class File : public FsPath {
open(open_mode);
}
private:
// Between move semantics (confusing and error-prone) and denying copying,
// I choose to deny.
File(const File& other); // copy constructor. needed to override File(const FsPath&).
File& operator= (const File& other);
public:
/*
// Copy constructor. Move semantics are implemented - the ownership is transferred
// exclusively.
File(File& other) : file_(NULL)
@@ -110,16 +123,18 @@ class File : public FsPath {
*this = other; // operator=
}
// move semantics, as with copy constructor.
inline File& operator= (File& other);
*/
// Destructor which invokes close() if needed.
~File()
{
if (file_)
close();
}
// move semantics, as with copy constructor.
inline File& operator= (File& other);
// --- these may set bad() status
@@ -160,7 +175,7 @@ class File : public FsPath {
// Do NOT assign the result to int - you'll break LFS support.
inline bool get_size(file_size_t& put_here);
inline bool get_size(file_size_t& put_here, bool use_read = false);
// Move (rename) a file to "to". The destination will be overwritten (if it exists and
@@ -178,6 +193,13 @@ class File : public FsPath {
private:
// for move semantics:
// File(const File& other); // don't allow it. allow only from non-const.
// const File& operator=(const File& other); // don't allow it. allow only from non-const.
handle_type file_; // FILE*
};
@@ -189,7 +211,7 @@ class File : public FsPath {
// ------------------------------------------- Implementation
/*
// move semantics, as with copy constructor.
inline File& File::operator= (File& other)
{
@@ -202,7 +224,7 @@ inline File& File::operator= (File& other)
return *this;
}
*/
// Open the file with open_mode.
@@ -262,6 +284,7 @@ inline bool File::get_contents(unsigned char*& put_data_here,
// If the buffer is of insufficient size, false is returned and buffer is left untouched.
// If any other error occurs, the buffer may be left in unspecified state.
// Internal usage only: If buf_size is -1, the buffer will be automatically allocated.
// FIXME: Support files which can't be seeked and don't have a size attribute (e.g. /proc/*).
inline bool File::get_contents_noalloc(unsigned char*& put_data_here, file_size_t buf_size,
file_size_t& put_size_here, file_size_t max_size)
{
@@ -312,8 +335,9 @@ inline bool File::get_contents_noalloc(unsigned char*& put_data_here, file_size_
unsigned char* buf = put_data_here;
if (auto_alloc)
buf = new unsigned char[size]; // this may throw!
buf = new unsigned char[static_cast<unsigned int>(size)]; // this may throw!
// FIXME: Large file support
size_t read_bytes = fread(buf, 1, size, f); // I really hope this is not a byte-by-byte operation
if (size != static_cast<file_size_t>(read_bytes)) {
set_error(std::string(HZ__("Unable to read file \"/path1/\": "))
@@ -352,8 +376,8 @@ inline bool File::get_contents(std::string& put_data_here, file_size_t max_size)
return false;
// Note: No need for appending 0, it's all automatic in string.
put_data_here.reserve(size); // string takes size without trailing 0.
put_data_here.append(reinterpret_cast<char*>(buf), size);
put_data_here.reserve(static_cast<std::string::size_type>(size)); // string takes size without trailing 0.
put_data_here.append(reinterpret_cast<char*>(buf), static_cast<std::string::size_type>(size));
delete[] buf;
return ok();
@@ -377,8 +401,25 @@ inline bool File::put_contents(const unsigned char* data, file_size_t data_size)
return false;
}
const size_t written = fwrite(data, 1, data_size, f); // I really hope this is not a byte-by-byte operation
if (data_size != static_cast<file_size_t>(written)) {
// We write in chunks to support large files.
const size_t chunk_size = 32*1024; // 32K block devices will be happy.
file_size_t left_to_write = data_size;
bool write_error = false;
while (left_to_write >= static_cast<file_size_t>(chunk_size)) {
// better loop than specify all in one, this way we can support _really_ large files.
if (fwrite(data + data_size - left_to_write, chunk_size, 1, f) != 1) {
write_error = true;
break;
}
left_to_write -= chunk_size;
}
// write the remainder
if (!write_error && fwrite(data + data_size - left_to_write, left_to_write, 1, f) != 1)
write_error = true;
if (write_error) {
set_error(std::string(HZ__("Unable to write file \"/path1/\": "))
+ HZ__("Number of written bytes doesn't match the data size."), 0, path_);
fclose(f); // don't check anything, it's too late
@@ -392,7 +433,7 @@ inline bool File::put_contents(const unsigned char* data, file_size_t data_size)
}
// // same as above, for std::string (no terminating 0 is needed inside data or anywhere else)
// same as above, for std::string (no terminating 0 is needed inside data or anywhere else)
inline bool File::put_contents(const std::string& data)
{
return put_contents(reinterpret_cast<const unsigned char*>(data.data()), data.size());
@@ -402,7 +443,8 @@ inline bool File::put_contents(const std::string& data)
// Do NOT assign the result to int - you'll break LFS support.
inline bool File::get_size(file_size_t& put_here)
// use_read parameter forces actual reading of a file, useful for files in /proc.
inline bool File::get_size(file_size_t& put_here, bool use_read)
{
clear_error();
@@ -411,17 +453,46 @@ inline bool File::get_size(file_size_t& put_here)
return false;
}
if (!use_read) {
struct stat s;
if (stat(path_.c_str(), &s) == -1) {
set_error(HZ__("Unable to get file size of \"/path1/\": /errno/."), errno, path_);
return false;
}
if (!S_ISREG(s.st_mode)) { // no need for S_ISLNK - we're using stat(), not lstat().
set_error("Unable to get file size of \"/path1/\": Supplied path is not a regular file.", 0, path_);
return false;
}
// For symlinks st_size is a size in bytes of the pointed file.
// To get the size of a symlink itself, use lstat(). Size of a symlink
// is basically strlen(pointed_path).
put_here = s.st_size;
return true;
}
FILE* f = fopen(path_.c_str(), "rb");
if (!f) {
set_error(HZ__("Unable to open file \"/path1/\" for reading: /errno/."), errno, path_);
return false;
}
do { // goto emulation
if (fseek(f, 0, SEEK_END) != 0) { // this should work in LFS too (I think)
set_error(HZ__("Unable to read file \"/path1/\": /errno/."), errno, path_);
break; // goto cleanup
}
const size_t buf_size = 32*1024; // 32K block devices will be happy
char buf[buf_size];
// read until the end
while (fread(buf, buf_size, 1, f) == 1) {
// nothing
}
if (ferror(f)) {
set_error(HZ__("Unable to read file \"/path1/\": /errno/."), errno, path_);
} else { // file read completely
#ifndef _WIN32 // win32 doesn't have ftello()
const file_size_t size = ftello(f); // LFS variant of ftell()
@@ -430,18 +501,17 @@ inline bool File::get_size(file_size_t& put_here)
#endif
if (size == static_cast<file_size_t>(-1)) { // size may be unsigned, it's the way it works
set_error(HZ__("Unable to read file \"/path1/\": /errno/."), errno, path_);
break; // goto cleanup
} else {
// All OK
put_here = size;
}
// All OK
put_here = size;
} while (false);
}
// cleanup:
if (fclose(f) != 0) {
if (!bad()) // don't overwrite the previous error
set_error(HZ__("Error while closing file \"/path1/\": /errno/."), errno, path_);
set_error(HZ__("Error while closing file \"/path1/\": /errno/."), errno, path_);
}
return ok();
@@ -531,10 +601,11 @@ inline bool File::copy(const std::string& to)
}
const unsigned int buf_size = 32*1024; // 32K
const size_t buf_size = 32*1024; // 32K
unsigned char buf[buf_size] = {0};
while (true) {
// let's hope these are not byte-by-byte operations
size_t read_bytes = fread(buf, 1, buf_size, fsrc);
if (read_bytes != buf_size && ferror(fsrc)) { // error
+46
View File
@@ -0,0 +1,46 @@
/**************************************************************************
Copyright:
(C) 2008 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_zlib.txt file
***************************************************************************/
// disable libdebug, we don't link to it
#undef HZ_USE_LIBDEBUG
#define HZ_USE_LIBDEBUG 0
// enable libdebug emulation through std::cerr
#undef HZ_EMULATE_LIBDEBUG
#define HZ_EMULATE_LIBDEBUG 1
// The first header should be then one we're testing, to avoid missing
// header pitfalls.
#include "fs_file.h"
#include <iostream>
#include <vector>
int main()
{
std::vector<std::string> files;
files.push_back("/usr/bin/ar");
files.push_back("/proc/partitions");
for (unsigned int i = 0; i < files.size(); ++i) {
hz::File file(files[i]);
hz::file_size_t size1 = 0, size2 = 0;
file.get_size(size1); // stat() method
file.get_size(size2, true); // fread() method
std::cerr << "File " << file.str() << "\n";
std::cerr << "stat size: " << size1 << ", fread size: " << size2 << "\n\n";
}
return 0;
}
+13 -6
View File
@@ -534,7 +534,7 @@ inline bool FsPath::is_symlink()
}
#ifdef _WIN32
// set_error("The path \"" + path_ + "\" doesn't point to a symbolic link."); // well, win32 and all...
// well, win32 and all...
return false;
#else
@@ -545,7 +545,6 @@ inline bool FsPath::is_symlink()
}
if (!S_ISLNK(s.st_mode)) {
// set_error("The path \"" + path_ + "\" doesn't point to a symbolic link.");
return false;
}
@@ -721,7 +720,7 @@ namespace internal {
// Helper function, internal.
// returns the number of not removed files. 0 on success. pass directory only.
inline int path_remove_recursive(const std::string& path)
inline int path_remove_dir_recursive(const std::string& path)
{
hz::FsPath p(path);
if (!p.exists())
@@ -759,7 +758,7 @@ namespace internal {
FsPath ep(entry_path);
if (ep.is_dir() && !ep.is_symlink()) { // if it's a directory and not a symlink, go recursive
error_count += path_remove_recursive(entry_path);
error_count += path_remove_dir_recursive(entry_path);
} else { // just remove it
if (::unlink(entry_path.c_str()) == -1)
@@ -799,16 +798,17 @@ inline bool FsPath::remove(bool recursive)
if (recursive && !is_file()) {
clear_error(); // clear previous function call
if (internal::path_remove_recursive(path_) > 0) { // supply dirs here only.
if (internal::path_remove_dir_recursive(path_) > 0) { // supply dirs here only.
set_error(HZ__("Unable to remove directory \"/path1/\" completely: Some files couldn't be deleted."), 0, path_);
}
return false;
}
#ifdef _WIN32 // win2k (maybe later wins' too) remove() says "permission denied" (!) on directories.
#ifdef _WIN32 // win2k (maybe later wins too) remove() says "permission denied" (!) on directories.
int status = 0;
if (is_dir()) {
status = ::rmdir(path_.c_str()); // empty dir only
} else {
status = ::unlink(path_.c_str()); // files only
}
@@ -819,6 +819,13 @@ inline bool FsPath::remove(bool recursive)
#else
if (::remove(path_.c_str()) == -1) {
// In case of not empty directory, POSIX says the error will be EEXIST
// or ENOTEMPTY. Linux uses ENOTEMPTY, Solaris uses EEXIST.
// ENOTEMPTY makes more sense for error messages, so convert.
if (errno == EEXIST)
errno = ENOTEMPTY;
set_error(HZ__("Unable to remove file or directory \"/path1/\": /errno/."), errno, path_);
}
#endif

Some files were not shown because too many files have changed in this diff Show More