Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4232e3a50 | ||
|
|
a95be84eac | ||
|
|
b76b5f2227 | ||
|
|
7a12c0880f | ||
|
|
58b0b1fab2 | ||
|
|
16b5c322d5 | ||
|
|
16d5b82ac8 | ||
|
|
be66a8749c | ||
|
|
5192a9d447 | ||
|
|
b9160ec173 | ||
|
|
675fad3fc1 | ||
|
|
f7c01d6c7b | ||
|
|
5bc6318dcd | ||
|
|
3d8b0c94df | ||
|
|
73fec7fa3f | ||
|
|
22971e8567 | ||
|
|
3df4cdae56 | ||
|
|
062096a0b4 | ||
|
|
978e7d41b7 | ||
|
|
33028f81d9 | ||
|
|
4cb12c5e0c | ||
|
|
b01acfeef3 | ||
|
|
b7676aaba8 | ||
|
|
e0a13c6867 | ||
|
|
9f5db6d034 | ||
|
|
5c3bfe8db0 | ||
|
|
5926e43bc5 | ||
|
|
f21f076386 | ||
|
|
da403b28c9 | ||
|
|
faba9270a0 | ||
|
|
9ca87eb296 | ||
|
|
b2b97f2c9a | ||
|
|
8494880943 | ||
|
|
0f8f38f0e1 | ||
|
|
8544163c46 | ||
|
|
fdcd190592 | ||
|
|
40e08796d1 | ||
|
|
1e72729056 | ||
|
|
731122e891 | ||
|
|
ebb3ab6017 | ||
|
|
3d2fe7fd50 | ||
|
|
9a70345f4a | ||
|
|
897a4efa96 | ||
|
|
6ab53c5436 |
@@ -23,10 +23,6 @@ examples are covered under the Unlicense. See LICENSE_unlicense.txt for
|
||||
details.
|
||||
|
||||
|
||||
Portions of this software are copyright Google Inc. See LICENSE_pcrecpp.txt
|
||||
for details.
|
||||
|
||||
|
||||
This product includes the HZ library (or portions of it). See LICENSE_hz.txt
|
||||
for details.
|
||||
|
||||
@@ -6,7 +6,7 @@ SUBDIRS = data debian.dist src
|
||||
|
||||
|
||||
# These files are actually needed for compilation.
|
||||
noinst_DATA = AUTHORS.txt LICENSE_gsmartcontrol.txt README.txt
|
||||
noinst_DATA = AUTHORS.txt LICENSE_gsmartcontrol.txt
|
||||
|
||||
|
||||
# For the files to be bundled with the distribution, specify them in
|
||||
@@ -22,7 +22,6 @@ dist_doc_DATA = AUTHORS.txt ChangeLog NEWS README.txt \
|
||||
LICENSE_gpl3.txt \
|
||||
LICENSE_gsmartcontrol.txt \
|
||||
LICENSE_hz.txt \
|
||||
LICENSE_pcrecpp.txt \
|
||||
LICENSE_unlicense.txt \
|
||||
LICENSE_zlib.txt
|
||||
|
||||
@@ -51,7 +50,7 @@ src-rpm: dist
|
||||
|
||||
# NSIS installer support.
|
||||
# Requires installed NSIS, smartctl-nc.exe, smartctl.exe, update-smart-drivedb.exe.
|
||||
# Gtkmm dlls and pcre dlls are required if linking against them.
|
||||
# Gtkmm and pcre dlls are required if linking against them.
|
||||
# dos2unix on build machine.
|
||||
# Execute only with win32 build present.
|
||||
|
||||
@@ -142,11 +141,13 @@ win-dist-prepare: all
|
||||
|
||||
for file in $(GTK_BIN_FILES); do for f in "@WINDOWS_SYSROOT@/bin/"$$file; do if test -f "$${f}"; then cp -p "$${f}" win-dist/; fi; done; done
|
||||
|
||||
# <prefix>/etc should contain gtk-3.0/settings.ini
|
||||
# <prefix>/etc/gtk-3.0/ should contain settings.ini with a win32 theme.
|
||||
$(MKDIR_P) win-dist/etc
|
||||
$(MKDIR_P) win-dist/etc/fonts
|
||||
cp -p "@WINDOWS_SYSROOT@"/etc/fonts/fonts.conf win-dist/etc/fonts
|
||||
cp -p -r "@WINDOWS_SYSROOT@"/etc/gtk-3.0 win-dist/etc
|
||||
$(MKDIR_P) win-dist/etc/gtk-3.0
|
||||
cp -p "@WINDOWS_SYSROOT@"/etc/fonts/fonts.conf win-dist/etc/fonts/
|
||||
# cp -p "@WINDOWS_SYSROOT@"/etc/gtk-3.0/im-multipress.conf win-dist/etc/gtk-3.0/
|
||||
cp -p "@WINDOWS_SYSROOT@"/etc/gtk-3.0/settings.ini win-dist/etc/gtk-3.0/
|
||||
|
||||
$(MKDIR_P) win-dist/share
|
||||
cp -p -r "@WINDOWS_SYSROOT@"/share/themes win-dist/share
|
||||
@@ -222,6 +223,7 @@ win-dist-prepare: all
|
||||
# other
|
||||
cp -p "@WINDOWS_SYSROOT@"/bin/libgcc_s_*.dll win-dist/
|
||||
cp -p "@WINDOWS_SYSROOT@"/bin/libpcre-1.dll win-dist/
|
||||
cp -p "@WINDOWS_SYSROOT@"/bin/libpcrecpp-0.dll win-dist/
|
||||
|
||||
|
||||
nsis-dist-prepare: win-dist-prepare
|
||||
@@ -1,3 +1,23 @@
|
||||
Version 1.1.0, released on 2017-09-07
|
||||
New Statistics, Temperature Log, Error Recovery, Physical and Directory
|
||||
tabs.
|
||||
General tab shows non-SMART device settings as well.
|
||||
Attributes tab shows entries in "brief" format.
|
||||
Error Log tab shows Extended error log by default (if supported).
|
||||
Self-Test Log tab now shows Extended self-test log by default (if
|
||||
supported).
|
||||
GSmartControl now uses "-x" equivalent for retrieving data (as upposed to
|
||||
"-a"); loading "-x" outputs as virtual drives is also supported.
|
||||
Implemented ability to copy rows in CSV format from Attribute, Statistics
|
||||
and Self-Test Log tables.
|
||||
Implemented "Update Drive Database" functionality.
|
||||
Windows: Drive letters are shown for each drive.
|
||||
Scan time is shown under virtual drive icons.
|
||||
Polkit is supported with gsmartcontrol-root script now.
|
||||
Pcrecpp is no longer bundled, use system-installed one instead.
|
||||
Smartmontools version 5.43 is required at runtime.
|
||||
Other minor improvements and fixes.
|
||||
|
||||
Version 1.0.2, released on 2017-07-21
|
||||
Fixed incomplete capturing of smartctl output under Windows.
|
||||
Added missing icons under Windows.
|
||||
@@ -0,0 +1,101 @@
|
||||
|
||||
About GSmartControl
|
||||
|
||||
GSmartControl - Hard disk drive and SSD health inspection tool.
|
||||
|
||||
GSmartControl is a graphical user interface for smartctl (from smartmontools
|
||||
package, see https://www.smartmontools.org/), which is a tool for
|
||||
querying and controlling SMART (Self-Monitoring, Analysis, and Reporting
|
||||
Technology) data on modern hard disk and solid-state drives. It allows you to
|
||||
inspect the drive's SMART data to determine its health, as well as run various
|
||||
tests on it.
|
||||
|
||||
GSmartControl supports ATA drives (both PATA and SATA), various USB to
|
||||
ATA bridges and drives behind some RAID controllers:
|
||||
* Adaptec (Linux, some models only)
|
||||
* Areca (Linux, Windows)
|
||||
* HP CCISS (Linux)
|
||||
* HP hpsa / hpahcisr (Linux)
|
||||
* Intel Matrix Storage (CSMI) (Linux, Windows, FreeBSD)
|
||||
* LSI 3ware (Linux, Windows)
|
||||
* LSI MegaRAID (Windows)
|
||||
|
||||
Note: Smartmontools supports even more RAID Controllers. The drives
|
||||
behind such controllers can be manually added to GSmartControl using
|
||||
"Add Device..." functionality or --add-device command-line option.
|
||||
See https://www.smartmontools.org/wiki/Supported_RAID-Controllers .
|
||||
|
||||
https://gsmartcontrol.sourceforge.io/
|
||||
|
||||
|
||||
|
||||
Software Requirements
|
||||
|
||||
Note: If using the official Windows package, no additional software is required.
|
||||
|
||||
Build requirements:
|
||||
* pcre 1 - http://www.pcre.org .
|
||||
* GTK+, version 3.4 or higher - see http://www.gtk.org .
|
||||
* Gtkmm, version 3.4 or higher - see http://www.gtkmm.org .
|
||||
|
||||
Runtime requirements:
|
||||
* smartmontools - see https://www.smartmontools.org/ .
|
||||
* xterm
|
||||
|
||||
The following operating systems are supported:
|
||||
* Linux - All the popular configurations should work.
|
||||
* FreeBSD - Tested with DesktopBSD / x86.
|
||||
* NetBSD - Tested with NetBSD / x86.
|
||||
* OpenBSD - Tested with OpenBSD / x86-64.
|
||||
* DragonFlyBSD - Code written but no testing has been performed yet. Expected
|
||||
to work without any issues.
|
||||
* Windows Vista SP2 (32-bit and 64-bit), Windows 7 SP1, Windows Server 2008,
|
||||
Windows 8.1, Windows 10. The Windows port uses pd0, pd1, etc...
|
||||
for physical drives 0, 1, etc... .
|
||||
* Mac OS X.
|
||||
* Solaris.
|
||||
* QNX - Code written but no testing has been performed yet.
|
||||
|
||||
|
||||
|
||||
Command Line Options
|
||||
|
||||
GSmartControl inherits options from GTK+ and other libraries, so be sure to
|
||||
run it with --help option to get a full list of accepted parameters.
|
||||
Note: The Windows version may not have a text output at all, so --help and
|
||||
similar arguments won't have any effect.
|
||||
|
||||
The most important parameters are:
|
||||
|
||||
-?, --help - Show help options.
|
||||
|
||||
-l, --no-locale - Don't use system locale.
|
||||
|
||||
-V, --version - Display version information.
|
||||
|
||||
--no-scan - Don't scan devices on startup.
|
||||
|
||||
--no-hide-tabs - Don't hide non-identity tabs when SMART is disabled. Useful
|
||||
for debugging.
|
||||
|
||||
--add-virtual - Load smartctl data from file, creating a virtual drive. You
|
||||
can specify this option multiple times.
|
||||
|
||||
--add-device - Add this device to device list. The format of the device is
|
||||
"<device>::<type>::<extra_args>", where type and extra_args are optional. This
|
||||
option is useful with --no-scan to list certain drives only. You can specify
|
||||
this option multiple times.
|
||||
Example: --add-device /dev/sda --add-device /dev/twa0::3ware,2 --add-device
|
||||
'/dev/sdb::::-T permissive'.
|
||||
|
||||
-v, --verbose - Enable verbose logging; same as --verbosity-level 5.
|
||||
|
||||
-q, --quiet - Disable logging; same as --verbosity-level 0.
|
||||
|
||||
-b, --verbosity-level - Set verbosity level [0-5].
|
||||
|
||||
|
||||
|
||||
License and Copyright
|
||||
|
||||
For license information, see LICENSE_gsmartcontrol.txt file.
|
||||
@@ -0,0 +1,47 @@
|
||||
|
||||
|
||||
GTKMM Notes:
|
||||
|
||||
To transfer an ownership from your local code to a container, use
|
||||
Gtk::Button* pButton = Gtk::manage(new Gtk::Button("Test"));
|
||||
container->add(*pButton);
|
||||
(add() takes an object by reference).
|
||||
otherwise, you will need to delete() it.
|
||||
|
||||
local scope destruction also works if using stack variables.
|
||||
{
|
||||
Gtk::Button aButton;
|
||||
// destroyed here
|
||||
}
|
||||
|
||||
|
||||
|
||||
--------------------------------------------------------
|
||||
|
||||
TODO:
|
||||
|
||||
|
||||
Don't rely on smartctl return code (2), parse the output instead.
|
||||
This will allow us to show the Info page.
|
||||
Need usage cases.
|
||||
|
||||
|
||||
Testing:
|
||||
If ETA time has elapsed, but it's still only at 10% completion,
|
||||
ETA 0 is displayed. Fix.
|
||||
Detect running tests on launch (maybe ask the user too? some tests
|
||||
may be stuck due to bad firmware, e.g. 3ware/windows).
|
||||
If smartctl outputs gibberish while testing, the GUI hangs.
|
||||
|
||||
|
||||
Support RAID for these controllers (supported by smartctl 6.5):
|
||||
https://www.smartmontools.org/wiki/Supported_RAID-Controllers
|
||||
|
||||
Adaptec (Windows, Linux using "-d aacraid")
|
||||
HighPoint RocketRAID (Linux, FreeBSD (analyze sent info))
|
||||
LSI MegaRAID (Linux, FreeBSD)
|
||||
3ware (FreeBSD)
|
||||
Areca (FreeBSD)
|
||||
HP CCISS (FreeBSD)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
AC_INIT([GSmartControl],[1.0.2],[],[gsmartcontrol])
|
||||
AC_INIT([GSmartControl],[1.1.1],[],[gsmartcontrol])
|
||||
|
||||
AC_CONFIG_SRCDIR([configure.ac])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
@@ -128,9 +128,6 @@ APP_CXX_EXTERN_C_OVERLOAD
|
||||
# This may be needed for pch support
|
||||
AC_CHECK_HEADERS([stdc++.h])
|
||||
|
||||
# pcrecpp needs this (break on first function found)
|
||||
AC_CHECK_FUNCS([strtoll _strtoi64], [break])
|
||||
|
||||
# Non-C99 systems don't have strtof and strtold. some have strtodf for strtof,
|
||||
# but let's ignore it. Note: They may be supported by system libc,
|
||||
# but not enabled by default (e.g. by glibc feature macros). In that case,
|
||||
@@ -212,27 +209,27 @@ LIBS="$GTKMM_LIBS $LIBS"
|
||||
# -------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
PCRE_CFLAGS="";
|
||||
PCRE_LIBS="";
|
||||
PCRECPP_CFLAGS="";
|
||||
PCRECPP_LIBS="";
|
||||
|
||||
# try pkg-config first
|
||||
PKG_CHECK_MODULES(PCRE, [libpcre], [pcre_found=yes], [pcre_found=no])
|
||||
PKG_CHECK_MODULES(PCRECPP, [libpcrecpp], [pcrecpp_found=yes], [pcrecpp_found=no])
|
||||
|
||||
# if not found, try pcre-config (solaris had it but not pkg-config).
|
||||
if test "x$pcre_found" = "xno"; then
|
||||
if test "x$pcrecpp_found" = "xno"; then
|
||||
AC_PATH_PROG(pcre_config_binary, [pcre-config])
|
||||
if test "x$pcre_config_binary" != "x"; then
|
||||
PCRE_CFLAGS="`pcre-config --cflags`"
|
||||
PCRE_LIBS="`pcre-config --libs`"
|
||||
PCRE_LIBS="`pcre-config --libs-cpp`"
|
||||
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])
|
||||
# AC_MSG_ERROR([Could not find pcrecpp through pkg-config and pcre-config. Make sure the pcre and pcrecpp libraries are installed])
|
||||
AC_MSG_ERROR([$PCRECPP_PKG_ERRORS])
|
||||
fi
|
||||
fi
|
||||
|
||||
CFLAGS="$CFLAGS $PCRE_CFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $PCRE_CFLAGS"
|
||||
LIBS="$LIBS $PCRE_LIBS"
|
||||
CFLAGS="$CFLAGS $PCRECPP_CFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $PCRECPP_CFLAGS"
|
||||
LIBS="$LIBS $PCRECPP_LIBS"
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------------
|
||||
@@ -312,12 +309,11 @@ AC_SUBST(WINDOWS_ARCH)
|
||||
RES_FILES="src/res/gsc_about_dialog.ui \
|
||||
src/res/gsc_add_device_window.ui \
|
||||
src/res/gsc_executor_log_window.ui \
|
||||
src/res/gsc_help_window.ui \
|
||||
src/res/gsc_info_window.ui \
|
||||
src/res/gsc_main_window.ui \
|
||||
src/res/gsc_preferences_window.ui \
|
||||
src/res/gsc_text_window.ui \
|
||||
AUTHORS.txt LICENSE_gsmartcontrol.txt README.txt"
|
||||
AUTHORS.txt LICENSE_gsmartcontrol.txt"
|
||||
|
||||
# Unfortunately, _SOURCES can't contain substitutions, so we use LIBADD.
|
||||
RES_LIBADD="" # .o file to put into .a, libres_a_LIBADD, libres_a_DEPENDENCIES.
|
||||
@@ -389,8 +385,7 @@ CXXFLAGS="$CXXFLAGS $ADDITIONAL_FLAGS"
|
||||
# Add global configuration file. Note: It must be searched in top_builddir
|
||||
# first (that's why -I is the first flag), for precompiled headers to work.
|
||||
|
||||
# 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"
|
||||
ADDITIONAL_INCLUDES="-I\$(top_builddir) -I\$(top_srcdir)/src"
|
||||
|
||||
# -include works with gcc, intel, pathscale and sunstudio since 12u1, but doesn't work
|
||||
# with pgi and older sunstudio (the one in solaris10, for example).
|
||||
@@ -486,7 +481,7 @@ AC_CONFIG_FILES([data/gsmartcontrol-root], [chmod +x data/gsmartcontrol-root])
|
||||
# these are all the makefiles to generate
|
||||
|
||||
AC_CONFIG_FILES([Makefile src/Makefile src/applib/Makefile src/res/Makefile src/hz/Makefile \
|
||||
src/libdebug/Makefile src/pcrecpp/Makefile src/rconfig/Makefile src/rmn/Makefile \
|
||||
src/libdebug/Makefile src/rconfig/Makefile src/rmn/Makefile \
|
||||
data/Makefile data/16/Makefile data/22/Makefile data/24/Makefile data/32/Makefile \
|
||||
data/48/Makefile data/64/Makefile data/128/Makefile data/256/Makefile data/nsis/Makefile \
|
||||
debian.dist/Makefile])
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
@@ -44,6 +44,10 @@ desktop_DATA = gsmartcontrol.desktop
|
||||
metainfodir = $(datadir)/metainfo
|
||||
metainfo_DATA = gsmartcontrol.appdata.xml
|
||||
|
||||
# PolKit files
|
||||
polkitdir = $(datadir)/polkit-1/actions
|
||||
polkit_DATA = org.gsmartcontrol.policy
|
||||
|
||||
|
||||
# Application pixmap (fallback icon for desktop files).
|
||||
# Same as 48/gsmartcontrol.png.
|
||||
@@ -53,7 +57,7 @@ pixmaps_DATA = 48/gsmartcontrol.png gsmartcontrol.xpm
|
||||
|
||||
|
||||
# Put everything inside the distribution
|
||||
EXTRA_DIST = $(appdata_DATA) gsmartcontrol.ico gsmartcontrol.xpm
|
||||
EXTRA_DIST = $(appdata_DATA) $(polkit_DATA) gsmartcontrol.ico gsmartcontrol.xpm
|
||||
|
||||
|
||||
bin_SCRIPTS = gsmartcontrol-root
|
||||
@@ -8,7 +8,7 @@
|
||||
# Run gsmartcontrol with root, asking for root password first.
|
||||
# export GSMARTCONTROL_SU to override a su command (e.g. "kdesu -c").
|
||||
|
||||
EXEC_BIN="@prefix@/bin/gsmartcontrol";
|
||||
EXEC_BIN="@prefix@/sbin/gsmartcontrol";
|
||||
prog_name="gsmartcontrol"
|
||||
|
||||
|
||||
@@ -71,11 +71,12 @@ fi
|
||||
|
||||
|
||||
# They're basically the same, only the order is different.
|
||||
# pkexec is for PolKit
|
||||
# sux requires xterm to ask for the password.
|
||||
# xdg-su is basically like this script, except worse :)
|
||||
# su-to-root is a debian/ubuntu official method (although gksu is available).
|
||||
gnome_sus="su-to-root gnomesu gksu kdesu beesu xdg-su sux";
|
||||
kde_sus="su-to-root kdesu gnomesu gksu beesu xdg-su sux";
|
||||
gnome_sus="pkexec su-to-root gnomesu gksu kdesu beesu xdg-su sux";
|
||||
kde_sus="pkexec su-to-root kdesu gnomesu gksu beesu xdg-su sux";
|
||||
other_sus="$gnome_sus";
|
||||
|
||||
|
||||
@@ -136,6 +137,9 @@ full_cmd="";
|
||||
if [ "$GSMARTCONTROL_SU" != "" ]; then
|
||||
full_cmd="$GSMARTCONTROL_SU '$EXEC_BIN $final_args_quoted'";
|
||||
|
||||
elif [ "$found_su" = "pkexec" ]; then
|
||||
full_cmd="pkexec --disable-internal-agent $EXEC_BIN $final_args_quoted";
|
||||
|
||||
elif [ "$found_su" = "sux" ]; then
|
||||
full_cmd="xterm -e sux -c '$EXEC_BIN $final_args_quoted'";
|
||||
|
||||
@@ -16,19 +16,19 @@
|
||||
</p>
|
||||
</description>
|
||||
<launchable type="desktop-id">gsmartcontrol.desktop</launchable>
|
||||
<url type="homepage">http://gsmartcontrol.sourceforge.net</url>
|
||||
<url type="homepage">https://gsmartcontrol.sourceforge.io</url>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<caption>Main window</caption>
|
||||
<image>http://gsmartcontrol.sourceforge.net/home/images/main_ok.png</image>
|
||||
<image>https://gsmartcontrol.sourceforge.io/home/images/main_ok.png</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<caption>Drive identity information</caption>
|
||||
<image>http://gsmartcontrol.sourceforge.net/home/images/info_identity.png</image>
|
||||
<image>https://gsmartcontrol.sourceforge.io/home/images/info_identity.png</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<caption>Attribute list of a failing drive</caption>
|
||||
<image>http://gsmartcontrol.sourceforge.net/home/images/info_failing.png</image>
|
||||
<image>https://gsmartcontrol.sourceforge.io/home/images/info_failing.png</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
</component>
|
||||
@@ -9,8 +9,11 @@ Categories=System;Monitor;
|
||||
Name=GSmartControl
|
||||
# short description, usually shown in parentheses after Name
|
||||
GenericName=Hard Disk and SSD Health Inspection
|
||||
GenericName[fr]=Inspecteur de santé de disque dur et de SSD
|
||||
|
||||
# tooltip
|
||||
Comment=Monitor and control SMART data on hard disk and solid-state drives
|
||||
Comment[fr]=Surveille et contrôle les données SMART des disques durs et des SSD
|
||||
|
||||
# If it's a name only, it looks for "name.[png|xpm]" file in
|
||||
# $XDG_DATA_DIRS/icons and /usr/share/pixmaps.
|
||||
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
@@ -4,7 +4,7 @@ Version @VERSION@
|
||||
|
||||
The latest version of this distribution, as well as the source code of
|
||||
GSmartControl, can be found at
|
||||
http://gsmartcontrol.sourceforge.net
|
||||
https://gsmartcontrol.sourceforge.io
|
||||
and
|
||||
https://sourceforge.net/projects/gsmartcontrol/files/
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
!define PRODUCT_NAME "GSmartControl"
|
||||
!define PRODUCT_NAME_SMALL "gsmartcontrol"
|
||||
!define PRODUCT_PUBLISHER "Alexander Shaduri"
|
||||
!define PRODUCT_WEB_SITE "http://gsmartcontrol.sourceforge.net"
|
||||
!define PRODUCT_WEB_SITE "https://gsmartcontrol.sourceforge.io"
|
||||
|
||||
;!define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\AppMainExe.exe"
|
||||
!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
|
||||
|
Before Width: | Height: | Size: 766 B After Width: | Height: | Size: 766 B |
|
Before Width: | Height: | Size: 766 B After Width: | Height: | Size: 766 B |
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE policyconfig PUBLIC
|
||||
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
|
||||
<policyconfig>
|
||||
|
||||
<action id="org.gsmartcontrol">
|
||||
<message>Authentication is required to run GSmartControl</message>
|
||||
<icon_name>gsmartcontrol</icon_name>
|
||||
<defaults>
|
||||
<allow_any>auth_admin</allow_any>
|
||||
<allow_inactive>auth_admin</allow_inactive>
|
||||
<allow_active>auth_admin</allow_active>
|
||||
</defaults>
|
||||
<annotate key="org.freedesktop.policykit.exec.path">/usr/sbin/gsmartcontrol</annotate>
|
||||
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
|
||||
</action>
|
||||
|
||||
</policyconfig>
|
||||
@@ -1,7 +1,7 @@
|
||||
Source: gsmartcontrol
|
||||
Section: utils
|
||||
Priority: extra
|
||||
Homepage: http://gsmartcontrol.sourceforge.net/
|
||||
Homepage: https://gsmartcontrol.sourceforge.io/
|
||||
Maintainer: Alexander Shaduri <ashaduriREMOVETHIS@gmail.com>
|
||||
Build-Depends: debhelper (>= 5), autotools-dev,
|
||||
libpcre3-dev, libgtkmm-3.0-dev (>= 3.4.0)
|
||||
@@ -9,8 +9,7 @@ Standards-Version: 3.7.3
|
||||
|
||||
Package: gsmartcontrol
|
||||
Architecture: any
|
||||
#Depends: ${shlibs:Depends}, ${misc:Depends}, smartmontools, menu
|
||||
Depends: ${shlibs:Depends}, smartmontools, menu
|
||||
Depends: ${shlibs:Depends}, smartmontools (>= 5.43), xterm, menu
|
||||
Description: Hard disk drive and SSD health inspection tool
|
||||
GSmartControl is a graphical user interface for smartctl (from smartmontools
|
||||
package), which is a tool for querying and controlling SMART
|
||||
@@ -1,7 +1,7 @@
|
||||
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.sourceforge.net/>
|
||||
It was downloaded from <https://gsmartcontrol.sourceforge.io/>
|
||||
|
||||
Copyright:
|
||||
|
||||
@@ -1,34 +1,30 @@
|
||||
|
||||
# This spec file is for openSUSE Build Service.
|
||||
# Supported distributions: openSUSE, Fedora, CentOS, RHEL.
|
||||
|
||||
Name: gsmartcontrol
|
||||
Version: @VERSION@
|
||||
Release: 1as
|
||||
Release: 0
|
||||
License: GPL-2.0 AND GPL-3.0
|
||||
Url: http://gsmartcontrol.sourceforge.net/
|
||||
Url: https://gsmartcontrol.sourceforge.io/
|
||||
Vendor: Alexander Shaduri <ashaduri 'at' gmail.com>
|
||||
# Packager: Alexander Shaduri <ashaduri 'at' gmail.com>
|
||||
Source: http://sourceforge.net/projects/gsmartcontrol/files/%{version}/%{name}-%{version}.tar.bz2
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-build
|
||||
Summary: GSmartControl - Hard Disk Drive and SSD Health Inspection Tool
|
||||
|
||||
%if 0%{?mandriva_version}
|
||||
Group: System/Kernel and hardware
|
||||
%else
|
||||
Group: Hardware/Other
|
||||
%endif
|
||||
|
||||
# Empty debug packages cause errors in new RPM. Disable them.
|
||||
%global debug_package %{nil}
|
||||
|
||||
|
||||
# Dependencies for various distributions. The actual deps are:
|
||||
# smartmontools, pcre, gtkmm3 >= 3.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.
|
||||
|
||||
|
||||
# SUSE / OpenSUSE. SLES also defines the correct suse_version.
|
||||
%if 0%{?suse_version}
|
||||
|
||||
Requires: smartmontools
|
||||
Requires: smartmontools >= 5.43, polkit, bash, xterm
|
||||
BuildRequires: gcc-c++, libstdc++-devel, pcre-devel, gtkmm3-devel >= 3.4.0
|
||||
BuildRequires: update-desktop-files
|
||||
BuildRequires: fdupes
|
||||
@@ -39,16 +35,9 @@ BuildRequires: fdupes
|
||||
# Fedora, CentOS, RHEL
|
||||
%if 0%{?fedora_version} || 0%{?rhel_version} || 0%{?centos_version}
|
||||
|
||||
# I'm not sure about centos and rhel versions
|
||||
Requires: smartmontools
|
||||
Requires: smartmontools >= 5.43, polkit, bash, xterm
|
||||
BuildRequires: gcc-c++, pcre-devel, gtkmm30-devel >= 3.4.0
|
||||
|
||||
# Fedora with consolehelper.
|
||||
# usermode-gtk is for consolehelper-gtk.
|
||||
%if 0%{?fedora_version}
|
||||
Requires: usermode-gtk
|
||||
%endif
|
||||
|
||||
%endif
|
||||
|
||||
|
||||
@@ -97,43 +86,6 @@ rm -f $RPM_BUILD_ROOT%{_datadir}/icons/hicolor/icon-theme.cache
|
||||
%endif
|
||||
|
||||
|
||||
%if 0%{?fedora_version}
|
||||
# We still retain gsmartcontrol-root for consistency with other distributions.
|
||||
|
||||
# Make the desktop file run gsmartcontrol instead of gsmartcontrol-root
|
||||
sed "s|gsmartcontrol-root|gsmartcontrol|g" %{buildroot}%{_datadir}/applications/gsmartcontrol.desktop > %{buildroot}%{_datadir}/applications/gsmartcontrol.desktop.new
|
||||
mv -f %{buildroot}%{_datadir}/applications/gsmartcontrol.desktop.new %{buildroot}%{_datadir}/applications/gsmartcontrol.desktop
|
||||
|
||||
# The rest is taken from the official fedora spec:
|
||||
# http://pkgs.fedoraproject.org/cgit/gsmartcontrol.git/tree/
|
||||
|
||||
# Set up for console-helper
|
||||
mkdir -p %{buildroot}%{_sbindir}
|
||||
# Add a copy as "gsmartcontrol-noroot", since gsmartcontrol will always
|
||||
# ask for root password.
|
||||
cp %{buildroot}%{_bindir}/%{name} %{buildroot}%{_bindir}/%{name}-noroot
|
||||
mv %{buildroot}%{_bindir}/%{name} %{buildroot}%{_sbindir}/%{name}
|
||||
ln -s consolehelper %{buildroot}%{_bindir}/%{name}
|
||||
|
||||
mkdir -p %{buildroot}%{_sysconfdir}/pam.d
|
||||
cat <<EOF >%{buildroot}%{_sysconfdir}/pam.d/%{name}
|
||||
#%%PAM-1.0
|
||||
auth include config-util
|
||||
account include config-util
|
||||
session include config-util
|
||||
EOF
|
||||
|
||||
mkdir -p %{buildroot}%{_sysconfdir}/security/console.apps
|
||||
cat <<EOF >%{buildroot}%{_sysconfdir}/security/console.apps/%{name}
|
||||
USER=root
|
||||
PROGRAM=/usr/sbin/gsmartcontrol
|
||||
SESSION=true
|
||||
EOF
|
||||
|
||||
# endif fedora
|
||||
%endif
|
||||
|
||||
|
||||
%clean
|
||||
rm -rf %buildroot
|
||||
|
||||
@@ -141,12 +93,8 @@ rm -rf %buildroot
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
|
||||
# In fedora /usr/bin/gsmartcontrol is linked to consolehelper, cannot set attrs on symlink.
|
||||
%if 0%{?fedora_version}
|
||||
%{_bindir}/*
|
||||
%else
|
||||
%attr(0755,root,root) %{_bindir}/*
|
||||
%endif
|
||||
%attr(0755,root,root) %{_bindir}/gsmartcontrol-root
|
||||
%attr(0755,root,root) %{_sbindir}/gsmartcontrol
|
||||
|
||||
# %%attr(0644,root,root) %%config(noreplace) %%{_sysconfdir}/*
|
||||
|
||||
@@ -157,13 +105,11 @@ rm -rf %buildroot
|
||||
# %%{_datadir}/gsmartcontrol/*
|
||||
%{_datadir}/applications/*.desktop
|
||||
%{_datadir}/metainfo
|
||||
%{_datadir}/metainfo/gsmartcontrol.appdata.xml
|
||||
%{_datadir}/polkit-1
|
||||
%{_datadir}/polkit-1/actions
|
||||
%{_datadir}/polkit-1/actions/org.gsmartcontrol.policy
|
||||
%{_datadir}/icons/*
|
||||
%{_datadir}/pixmaps/*
|
||||
|
||||
%if 0%{?fedora_version}
|
||||
%config(noreplace) %{_sysconfdir}/pam.d/%{name}
|
||||
%config(noreplace) %{_sysconfdir}/security/console.apps/%{name}
|
||||
%{_sbindir}/gsmartcontrol
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
@@ -1,28 +0,0 @@
|
||||
Copyright (c) 2005 - 2010, Google Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -1,407 +0,0 @@
|
||||
|
||||
About GSmartControl
|
||||
|
||||
GSmartControl - Hard disk drive and SSD health inspection tool.
|
||||
|
||||
GSmartControl is a graphical user interface for smartctl (from smartmontools
|
||||
package, see http://smartmontools.sourceforge.net), which is a tool for
|
||||
querying and controlling SMART (Self-Monitoring, Analysis, and Reporting
|
||||
Technology) data on modern hard disk and solid-state drives. It allows you to
|
||||
inspect the drive's SMART data to determine its health, as well as run various
|
||||
tests on it.
|
||||
|
||||
Note: GSmartControl supports ATA drives (both PATA and SATA), various USB to
|
||||
ATA bridges and drives behind some RAID controllers (selected models of
|
||||
3ware, Areca, Adaptec, Intel RAID controllers).
|
||||
|
||||
http://gsmartcontrol.sourceforge.net
|
||||
|
||||
|
||||
|
||||
Features
|
||||
|
||||
* automatically reports and highlights any anomalies;
|
||||
|
||||
* allows enabling/disabling SMART;
|
||||
|
||||
* allows enabling/disabling Automatic Offline Data Collection - a short
|
||||
self-check that the drive will perform automatically every four hours with no
|
||||
impact on performance;
|
||||
|
||||
* supports configuration of global and per-drive options for smartctl;
|
||||
|
||||
* performs SMART self-tests;
|
||||
|
||||
* displays drive identity information, capabilities, attributes, and
|
||||
self-test/error logs;
|
||||
|
||||
* can read in smartctl output from a saved file, interpreting it as a
|
||||
read-only virtual device;
|
||||
|
||||
* works on most smartctl-supported operating systems;
|
||||
|
||||
* has extensive help information.
|
||||
|
||||
|
||||
|
||||
What Is SMART?
|
||||
|
||||
Short answer: SMART is a technology which provides hard disk drives with
|
||||
methods to predict certain kinds of failures with certain chance of success.
|
||||
|
||||
Long answer: read below.
|
||||
|
||||
Self-Monitoring, Analysis, and Reporting Technology, or SMART, is a
|
||||
monitoring system for hard drives and SSDs to detect and report various
|
||||
indicators of reliability, in the hope of anticipating failures. SMART is
|
||||
implemented inside the drives, providing several ways of monitoring the drive
|
||||
health. It may present information about general health, various drive
|
||||
attributes (for example, number of unreadable sectors), error logs, and so on.
|
||||
It may also provide ways to instruct the drive to run various self-tests,
|
||||
which may report valuable information. It may even automatically scan the disk
|
||||
surface in when the drive is idle, repairing the defects while reallocating
|
||||
the data to more safe areas.
|
||||
|
||||
While having SMART sounds really good, there are some nuances to consider. One
|
||||
of the common pitfalls is that it may create a false sense of security. That
|
||||
is, a perfectly good SMART data is NOT an indication that the drive won't fail
|
||||
the next minute. The reverse is also true - some drives may function perfectly
|
||||
even with not-so-good-looking SMART data. However, as studies indicate, given
|
||||
a large population of drives, some SMART attributes may reliably predict
|
||||
drive failures within up to two months.
|
||||
|
||||
Another common mistake is to assume that the attribute values are the real
|
||||
physical values, as experienced by the drive. As manufacturers do not
|
||||
necessarily agree on precise attribute definitions and measurement units, the
|
||||
exact meaning of the attributes may vary greatly across different drive
|
||||
models.
|
||||
|
||||
At present SMART is implemented individually by manufacturers. While some
|
||||
aspects are standardized for compatibility, others are not. In fact, most
|
||||
manufacturers refer the users to their own health monitoring utilities and
|
||||
advice against taking SMART data seriously. Nevertheless, SMART may prove an
|
||||
effective measure against data loss.
|
||||
|
||||
Yet another issue is that quite often the drives have bugs which prevent
|
||||
correct SMART usage. This is usually due to buggy firmware, or the
|
||||
manufacturer ignoring the standards. Luckily, smartmontools usually detects
|
||||
these bugs and works around them.
|
||||
|
||||
|
||||
|
||||
Software Requirements
|
||||
|
||||
You need to have the following software installed:
|
||||
|
||||
* pcre - http://www.pcre.org .
|
||||
|
||||
* smartmontools - see http://smartmontools.sourceforge.net .
|
||||
|
||||
* GTK+, version 3.4 or higher - see http://www.gtk.org .
|
||||
|
||||
* Gtkmm, version 3.4 or higher - see http://www.gtkmm.org .
|
||||
|
||||
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, gtkmm3-devel.
|
||||
|
||||
Fedora, CentOS, RHEL:
|
||||
gcc-c++, pcre-devel, gtkmm30-devel.
|
||||
|
||||
Ubuntu, Debian GNU/Linux:
|
||||
g++, libpcre3-dev, libgtkmm-3.0-dev.
|
||||
|
||||
Note that usually you need to specify only these packages - the rest is
|
||||
installed automatically by the package manager's dependency resolver. Keep in
|
||||
mind that you also need smartmontools to run the program.
|
||||
|
||||
Note: If using the official Windows package, the requirements are listed on
|
||||
GSmartControl's website.
|
||||
|
||||
|
||||
The following operating systems are supported:
|
||||
|
||||
* Linux - All the popular configurations should work.
|
||||
|
||||
* FreeBSD - Tested with DesktopBSD / x86.
|
||||
|
||||
* NetBSD - Tested with NetBSD / x86.
|
||||
|
||||
* OpenBSD - Tested with OpenBSD / x86-64.
|
||||
|
||||
* DragonFlyBSD - Code written but no testing has been performed yet. Expected
|
||||
to work without any issues.
|
||||
|
||||
* Windows Vista SP2 (32-bit and 64-bit), Windows 7 SP1, Windows Server 2008,
|
||||
Windows 8.1, Windows 10. The Windows port uses pd0, pd1, etc...
|
||||
for physical drives 0, 1, etc... .
|
||||
|
||||
* Solaris.
|
||||
|
||||
* Mac OS X - 3rd-party testing by the MacPorts and Fink projects.
|
||||
|
||||
* QNX - Code written but no testing has been performed yet.
|
||||
|
||||
|
||||
|
||||
Installation
|
||||
|
||||
Short answer: build and install via: ./configure; make; make install
|
||||
|
||||
Run gsmartcontrol-root to invoke gsmartcontrol with your desktop's su
|
||||
mechanism, or use the desktop menu entry.
|
||||
|
||||
|
||||
Long answer: read below.
|
||||
|
||||
First, check if you can find a pre-built package for your distribution or
|
||||
operating system - they usually provide the best integration and the easiest
|
||||
installation procedure. For Linux, one option is to try the openSUSE Build
|
||||
Service - it provides ready-to-install packages for various popular Linux
|
||||
distributions (openSUSE, Fedora, etc...). See
|
||||
http://download.opensuse.org/repositories/home:/alex_sh/ .
|
||||
|
||||
If you want to compile from source, check that you have all the required
|
||||
dependencies (see Software Requirements section). Then the usual
|
||||
|
||||
./configure; make; make install
|
||||
|
||||
will build and install it. Installation usually requires administrative
|
||||
privileges, but you don't need to install the program in order to run it
|
||||
directly from the build directory.
|
||||
|
||||
|
||||
|
||||
Smartctl Options
|
||||
|
||||
GSmartControl tries its best to guard the user from having to specify smartctl
|
||||
options. However, this is not always possible due to drive firmware bugs,
|
||||
unimplemented features, and so on. The smartctl manual page contains all the
|
||||
information you may need when dealing with smartctl.
|
||||
See http://smartmontools.sourceforge.net/man/smartctl.8.html
|
||||
|
||||
Additional information is available at http://smartmontools.sourceforge.net
|
||||
|
||||
|
||||
|
||||
Command Line Options
|
||||
|
||||
GSmartControl inherits options from GTK+ and other libraries, so be sure to
|
||||
run it with --help option to get a full list of accepted parameters.
|
||||
Note: The Windows version may not have a text output at all, so --help and
|
||||
similar arguments won't have any effect. However, --verbose will still
|
||||
increase the verbosity of a log saved via "Options -> View Execution Log ->
|
||||
Save All".
|
||||
|
||||
The most important parameters are:
|
||||
|
||||
-?, --help - Show help options.
|
||||
|
||||
-l, --no-locale - Don't use system locale.
|
||||
|
||||
-V, --version - Display version information.
|
||||
|
||||
--no-scan - Don't scan devices on startup.
|
||||
|
||||
--no-hide-tabs - Don't hide non-identity tabs when SMART is disabled. Useful
|
||||
for debugging.
|
||||
|
||||
--add-virtual - Load smartctl data from file, creating a virtual drive. You
|
||||
can specify this option multiple times.
|
||||
|
||||
--add-device - Add this device to device list. The format of the device is
|
||||
"<device>::<type>::<extra_args>", where type and extra_args are optional. This
|
||||
option is useful with --no-scan to list certain drives only. You can specify
|
||||
this option multiple times.
|
||||
Example: --add-device /dev/sda --add-device /dev/twa0::3ware,2 --add-device
|
||||
'/dev/sdb::::-T permissive'.
|
||||
|
||||
-v, --verbose - Enable verbose logging; same as --verbosity-level 5.
|
||||
|
||||
-q, --quiet - Disable logging; same as --verbosity-level 0.
|
||||
|
||||
-b, --verbosity-level - Set verbosity level [0-5].
|
||||
|
||||
|
||||
|
||||
Permission Problems
|
||||
|
||||
Short answer: you need to be root (that's Administrator in Windows).
|
||||
In X11, use kdesu, gnomesu, sux, xdg-su or similar.
|
||||
|
||||
Long answer: read below.
|
||||
|
||||
Most operating systems prohibit direct access to hardware to users with
|
||||
non-administrative privileges. Unfortunately, to access SMART data, smartctl
|
||||
needs to directly access the hard drive.
|
||||
|
||||
The provided X11 desktop and menu icons should show the "Please enter the root
|
||||
password" dialog boxes, and, after correct information is entered, should run
|
||||
this program with root privileges. The dialogs should be available in most
|
||||
commonly used X11 desktop environments.
|
||||
|
||||
Another way is to use the included gsmartcontrol-root script, which finds the
|
||||
available su program and runs gsmartcontrol with it.
|
||||
|
||||
Yet another way is to manually invoke the program with kdesu, gnomesu, sux or
|
||||
similar programs. For example,
|
||||
|
||||
kdesu -u root -c gsmartcontrol
|
||||
|
||||
will ask for root password and run gsmartcontrol with root privileges. Replace
|
||||
"kdesu" with "gnomesu" if using Gnome. The "sux" or "xdg-su" commands may also
|
||||
help, if neither KDE or GNOME are available.
|
||||
|
||||
Please don't set the "setuid" flag on smartctl binary. It is considered a
|
||||
security risk.
|
||||
|
||||
|
||||
|
||||
Enable SMART Permanently
|
||||
|
||||
Specifications say that once you set a SMART-related property, it will be
|
||||
preserved across reboots. So, when you, say, enable SMART and Automatic
|
||||
Offline Data Collection, both will stay enabled until you disable them.
|
||||
|
||||
However, BIOS, your operating system, your other operating systems (if
|
||||
present), and various startup programs may affect that. For example, BIOS may
|
||||
enable SMART each time you start your computer, so if you disabled SMART
|
||||
previously, it will be re-enabled on reboot.
|
||||
|
||||
The easiest way to work around this is to set the desired settings on system
|
||||
startup. You may use smartctl or smartd to do that. For example, to enable
|
||||
both SMART and Automatic Offline Data Collection on /dev/sda, one would write
|
||||
the following to the system startup script (e.g. boot.local, rc.local or
|
||||
similar on Linux):
|
||||
|
||||
smartctl -s on -o on /dev/sda
|
||||
|
||||
For more information, see smartctl and smartd documentation.
|
||||
|
||||
|
||||
|
||||
Known Issues
|
||||
|
||||
Only ATA drives (both PATA and SATA), various USB to ATA bridges and drives
|
||||
behind some RAID controllers are supported for now. The main reasons are:
|
||||
|
||||
* We can't support drives which don't work with smartmontools. This affects
|
||||
drives which don't support SMART or don't export SMART data correctly (e.g.
|
||||
some USB enclosures, RAIDs, etc...).
|
||||
|
||||
* Smartctl's output for SCSI drives is completely different compared to ATA.
|
||||
Also, SCSI drives are rarely found in desktop systems and the servers rarely
|
||||
have X11 / Gtkmm running, so this is a low priority task.
|
||||
|
||||
* I only have ATA drives, so testing anything else is very difficult.
|
||||
|
||||
Immediate Offline Tests are not supported. I haven't found a way to reliably
|
||||
monitor them yet. Besides, they run automatically anyway if Automatic Offline
|
||||
Data Collection is enabled.
|
||||
|
||||
Testing is only supported on drives which correctly report their progress
|
||||
information in capabilities.
|
||||
|
||||
Not all drives support disabling Automatic Offline Data Collection, even if
|
||||
they report otherwise. Unfortunately, there's no way to detect such drives.
|
||||
|
||||
The texts probably contain a lot of grammatical errors, English being my third
|
||||
language and all.
|
||||
|
||||
|
||||
|
||||
Reporting Bugs
|
||||
|
||||
If it is a SMART or drive-related problem, please try to test it with smartctl
|
||||
first. Chances are, the problem you're experiencing is not tied to
|
||||
GSmartControl, but is a drive firmware or smartctl problem. For example, to
|
||||
see a complete information about your /dev/sda drive, type the following in a
|
||||
terminal emulator (e.g., xterm, konsole or gnome-terminal):
|
||||
|
||||
smartctl -a /dev/sda
|
||||
|
||||
Note: If using Windows, the device name should be /dev/pd1 for the second
|
||||
physical drive, etc... .
|
||||
|
||||
If you still think it's a GSmartControl issue, please collect the following
|
||||
information about your system. Without it, it may be very hard or impossible
|
||||
to fix the bug.
|
||||
|
||||
* Which operating system you use (for example, openSUSE Linux 11.4).
|
||||
|
||||
* Which version of GTK and Gtkmm you have installed. Finding this out is very
|
||||
distribution-specific. For example, on openSUSE it would be
|
||||
"rpm -q gtk3 gtkmm3". Some distributions have gtkmm30 instead. You may also
|
||||
search them in your distribution's graphical package manager, if there is one.
|
||||
|
||||
* Execution log from the program, if possible. To obtain it, run the program
|
||||
with -v option, e.g. (type the following in a terminal emulator or Run
|
||||
dialog):
|
||||
|
||||
gsmartcontrol-root auto -v
|
||||
|
||||
Note: On Windows, run gsmartcontrol.exe with "-v" switch as Administrator.
|
||||
|
||||
Perform the steps needed to reproduce the bug, then go to
|
||||
"Options -> View Execution Log", and click "Save All".
|
||||
|
||||
* Detailed description of steps you performed when the bug occurred.
|
||||
|
||||
Once you have this information, send an email to me, Alexander Shaduri
|
||||
<ashaduri 'at' gmail.com>. Note that I may refer you to smartmontools support
|
||||
if it's a bug in smartmontools and not GSmartControl. Normally, I won't
|
||||
redirect your support request to them myself, because they may ask questions
|
||||
which only you have the answers to.
|
||||
|
||||
Please read the "License and Copyright" and "Patch Licensing" sections before
|
||||
sending any patches.
|
||||
|
||||
|
||||
|
||||
License and Copyright
|
||||
|
||||
For license information, see LICENSE_gsmartcontrol.txt file.
|
||||
|
||||
You may notice that GSmartControl is not licensed under "GNU GPL version X or
|
||||
later", but under "GNU GPL version X and Y". I firmly believe that it's unwise
|
||||
to license a piece of code under non-existent licenses, whatever anyone else
|
||||
might say. The reason for this is that one simply CANNOT know that, say, in 20
|
||||
years FSF won't be bought by some corporation who will release GPL version Z
|
||||
which will completely reverse the reasons GPL was created for.
|
||||
|
||||
The removal of "or later" clause somewhat imposes responsibility on the
|
||||
copyright holders to review every future version of the license once it's
|
||||
released, and, if deemed acceptable, re-license the code under the new license
|
||||
(possibly retaining the old licenses). Unfortunately, this is a necessary
|
||||
inconvenience we will have to deal with.
|
||||
|
||||
|
||||
|
||||
Patch Licensing
|
||||
|
||||
Due to reasons described in "License and Copyright" section, to make it
|
||||
possible to re-license the code without tracking down all the people who ever
|
||||
wrote a patch, the copyrights of all minor patches must be assigned to the
|
||||
central copyright holder of the project. If the patch is major enough (that
|
||||
is, it forms a significant part of the program source code), the author may
|
||||
retain the copyright, if he or she chooses to do so. However, unless the
|
||||
author plans to maintain his part of the source code, he / she is humbly asked
|
||||
to consider assigning away his / her copyright. A simple "I disclaim all
|
||||
copyright to this patch" by the author is sufficient. All credits will be
|
||||
mentioned in product documentation, whatever the size of the patch is.
|
||||
|
||||
Please note that centralization of copyright is needed to maintain a
|
||||
reasonably healthy legal status of the project. Also note that this method is
|
||||
not unique - FSF and many other organizations require exactly the same thing.
|
||||
|
||||
Some contributors may have reasonable doubts about the future status of this
|
||||
project. Let me assure you that this project will never have more restrictive
|
||||
license than GPLv2. If, some time in the future, the GPL is somehow
|
||||
invalidated in court, the project will be re-licensed under similar (in
|
||||
spirit) license, if possible, or a license less restrictive than GPL (for
|
||||
example, the three-clause BSD license).
|
||||
|
||||
|
||||
@@ -1,243 +0,0 @@
|
||||
|
||||
|
||||
GTKMM Notes:
|
||||
|
||||
To transfer an ownership from your local code to a container, use
|
||||
Gtk::Button* pButton = Gtk::manage(new Gtk::Button("Test"));
|
||||
container->add(*pButton);
|
||||
(add() takes an object by reference).
|
||||
otherwise, you will need to delete() it.
|
||||
|
||||
local scope destruction also works if using stack variables.
|
||||
{
|
||||
Gtk::Button aButton;
|
||||
// destroyed here
|
||||
}
|
||||
|
||||
|
||||
A treeview (also iconview, combobox) from glade comes
|
||||
with existing model, which should be unset.
|
||||
|
||||
|
||||
Bugs / patches:
|
||||
Patch pangomm for Pango::strip_markup().
|
||||
|
||||
|
||||
|
||||
--------------------------------------------------------
|
||||
|
||||
TODO:
|
||||
|
||||
|
||||
Don't rely on smartctl return code (2), parse the output instead.
|
||||
This will allow us to show the Info page.
|
||||
|
||||
?Fix not being able to quit while executing commands.
|
||||
Cannot reproduce.
|
||||
https://www.google.com/search?client=opera&q=IA__gtk_main_quit:+assertion+%60main_loops+!=+NULL'+failed&sourceid=opera&ie=utf-8&oe=utf-8&channel=suggest
|
||||
https://developer.gnome.org/gtk2/2.24/gtk2-General.html#gtk-main-quit
|
||||
https://www.google.com/search?num=50&hl=en&safe=off&client=opera&hs=Dr5&channel=suggest&q=calling+gtk_main_quit+during+gtk_main_iteration+warning&oq=calling+gtk_main_quit+during+gtk_main_iteration+warning&gs_l=serp.12...7064.8155.0.12753.8.8.0.0.0.0.216.935.0j6j1.7.0.les%3B..0.0...1c.1.4.serp.GUiDL5IM2U8
|
||||
https://developer.gnome.org/glib/2.34/glib-The-Main-Event-Loop.html#g-main-loop-quit
|
||||
|
||||
|
||||
In main window add multi-selection support and right-click menu with various
|
||||
tests, so that it's possible to run multiple tests in parallel from there.
|
||||
|
||||
|
||||
Areca detection
|
||||
Linux:
|
||||
Somehow differentiate between the cards with expanders (-d areca,N/E) and without (-d areca,N).
|
||||
The expander ones seem to have "ix" (case-insensitive) as their name suffixes.
|
||||
areca,N - N is [1,24].
|
||||
areca,N/E - N is [1,128], E is [1,8].
|
||||
We should limit these using some config key (36 for N and 4 for E seems ok).
|
||||
N/E syntax is since smartmontools 5.43.
|
||||
For older versions we get: /dev/<something>: Option -d areca,N requires N to be a non-negative integer
|
||||
|
||||
|
||||
|
||||
|
||||
Add ability (through ctrl-C?) to copy individual rows from attributes page,
|
||||
in tab-separated format (for easy pasting into libreoffice).
|
||||
|
||||
|
||||
Investiage the new "-l ssd" and "-l devstat" options (since 5.42)
|
||||
|
||||
Check also other options in 5.42 and 5.43.
|
||||
"-g all" looks promising alongside -i (shows APM and other stuff as well).
|
||||
|
||||
Add --identify option to context menu, for convenience.
|
||||
|
||||
Add menu item - "update drive database" (not sure about linux, should
|
||||
check if it's supported first; also need to run it in terminal).
|
||||
|
||||
|
||||
Testing:
|
||||
If ETA time has elapsed, but it's still only at 10% completion,
|
||||
ETA 0 is displayed. Fix.
|
||||
If smartctl -t ... returns an error, testing=true should not be set.
|
||||
Can't reproduce it?
|
||||
Detect running tests on launch (maybe ask the user too? some tests
|
||||
may be stuck due to bad firmware, e.g. 3ware/windows).
|
||||
|
||||
|
||||
|
||||
48-bit ATA command support:
|
||||
'-l xerror' -> Extended Comprehensive Error log: An multi-sector and
|
||||
LBA48 capable version of '-l error'. Output is similar, except more
|
||||
entries can exists and the high bytes of the registers are also printed.
|
||||
Some newer drives (Samsung) only write to this log.
|
||||
|
||||
'-l xselftest' -> Extended Self Test Log: An multi-sector and LBA48
|
||||
capable version of '-l selftest'. Output will be similar, except more
|
||||
entries can exists and the LBA values can be somewhat longer.
|
||||
|
||||
"smartctl -l this_wont_exist" gives the list of supported logs - this
|
||||
is needed to detect xerror / xselftest support in smartctl.
|
||||
|
||||
|
||||
Rework drive properties UI:
|
||||
use 48-bit or not or auto.
|
||||
Enable/disable/dont_touch SMART on startup for this drive.
|
||||
Enable/disable/dont_touch AODC on startup for this drive.
|
||||
Add --init-smart-only option to only apply the SMART/AODC enabling status
|
||||
from the drive properties.
|
||||
Add also --no-init-smart to avoid doing that on GUI startup.
|
||||
|
||||
Notifications:
|
||||
Tied to (drive::subdrive, notif_key, exact_value(optional)).
|
||||
If exact_value isn't specified when ignoring, notify on every value change.
|
||||
Ignore options:
|
||||
Per-drive: Ignore all notifications for this key, ignore only this value.
|
||||
Don't generate multiple notifications for the same drive/key combo.
|
||||
Clearing in preferences: Clear all ignored, clear ignored for this drive, clear individual.
|
||||
Soft-warnings require "-a" by default, tricky for scsi (maybe try -a, then -i?).
|
||||
|
||||
Monitor:
|
||||
Needs root access, so not gonna happen until the dbus/udisks stuff
|
||||
is finalized.
|
||||
Same program, add --tray option to start in tray only.
|
||||
Option to enable the tray icon.
|
||||
Option to continue running in the tray on window close.
|
||||
Use freedesktop notifications when notifications are generated
|
||||
and we're running in tray only.
|
||||
Don't generate the same notification at a single run time if it was
|
||||
ignored (untouched) by the user; Change the tray icon and
|
||||
show them in the main window instead.
|
||||
Periodically re-run smartctl to refresh the notifications.
|
||||
Periodically see if drives were added / removed (possibly use OS callbacks
|
||||
instead of polling?)
|
||||
|
||||
Win7 widget:
|
||||
Can be done in html/js.
|
||||
Shows temperature
|
||||
Detects drives
|
||||
Opens GSmartControl
|
||||
|
||||
If a drive is not recognized, suggest using -d (sat, sat,12, ...)
|
||||
Refer the user to
|
||||
http://sourceforge.net/apps/trac/smartmontools/wiki/Supported_USB-Devices
|
||||
|
||||
|
||||
|
||||
RAID:
|
||||
http://sourceforge.net/apps/trac/smartmontools/wiki/Supported_RAID-Controllers
|
||||
|
||||
Areca Windows:
|
||||
Detection not supported by smartctl --scan-open.
|
||||
|
||||
Areca FreeBSD: since 5.42.
|
||||
Call as: smartctl -a -d areca,[1-24] /dev/arcmsr1
|
||||
|
||||
3ware FreeBSD:
|
||||
Call as: smartctl -i -d 3ware,[0-127] /dev/twa0 (or twe0)
|
||||
Detection: unknown.
|
||||
|
||||
LSI MegaRAID (SCSI/SAS), Linux:
|
||||
Call as: smartctl -i -d megaraid,[0-?] /dev/sda
|
||||
We should differentiate between ordinary sda and RAID one (not sure if sda is detected).
|
||||
Detection: unknown.
|
||||
|
||||
HighPoint RocketRAID ((S)ATA) Linux:
|
||||
Call as: smartctl -i -d hpt,[1-8]/[1-8] /dev/sda
|
||||
Call as: smartctl -i -d hpt,[1-8]/[1-8]/[1-15] /dev/sda
|
||||
Note: /dev/sda should be derived from this controller.
|
||||
We should differentiate between ordinary sda and RAID one (not sure if sda is detected).
|
||||
Smartctl docs specify smaller limits, but the code says these are the ones.
|
||||
Detection: unknown.
|
||||
|
||||
HighPoint RocketRAID ((S)ATA) FreeBSD:
|
||||
Call as: smartctl -i -d hpt,[1-8]/[1-8] /dev/hptrr
|
||||
Call as: smartctl -i -d hpt,[1-8]/[1-8]/[1-15] /dev/hptrr
|
||||
Note: /dev/hptrr should be derived from this controller.
|
||||
Smartctl docs specify smaller limits, but the code says these are the ones.
|
||||
Detection: unknown.
|
||||
TODO: Analyze sent info.
|
||||
|
||||
CCISS (HP (Compaq) Smart Array Controller) FreeBSD:
|
||||
Seems to be the same as Linux (uses the same driver?).
|
||||
Detection: unknown.
|
||||
|
||||
|
||||
|
||||
|
||||
libdebug:
|
||||
per-channel disabling.
|
||||
per-channel formatting.
|
||||
all flags should be per-channel.
|
||||
bug: if -q is passed, Execution log doesn't contain libdebug log. (requires per-channel status control).
|
||||
use format_time() instead of ctime() and friends, with ISO formatting.
|
||||
|
||||
|
||||
|
||||
'-l sataphy' -> SATA Phy event counters: Non-SMART feature, useful to detect SATA cabling problems.
|
||||
|
||||
|
||||
SCT Temperatures
|
||||
-l scttemp prints temperature and history.
|
||||
-t scttempint,N[,p] (N - count, p - preserve across reset or not).
|
||||
This configures temperature logging interval (1 min by default) and clears the history.
|
||||
-l scterc[,...] to get/set the SCT Error Recovery Control time limit (5.40 and newer).
|
||||
|
||||
|
||||
Add detection for Interix (i586-pc-interix3), maybe with debian?
|
||||
|
||||
|
||||
|
||||
|
||||
----------- SMART stuff ----------
|
||||
|
||||
|
||||
Run one-time Immediate Offline test (-t offline).
|
||||
Offline tests only update Attributes, and if errors are found they will
|
||||
appear in SMART error log. Self-test logs are unaffected.
|
||||
Abort (-X). Abort works with Offline only if there's
|
||||
"Abort Offline collection upon new command" capability.
|
||||
If the drive has "Suspend Offline collection upon new command" capability,
|
||||
immediate offline test may be tracked through --capabilities (not on mine!).
|
||||
If it's "Abort Offline collection upon new command", then the test
|
||||
will abort on --capabilities or --abort.
|
||||
|
||||
|
||||
Polling time
|
||||
The self-test routine recommended polling time shall be equal to the number of minutes that is the minimum
|
||||
recommended time before which the host should first poll for test completion status. Actual test time could
|
||||
be several times this value. Polling before this time could extend the self-test execution time or abort the test
|
||||
depending on the state of bit 2 of the off-line data capability bits.
|
||||
|
||||
Autosave
|
||||
This command enables and disables the optional attribute autosave feature of the device. This command
|
||||
may either allow the device, after some vendor specified event, to save the device updated attribute values
|
||||
to non-volatile memory; or this command may cause the autosave feature to be disabled. The state of the
|
||||
attribute autosave feature (either enabled or disabled) shall be preserved by the device across power cycles.
|
||||
A value of zero written by the host into the device’s Sector Count register before issuing this command shall
|
||||
cause this feature to be disabled. Disabling this feature does not preclude the device from saving SMART
|
||||
data to non-volatile memory during some other normal operation such as during a power-on or power-off
|
||||
sequence or during an error recovery sequence.
|
||||
A value of F1h written by the host into the device’s Sector Count register before issuing this command shall
|
||||
cause this feature to be enabled. Any other meaning of this value or any other non-zero value written by the
|
||||
host into this register before issuing this command may differ from device to device. The meaning of any
|
||||
non-zero value written to this register at this time shall be preserved by the device across power cycles.
|
||||
|
||||
|
||||
|
||||
@@ -1,191 +0,0 @@
|
||||
/**************************************************************************
|
||||
Copyright:
|
||||
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
|
||||
License: See LICENSE_gsmartcontrol.txt
|
||||
***************************************************************************/
|
||||
/// \file
|
||||
/// \author Alexander Shaduri
|
||||
/// \ingroup applib
|
||||
/// \weakgroup applib
|
||||
/// @{
|
||||
|
||||
#include <map>
|
||||
#include <ostream> // not iosfwd - it doesn't work
|
||||
|
||||
#include "hz/string_num.h" // number_to_string
|
||||
#include "hz/stream_cast.h" // stream_cast<>
|
||||
#include "hz/format_unit.h" // format_time_length
|
||||
#include "hz/string_algo.h" // string_join
|
||||
|
||||
#include "storage_property.h"
|
||||
|
||||
|
||||
|
||||
std::ostream& operator<< (std::ostream& os, const StorageCapability& p)
|
||||
{
|
||||
os
|
||||
// << p.name << ": "
|
||||
<< p.flag_value;
|
||||
for (StorageCapability::strvalue_list_t::const_iterator iter = p.strvalues.begin(); iter != p.strvalues.end(); ++iter) {
|
||||
os << "\n\t" << *iter;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::ostream& operator<< (std::ostream& os, const StorageAttribute& p)
|
||||
{
|
||||
// os << p.name << ": "
|
||||
if (p.value.defined()) {
|
||||
os << static_cast<int>(p.value.value());
|
||||
} else {
|
||||
os << "-";
|
||||
}
|
||||
os << " (" << p.raw_value_int << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string StorageErrorBlock::get_readable_error_types(const std::vector<std::string>& types)
|
||||
{
|
||||
std::map<std::string, std::string> m;
|
||||
|
||||
m["ABRT"] = "Command aborted";
|
||||
m["AMNF"] = "Address mark not found";
|
||||
m["CCTO"] = "Command completion timed out";
|
||||
m["EOM"] = "End of media";
|
||||
m["ICRC"] = "Interface CRC error";
|
||||
m["IDNF"] = "Identity not found";
|
||||
m["ILI"] = "(Packet command-set specific)";
|
||||
m["MC"] = "Media changed";
|
||||
m["MCR"] = "Media change request";
|
||||
m["NM"] = "No media";
|
||||
m["obs"] = "Obsolete";
|
||||
m["TK0NF"] = "Track 0 not found";
|
||||
m["UNC"] = "Uncorrectable error in data";
|
||||
m["WP"] = "Media is write protected";
|
||||
|
||||
std::vector<std::string> sv;
|
||||
for (std::vector<std::string>::const_iterator iter = types.begin(); iter != types.end(); ++iter) {
|
||||
if (m.find(*iter) != m.end()) {
|
||||
sv.push_back(m[*iter]);
|
||||
} else {
|
||||
sv.push_back("[unknown type" + (iter->empty() ? "" : (": " + (*iter))) + "]");
|
||||
}
|
||||
}
|
||||
|
||||
return hz::string_join(sv, ", ");
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::ostream& operator<< (std::ostream& os, const StorageErrorBlock& b)
|
||||
{
|
||||
os << "Error number " << b.error_num << ": "
|
||||
<< hz::string_join(b.reported_types, ", ")
|
||||
<< " [" << StorageErrorBlock::get_readable_error_types(b.reported_types) << "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::ostream& operator<< (std::ostream& os, const StorageSelftestEntry& b)
|
||||
{
|
||||
os << "Test entry " << b.test_num << ": "
|
||||
<< b.type << ", status: " << b.get_status_str() << ", remaining: " << int(b.remaining_percent);
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void StorageProperty::dump(std::ostream& os, int internal_offset) const
|
||||
{
|
||||
std::string offset(internal_offset, ' ');
|
||||
|
||||
os << offset << "[" << get_section_name(section)
|
||||
<< (section == section_data ? (", " + get_subsection_name(subsection)) : "") << "]"
|
||||
<< " " << generic_name
|
||||
// << (generic_name == reported_name ? "" : (" (" + reported_name + ")"))
|
||||
<< ": [" << get_value_type_name(value_type) << "] ";
|
||||
|
||||
// if (!readable_value.empty())
|
||||
// os << readable_value;
|
||||
|
||||
if (value_type == StorageProperty::value_type_unknown) {
|
||||
os << "[empty]";
|
||||
|
||||
} else if (value_type == StorageProperty::value_type_string) {
|
||||
os << "\"" << value_string << "\"";
|
||||
|
||||
} else if (value_type == StorageProperty::value_type_integer) {
|
||||
os << value_integer << " [" << reported_value << "]";
|
||||
|
||||
} else if (value_type == StorageProperty::value_type_bool) {
|
||||
os << value_bool << " [" << reported_value << "]";
|
||||
|
||||
} else if (value_type == StorageProperty::value_type_time_length) {
|
||||
os << value_time_length << " [" << reported_value << "]";
|
||||
|
||||
} else if (value_type == StorageProperty::value_type_capability) {
|
||||
os << value_capability;
|
||||
|
||||
} else if (value_type == StorageProperty::value_type_attribute) {
|
||||
os << value_attribute;
|
||||
|
||||
} else if (value_type == StorageProperty::value_type_error_block) {
|
||||
os << value_error_block;
|
||||
|
||||
} else if (value_type == StorageProperty::value_type_selftest_entry) {
|
||||
os << value_selftest_entry;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string StorageProperty::format_value(bool add_reported_too) const
|
||||
{
|
||||
if (!readable_value.empty())
|
||||
return readable_value;
|
||||
|
||||
if (value_type == StorageProperty::value_type_unknown) {
|
||||
return "[unknown]";
|
||||
|
||||
} else if (value_type == StorageProperty::value_type_string) {
|
||||
return value_string;
|
||||
|
||||
} else if (value_type == StorageProperty::value_type_integer) {
|
||||
return hz::number_to_string(value_integer) + (add_reported_too ? (" [" + reported_value + "]") : "");
|
||||
|
||||
} else if (value_type == StorageProperty::value_type_bool) {
|
||||
return std::string(value_bool ? "Yes" : "No") + (add_reported_too ? (" [" + reported_value + "]") : "");
|
||||
|
||||
} else if (value_type == StorageProperty::value_type_time_length) {
|
||||
return hz::format_time_length(value_time_length) + (add_reported_too ? (" [" + reported_value + "]") : "");
|
||||
|
||||
} else if (value_type == StorageProperty::value_type_capability) {
|
||||
return hz::stream_cast<std::string>(value_capability);
|
||||
|
||||
} else if (value_type == StorageProperty::value_type_attribute) {
|
||||
return hz::stream_cast<std::string>(value_attribute);
|
||||
|
||||
} else if (value_type == StorageProperty::value_type_error_block) {
|
||||
return hz::stream_cast<std::string>(value_error_block);
|
||||
|
||||
} else if (value_type == StorageProperty::value_type_error_block) {
|
||||
return hz::stream_cast<std::string>(value_selftest_entry);
|
||||
}
|
||||
|
||||
return "[error]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// @}
|
||||
@@ -1,223 +0,0 @@
|
||||
/**************************************************************************
|
||||
Copyright:
|
||||
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
|
||||
License: See LICENSE_gsmartcontrol.txt
|
||||
***************************************************************************/
|
||||
/// \file
|
||||
/// \author Alexander Shaduri
|
||||
/// \ingroup gsc
|
||||
/// \weakgroup gsc
|
||||
/// @{
|
||||
|
||||
#include <vector>
|
||||
#include <gtkmm.h>
|
||||
#include <gdk/gdk.h> // GDK_KEY_Escape
|
||||
#include "applib/app_gtkmm_features.h"
|
||||
|
||||
#include "hz/string_algo.h"
|
||||
#include "applib/app_gtkmm_utils.h" // app_gtkmm_create_tree_view_column
|
||||
#include "applib/app_pcrecpp.h"
|
||||
|
||||
#include "gsc_help_window.h"
|
||||
|
||||
|
||||
|
||||
HZ_RES_DATA_INIT_NAMED(README_txt, "README.txt", ReadmeTextResData);
|
||||
|
||||
|
||||
|
||||
GscHelpWindow::GscHelpWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui)
|
||||
: AppUIResWidget<GscHelpWindow, false>(gtkcobj, ref_ui), selection_callback_enabled(true)
|
||||
{
|
||||
// Connect callbacks
|
||||
|
||||
APP_GTKMM_CONNECT_VIRTUAL(delete_event); // make sure the event handler is called
|
||||
|
||||
Gtk::Button* window_close_button = 0;
|
||||
APP_UI_RES_AUTO_CONNECT(window_close_button, clicked);
|
||||
|
||||
|
||||
// Accelerators
|
||||
|
||||
Glib::RefPtr<Gtk::AccelGroup> accel_group = this->get_accel_group();
|
||||
if (window_close_button) {
|
||||
window_close_button->add_accelerator("clicked", accel_group, GDK_KEY_Escape,
|
||||
Gdk::ModifierType(0), Gtk::AccelFlags(0));
|
||||
}
|
||||
|
||||
|
||||
// --------------- Make a treeview
|
||||
|
||||
Gtk::TreeView* treeview = this->lookup_widget<Gtk::TreeView*>("topics_treeview");
|
||||
if (treeview) {
|
||||
Gtk::TreeModelColumnRecord model_columns;
|
||||
// Topic
|
||||
model_columns.add(col_topic);
|
||||
app_gtkmm_create_tree_view_column(col_topic, *treeview, "Topic", "Topic");
|
||||
|
||||
// create a TreeModel (ListStore)
|
||||
list_store = Gtk::ListStore::create(model_columns);
|
||||
treeview->set_model(list_store);
|
||||
|
||||
selection = treeview->get_selection();
|
||||
selection->signal_changed().connect(sigc::mem_fun(*this,
|
||||
&self_type::on_tree_selection_changed) );
|
||||
|
||||
}
|
||||
|
||||
|
||||
// --------------- Parse help text
|
||||
|
||||
/*
|
||||
README.txt File Format
|
||||
|
||||
The whole text is converted to unix newline format before parsing.
|
||||
Sections are separated by 3 newlines (two empty lines).
|
||||
The first line of the section is its header.
|
||||
When splitting the file to sections and headers, any leading or trailing
|
||||
whitespace is removed.
|
||||
If there is a single newline inside a section, it is converted to
|
||||
space to enable correct wrapping.
|
||||
If there are two consequent newlines, they are left as they are,
|
||||
essentially making a paragraph break.
|
||||
*/
|
||||
|
||||
std::string readme = hz::string_any_to_unix_copy(ReadmeTextResData().get_string());
|
||||
|
||||
|
||||
// Paragraphs are delimited by 3 empty lines
|
||||
std::vector<std::string> topics;
|
||||
hz::string_split(readme, "\n\n\n\n", topics, true); // skip empty
|
||||
|
||||
|
||||
// Add to treeview and textview
|
||||
|
||||
Gtk::TextView* content = this->lookup_widget<Gtk::TextView*>("content_textview");
|
||||
|
||||
if (treeview && content) {
|
||||
Glib::RefPtr<Gtk::TextBuffer> buffer = content->get_buffer();
|
||||
|
||||
buffer->create_mark("Begin", buffer->begin(), true);
|
||||
|
||||
for (unsigned int i = 0; i < topics.size(); ++i) {
|
||||
std::string topic = hz::string_trim_copy(topics[i]);
|
||||
|
||||
// The first line of topic is its title
|
||||
std::vector<std::string> topic_split;
|
||||
hz::string_split(topic, "\n\n", topic_split, true, 2); // skip empty, get 2 elements only
|
||||
|
||||
if (topic_split.size() < 2) {
|
||||
debug_out_warn("app", DBG_FUNC_MSG << "Cannot extract topic title in topic " << i << "\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string topic_title = hz::string_trim_copy(topic_split[0]);
|
||||
std::string topic_body = hz::string_trim_copy(topic_split[1]);
|
||||
|
||||
buffer->create_mark(topic_title, buffer->end(), true); // set topic mark to the end of what's there
|
||||
|
||||
// add the title and make it bold
|
||||
buffer->insert(buffer->end(), "\n" + topic_title);
|
||||
|
||||
Gtk::TextIter first = buffer->end(), last = first;
|
||||
first.backward_lines(1);
|
||||
|
||||
Glib::RefPtr<Gtk::TextTag> tag = buffer->create_tag();
|
||||
tag->property_weight() = Pango::WEIGHT_BOLD;
|
||||
tag->property_size_points() = 14;
|
||||
|
||||
buffer->apply_tag(tag, first, last);
|
||||
|
||||
// add the rest
|
||||
|
||||
// single newlines to spaces, to allow proper wrapping.
|
||||
app_pcre_replace("/([^\\n])\\n([^\\n])/", "\\1 \\2", topic_body);
|
||||
buffer->insert(buffer->end(), "\n\n" + topic_body + "\n\n");
|
||||
|
||||
|
||||
// Add to treeview
|
||||
|
||||
Gtk::TreeRow row = *(list_store->append());
|
||||
row[col_topic] = topic_title;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ---------------
|
||||
|
||||
|
||||
// show();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GscHelpWindow::set_topic(const Glib::ustring& topic)
|
||||
{
|
||||
this->selection_callback_enabled = false; // temporarily disable it
|
||||
|
||||
// scroll to it
|
||||
|
||||
Gtk::TextView* content = this->lookup_widget<Gtk::TextView*>("content_textview");
|
||||
if (content) {
|
||||
Glib::RefPtr<Gtk::TextBuffer> buffer = content->get_buffer();
|
||||
|
||||
Glib::RefPtr<Gtk::TextMark> mark = buffer->get_mark(topic);
|
||||
if (mark)
|
||||
content->scroll_to(mark, 0., 0., 0.);
|
||||
}
|
||||
|
||||
// select it in tree view
|
||||
Gtk::TreeView* treeview = this->lookup_widget<Gtk::TreeView*>("topics_treeview");
|
||||
|
||||
if (treeview && !list_store->children().empty()) {
|
||||
for (Gtk::TreeIter iter = list_store->children().begin(); iter != list_store->children().end(); ++iter) {
|
||||
if (iter->get_value(col_topic) == topic) {
|
||||
selection->select(*iter);
|
||||
// about 30% from top
|
||||
treeview->scroll_to_cell(list_store->get_path(iter), *(treeview->get_column(0)), 0.3f, 0.f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->selection_callback_enabled = true; // enable it back
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool GscHelpWindow::on_delete_event_before(GdkEventAny* e)
|
||||
{
|
||||
destroy(this);
|
||||
return true; // event handled, don't call default virtual handler
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GscHelpWindow::on_window_close_button_clicked()
|
||||
{
|
||||
destroy(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GscHelpWindow::on_tree_selection_changed()
|
||||
{
|
||||
if (!this->selection_callback_enabled)
|
||||
return;
|
||||
|
||||
if (selection->count_selected_rows()) {
|
||||
Gtk::TreeIter iter = selection->get_selected();
|
||||
Gtk::TreeRow row = *iter;
|
||||
|
||||
set_topic(row[col_topic]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// @}
|
||||
@@ -1,79 +0,0 @@
|
||||
/**************************************************************************
|
||||
Copyright:
|
||||
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
|
||||
License: See LICENSE_gsmartcontrol.txt
|
||||
***************************************************************************/
|
||||
/// \file
|
||||
/// \author Alexander Shaduri
|
||||
/// \ingroup gsc
|
||||
/// \weakgroup gsc
|
||||
/// @{
|
||||
|
||||
#ifndef GSC_HELP_WINDOW_H
|
||||
#define GSC_HELP_WINDOW_H
|
||||
|
||||
#include <gtkmm.h>
|
||||
|
||||
#include "applib/app_ui_res_utils.h"
|
||||
|
||||
|
||||
|
||||
|
||||
/// The Help window.
|
||||
/// Use create() / destroy() with this class instead of new / delete!
|
||||
class GscHelpWindow : public AppUIResWidget<GscHelpWindow, false> {
|
||||
public:
|
||||
|
||||
// name of glade/ui file without a .glade/.ui extension and quotes
|
||||
APP_UI_RES_DATA_INIT(gsc_help_window);
|
||||
|
||||
|
||||
/// Constructor, gtkbuilder/glade needs this.
|
||||
GscHelpWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui);
|
||||
|
||||
/// Virtual destructor
|
||||
virtual ~GscHelpWindow()
|
||||
{ }
|
||||
|
||||
|
||||
/// Set the current help topic
|
||||
void set_topic(const Glib::ustring& topic);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
// ---------- overriden virtual methods
|
||||
|
||||
/// Destroy this object on delete event (by default it calls hide()).
|
||||
/// Reimplemented from Gtk::Window.
|
||||
bool on_delete_event_before(GdkEventAny* e);
|
||||
|
||||
|
||||
// ---------- other callbacks
|
||||
|
||||
/// Button click callback
|
||||
void on_window_close_button_clicked();
|
||||
|
||||
/// Callback
|
||||
void on_tree_selection_changed();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
Glib::RefPtr<Gtk::ListStore> list_store; ///< List store
|
||||
Glib::RefPtr<Gtk::TreeSelection> selection; ///< Tree selection
|
||||
|
||||
Gtk::TreeModelColumn<Glib::ustring> col_topic; /// Tree column
|
||||
|
||||
bool selection_callback_enabled; ///< Helper for set_topic(), temporarily disables the tree selection changed callback
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/// @}
|
||||
@@ -1,10 +0,0 @@
|
||||
AM_CPPFLAGS = $(all_includes)
|
||||
METASOURCES = AUTO
|
||||
noinst_LIBRARIES = libpcrecpp.a
|
||||
libpcrecpp_a_SOURCES = pcre_scanner.cpp pcre_stringpiece.cpp pcrecpp.cpp
|
||||
noinst_HEADERS = pcre_scanner.h pcre_stringpiece.h pcrecpp.h pcrecpp_internal.h \
|
||||
pcrecpparg.h
|
||||
|
||||
# Distribute original, unpatched files too. Append .orig_dist to prevent conflict
|
||||
# with debian .orig files.
|
||||
EXTRA_DIST = pcrecpp.cc.orig_dist
|
||||
@@ -1,13 +0,0 @@
|
||||
|
||||
Part of PCRE library, version 8.01.
|
||||
|
||||
pcrecpparg.h and pcre_stringpiece.h generated using:
|
||||
|
||||
./configure \
|
||||
--with-link-size=2 \
|
||||
--with-match-limit=10000000 \
|
||||
--enable-newline-is-lf \
|
||||
--enable-utf8 \
|
||||
--enable-unicode-properties
|
||||
|
||||
The system was OpenSUSE 11.1, with gcc-4.3-34.243 and glibc-2.9-2.12.1.
|
||||
@@ -1,200 +0,0 @@
|
||||
// Copyright (c) 2005, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: Sanjay Ghemawat
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
#include <cassert> // Custom
|
||||
#include <assert.h>
|
||||
|
||||
#include "pcrecpp_internal.h"
|
||||
#include "pcre_scanner.h"
|
||||
|
||||
using std::vector;
|
||||
|
||||
namespace pcrecpp {
|
||||
|
||||
Scanner::Scanner()
|
||||
: data_(),
|
||||
input_(data_),
|
||||
skip_(NULL),
|
||||
should_skip_(false),
|
||||
skip_repeat_(false),
|
||||
save_comments_(false),
|
||||
comments_(NULL),
|
||||
comments_offset_(0) {
|
||||
}
|
||||
|
||||
Scanner::Scanner(const string& in)
|
||||
: data_(in),
|
||||
input_(data_),
|
||||
skip_(NULL),
|
||||
should_skip_(false),
|
||||
skip_repeat_(false),
|
||||
save_comments_(false),
|
||||
comments_(NULL),
|
||||
comments_offset_(0) {
|
||||
}
|
||||
|
||||
Scanner::~Scanner() {
|
||||
delete skip_;
|
||||
delete comments_;
|
||||
}
|
||||
|
||||
void Scanner::SetSkipExpression(const char* re) {
|
||||
delete skip_;
|
||||
if (re != NULL) {
|
||||
skip_ = new RE(re);
|
||||
should_skip_ = true;
|
||||
skip_repeat_ = true;
|
||||
ConsumeSkip();
|
||||
} else {
|
||||
skip_ = NULL;
|
||||
should_skip_ = false;
|
||||
skip_repeat_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Scanner::Skip(const char* re) {
|
||||
delete skip_;
|
||||
if (re != NULL) {
|
||||
skip_ = new RE(re);
|
||||
should_skip_ = true;
|
||||
skip_repeat_ = false;
|
||||
ConsumeSkip();
|
||||
} else {
|
||||
skip_ = NULL;
|
||||
should_skip_ = false;
|
||||
skip_repeat_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Scanner::DisableSkip() {
|
||||
assert(skip_ != NULL);
|
||||
should_skip_ = false;
|
||||
}
|
||||
|
||||
void Scanner::EnableSkip() {
|
||||
assert(skip_ != NULL);
|
||||
should_skip_ = true;
|
||||
ConsumeSkip();
|
||||
}
|
||||
|
||||
int Scanner::LineNumber() const {
|
||||
// TODO: Make it more efficient by keeping track of the last point
|
||||
// where we computed line numbers and counting newlines since then.
|
||||
// We could use std:count, but not all systems have it. :-(
|
||||
int count = 1;
|
||||
for (const char* p = data_.data(); p < input_.data(); ++p)
|
||||
if (*p == '\n')
|
||||
++count;
|
||||
return count;
|
||||
}
|
||||
|
||||
int Scanner::Offset() const {
|
||||
return input_.data() - data_.c_str();
|
||||
}
|
||||
|
||||
bool Scanner::LookingAt(const RE& re) const {
|
||||
int consumed;
|
||||
return re.DoMatch(input_, RE::ANCHOR_START, &consumed, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
bool Scanner::Consume(const RE& re,
|
||||
const Arg& arg0,
|
||||
const Arg& arg1,
|
||||
const Arg& arg2) {
|
||||
const bool result = re.Consume(&input_, arg0, arg1, arg2);
|
||||
if (result && should_skip_) ConsumeSkip();
|
||||
return result;
|
||||
}
|
||||
|
||||
// helper function to consume *skip_ and honour save_comments_
|
||||
void Scanner::ConsumeSkip() {
|
||||
const char* start_data = input_.data();
|
||||
while (skip_->Consume(&input_)) {
|
||||
if (!skip_repeat_) {
|
||||
// Only one skip allowed.
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (save_comments_) {
|
||||
if (comments_ == NULL) {
|
||||
comments_ = new vector<StringPiece>;
|
||||
}
|
||||
// already pointing one past end, so no need to +1
|
||||
int length = input_.data() - start_data;
|
||||
if (length > 0) {
|
||||
comments_->push_back(StringPiece(start_data, length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Scanner::GetComments(int start, int end, vector<StringPiece> *ranges) {
|
||||
// short circuit out if we've not yet initialized comments_
|
||||
// (e.g., when save_comments is false)
|
||||
if (!comments_) {
|
||||
return;
|
||||
}
|
||||
// TODO: if we guarantee that comments_ will contain StringPieces
|
||||
// that are ordered by their start, then we can do a binary search
|
||||
// for the first StringPiece at or past start and then scan for the
|
||||
// ones contained in the range, quit early (use equal_range or
|
||||
// lower_bound)
|
||||
for (vector<StringPiece>::const_iterator it = comments_->begin();
|
||||
it != comments_->end(); ++it) {
|
||||
if ((it->data() >= data_.c_str() + start &&
|
||||
it->data() + it->size() <= data_.c_str() + end)) {
|
||||
ranges->push_back(*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Scanner::GetNextComments(vector<StringPiece> *ranges) {
|
||||
// short circuit out if we've not yet initialized comments_
|
||||
// (e.g., when save_comments is false)
|
||||
if (!comments_) {
|
||||
return;
|
||||
}
|
||||
for (vector<StringPiece>::const_iterator it =
|
||||
comments_->begin() + comments_offset_;
|
||||
it != comments_->end(); ++it) {
|
||||
ranges->push_back(*it);
|
||||
++comments_offset_;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace pcrecpp
|
||||
@@ -1,173 +0,0 @@
|
||||
// Copyright (c) 2005, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: Sanjay Ghemawat
|
||||
//
|
||||
// Regular-expression based scanner for parsing an input stream.
|
||||
//
|
||||
// Example 1: parse a sequence of "var = number" entries from input:
|
||||
//
|
||||
// Scanner scanner(input);
|
||||
// string var;
|
||||
// int number;
|
||||
// scanner.SetSkipExpression("\\s+"); // Skip any white space we encounter
|
||||
// while (scanner.Consume("(\\w+) = (\\d+)", &var, &number)) {
|
||||
// ...;
|
||||
// }
|
||||
|
||||
#ifndef _PCRE_SCANNER_H
|
||||
#define _PCRE_SCANNER_H
|
||||
|
||||
#include <cassert> // Custom
|
||||
#include <assert.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <pcrecpp.h>
|
||||
#include <pcre_stringpiece.h>
|
||||
|
||||
namespace pcrecpp {
|
||||
|
||||
class PCRECPP_EXP_DEFN Scanner {
|
||||
public:
|
||||
Scanner();
|
||||
explicit Scanner(const std::string& input);
|
||||
~Scanner();
|
||||
|
||||
// Return current line number. The returned line-number is
|
||||
// one-based. I.e. it returns 1 + the number of consumed newlines.
|
||||
//
|
||||
// Note: this method may be slow. It may take time proportional to
|
||||
// the size of the input.
|
||||
int LineNumber() const;
|
||||
|
||||
// Return the byte-offset that the scanner is looking in the
|
||||
// input data;
|
||||
int Offset() const;
|
||||
|
||||
// Return true iff the start of the remaining input matches "re"
|
||||
bool LookingAt(const RE& re) const;
|
||||
|
||||
// Return true iff all of the following are true
|
||||
// a. the start of the remaining input matches "re",
|
||||
// b. if any arguments are supplied, matched sub-patterns can be
|
||||
// parsed and stored into the arguments.
|
||||
// If it returns true, it skips over the matched input and any
|
||||
// following input that matches the "skip" regular expression.
|
||||
bool Consume(const RE& re,
|
||||
const Arg& arg0 = RE::no_arg,
|
||||
const Arg& arg1 = RE::no_arg,
|
||||
const Arg& arg2 = RE::no_arg
|
||||
// TODO: Allow more arguments?
|
||||
);
|
||||
|
||||
// Set the "skip" regular expression. If after consuming some data,
|
||||
// a prefix of the input matches this RE, it is automatically
|
||||
// skipped. For example, a programming language scanner would use
|
||||
// a skip RE that matches white space and comments.
|
||||
//
|
||||
// scanner.SetSkipExpression("\\s+|//.*|/[*](.|\n)*?[*]/");
|
||||
//
|
||||
// Skipping repeats as long as it succeeds. We used to let people do
|
||||
// this by writing "(...)*" in the regular expression, but that added
|
||||
// up to lots of recursive calls within the pcre library, so now we
|
||||
// control repetition explicitly via the function call API.
|
||||
//
|
||||
// You can pass NULL for "re" if you do not want any data to be skipped.
|
||||
void Skip(const char* re); // DEPRECATED; does *not* repeat
|
||||
void SetSkipExpression(const char* re);
|
||||
|
||||
// Temporarily pause "skip"ing. This
|
||||
// Skip("Foo"); code ; DisableSkip(); code; EnableSkip()
|
||||
// is similar to
|
||||
// Skip("Foo"); code ; Skip(NULL); code ; Skip("Foo");
|
||||
// but avoids creating/deleting new RE objects.
|
||||
void DisableSkip();
|
||||
|
||||
// Reenable previously paused skipping. Any prefix of the input
|
||||
// that matches the skip pattern is immediately dropped.
|
||||
void EnableSkip();
|
||||
|
||||
/***** Special wrappers around SetSkip() for some common idioms *****/
|
||||
|
||||
// Arranges to skip whitespace, C comments, C++ comments.
|
||||
// The overall RE is a disjunction of the following REs:
|
||||
// \\s whitespace
|
||||
// //.*\n C++ comment
|
||||
// /[*](.|\n)*?[*]/ C comment (x*? means minimal repetitions of x)
|
||||
// We get repetition via the semantics of SetSkipExpression, not by using *
|
||||
void SkipCXXComments() {
|
||||
SetSkipExpression("\\s|//.*\n|/[*](?:\n|.)*?[*]/");
|
||||
}
|
||||
|
||||
void set_save_comments(bool comments) {
|
||||
save_comments_ = comments;
|
||||
}
|
||||
|
||||
bool save_comments() {
|
||||
return save_comments_;
|
||||
}
|
||||
|
||||
// Append to vector ranges the comments found in the
|
||||
// byte range [start,end] (inclusive) of the input data.
|
||||
// Only comments that were extracted entirely within that
|
||||
// range are returned: no range splitting of atomically-extracted
|
||||
// comments is performed.
|
||||
void GetComments(int start, int end, std::vector<StringPiece> *ranges);
|
||||
|
||||
// Append to vector ranges the comments added
|
||||
// since the last time this was called. This
|
||||
// functionality is provided for efficiency when
|
||||
// interleaving scanning with parsing.
|
||||
void GetNextComments(std::vector<StringPiece> *ranges);
|
||||
|
||||
private:
|
||||
std::string data_; // All the input data
|
||||
StringPiece input_; // Unprocessed input
|
||||
RE* skip_; // If non-NULL, RE for skipping input
|
||||
bool should_skip_; // If true, use skip_
|
||||
bool skip_repeat_; // If true, repeat skip_ as long as it works
|
||||
bool save_comments_; // If true, aggregate the skip expression
|
||||
|
||||
// the skipped comments
|
||||
// TODO: later consider requiring that the StringPieces be added
|
||||
// in order by their start position
|
||||
std::vector<StringPiece> *comments_;
|
||||
|
||||
// the offset into comments_ that has been returned by GetNextComments
|
||||
int comments_offset_;
|
||||
|
||||
// helper function to consume *skip_ and honour
|
||||
// save_comments_
|
||||
void ConsumeSkip();
|
||||
};
|
||||
|
||||
} // namespace pcrecpp
|
||||
|
||||
#endif /* _PCRE_SCANNER_H */
|
||||
@@ -1,43 +0,0 @@
|
||||
// Copyright (c) 2005, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: wilsonh@google.com (Wilson Hsieh)
|
||||
//
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include "pcrecpp_internal.h"
|
||||
#include "pcre_stringpiece.h"
|
||||
|
||||
std::ostream& operator<<(std::ostream& o, const pcrecpp::StringPiece& piece) {
|
||||
return (o << piece.as_string());
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
// Copyright (c) 2005, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: Sanjay Ghemawat
|
||||
//
|
||||
// A string like object that points into another piece of memory.
|
||||
// Useful for providing an interface that allows clients to easily
|
||||
// pass in either a "const char*" or a "string".
|
||||
//
|
||||
// Arghh! I wish C++ literals were automatically of type "string".
|
||||
|
||||
#ifndef _PCRE_STRINGPIECE_H
|
||||
#define _PCRE_STRINGPIECE_H
|
||||
|
||||
#include <cstring> // Custom
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <iosfwd> // for ostream forward-declaration
|
||||
|
||||
#if 0
|
||||
#define HAVE_TYPE_TRAITS
|
||||
#include <type_traits.h>
|
||||
#elif 0
|
||||
#define HAVE_TYPE_TRAITS
|
||||
#include <bits/type_traits.h>
|
||||
#endif
|
||||
|
||||
#include <pcre.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace pcrecpp {
|
||||
|
||||
class PCRECPP_EXP_DEFN StringPiece {
|
||||
private:
|
||||
const char* ptr_;
|
||||
int length_;
|
||||
|
||||
public:
|
||||
// We provide non-explicit singleton constructors so users can pass
|
||||
// in a "const char*" or a "string" wherever a "StringPiece" is
|
||||
// expected.
|
||||
StringPiece()
|
||||
: ptr_(NULL), length_(0) { }
|
||||
StringPiece(const char* str)
|
||||
: ptr_(str), length_(static_cast<int>(strlen(ptr_))) { }
|
||||
StringPiece(const unsigned char* str)
|
||||
: ptr_(reinterpret_cast<const char*>(str)),
|
||||
length_(static_cast<int>(strlen(ptr_))) { }
|
||||
StringPiece(const string& str)
|
||||
: ptr_(str.data()), length_(static_cast<int>(str.size())) { }
|
||||
StringPiece(const char* offset, int len)
|
||||
: ptr_(offset), length_(len) { }
|
||||
|
||||
// data() may return a pointer to a buffer with embedded NULs, and the
|
||||
// returned buffer may or may not be null terminated. Therefore it is
|
||||
// typically a mistake to pass data() to a routine that expects a NUL
|
||||
// terminated string. Use "as_string().c_str()" if you really need to do
|
||||
// this. Or better yet, change your routine so it does not rely on NUL
|
||||
// termination.
|
||||
const char* data() const { return ptr_; }
|
||||
int size() const { return length_; }
|
||||
bool empty() const { return length_ == 0; }
|
||||
|
||||
void clear() { ptr_ = NULL; length_ = 0; }
|
||||
void set(const char* buffer, int len) { ptr_ = buffer; length_ = len; }
|
||||
void set(const char* str) {
|
||||
ptr_ = str;
|
||||
length_ = static_cast<int>(strlen(str));
|
||||
}
|
||||
void set(const void* buffer, int len) {
|
||||
ptr_ = reinterpret_cast<const char*>(buffer);
|
||||
length_ = len;
|
||||
}
|
||||
|
||||
char operator[](int i) const { return ptr_[i]; }
|
||||
|
||||
void remove_prefix(int n) {
|
||||
ptr_ += n;
|
||||
length_ -= n;
|
||||
}
|
||||
|
||||
void remove_suffix(int n) {
|
||||
length_ -= n;
|
||||
}
|
||||
|
||||
bool operator==(const StringPiece& x) const {
|
||||
return ((length_ == x.length_) &&
|
||||
(memcmp(ptr_, x.ptr_, length_) == 0));
|
||||
}
|
||||
bool operator!=(const StringPiece& x) const {
|
||||
return !(*this == x);
|
||||
}
|
||||
|
||||
#define STRINGPIECE_BINARY_PREDICATE(cmp,auxcmp) \
|
||||
bool operator cmp (const StringPiece& x) const { \
|
||||
int r = memcmp(ptr_, x.ptr_, length_ < x.length_ ? length_ : x.length_); \
|
||||
return ((r auxcmp 0) || ((r == 0) && (length_ cmp x.length_))); \
|
||||
}
|
||||
STRINGPIECE_BINARY_PREDICATE(<, <);
|
||||
STRINGPIECE_BINARY_PREDICATE(<=, <);
|
||||
STRINGPIECE_BINARY_PREDICATE(>=, >);
|
||||
STRINGPIECE_BINARY_PREDICATE(>, >);
|
||||
#undef STRINGPIECE_BINARY_PREDICATE
|
||||
|
||||
int compare(const StringPiece& x) const {
|
||||
int r = memcmp(ptr_, x.ptr_, length_ < x.length_ ? length_ : x.length_);
|
||||
if (r == 0) {
|
||||
if (length_ < x.length_) r = -1;
|
||||
else if (length_ > x.length_) r = +1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
string as_string() const {
|
||||
return string(data(), size());
|
||||
}
|
||||
|
||||
void CopyToString(string* target) const {
|
||||
target->assign(ptr_, length_);
|
||||
}
|
||||
|
||||
// Does "this" start with "x"
|
||||
bool starts_with(const StringPiece& x) const {
|
||||
return ((length_ >= x.length_) && (memcmp(ptr_, x.ptr_, x.length_) == 0));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace pcrecpp
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Functions used to create STL containers that use StringPiece
|
||||
// Remember that a StringPiece's lifetime had better be less than
|
||||
// that of the underlying string or char*. If it is not, then you
|
||||
// cannot safely store a StringPiece into an STL container
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
#ifdef HAVE_TYPE_TRAITS
|
||||
// This makes vector<StringPiece> really fast for some STL implementations
|
||||
template<> struct __type_traits<pcrecpp::StringPiece> {
|
||||
typedef __true_type has_trivial_default_constructor;
|
||||
typedef __true_type has_trivial_copy_constructor;
|
||||
typedef __true_type has_trivial_assignment_operator;
|
||||
typedef __true_type has_trivial_destructor;
|
||||
typedef __true_type is_POD_type;
|
||||
};
|
||||
#endif
|
||||
|
||||
// allow StringPiece to be logged
|
||||
std::ostream& operator<<(std::ostream& o, const pcrecpp::StringPiece& piece);
|
||||
|
||||
#endif /* _PCRE_STRINGPIECE_H */
|
||||
@@ -1,920 +0,0 @@
|
||||
// Copyright (c) 2010, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: Sanjay Ghemawat
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h> /* for SHRT_MIN, USHRT_MAX, etc */
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
#include "pcrecpp_internal.h"
|
||||
#include "pcre.h"
|
||||
#include "pcrecpp.h"
|
||||
#include "pcre_stringpiece.h"
|
||||
|
||||
|
||||
namespace pcrecpp {
|
||||
|
||||
// Maximum number of args we can set
|
||||
static const int kMaxArgs = 16;
|
||||
static const int kVecSize = (1 + kMaxArgs) * 3; // results + PCRE workspace
|
||||
|
||||
// Special object that stands-in for no argument
|
||||
Arg RE::no_arg((void*)NULL);
|
||||
|
||||
// This is for ABI compatibility with old versions of pcre (pre-7.6),
|
||||
// which defined a global no_arg variable instead of putting it in the
|
||||
// RE class. This works on GCC >= 3, at least. It definitely works
|
||||
// for ELF, but may not for other object formats (Mach-O, for
|
||||
// instance, does not support aliases.) We could probably have a more
|
||||
// inclusive test if we ever needed it. (Note that not only the
|
||||
// __attribute__ syntax, but also __USER_LABEL_PREFIX__, are
|
||||
// gnu-specific.)
|
||||
#if defined(__GNUC__) && __GNUC__ >= 3 && defined(__ELF__)
|
||||
# define ULP_AS_STRING(x) ULP_AS_STRING_INTERNAL(x)
|
||||
# define ULP_AS_STRING_INTERNAL(x) #x
|
||||
# define USER_LABEL_PREFIX_STR ULP_AS_STRING(__USER_LABEL_PREFIX__)
|
||||
extern Arg no_arg
|
||||
__attribute__((alias(USER_LABEL_PREFIX_STR "_ZN7pcrecpp2RE6no_argE")));
|
||||
#endif
|
||||
|
||||
// If a regular expression has no error, its error_ field points here
|
||||
static const string empty_string;
|
||||
|
||||
// If the user doesn't ask for any options, we just use this one
|
||||
static RE_Options default_options;
|
||||
|
||||
void RE::Init(const string& pat, const RE_Options* options) {
|
||||
pattern_ = pat;
|
||||
if (options == NULL) {
|
||||
options_ = default_options;
|
||||
} else {
|
||||
options_ = *options;
|
||||
}
|
||||
error_ = &empty_string;
|
||||
re_full_ = NULL;
|
||||
re_partial_ = NULL;
|
||||
|
||||
re_partial_ = Compile(UNANCHORED);
|
||||
if (re_partial_ != NULL) {
|
||||
re_full_ = Compile(ANCHOR_BOTH);
|
||||
}
|
||||
}
|
||||
|
||||
void RE::Cleanup() {
|
||||
if (re_full_ != NULL) (*pcre_free)(re_full_);
|
||||
if (re_partial_ != NULL) (*pcre_free)(re_partial_);
|
||||
if (error_ != &empty_string) delete error_;
|
||||
}
|
||||
|
||||
|
||||
RE::~RE() {
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
|
||||
pcre* RE::Compile(Anchor anchor) {
|
||||
// First, convert RE_Options into pcre options
|
||||
int pcre_options = 0;
|
||||
pcre_options = options_.all_options();
|
||||
|
||||
// Special treatment for anchoring. This is needed because at
|
||||
// runtime pcre only provides an option for anchoring at the
|
||||
// beginning of a string (unless you use offset).
|
||||
//
|
||||
// There are three types of anchoring we want:
|
||||
// UNANCHORED Compile the original pattern, and use
|
||||
// a pcre unanchored match.
|
||||
// ANCHOR_START Compile the original pattern, and use
|
||||
// a pcre anchored match.
|
||||
// ANCHOR_BOTH Tack a "\z" to the end of the original pattern
|
||||
// and use a pcre anchored match.
|
||||
|
||||
const char* compile_error;
|
||||
int eoffset;
|
||||
pcre* re;
|
||||
if (anchor != ANCHOR_BOTH) {
|
||||
re = pcre_compile(pattern_.c_str(), pcre_options,
|
||||
&compile_error, &eoffset, NULL);
|
||||
} else {
|
||||
// Tack a '\z' at the end of RE. Parenthesize it first so that
|
||||
// the '\z' applies to all top-level alternatives in the regexp.
|
||||
string wrapped = "(?:"; // A non-counting grouping operator
|
||||
wrapped += pattern_;
|
||||
wrapped += ")\\z";
|
||||
re = pcre_compile(wrapped.c_str(), pcre_options,
|
||||
&compile_error, &eoffset, NULL);
|
||||
}
|
||||
if (re == NULL) {
|
||||
if (error_ == &empty_string) error_ = new string(compile_error);
|
||||
}
|
||||
return re;
|
||||
}
|
||||
|
||||
/***** Matching interfaces *****/
|
||||
|
||||
bool RE::FullMatch(const StringPiece& text,
|
||||
const Arg& ptr1,
|
||||
const Arg& ptr2,
|
||||
const Arg& ptr3,
|
||||
const Arg& ptr4,
|
||||
const Arg& ptr5,
|
||||
const Arg& ptr6,
|
||||
const Arg& ptr7,
|
||||
const Arg& ptr8,
|
||||
const Arg& ptr9,
|
||||
const Arg& ptr10,
|
||||
const Arg& ptr11,
|
||||
const Arg& ptr12,
|
||||
const Arg& ptr13,
|
||||
const Arg& ptr14,
|
||||
const Arg& ptr15,
|
||||
const Arg& ptr16) const {
|
||||
const Arg* args[kMaxArgs];
|
||||
int n = 0;
|
||||
if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1;
|
||||
if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2;
|
||||
if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3;
|
||||
if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4;
|
||||
if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5;
|
||||
if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6;
|
||||
if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7;
|
||||
if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8;
|
||||
if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9;
|
||||
if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10;
|
||||
if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11;
|
||||
if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12;
|
||||
if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13;
|
||||
if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14;
|
||||
if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15;
|
||||
if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16;
|
||||
done:
|
||||
|
||||
int consumed;
|
||||
int vec[kVecSize];
|
||||
return DoMatchImpl(text, ANCHOR_BOTH, &consumed, args, n, vec, kVecSize);
|
||||
}
|
||||
|
||||
bool RE::PartialMatch(const StringPiece& text,
|
||||
const Arg& ptr1,
|
||||
const Arg& ptr2,
|
||||
const Arg& ptr3,
|
||||
const Arg& ptr4,
|
||||
const Arg& ptr5,
|
||||
const Arg& ptr6,
|
||||
const Arg& ptr7,
|
||||
const Arg& ptr8,
|
||||
const Arg& ptr9,
|
||||
const Arg& ptr10,
|
||||
const Arg& ptr11,
|
||||
const Arg& ptr12,
|
||||
const Arg& ptr13,
|
||||
const Arg& ptr14,
|
||||
const Arg& ptr15,
|
||||
const Arg& ptr16) const {
|
||||
const Arg* args[kMaxArgs];
|
||||
int n = 0;
|
||||
if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1;
|
||||
if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2;
|
||||
if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3;
|
||||
if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4;
|
||||
if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5;
|
||||
if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6;
|
||||
if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7;
|
||||
if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8;
|
||||
if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9;
|
||||
if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10;
|
||||
if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11;
|
||||
if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12;
|
||||
if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13;
|
||||
if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14;
|
||||
if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15;
|
||||
if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16;
|
||||
done:
|
||||
|
||||
int consumed;
|
||||
int vec[kVecSize];
|
||||
return DoMatchImpl(text, UNANCHORED, &consumed, args, n, vec, kVecSize);
|
||||
}
|
||||
|
||||
bool RE::Consume(StringPiece* input,
|
||||
const Arg& ptr1,
|
||||
const Arg& ptr2,
|
||||
const Arg& ptr3,
|
||||
const Arg& ptr4,
|
||||
const Arg& ptr5,
|
||||
const Arg& ptr6,
|
||||
const Arg& ptr7,
|
||||
const Arg& ptr8,
|
||||
const Arg& ptr9,
|
||||
const Arg& ptr10,
|
||||
const Arg& ptr11,
|
||||
const Arg& ptr12,
|
||||
const Arg& ptr13,
|
||||
const Arg& ptr14,
|
||||
const Arg& ptr15,
|
||||
const Arg& ptr16) const {
|
||||
const Arg* args[kMaxArgs];
|
||||
int n = 0;
|
||||
if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1;
|
||||
if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2;
|
||||
if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3;
|
||||
if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4;
|
||||
if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5;
|
||||
if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6;
|
||||
if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7;
|
||||
if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8;
|
||||
if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9;
|
||||
if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10;
|
||||
if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11;
|
||||
if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12;
|
||||
if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13;
|
||||
if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14;
|
||||
if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15;
|
||||
if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16;
|
||||
done:
|
||||
|
||||
int consumed;
|
||||
int vec[kVecSize];
|
||||
if (DoMatchImpl(*input, ANCHOR_START, &consumed,
|
||||
args, n, vec, kVecSize)) {
|
||||
input->remove_prefix(consumed);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool RE::FindAndConsume(StringPiece* input,
|
||||
const Arg& ptr1,
|
||||
const Arg& ptr2,
|
||||
const Arg& ptr3,
|
||||
const Arg& ptr4,
|
||||
const Arg& ptr5,
|
||||
const Arg& ptr6,
|
||||
const Arg& ptr7,
|
||||
const Arg& ptr8,
|
||||
const Arg& ptr9,
|
||||
const Arg& ptr10,
|
||||
const Arg& ptr11,
|
||||
const Arg& ptr12,
|
||||
const Arg& ptr13,
|
||||
const Arg& ptr14,
|
||||
const Arg& ptr15,
|
||||
const Arg& ptr16) const {
|
||||
const Arg* args[kMaxArgs];
|
||||
int n = 0;
|
||||
if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1;
|
||||
if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2;
|
||||
if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3;
|
||||
if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4;
|
||||
if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5;
|
||||
if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6;
|
||||
if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7;
|
||||
if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8;
|
||||
if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9;
|
||||
if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10;
|
||||
if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11;
|
||||
if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12;
|
||||
if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13;
|
||||
if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14;
|
||||
if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15;
|
||||
if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16;
|
||||
done:
|
||||
|
||||
int consumed;
|
||||
int vec[kVecSize];
|
||||
if (DoMatchImpl(*input, UNANCHORED, &consumed,
|
||||
args, n, vec, kVecSize)) {
|
||||
input->remove_prefix(consumed);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool RE::Replace(const StringPiece& rewrite,
|
||||
string *str) const {
|
||||
int vec[kVecSize];
|
||||
int matches = TryMatch(*str, 0, UNANCHORED, true, vec, kVecSize);
|
||||
if (matches == 0)
|
||||
return false;
|
||||
|
||||
string s;
|
||||
if (!Rewrite(&s, rewrite, *str, vec, matches))
|
||||
return false;
|
||||
|
||||
assert(vec[0] >= 0);
|
||||
assert(vec[1] >= 0);
|
||||
str->replace(vec[0], vec[1] - vec[0], s);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns PCRE_NEWLINE_CRLF, PCRE_NEWLINE_CR, or PCRE_NEWLINE_LF.
|
||||
// Note that PCRE_NEWLINE_CRLF is defined to be P_N_CR | P_N_LF.
|
||||
// Modified by PH to add PCRE_NEWLINE_ANY and PCRE_NEWLINE_ANYCRLF.
|
||||
|
||||
static int NewlineMode(int pcre_options) {
|
||||
// TODO: if we can make it threadsafe, cache this var
|
||||
int newline_mode = 0;
|
||||
/* if (newline_mode) return newline_mode; */ // do this once it's cached
|
||||
if (pcre_options & (PCRE_NEWLINE_CRLF|PCRE_NEWLINE_CR|PCRE_NEWLINE_LF|
|
||||
PCRE_NEWLINE_ANY|PCRE_NEWLINE_ANYCRLF)) {
|
||||
newline_mode = (pcre_options &
|
||||
(PCRE_NEWLINE_CRLF|PCRE_NEWLINE_CR|PCRE_NEWLINE_LF|
|
||||
PCRE_NEWLINE_ANY|PCRE_NEWLINE_ANYCRLF));
|
||||
} else {
|
||||
int newline;
|
||||
pcre_config(PCRE_CONFIG_NEWLINE, &newline);
|
||||
if (newline == 10)
|
||||
newline_mode = PCRE_NEWLINE_LF;
|
||||
else if (newline == 13)
|
||||
newline_mode = PCRE_NEWLINE_CR;
|
||||
else if (newline == 3338)
|
||||
newline_mode = PCRE_NEWLINE_CRLF;
|
||||
else if (newline == -1)
|
||||
newline_mode = PCRE_NEWLINE_ANY;
|
||||
else if (newline == -2)
|
||||
newline_mode = PCRE_NEWLINE_ANYCRLF;
|
||||
else
|
||||
assert(NULL == "Unexpected return value from pcre_config(NEWLINE)");
|
||||
}
|
||||
return newline_mode;
|
||||
}
|
||||
|
||||
int RE::GlobalReplace(const StringPiece& rewrite,
|
||||
string *str) const {
|
||||
int count = 0;
|
||||
int vec[kVecSize];
|
||||
string out;
|
||||
int start = 0;
|
||||
int lastend = -1;
|
||||
bool last_match_was_empty_string = false;
|
||||
|
||||
while (start <= static_cast<int>(str->length())) {
|
||||
// If the previous match was for the empty string, we shouldn't
|
||||
// just match again: we'll match in the same way and get an
|
||||
// infinite loop. Instead, we do the match in a special way:
|
||||
// anchored -- to force another try at the same position --
|
||||
// and with a flag saying that this time, ignore empty matches.
|
||||
// If this special match returns, that means there's a non-empty
|
||||
// match at this position as well, and we can continue. If not,
|
||||
// we do what perl does, and just advance by one.
|
||||
// Notice that perl prints '@@@' for this;
|
||||
// perl -le '$_ = "aa"; s/b*|aa/@/g; print'
|
||||
int matches;
|
||||
if (last_match_was_empty_string) {
|
||||
matches = TryMatch(*str, start, ANCHOR_START, false, vec, kVecSize);
|
||||
if (matches <= 0) {
|
||||
int matchend = start + 1; // advance one character.
|
||||
// If the current char is CR and we're in CRLF mode, skip LF too.
|
||||
// Note it's better to call pcre_fullinfo() than to examine
|
||||
// all_options(), since options_ could have changed bewteen
|
||||
// compile-time and now, but this is simpler and safe enough.
|
||||
// Modified by PH to add ANY and ANYCRLF.
|
||||
if (matchend < static_cast<int>(str->length()) &&
|
||||
(*str)[start] == '\r' && (*str)[matchend] == '\n' &&
|
||||
(NewlineMode(options_.all_options()) == PCRE_NEWLINE_CRLF ||
|
||||
NewlineMode(options_.all_options()) == PCRE_NEWLINE_ANY ||
|
||||
NewlineMode(options_.all_options()) == PCRE_NEWLINE_ANYCRLF)) {
|
||||
matchend++;
|
||||
}
|
||||
// We also need to advance more than one char if we're in utf8 mode.
|
||||
#ifdef SUPPORT_UTF8
|
||||
if (options_.utf8()) {
|
||||
while (matchend < static_cast<int>(str->length()) &&
|
||||
((*str)[matchend] & 0xc0) == 0x80)
|
||||
matchend++;
|
||||
}
|
||||
#endif
|
||||
if (start < static_cast<int>(str->length()))
|
||||
out.append(*str, start, matchend - start);
|
||||
start = matchend;
|
||||
last_match_was_empty_string = false;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
matches = TryMatch(*str, start, UNANCHORED, true, vec, kVecSize);
|
||||
if (matches <= 0)
|
||||
break;
|
||||
}
|
||||
int matchstart = vec[0], matchend = vec[1];
|
||||
assert(matchstart >= start);
|
||||
assert(matchend >= matchstart);
|
||||
out.append(*str, start, matchstart - start);
|
||||
Rewrite(&out, rewrite, *str, vec, matches);
|
||||
start = matchend;
|
||||
lastend = matchend;
|
||||
count++;
|
||||
last_match_was_empty_string = (matchstart == matchend);
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
return 0;
|
||||
|
||||
if (start < static_cast<int>(str->length()))
|
||||
out.append(*str, start, str->length() - start);
|
||||
swap(out, *str);
|
||||
return count;
|
||||
}
|
||||
|
||||
bool RE::Extract(const StringPiece& rewrite,
|
||||
const StringPiece& text,
|
||||
string *out) const {
|
||||
int vec[kVecSize];
|
||||
int matches = TryMatch(text, 0, UNANCHORED, true, vec, kVecSize);
|
||||
if (matches == 0)
|
||||
return false;
|
||||
out->erase();
|
||||
return Rewrite(out, rewrite, text, vec, matches);
|
||||
}
|
||||
|
||||
/*static*/ string RE::QuoteMeta(const StringPiece& unquoted) {
|
||||
string result;
|
||||
|
||||
// Escape any ascii character not in [A-Za-z_0-9].
|
||||
//
|
||||
// Note that it's legal to escape a character even if it has no
|
||||
// special meaning in a regular expression -- so this function does
|
||||
// that. (This also makes it identical to the perl function of the
|
||||
// same name; see `perldoc -f quotemeta`.) The one exception is
|
||||
// escaping NUL: rather than doing backslash + NUL, like perl does,
|
||||
// we do '\0', because pcre itself doesn't take embedded NUL chars.
|
||||
for (int ii = 0; ii < unquoted.size(); ++ii) {
|
||||
// Note that using 'isalnum' here raises the benchmark time from
|
||||
// 32ns to 58ns:
|
||||
if (unquoted[ii] == '\0') {
|
||||
result += "\\0";
|
||||
} else if ((unquoted[ii] < 'a' || unquoted[ii] > 'z') &&
|
||||
(unquoted[ii] < 'A' || unquoted[ii] > 'Z') &&
|
||||
(unquoted[ii] < '0' || unquoted[ii] > '9') &&
|
||||
unquoted[ii] != '_' &&
|
||||
// If this is the part of a UTF8 or Latin1 character, we need
|
||||
// to copy this byte without escaping. Experimentally this is
|
||||
// what works correctly with the regexp library.
|
||||
!(unquoted[ii] & 128)) {
|
||||
result += '\\';
|
||||
result += unquoted[ii];
|
||||
} else {
|
||||
result += unquoted[ii];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***** Actual matching and rewriting code *****/
|
||||
|
||||
int RE::TryMatch(const StringPiece& text,
|
||||
int startpos,
|
||||
Anchor anchor,
|
||||
bool empty_ok,
|
||||
int *vec,
|
||||
int vecsize) const {
|
||||
pcre* re = (anchor == ANCHOR_BOTH) ? re_full_ : re_partial_;
|
||||
if (re == NULL) {
|
||||
//fprintf(stderr, "Matching against invalid re: %s\n", error_->c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
pcre_extra extra = { 0, 0, 0, 0, 0, 0 };
|
||||
if (options_.match_limit() > 0) {
|
||||
extra.flags |= PCRE_EXTRA_MATCH_LIMIT;
|
||||
extra.match_limit = options_.match_limit();
|
||||
}
|
||||
if (options_.match_limit_recursion() > 0) {
|
||||
extra.flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION;
|
||||
extra.match_limit_recursion = options_.match_limit_recursion();
|
||||
}
|
||||
|
||||
int options = 0;
|
||||
if (anchor != UNANCHORED)
|
||||
options |= PCRE_ANCHORED;
|
||||
if (!empty_ok)
|
||||
options |= PCRE_NOTEMPTY;
|
||||
|
||||
int rc = pcre_exec(re, // The regular expression object
|
||||
&extra,
|
||||
(text.data() == NULL) ? "" : text.data(),
|
||||
text.size(),
|
||||
startpos,
|
||||
options,
|
||||
vec,
|
||||
vecsize);
|
||||
|
||||
// Handle errors
|
||||
if (rc == PCRE_ERROR_NOMATCH) {
|
||||
return 0;
|
||||
} else if (rc < 0) {
|
||||
//fprintf(stderr, "Unexpected return code: %d when matching '%s'\n",
|
||||
// re, pattern_.c_str());
|
||||
return 0;
|
||||
} else if (rc == 0) {
|
||||
// pcre_exec() returns 0 as a special case when the number of
|
||||
// capturing subpatterns exceeds the size of the vector.
|
||||
// When this happens, there is a match and the output vector
|
||||
// is filled, but we miss out on the positions of the extra subpatterns.
|
||||
rc = vecsize / 2;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool RE::DoMatchImpl(const StringPiece& text,
|
||||
Anchor anchor,
|
||||
int* consumed,
|
||||
const Arg* const* args,
|
||||
int n,
|
||||
int* vec,
|
||||
int vecsize) const {
|
||||
assert((1 + n) * 3 <= vecsize); // results + PCRE workspace
|
||||
int matches = TryMatch(text, 0, anchor, true, vec, vecsize);
|
||||
assert(matches >= 0); // TryMatch never returns negatives
|
||||
if (matches == 0)
|
||||
return false;
|
||||
|
||||
*consumed = vec[1];
|
||||
|
||||
if (n == 0 || args == NULL) {
|
||||
// We are not interested in results
|
||||
return true;
|
||||
}
|
||||
|
||||
if (NumberOfCapturingGroups() < n) {
|
||||
// RE has fewer capturing groups than number of arg pointers passed in
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we got here, we must have matched the whole pattern.
|
||||
// We do not need (can not do) any more checks on the value of 'matches' here
|
||||
// -- see the comment for TryMatch.
|
||||
for (int i = 0; i < n; i++) {
|
||||
const int start = vec[2*(i+1)];
|
||||
const int limit = vec[2*(i+1)+1];
|
||||
if (!args[i]->Parse(text.data() + start, limit-start)) {
|
||||
// TODO: Should we indicate what the error was?
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE::DoMatch(const StringPiece& text,
|
||||
Anchor anchor,
|
||||
int* consumed,
|
||||
const Arg* const args[],
|
||||
int n) const {
|
||||
assert(n >= 0);
|
||||
size_t const vecsize = (1 + n) * 3; // results + PCRE workspace
|
||||
// (as for kVecSize)
|
||||
int space[21]; // use stack allocation for small vecsize (common case)
|
||||
int* vec = vecsize <= 21 ? space : new int[vecsize];
|
||||
bool retval = DoMatchImpl(text, anchor, consumed, args, n, vec, vecsize);
|
||||
if (vec != space) delete [] vec;
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool RE::Rewrite(string *out, const StringPiece &rewrite,
|
||||
const StringPiece &text, int *vec, int veclen) const {
|
||||
for (const char *s = rewrite.data(), *end = s + rewrite.size();
|
||||
s < end; s++) {
|
||||
int c = *s;
|
||||
if (c == '\\') {
|
||||
c = *++s;
|
||||
if (isdigit(c)) {
|
||||
int n = (c - '0');
|
||||
if (n >= veclen) {
|
||||
//fprintf(stderr, requested group %d in regexp %.*s\n",
|
||||
// n, rewrite.size(), rewrite.data());
|
||||
return false;
|
||||
}
|
||||
int start = vec[2 * n];
|
||||
if (start >= 0)
|
||||
out->append(text.data() + start, vec[2 * n + 1] - start);
|
||||
} else if (c == '\\') {
|
||||
*out += '\\';
|
||||
} else {
|
||||
//fprintf(stderr, "invalid rewrite pattern: %.*s\n",
|
||||
// rewrite.size(), rewrite.data());
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
*out += c;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return the number of capturing subpatterns, or -1 if the
|
||||
// regexp wasn't valid on construction.
|
||||
int RE::NumberOfCapturingGroups() const {
|
||||
if (re_partial_ == NULL) return -1;
|
||||
|
||||
int result;
|
||||
int pcre_retval = pcre_fullinfo(re_partial_, // The regular expression object
|
||||
NULL, // We did not study the pattern
|
||||
PCRE_INFO_CAPTURECOUNT,
|
||||
&result);
|
||||
assert(pcre_retval == 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/***** Parsers for various types *****/
|
||||
|
||||
bool Arg::parse_null(const char* str, int n, void* dest) {
|
||||
// We fail if somebody asked us to store into a non-NULL void* pointer
|
||||
return (dest == NULL);
|
||||
}
|
||||
|
||||
bool Arg::parse_string(const char* str, int n, void* dest) {
|
||||
if (dest == NULL) return true;
|
||||
reinterpret_cast<string*>(dest)->assign(str, n);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arg::parse_stringpiece(const char* str, int n, void* dest) {
|
||||
if (dest == NULL) return true;
|
||||
reinterpret_cast<StringPiece*>(dest)->set(str, n);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arg::parse_char(const char* str, int n, void* dest) {
|
||||
if (n != 1) return false;
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<char*>(dest)) = str[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arg::parse_uchar(const char* str, int n, void* dest) {
|
||||
if (n != 1) return false;
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<unsigned char*>(dest)) = str[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
// Largest number spec that we are willing to parse
|
||||
static const int kMaxNumberLength = 32;
|
||||
|
||||
// REQUIRES "buf" must have length at least kMaxNumberLength+1
|
||||
// REQUIRES "n > 0"
|
||||
// Copies "str" into "buf" and null-terminates if necessary.
|
||||
// Returns one of:
|
||||
// a. "str" if no termination is needed
|
||||
// b. "buf" if the string was copied and null-terminated
|
||||
// c. "" if the input was invalid and has no hope of being parsed
|
||||
static const char* TerminateNumber(char* buf, const char* str, int n) {
|
||||
if ((n > 0) && isspace(*str)) {
|
||||
// We are less forgiving than the strtoxxx() routines and do not
|
||||
// allow leading spaces.
|
||||
return "";
|
||||
}
|
||||
|
||||
// See if the character right after the input text may potentially
|
||||
// look like a digit.
|
||||
if (isdigit(str[n]) ||
|
||||
((str[n] >= 'a') && (str[n] <= 'f')) ||
|
||||
((str[n] >= 'A') && (str[n] <= 'F'))) {
|
||||
if (n > kMaxNumberLength) return ""; // Input too big to be a valid number
|
||||
memcpy(buf, str, n);
|
||||
buf[n] = '\0';
|
||||
return buf;
|
||||
} else {
|
||||
// We can parse right out of the supplied string, so return it.
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
bool Arg::parse_long_radix(const char* str,
|
||||
int n,
|
||||
void* dest,
|
||||
int radix) {
|
||||
if (n == 0) return false;
|
||||
char buf[kMaxNumberLength+1];
|
||||
str = TerminateNumber(buf, str, n);
|
||||
char* end;
|
||||
errno = 0;
|
||||
long r = strtol(str, &end, radix);
|
||||
if (end != str + n) return false; // Leftover junk
|
||||
if (errno) return false;
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<long*>(dest)) = r;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arg::parse_ulong_radix(const char* str,
|
||||
int n,
|
||||
void* dest,
|
||||
int radix) {
|
||||
if (n == 0) return false;
|
||||
char buf[kMaxNumberLength+1];
|
||||
str = TerminateNumber(buf, str, n);
|
||||
if (str[0] == '-') return false; // strtoul() on a negative number?!
|
||||
char* end;
|
||||
errno = 0;
|
||||
unsigned long r = strtoul(str, &end, radix);
|
||||
if (end != str + n) return false; // Leftover junk
|
||||
if (errno) return false;
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<unsigned long*>(dest)) = r;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arg::parse_short_radix(const char* str,
|
||||
int n,
|
||||
void* dest,
|
||||
int radix) {
|
||||
long r;
|
||||
if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse
|
||||
if (r < SHRT_MIN || r > SHRT_MAX) return false; // Out of range
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<short*>(dest)) = static_cast<short>(r);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arg::parse_ushort_radix(const char* str,
|
||||
int n,
|
||||
void* dest,
|
||||
int radix) {
|
||||
unsigned long r;
|
||||
if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse
|
||||
if (r > USHRT_MAX) return false; // Out of range
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<unsigned short*>(dest)) = static_cast<unsigned short>(r);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arg::parse_int_radix(const char* str,
|
||||
int n,
|
||||
void* dest,
|
||||
int radix) {
|
||||
long r;
|
||||
if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse
|
||||
if (r < INT_MIN || r > INT_MAX) return false; // Out of range
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<int*>(dest)) = r;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arg::parse_uint_radix(const char* str,
|
||||
int n,
|
||||
void* dest,
|
||||
int radix) {
|
||||
unsigned long r;
|
||||
if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse
|
||||
if (r > UINT_MAX) return false; // Out of range
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<unsigned int*>(dest)) = r;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arg::parse_longlong_radix(const char* str,
|
||||
int n,
|
||||
void* dest,
|
||||
int radix) {
|
||||
#ifndef HAVE_LONG_LONG
|
||||
return false;
|
||||
#else
|
||||
if (n == 0) return false;
|
||||
char buf[kMaxNumberLength+1];
|
||||
str = TerminateNumber(buf, str, n);
|
||||
char* end;
|
||||
errno = 0;
|
||||
#if defined HAVE_STRTOQ
|
||||
long long r = strtoq(str, &end, radix);
|
||||
#elif defined HAVE_STRTOLL
|
||||
long long r = strtoll(str, &end, radix);
|
||||
#elif defined HAVE__STRTOI64
|
||||
long long r = _strtoi64(str, &end, radix);
|
||||
#elif defined HAVE_STRTOIMAX
|
||||
long long r = strtoimax(str, &end, radix);
|
||||
#else
|
||||
#error parse_longlong_radix: cannot convert input to a long-long
|
||||
#endif
|
||||
if (end != str + n) return false; // Leftover junk
|
||||
if (errno) return false;
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<long long*>(dest)) = r;
|
||||
return true;
|
||||
#endif /* HAVE_LONG_LONG */
|
||||
}
|
||||
|
||||
bool Arg::parse_ulonglong_radix(const char* str,
|
||||
int n,
|
||||
void* dest,
|
||||
int radix) {
|
||||
#ifndef HAVE_UNSIGNED_LONG_LONG
|
||||
return false;
|
||||
#else
|
||||
if (n == 0) return false;
|
||||
char buf[kMaxNumberLength+1];
|
||||
str = TerminateNumber(buf, str, n);
|
||||
if (str[0] == '-') return false; // strtoull() on a negative number?!
|
||||
char* end;
|
||||
errno = 0;
|
||||
#if defined HAVE_STRTOQ
|
||||
unsigned long long r = strtouq(str, &end, radix);
|
||||
#elif defined HAVE_STRTOLL
|
||||
unsigned long long r = strtoull(str, &end, radix);
|
||||
#elif defined HAVE__STRTOI64
|
||||
unsigned long long r = _strtoui64(str, &end, radix);
|
||||
#elif defined HAVE_STRTOIMAX
|
||||
unsigned long long r = strtoumax(str, &end, radix);
|
||||
#else
|
||||
#error parse_ulonglong_radix: cannot convert input to a long-long
|
||||
#endif
|
||||
if (end != str + n) return false; // Leftover junk
|
||||
if (errno) return false;
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<unsigned long long*>(dest)) = r;
|
||||
return true;
|
||||
#endif /* HAVE_UNSIGNED_LONG_LONG */
|
||||
}
|
||||
|
||||
bool Arg::parse_double(const char* str, int n, void* dest) {
|
||||
if (n == 0) return false;
|
||||
static const int kMaxLength = 200;
|
||||
char buf[kMaxLength];
|
||||
if (n >= kMaxLength) return false;
|
||||
memcpy(buf, str, n);
|
||||
buf[n] = '\0';
|
||||
errno = 0;
|
||||
char* end;
|
||||
double r = strtod(buf, &end);
|
||||
if (end != buf + n) return false; // Leftover junk
|
||||
if (errno) return false;
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<double*>(dest)) = r;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arg::parse_float(const char* str, int n, void* dest) {
|
||||
double r;
|
||||
if (!parse_double(str, n, &r)) return false;
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<float*>(dest)) = static_cast<float>(r);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#define DEFINE_INTEGER_PARSERS(name) \
|
||||
bool Arg::parse_##name(const char* str, int n, void* dest) { \
|
||||
return parse_##name##_radix(str, n, dest, 10); \
|
||||
} \
|
||||
bool Arg::parse_##name##_hex(const char* str, int n, void* dest) { \
|
||||
return parse_##name##_radix(str, n, dest, 16); \
|
||||
} \
|
||||
bool Arg::parse_##name##_octal(const char* str, int n, void* dest) { \
|
||||
return parse_##name##_radix(str, n, dest, 8); \
|
||||
} \
|
||||
bool Arg::parse_##name##_cradix(const char* str, int n, void* dest) { \
|
||||
return parse_##name##_radix(str, n, dest, 0); \
|
||||
}
|
||||
|
||||
DEFINE_INTEGER_PARSERS(short) /* */
|
||||
DEFINE_INTEGER_PARSERS(ushort) /* */
|
||||
DEFINE_INTEGER_PARSERS(int) /* Don't use semicolons after these */
|
||||
DEFINE_INTEGER_PARSERS(uint) /* statements because they can cause */
|
||||
DEFINE_INTEGER_PARSERS(long) /* compiler warnings if the checking */
|
||||
DEFINE_INTEGER_PARSERS(ulong) /* level is turned up high enough. */
|
||||
DEFINE_INTEGER_PARSERS(longlong) /* */
|
||||
DEFINE_INTEGER_PARSERS(ulonglong) /* */
|
||||
|
||||
#undef DEFINE_INTEGER_PARSERS
|
||||
|
||||
} // namespace pcrecpp
|
||||
@@ -1,965 +0,0 @@
|
||||
// Copyright (c) 2010, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: Sanjay Ghemawat
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
// Custom:
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cctype>
|
||||
#include <climits>
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h> /* for SHRT_MIN, USHRT_MAX, etc */
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
#include "pcrecpp_internal.h"
|
||||
#include "pcre.h"
|
||||
#include "pcrecpp.h"
|
||||
#include "pcre_stringpiece.h"
|
||||
|
||||
|
||||
// Custom: Added for compatibility with older pcre versions
|
||||
#ifndef PCRE_NEWLINE_CR
|
||||
#define PCRE_NEWLINE_CR 0x00100000
|
||||
#endif
|
||||
#ifndef PCRE_NEWLINE_LF
|
||||
#define PCRE_NEWLINE_LF 0x00200000
|
||||
#endif
|
||||
#ifndef PCRE_NEWLINE_CRLF
|
||||
#define PCRE_NEWLINE_CRLF 0x00300000
|
||||
#endif
|
||||
#ifndef PCRE_NEWLINE_ANYCRLF
|
||||
#define PCRE_NEWLINE_ANYCRLF 0x00500000
|
||||
#endif
|
||||
#ifndef PCRE_NEWLINE_ANY
|
||||
#define PCRE_NEWLINE_ANY 0x00400000
|
||||
#endif
|
||||
|
||||
#ifndef PCRE_NEWLINE_ANY
|
||||
#define PCRE_EXTRA_MATCH_LIMIT_RECURSION 0x0010
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
namespace pcrecpp {
|
||||
|
||||
// Maximum number of args we can set
|
||||
static const int kMaxArgs = 16;
|
||||
static const int kVecSize = (1 + kMaxArgs) * 3; // results + PCRE workspace
|
||||
|
||||
// Special object that stands-in for no argument
|
||||
Arg RE::no_arg((void*)NULL);
|
||||
|
||||
// This is for ABI compatibility with old versions of pcre (pre-7.6),
|
||||
// which defined a global no_arg variable instead of putting it in the
|
||||
// RE class. This works on GCC >= 3, at least. It definitely works
|
||||
// for ELF, but may not for other object formats (Mach-O, for
|
||||
// instance, does not support aliases.) We could probably have a more
|
||||
// inclusive test if we ever needed it. (Note that not only the
|
||||
// __attribute__ syntax, but also __USER_LABEL_PREFIX__, are
|
||||
// gnu-specific.)
|
||||
#if defined(__GNUC__) && __GNUC__ >= 3 && defined(__ELF__)
|
||||
# define ULP_AS_STRING(x) ULP_AS_STRING_INTERNAL(x)
|
||||
# define ULP_AS_STRING_INTERNAL(x) #x
|
||||
# define USER_LABEL_PREFIX_STR ULP_AS_STRING(__USER_LABEL_PREFIX__)
|
||||
// Custom: This throws a warning on Intel C++, and we don't need it anyway.
|
||||
// extern Arg no_arg
|
||||
// __attribute__((alias(USER_LABEL_PREFIX_STR "_ZN7pcrecpp2RE6no_argE")));
|
||||
#endif
|
||||
|
||||
// If a regular expression has no error, its error_ field points here
|
||||
static const string empty_string;
|
||||
|
||||
// If the user doesn't ask for any options, we just use this one
|
||||
static RE_Options default_options;
|
||||
|
||||
void RE::Init(const string& pat, const RE_Options* options) {
|
||||
pattern_ = pat;
|
||||
if (options == NULL) {
|
||||
options_ = default_options;
|
||||
} else {
|
||||
options_ = *options;
|
||||
}
|
||||
error_ = &empty_string;
|
||||
re_full_ = NULL;
|
||||
re_partial_ = NULL;
|
||||
|
||||
re_partial_ = Compile(UNANCHORED);
|
||||
if (re_partial_ != NULL) {
|
||||
re_full_ = Compile(ANCHOR_BOTH);
|
||||
}
|
||||
}
|
||||
|
||||
void RE::Cleanup() {
|
||||
if (re_full_ != NULL) (*pcre_free)(re_full_);
|
||||
if (re_partial_ != NULL) (*pcre_free)(re_partial_);
|
||||
if (error_ != &empty_string) delete error_;
|
||||
}
|
||||
|
||||
|
||||
RE::~RE() {
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
|
||||
pcre* RE::Compile(Anchor anchor) {
|
||||
// First, convert RE_Options into pcre options
|
||||
int pcre_options = 0;
|
||||
pcre_options = options_.all_options();
|
||||
|
||||
// Special treatment for anchoring. This is needed because at
|
||||
// runtime pcre only provides an option for anchoring at the
|
||||
// beginning of a string (unless you use offset).
|
||||
//
|
||||
// There are three types of anchoring we want:
|
||||
// UNANCHORED Compile the original pattern, and use
|
||||
// a pcre unanchored match.
|
||||
// ANCHOR_START Compile the original pattern, and use
|
||||
// a pcre anchored match.
|
||||
// ANCHOR_BOTH Tack a "\z" to the end of the original pattern
|
||||
// and use a pcre anchored match.
|
||||
|
||||
const char* compile_error;
|
||||
int eoffset;
|
||||
pcre* re;
|
||||
if (anchor != ANCHOR_BOTH) {
|
||||
re = pcre_compile(pattern_.c_str(), pcre_options,
|
||||
&compile_error, &eoffset, NULL);
|
||||
} else {
|
||||
// Tack a '\z' at the end of RE. Parenthesize it first so that
|
||||
// the '\z' applies to all top-level alternatives in the regexp.
|
||||
string wrapped = "(?:"; // A non-counting grouping operator
|
||||
wrapped += pattern_;
|
||||
wrapped += ")\\z";
|
||||
re = pcre_compile(wrapped.c_str(), pcre_options,
|
||||
&compile_error, &eoffset, NULL);
|
||||
}
|
||||
if (re == NULL) {
|
||||
if (error_ == &empty_string) error_ = new string(compile_error);
|
||||
}
|
||||
return re;
|
||||
}
|
||||
|
||||
/***** Matching interfaces *****/
|
||||
|
||||
bool RE::FullMatch(const StringPiece& text,
|
||||
const Arg& ptr1,
|
||||
const Arg& ptr2,
|
||||
const Arg& ptr3,
|
||||
const Arg& ptr4,
|
||||
const Arg& ptr5,
|
||||
const Arg& ptr6,
|
||||
const Arg& ptr7,
|
||||
const Arg& ptr8,
|
||||
const Arg& ptr9,
|
||||
const Arg& ptr10,
|
||||
const Arg& ptr11,
|
||||
const Arg& ptr12,
|
||||
const Arg& ptr13,
|
||||
const Arg& ptr14,
|
||||
const Arg& ptr15,
|
||||
const Arg& ptr16) const {
|
||||
const Arg* args[kMaxArgs];
|
||||
int n = 0;
|
||||
if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1;
|
||||
if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2;
|
||||
if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3;
|
||||
if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4;
|
||||
if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5;
|
||||
if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6;
|
||||
if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7;
|
||||
if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8;
|
||||
if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9;
|
||||
if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10;
|
||||
if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11;
|
||||
if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12;
|
||||
if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13;
|
||||
if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14;
|
||||
if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15;
|
||||
if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16;
|
||||
done:
|
||||
|
||||
int consumed;
|
||||
int vec[kVecSize];
|
||||
return DoMatchImpl(text, ANCHOR_BOTH, &consumed, args, n, vec, kVecSize);
|
||||
}
|
||||
|
||||
bool RE::PartialMatch(const StringPiece& text,
|
||||
const Arg& ptr1,
|
||||
const Arg& ptr2,
|
||||
const Arg& ptr3,
|
||||
const Arg& ptr4,
|
||||
const Arg& ptr5,
|
||||
const Arg& ptr6,
|
||||
const Arg& ptr7,
|
||||
const Arg& ptr8,
|
||||
const Arg& ptr9,
|
||||
const Arg& ptr10,
|
||||
const Arg& ptr11,
|
||||
const Arg& ptr12,
|
||||
const Arg& ptr13,
|
||||
const Arg& ptr14,
|
||||
const Arg& ptr15,
|
||||
const Arg& ptr16) const {
|
||||
const Arg* args[kMaxArgs];
|
||||
int n = 0;
|
||||
if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1;
|
||||
if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2;
|
||||
if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3;
|
||||
if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4;
|
||||
if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5;
|
||||
if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6;
|
||||
if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7;
|
||||
if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8;
|
||||
if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9;
|
||||
if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10;
|
||||
if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11;
|
||||
if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12;
|
||||
if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13;
|
||||
if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14;
|
||||
if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15;
|
||||
if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16;
|
||||
done:
|
||||
|
||||
int consumed;
|
||||
int vec[kVecSize];
|
||||
return DoMatchImpl(text, UNANCHORED, &consumed, args, n, vec, kVecSize);
|
||||
}
|
||||
|
||||
bool RE::Consume(StringPiece* input,
|
||||
const Arg& ptr1,
|
||||
const Arg& ptr2,
|
||||
const Arg& ptr3,
|
||||
const Arg& ptr4,
|
||||
const Arg& ptr5,
|
||||
const Arg& ptr6,
|
||||
const Arg& ptr7,
|
||||
const Arg& ptr8,
|
||||
const Arg& ptr9,
|
||||
const Arg& ptr10,
|
||||
const Arg& ptr11,
|
||||
const Arg& ptr12,
|
||||
const Arg& ptr13,
|
||||
const Arg& ptr14,
|
||||
const Arg& ptr15,
|
||||
const Arg& ptr16) const {
|
||||
const Arg* args[kMaxArgs];
|
||||
int n = 0;
|
||||
if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1;
|
||||
if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2;
|
||||
if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3;
|
||||
if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4;
|
||||
if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5;
|
||||
if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6;
|
||||
if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7;
|
||||
if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8;
|
||||
if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9;
|
||||
if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10;
|
||||
if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11;
|
||||
if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12;
|
||||
if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13;
|
||||
if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14;
|
||||
if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15;
|
||||
if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16;
|
||||
done:
|
||||
|
||||
int consumed;
|
||||
int vec[kVecSize];
|
||||
if (DoMatchImpl(*input, ANCHOR_START, &consumed,
|
||||
args, n, vec, kVecSize)) {
|
||||
input->remove_prefix(consumed);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool RE::FindAndConsume(StringPiece* input,
|
||||
const Arg& ptr1,
|
||||
const Arg& ptr2,
|
||||
const Arg& ptr3,
|
||||
const Arg& ptr4,
|
||||
const Arg& ptr5,
|
||||
const Arg& ptr6,
|
||||
const Arg& ptr7,
|
||||
const Arg& ptr8,
|
||||
const Arg& ptr9,
|
||||
const Arg& ptr10,
|
||||
const Arg& ptr11,
|
||||
const Arg& ptr12,
|
||||
const Arg& ptr13,
|
||||
const Arg& ptr14,
|
||||
const Arg& ptr15,
|
||||
const Arg& ptr16) const {
|
||||
const Arg* args[kMaxArgs];
|
||||
int n = 0;
|
||||
if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1;
|
||||
if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2;
|
||||
if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3;
|
||||
if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4;
|
||||
if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5;
|
||||
if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6;
|
||||
if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7;
|
||||
if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8;
|
||||
if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9;
|
||||
if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10;
|
||||
if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11;
|
||||
if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12;
|
||||
if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13;
|
||||
if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14;
|
||||
if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15;
|
||||
if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16;
|
||||
done:
|
||||
|
||||
int consumed;
|
||||
int vec[kVecSize];
|
||||
if (DoMatchImpl(*input, UNANCHORED, &consumed,
|
||||
args, n, vec, kVecSize)) {
|
||||
input->remove_prefix(consumed);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool RE::Replace(const StringPiece& rewrite,
|
||||
string *str) const {
|
||||
int vec[kVecSize];
|
||||
int matches = TryMatch(*str, 0, UNANCHORED, true, vec, kVecSize);
|
||||
if (matches == 0)
|
||||
return false;
|
||||
|
||||
string s;
|
||||
if (!Rewrite(&s, rewrite, *str, vec, matches))
|
||||
return false;
|
||||
|
||||
assert(vec[0] >= 0);
|
||||
assert(vec[1] >= 0);
|
||||
str->replace(vec[0], vec[1] - vec[0], s);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns PCRE_NEWLINE_CRLF, PCRE_NEWLINE_CR, or PCRE_NEWLINE_LF.
|
||||
// Note that PCRE_NEWLINE_CRLF is defined to be P_N_CR | P_N_LF.
|
||||
// Modified by PH to add PCRE_NEWLINE_ANY and PCRE_NEWLINE_ANYCRLF.
|
||||
|
||||
static int NewlineMode(int pcre_options) {
|
||||
// TODO: if we can make it threadsafe, cache this var
|
||||
int newline_mode = 0;
|
||||
/* if (newline_mode) return newline_mode; */ // do this once it's cached
|
||||
if (pcre_options & (PCRE_NEWLINE_CRLF|PCRE_NEWLINE_CR|PCRE_NEWLINE_LF|
|
||||
PCRE_NEWLINE_ANY|PCRE_NEWLINE_ANYCRLF)) {
|
||||
newline_mode = (pcre_options &
|
||||
(PCRE_NEWLINE_CRLF|PCRE_NEWLINE_CR|PCRE_NEWLINE_LF|
|
||||
PCRE_NEWLINE_ANY|PCRE_NEWLINE_ANYCRLF));
|
||||
} else {
|
||||
int newline;
|
||||
pcre_config(PCRE_CONFIG_NEWLINE, &newline);
|
||||
if (newline == 10)
|
||||
newline_mode = PCRE_NEWLINE_LF;
|
||||
else if (newline == 13)
|
||||
newline_mode = PCRE_NEWLINE_CR;
|
||||
else if (newline == 3338)
|
||||
newline_mode = PCRE_NEWLINE_CRLF;
|
||||
else if (newline == -1)
|
||||
newline_mode = PCRE_NEWLINE_ANY;
|
||||
else if (newline == -2)
|
||||
newline_mode = PCRE_NEWLINE_ANYCRLF;
|
||||
else
|
||||
assert(NULL == "Unexpected return value from pcre_config(NEWLINE)");
|
||||
}
|
||||
return newline_mode;
|
||||
}
|
||||
|
||||
int RE::GlobalReplace(const StringPiece& rewrite,
|
||||
string *str) const {
|
||||
int count = 0;
|
||||
int vec[kVecSize];
|
||||
string out;
|
||||
int start = 0;
|
||||
int lastend = -1;
|
||||
bool last_match_was_empty_string = false;
|
||||
|
||||
while (start <= static_cast<int>(str->length())) {
|
||||
// If the previous match was for the empty string, we shouldn't
|
||||
// just match again: we'll match in the same way and get an
|
||||
// infinite loop. Instead, we do the match in a special way:
|
||||
// anchored -- to force another try at the same position --
|
||||
// and with a flag saying that this time, ignore empty matches.
|
||||
// If this special match returns, that means there's a non-empty
|
||||
// match at this position as well, and we can continue. If not,
|
||||
// we do what perl does, and just advance by one.
|
||||
// Notice that perl prints '@@@' for this;
|
||||
// perl -le '$_ = "aa"; s/b*|aa/@/g; print'
|
||||
int matches;
|
||||
if (last_match_was_empty_string) {
|
||||
matches = TryMatch(*str, start, ANCHOR_START, false, vec, kVecSize);
|
||||
if (matches <= 0) {
|
||||
int matchend = start + 1; // advance one character.
|
||||
// If the current char is CR and we're in CRLF mode, skip LF too.
|
||||
// Note it's better to call pcre_fullinfo() than to examine
|
||||
// all_options(), since options_ could have changed bewteen
|
||||
// compile-time and now, but this is simpler and safe enough.
|
||||
// Modified by PH to add ANY and ANYCRLF.
|
||||
if (matchend < static_cast<int>(str->length()) &&
|
||||
(*str)[start] == '\r' && (*str)[matchend] == '\n' &&
|
||||
(NewlineMode(options_.all_options()) == PCRE_NEWLINE_CRLF ||
|
||||
NewlineMode(options_.all_options()) == PCRE_NEWLINE_ANY ||
|
||||
NewlineMode(options_.all_options()) == PCRE_NEWLINE_ANYCRLF)) {
|
||||
matchend++;
|
||||
}
|
||||
// We also need to advance more than one char if we're in utf8 mode.
|
||||
#ifdef SUPPORT_UTF8
|
||||
if (options_.utf8()) {
|
||||
while (matchend < static_cast<int>(str->length()) &&
|
||||
((*str)[matchend] & 0xc0) == 0x80)
|
||||
matchend++;
|
||||
}
|
||||
#endif
|
||||
if (start < static_cast<int>(str->length()))
|
||||
out.append(*str, start, matchend - start);
|
||||
start = matchend;
|
||||
last_match_was_empty_string = false;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
matches = TryMatch(*str, start, UNANCHORED, true, vec, kVecSize);
|
||||
if (matches <= 0)
|
||||
break;
|
||||
}
|
||||
int matchstart = vec[0], matchend = vec[1];
|
||||
assert(matchstart >= start);
|
||||
assert(matchend >= matchstart);
|
||||
out.append(*str, start, matchstart - start);
|
||||
Rewrite(&out, rewrite, *str, vec, matches);
|
||||
start = matchend;
|
||||
lastend = matchend;
|
||||
count++;
|
||||
last_match_was_empty_string = (matchstart == matchend);
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
return 0;
|
||||
|
||||
if (start < static_cast<int>(str->length()))
|
||||
out.append(*str, start, str->length() - start);
|
||||
swap(out, *str);
|
||||
return count;
|
||||
}
|
||||
|
||||
bool RE::Extract(const StringPiece& rewrite,
|
||||
const StringPiece& text,
|
||||
string *out) const {
|
||||
int vec[kVecSize];
|
||||
int matches = TryMatch(text, 0, UNANCHORED, true, vec, kVecSize);
|
||||
if (matches == 0)
|
||||
return false;
|
||||
out->erase();
|
||||
return Rewrite(out, rewrite, text, vec, matches);
|
||||
}
|
||||
|
||||
/*static*/ string RE::QuoteMeta(const StringPiece& unquoted) {
|
||||
string result;
|
||||
|
||||
// Escape any ascii character not in [A-Za-z_0-9].
|
||||
//
|
||||
// Note that it's legal to escape a character even if it has no
|
||||
// special meaning in a regular expression -- so this function does
|
||||
// that. (This also makes it identical to the perl function of the
|
||||
// same name; see `perldoc -f quotemeta`.) The one exception is
|
||||
// escaping NUL: rather than doing backslash + NUL, like perl does,
|
||||
// we do '\0', because pcre itself doesn't take embedded NUL chars.
|
||||
for (int ii = 0; ii < unquoted.size(); ++ii) {
|
||||
// Note that using 'isalnum' here raises the benchmark time from
|
||||
// 32ns to 58ns:
|
||||
if (unquoted[ii] == '\0') {
|
||||
result += "\\0";
|
||||
} else if ((unquoted[ii] < 'a' || unquoted[ii] > 'z') &&
|
||||
(unquoted[ii] < 'A' || unquoted[ii] > 'Z') &&
|
||||
(unquoted[ii] < '0' || unquoted[ii] > '9') &&
|
||||
unquoted[ii] != '_' &&
|
||||
// If this is the part of a UTF8 or Latin1 character, we need
|
||||
// to copy this byte without escaping. Experimentally this is
|
||||
// what works correctly with the regexp library.
|
||||
!(unquoted[ii] & 128)) {
|
||||
result += '\\';
|
||||
result += unquoted[ii];
|
||||
} else {
|
||||
result += unquoted[ii];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***** Actual matching and rewriting code *****/
|
||||
|
||||
int RE::TryMatch(const StringPiece& text,
|
||||
int startpos,
|
||||
Anchor anchor,
|
||||
bool empty_ok,
|
||||
int *vec,
|
||||
int vecsize) const {
|
||||
pcre* re = (anchor == ANCHOR_BOTH) ? re_full_ : re_partial_;
|
||||
if (re == NULL) {
|
||||
//fprintf(stderr, "Matching against invalid re: %s\n", error_->c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Custom: match_limit_recursion field is since pcre 6.5, tables field since 5.0.
|
||||
#if PCRE_MAJOR > 6 || (PCRE_MAJOR == 6 && PCRE_MINOR >= 5)
|
||||
pcre_extra extra = { 0, 0, 0, 0, 0, 0 };
|
||||
#elif PCRE_MAJOR > 5 || (PCRE_MAJOR == 5 && PCRE_MINOR >= 0)
|
||||
pcre_extra extra = { 0, 0, 0, 0, 0 };
|
||||
#else
|
||||
pcre_extra extra = { 0, 0, 0, 0 };
|
||||
#endif
|
||||
|
||||
if (options_.match_limit() > 0) {
|
||||
extra.flags |= PCRE_EXTRA_MATCH_LIMIT;
|
||||
extra.match_limit = options_.match_limit();
|
||||
}
|
||||
|
||||
// Custom: match_limit_recursion is since pcre 6.5.
|
||||
#if PCRE_MAJOR > 6 || (PCRE_MAJOR == 6 && PCRE_MINOR >= 5)
|
||||
if (options_.match_limit_recursion() > 0) {
|
||||
extra.flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION;
|
||||
extra.match_limit_recursion = options_.match_limit_recursion();
|
||||
}
|
||||
#endif
|
||||
|
||||
int options = 0;
|
||||
if (anchor != UNANCHORED)
|
||||
options |= PCRE_ANCHORED;
|
||||
if (!empty_ok)
|
||||
options |= PCRE_NOTEMPTY;
|
||||
|
||||
int rc = pcre_exec(re, // The regular expression object
|
||||
&extra,
|
||||
(text.data() == NULL) ? "" : text.data(),
|
||||
text.size(),
|
||||
startpos,
|
||||
options,
|
||||
vec,
|
||||
vecsize);
|
||||
|
||||
// Handle errors
|
||||
if (rc == PCRE_ERROR_NOMATCH) {
|
||||
return 0;
|
||||
} else if (rc < 0) {
|
||||
//fprintf(stderr, "Unexpected return code: %d when matching '%s'\n",
|
||||
// re, pattern_.c_str());
|
||||
return 0;
|
||||
} else if (rc == 0) {
|
||||
// pcre_exec() returns 0 as a special case when the number of
|
||||
// capturing subpatterns exceeds the size of the vector.
|
||||
// When this happens, there is a match and the output vector
|
||||
// is filled, but we miss out on the positions of the extra subpatterns.
|
||||
rc = vecsize / 2;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool RE::DoMatchImpl(const StringPiece& text,
|
||||
Anchor anchor,
|
||||
int* consumed,
|
||||
const Arg* const* args,
|
||||
int n,
|
||||
int* vec,
|
||||
int vecsize) const {
|
||||
assert((1 + n) * 3 <= vecsize); // results + PCRE workspace
|
||||
int matches = TryMatch(text, 0, anchor, true, vec, vecsize);
|
||||
assert(matches >= 0); // TryMatch never returns negatives
|
||||
if (matches == 0)
|
||||
return false;
|
||||
|
||||
*consumed = vec[1];
|
||||
|
||||
if (n == 0 || args == NULL) {
|
||||
// We are not interested in results
|
||||
return true;
|
||||
}
|
||||
|
||||
if (NumberOfCapturingGroups() < n) {
|
||||
// RE has fewer capturing groups than number of arg pointers passed in
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we got here, we must have matched the whole pattern.
|
||||
// We do not need (can not do) any more checks on the value of 'matches' here
|
||||
// -- see the comment for TryMatch.
|
||||
for (int i = 0; i < n; i++) {
|
||||
const int start = vec[2*(i+1)];
|
||||
const int limit = vec[2*(i+1)+1];
|
||||
if (!args[i]->Parse(text.data() + start, limit-start)) {
|
||||
// TODO: Should we indicate what the error was?
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE::DoMatch(const StringPiece& text,
|
||||
Anchor anchor,
|
||||
int* consumed,
|
||||
const Arg* const args[],
|
||||
int n) const {
|
||||
assert(n >= 0);
|
||||
size_t const vecsize = (1 + n) * 3; // results + PCRE workspace
|
||||
// (as for kVecSize)
|
||||
int space[21]; // use stack allocation for small vecsize (common case)
|
||||
int* vec = vecsize <= 21 ? space : new int[vecsize];
|
||||
bool retval = DoMatchImpl(text, anchor, consumed, args, n, vec, vecsize);
|
||||
if (vec != space) delete [] vec;
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool RE::Rewrite(string *out, const StringPiece &rewrite,
|
||||
const StringPiece &text, int *vec, int veclen) const {
|
||||
for (const char *s = rewrite.data(), *end = s + rewrite.size();
|
||||
s < end; s++) {
|
||||
int c = *s;
|
||||
if (c == '\\') {
|
||||
c = *++s;
|
||||
if (isdigit(c)) {
|
||||
int n = (c - '0');
|
||||
if (n >= veclen) {
|
||||
//fprintf(stderr, requested group %d in regexp %.*s\n",
|
||||
// n, rewrite.size(), rewrite.data());
|
||||
return false;
|
||||
}
|
||||
int start = vec[2 * n];
|
||||
if (start >= 0)
|
||||
out->append(text.data() + start, vec[2 * n + 1] - start);
|
||||
} else if (c == '\\') {
|
||||
*out += '\\';
|
||||
} else {
|
||||
//fprintf(stderr, "invalid rewrite pattern: %.*s\n",
|
||||
// rewrite.size(), rewrite.data());
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
*out += c;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return the number of capturing subpatterns, or -1 if the
|
||||
// regexp wasn't valid on construction.
|
||||
int RE::NumberOfCapturingGroups() const {
|
||||
if (re_partial_ == NULL) return -1;
|
||||
|
||||
int result;
|
||||
int pcre_retval = pcre_fullinfo(re_partial_, // The regular expression object
|
||||
NULL, // We did not study the pattern
|
||||
PCRE_INFO_CAPTURECOUNT,
|
||||
&result);
|
||||
assert(pcre_retval == 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/***** Parsers for various types *****/
|
||||
|
||||
bool Arg::parse_null(const char* str, int n, void* dest) {
|
||||
// We fail if somebody asked us to store into a non-NULL void* pointer
|
||||
return (dest == NULL);
|
||||
}
|
||||
|
||||
bool Arg::parse_string(const char* str, int n, void* dest) {
|
||||
if (dest == NULL) return true;
|
||||
reinterpret_cast<string*>(dest)->assign(str, n);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arg::parse_stringpiece(const char* str, int n, void* dest) {
|
||||
if (dest == NULL) return true;
|
||||
reinterpret_cast<StringPiece*>(dest)->set(str, n);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arg::parse_char(const char* str, int n, void* dest) {
|
||||
if (n != 1) return false;
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<char*>(dest)) = str[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arg::parse_uchar(const char* str, int n, void* dest) {
|
||||
if (n != 1) return false;
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<unsigned char*>(dest)) = str[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
// Largest number spec that we are willing to parse
|
||||
static const int kMaxNumberLength = 32;
|
||||
|
||||
// REQUIRES "buf" must have length at least kMaxNumberLength+1
|
||||
// REQUIRES "n > 0"
|
||||
// Copies "str" into "buf" and null-terminates if necessary.
|
||||
// Returns one of:
|
||||
// a. "str" if no termination is needed
|
||||
// b. "buf" if the string was copied and null-terminated
|
||||
// c. "" if the input was invalid and has no hope of being parsed
|
||||
static const char* TerminateNumber(char* buf, const char* str, int n) {
|
||||
if ((n > 0) && isspace(*str)) {
|
||||
// We are less forgiving than the strtoxxx() routines and do not
|
||||
// allow leading spaces.
|
||||
return "";
|
||||
}
|
||||
|
||||
// See if the character right after the input text may potentially
|
||||
// look like a digit.
|
||||
if (isdigit(str[n]) ||
|
||||
((str[n] >= 'a') && (str[n] <= 'f')) ||
|
||||
((str[n] >= 'A') && (str[n] <= 'F'))) {
|
||||
if (n > kMaxNumberLength) return ""; // Input too big to be a valid number
|
||||
memcpy(buf, str, n);
|
||||
buf[n] = '\0';
|
||||
return buf;
|
||||
} else {
|
||||
// We can parse right out of the supplied string, so return it.
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
bool Arg::parse_long_radix(const char* str,
|
||||
int n,
|
||||
void* dest,
|
||||
int radix) {
|
||||
if (n == 0) return false;
|
||||
char buf[kMaxNumberLength+1];
|
||||
str = TerminateNumber(buf, str, n);
|
||||
char* end;
|
||||
errno = 0;
|
||||
long r = strtol(str, &end, radix);
|
||||
if (end != str + n) return false; // Leftover junk
|
||||
if (errno) return false;
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<long*>(dest)) = r;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arg::parse_ulong_radix(const char* str,
|
||||
int n,
|
||||
void* dest,
|
||||
int radix) {
|
||||
if (n == 0) return false;
|
||||
char buf[kMaxNumberLength+1];
|
||||
str = TerminateNumber(buf, str, n);
|
||||
if (str[0] == '-') return false; // strtoul() on a negative number?!
|
||||
char* end;
|
||||
errno = 0;
|
||||
unsigned long r = strtoul(str, &end, radix);
|
||||
if (end != str + n) return false; // Leftover junk
|
||||
if (errno) return false;
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<unsigned long*>(dest)) = r;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arg::parse_short_radix(const char* str,
|
||||
int n,
|
||||
void* dest,
|
||||
int radix) {
|
||||
long r;
|
||||
if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse
|
||||
if (r < SHRT_MIN || r > SHRT_MAX) return false; // Out of range
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<short*>(dest)) = static_cast<short>(r);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arg::parse_ushort_radix(const char* str,
|
||||
int n,
|
||||
void* dest,
|
||||
int radix) {
|
||||
unsigned long r;
|
||||
if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse
|
||||
if (r > USHRT_MAX) return false; // Out of range
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<unsigned short*>(dest)) = static_cast<unsigned short>(r);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arg::parse_int_radix(const char* str,
|
||||
int n,
|
||||
void* dest,
|
||||
int radix) {
|
||||
long r;
|
||||
if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse
|
||||
if (r < INT_MIN || r > INT_MAX) return false; // Out of range
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<int*>(dest)) = r;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arg::parse_uint_radix(const char* str,
|
||||
int n,
|
||||
void* dest,
|
||||
int radix) {
|
||||
unsigned long r;
|
||||
if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse
|
||||
if (r > UINT_MAX) return false; // Out of range
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<unsigned int*>(dest)) = r;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arg::parse_longlong_radix(const char* str,
|
||||
int n,
|
||||
void* dest,
|
||||
int radix) {
|
||||
#ifndef HAVE_LONG_LONG
|
||||
return false;
|
||||
#else
|
||||
if (n == 0) return false;
|
||||
char buf[kMaxNumberLength+1];
|
||||
str = TerminateNumber(buf, str, n);
|
||||
char* end;
|
||||
errno = 0;
|
||||
#if defined HAVE_STRTOQ
|
||||
long long r = strtoq(str, &end, radix);
|
||||
#elif defined HAVE_STRTOLL
|
||||
long long r = strtoll(str, &end, radix);
|
||||
#elif defined HAVE__STRTOI64
|
||||
long long r = _strtoi64(str, &end, radix);
|
||||
#elif defined HAVE_STRTOIMAX
|
||||
long long r = strtoimax(str, &end, radix);
|
||||
#else
|
||||
#error parse_longlong_radix: cannot convert input to a long-long
|
||||
#endif
|
||||
if (end != str + n) return false; // Leftover junk
|
||||
if (errno) return false;
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<long long*>(dest)) = r;
|
||||
return true;
|
||||
#endif /* HAVE_LONG_LONG */
|
||||
}
|
||||
|
||||
bool Arg::parse_ulonglong_radix(const char* str,
|
||||
int n,
|
||||
void* dest,
|
||||
int radix) {
|
||||
#ifndef HAVE_UNSIGNED_LONG_LONG
|
||||
return false;
|
||||
#else
|
||||
if (n == 0) return false;
|
||||
char buf[kMaxNumberLength+1];
|
||||
str = TerminateNumber(buf, str, n);
|
||||
if (str[0] == '-') return false; // strtoull() on a negative number?!
|
||||
char* end;
|
||||
errno = 0;
|
||||
#if defined HAVE_STRTOQ
|
||||
unsigned long long r = strtouq(str, &end, radix);
|
||||
#elif defined HAVE_STRTOLL
|
||||
unsigned long long r = strtoull(str, &end, radix);
|
||||
#elif defined HAVE__STRTOI64
|
||||
unsigned long long r = _strtoui64(str, &end, radix);
|
||||
#elif defined HAVE_STRTOIMAX
|
||||
unsigned long long r = strtoumax(str, &end, radix);
|
||||
#else
|
||||
#error parse_ulonglong_radix: cannot convert input to a long-long
|
||||
#endif
|
||||
if (end != str + n) return false; // Leftover junk
|
||||
if (errno) return false;
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<unsigned long long*>(dest)) = r;
|
||||
return true;
|
||||
#endif /* HAVE_UNSIGNED_LONG_LONG */
|
||||
}
|
||||
|
||||
bool Arg::parse_double(const char* str, int n, void* dest) {
|
||||
if (n == 0) return false;
|
||||
static const int kMaxLength = 200;
|
||||
char buf[kMaxLength];
|
||||
if (n >= kMaxLength) return false;
|
||||
memcpy(buf, str, n);
|
||||
buf[n] = '\0';
|
||||
errno = 0;
|
||||
char* end;
|
||||
double r = strtod(buf, &end);
|
||||
if (end != buf + n) return false; // Leftover junk
|
||||
if (errno) return false;
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<double*>(dest)) = r;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arg::parse_float(const char* str, int n, void* dest) {
|
||||
double r;
|
||||
if (!parse_double(str, n, &r)) return false;
|
||||
if (dest == NULL) return true;
|
||||
*(reinterpret_cast<float*>(dest)) = static_cast<float>(r);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#define DEFINE_INTEGER_PARSERS(name) \
|
||||
bool Arg::parse_##name(const char* str, int n, void* dest) { \
|
||||
return parse_##name##_radix(str, n, dest, 10); \
|
||||
} \
|
||||
bool Arg::parse_##name##_hex(const char* str, int n, void* dest) { \
|
||||
return parse_##name##_radix(str, n, dest, 16); \
|
||||
} \
|
||||
bool Arg::parse_##name##_octal(const char* str, int n, void* dest) { \
|
||||
return parse_##name##_radix(str, n, dest, 8); \
|
||||
} \
|
||||
bool Arg::parse_##name##_cradix(const char* str, int n, void* dest) { \
|
||||
return parse_##name##_radix(str, n, dest, 0); \
|
||||
}
|
||||
|
||||
DEFINE_INTEGER_PARSERS(short) /* */
|
||||
DEFINE_INTEGER_PARSERS(ushort) /* */
|
||||
DEFINE_INTEGER_PARSERS(int) /* Don't use semicolons after these */
|
||||
DEFINE_INTEGER_PARSERS(uint) /* statements because they can cause */
|
||||
DEFINE_INTEGER_PARSERS(long) /* compiler warnings if the checking */
|
||||
DEFINE_INTEGER_PARSERS(ulong) /* level is turned up high enough. */
|
||||
DEFINE_INTEGER_PARSERS(longlong) /* */
|
||||
DEFINE_INTEGER_PARSERS(ulonglong) /* */
|
||||
|
||||
#undef DEFINE_INTEGER_PARSERS
|
||||
|
||||
} // namespace pcrecpp
|
||||