Compare commits

...
39 Commits
Author SHA1 Message Date
Alexander Shaduri aec8ba4d12 Tagged 1.1.0. 2017-09-07 15:08:03 +00:00
Alexander Shaduri 16d5b82ac8 Fixed compilation under C++98.
Fixed smartmontools URLs.
2017-09-07 13:51:26 +00:00
Alexander Shaduri be66a8749c Bumped version to 1.1.0.
Updated NEWS file.
Clarified a message for self-test log entry count.
2017-09-07 12:43:38 +00:00
Alexander Shaduri 5192a9d447 Implemented ability to copy rows from Attribute, Statistics and Self-Test Log
tables.
2017-09-06 20:11:39 +00:00
Alexander Shaduri b9160ec173 Improved parser so that all the "-x" test data is parsed. 2017-09-05 20:53:19 +00:00
Alexander Shaduri 675fad3fc1 Documented RAID support. 2017-09-05 17:27:31 +00:00
Alexander Shaduri f7c01d6c7b Implemented "Update Drive Database" item in main window menu. 2017-09-05 16:12:19 +00:00
Alexander Shaduri 5bc6318dcd Implemented parsing of "==> Warning" in Info section. 2017-09-05 15:45:21 +00:00
Alexander Shaduri 3d8b0c94df Allow loading multiple virtual drives from one Open dialog. 2017-09-05 15:14:02 +00:00
Alexander Shaduri 73fec7fa3f Rate individual error log entries for error severity. 2017-09-05 14:53:47 +00:00
Alexander Shaduri 22971e8567 Show scan time on virtual drive icons.
Show windows drive letters if "device name under icon" is enabled.
2017-09-04 18:52:41 +00:00
Alexander Shaduri 3df4cdae56 Implemented devstat warnings. 2017-09-04 18:12:27 +00:00
Alexander Shaduri 062096a0b4 Improve GtkNotebook appearances in Windows.
Don't show Info window tabs if only one tab is present.
2017-09-04 16:57:08 +00:00
Alexander Shaduri 978e7d41b7 Added filters to file chooser dialogs.
Add .txt when saving files if it's not present (fixes a Windows bug when the
default drive name is cleared by typing something else).
2017-09-04 14:38:10 +00:00
Alexander Shaduri 33028f81d9 Fixed a few parser issues.
SCT temperature overrides all others now.
2017-09-04 13:43:40 +00:00
Alexander Shaduri 4cb12c5e0c Show temperature (from sct, devstat, or attributes) in Temperature Log tab,
including warnings.
Don't warn if SCT is unsupported.
2017-09-01 19:21:15 +00:00
Alexander Shaduri b01acfeef3 Added entry descriptions to Statistics table. 2017-09-01 17:53:09 +00:00
Alexander Shaduri b7676aaba8 Added Page,Offset column to Statistics.
Enabled multiple selection in treeviews.
Renamed ERC tab to Error Recovery.
2017-09-01 14:20:34 +00:00
Alexander Shaduri e0a13c6867 Implemented parsing of devstat section.
Implemented Statistics tab.
Increased borders in some tabs.
Some code style changes.
2017-09-01 13:42:51 +00:00
Alexander Shaduri 9f5db6d034 Visual improvements in Error Log tab. 2017-08-31 20:50:41 +00:00
Alexander Shaduri 5c3bfe8db0 Fixed not detecting the number of errors in extended error log. 2017-08-31 20:44:00 +00:00
Alexander Shaduri 5926e43bc5 Refactored Info Window tabs (again).
Implemented support for Temperature Log, ERC, Physical, Directory tabs.
Added detection of a few "not supported" messages.
Added some more --get=all output lines.
Smartctl invocation now corresponds to -x, and -x output can be loaded as
a virtual drive (parsed fully except for devstat).
2017-08-31 20:03:04 +00:00
Alexander Shaduri f21f076386 Refactored Info window UI (added tabs for various logs).
Hid annoying gcc warning.
2017-08-29 19:06:46 +00:00
Alexander Shaduri da403b28c9 Require smartmontools version 5.43 at runtime. 2017-08-29 15:13:13 +00:00
Alexander Shaduri faba9270a0 Use --format=brief attribute format by default.
Removed "Updated" column from attributes; advanced users can look at flags to
get that information.
2017-08-29 13:41:38 +00:00
Alexander Shaduri 9ca87eb296 Use -l xerror,error and -l xselftest,selftest by default.
Implemented parsing of xerror, xselftest sections.
2017-08-29 13:26:25 +00:00
Alexander Shaduri b2b97f2c9a Added more info output detection (APM feature is...).
Enable -v by default on Windows (makes debugging easier).
2017-08-29 11:47:14 +00:00
Alexander Shaduri 8494880943 Fixes in spec file.
Fixes with polkit's pkexec invocation.
2017-08-28 12:29:54 +00:00
Alexander Shaduri 0f8f38f0e1 Fixed compilation. 2017-08-28 10:12:39 +00:00
Alexander Shaduri 8544163c46 Make sure we bundle the polkit file on make dist. 2017-08-28 09:59:42 +00:00
Alexander Shaduri fdcd190592 Added --get=all information to General section.
Made General section scrollable
Changed all smartctl invocation parameters to full versions (very old smartctl
versions didn't support these, but we can drop their support from now on).
2017-08-27 20:00:43 +00:00
Alexander Shaduri 40e08796d1 Use pkexec --disable-internal-agent for polkit.
Don't warn on Unknown property "Form Factor".
2017-08-27 19:07:52 +00:00
Alexander Shaduri 1e72729056 Implemented PolKit support.
Install gsmartcontrol binary to /usr/sbin.
Removed Fedora consolehelper support from spec file (in favor of PolKit).
2017-08-27 17:33:09 +00:00
Alexander Shaduri 731122e891 Bumped version number to 1.0.3. 2017-08-26 17:23:43 +00:00
Alexander Shaduri ebb3ab6017 Removed local copy of pcrecpp, always use system-installed one (it's not a
rarity anymore).
2017-08-26 17:23:14 +00:00
Alexander Shaduri 3d2fe7fd50 Windows: Show drive letters for each device. 2017-08-26 16:47:11 +00:00
Alexander Shaduri 9a70345f4a Format large numbers according to locale (usually with commas).
Save Open/Save paths across windows and program restarts.
2017-08-26 13:18:32 +00:00
Alexander Shaduri 897a4efa96 Save / load default window size and position (both for main window, size only
for info window).
2017-08-26 12:09:51 +00:00
Alexander Shaduri 6ab53c5436 Updated site URLs to https://gsmartcontrol.sourceforge.io/ . 2017-08-18 09:33:55 +00:00
307 changed files with 4040 additions and 5317 deletions
View File
View File
View File
@@ -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.
+2 -2
View File
@@ -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.
@@ -222,6 +221,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
+20
View File
@@ -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.
+32 -24
View File
@@ -4,17 +4,28 @@ 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
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.
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).
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)
http://gsmartcontrol.sourceforge.net
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/
@@ -32,8 +43,8 @@ impact on performance;
* performs SMART self-tests;
* displays drive identity information, capabilities, attributes, and
self-test/error logs;
* displays drive identity information, capabilities, attributes,
self-test/error logs, device statistics, etc...;
* can read in smartctl output from a saved file, interpreting it as a
read-only virtual device;
@@ -91,11 +102,13 @@ these bugs and works around them.
Software Requirements
Note: If using the official Windows package, no additional software is required.
You need to have the following software installed:
* pcre - http://www.pcre.org .
* smartmontools - see http://smartmontools.sourceforge.net .
* smartmontools - see https://www.smartmontools.org/ .
* GTK+, version 3.4 or higher - see http://www.gtk.org .
@@ -118,9 +131,6 @@ 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:
@@ -139,9 +149,9 @@ to work without any issues.
Windows 8.1, Windows 10. The Windows port uses pd0, pd1, etc...
for physical drives 0, 1, etc... .
* Solaris.
* Mac OS X.
* Mac OS X - 3rd-party testing by the MacPorts and Fink projects.
* Solaris.
* QNX - Code written but no testing has been performed yet.
@@ -181,9 +191,9 @@ 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
See https://www.smartmontools.org/browser/trunk/smartmontools/smartctl.8.in
Additional information is available at http://smartmontools.sourceforge.net
Additional information is available at https://www.smartmontools.org/
@@ -192,9 +202,7 @@ 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".
similar arguments won't have any effect.
The most important parameters are:
@@ -319,18 +327,18 @@ 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):
terminal emulator (as root, using sudo or su):
smartctl -a /dev/sda
smartctl -x /dev/sda
Note: If using Windows, the device name should be /dev/pd1 for the second
physical drive, etc... .
physical drive, etc... . Run cmd as administrator first.
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 operating system you use (for example, openSUSE Leap 42.3).
* Which version of GTK and Gtkmm you have installed. Finding this out is very
distribution-specific. For example, on openSUSE it would be
@@ -341,9 +349,9 @@ search them in your distribution's graphical package manager, if there is one.
with -v option, e.g. (type the following in a terminal emulator or Run
dialog):
gsmartcontrol-root auto -v
gsmartcontrol-root -v
Note: On Windows, run gsmartcontrol.exe with "-v" switch as Administrator.
Note: On Windows, -v switch is on by default.
Perform the steps needed to reproduce the bug, then go to
"Options -> View Execution Log", and click "Save All".
+53
View File
@@ -0,0 +1,53 @@
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:
Move README.txt contents to the site, make Help menu open it.
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:
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)
+13 -17
View File
@@ -1,5 +1,5 @@
AC_INIT([GSmartControl],[1.0.2],[],[gsmartcontrol])
AC_INIT([GSmartControl],[1.1.0],[],[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"
# -------------------------------------------------------------------------------------
@@ -389,8 +386,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 +482,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>

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

+19
View File
@@ -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
-28
View File
@@ -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.
-243
View File
@@ -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]";
}
/// @}
-10
View File
@@ -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
-13
View File
@@ -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.
-200
View File
@@ -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
-173
View File
@@ -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
-965
View File
@@ -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
-710
View File
@@ -1,710 +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
// Support for PCRE_XXX modifiers added by Giuseppe Maxia, July 2005
#ifndef _PCRECPP_H
#define _PCRECPP_H
// C++ interface to the pcre regular-expression library. RE supports
// Perl-style regular expressions (with extensions like \d, \w, \s,
// ...).
//
// -----------------------------------------------------------------------
// REGEXP SYNTAX:
//
// This module is part of the pcre library and hence supports its syntax
// for regular expressions.
//
// The syntax is pretty similar to Perl's. For those not familiar
// with Perl's regular expressions, here are some examples of the most
// commonly used extensions:
//
// "hello (\\w+) world" -- \w matches a "word" character
// "version (\\d+)" -- \d matches a digit
// "hello\\s+world" -- \s matches any whitespace character
// "\\b(\\w+)\\b" -- \b matches empty string at a word boundary
// "(?i)hello" -- (?i) turns on case-insensitive matching
// "/\\*(.*?)\\*/" -- .*? matches . minimum no. of times possible
//
// -----------------------------------------------------------------------
// MATCHING INTERFACE:
//
// The "FullMatch" operation checks that supplied text matches a
// supplied pattern exactly.
//
// Example: successful match
// pcrecpp::RE re("h.*o");
// re.FullMatch("hello");
//
// Example: unsuccessful match (requires full match):
// pcrecpp::RE re("e");
// !re.FullMatch("hello");
//
// Example: creating a temporary RE object:
// pcrecpp::RE("h.*o").FullMatch("hello");
//
// You can pass in a "const char*" or a "string" for "text". The
// examples below tend to use a const char*.
//
// You can, as in the different examples above, store the RE object
// explicitly in a variable or use a temporary RE object. The
// examples below use one mode or the other arbitrarily. Either
// could correctly be used for any of these examples.
//
// -----------------------------------------------------------------------
// MATCHING WITH SUB-STRING EXTRACTION:
//
// You can supply extra pointer arguments to extract matched subpieces.
//
// Example: extracts "ruby" into "s" and 1234 into "i"
// int i;
// string s;
// pcrecpp::RE re("(\\w+):(\\d+)");
// re.FullMatch("ruby:1234", &s, &i);
//
// Example: does not try to extract any extra sub-patterns
// re.FullMatch("ruby:1234", &s);
//
// Example: does not try to extract into NULL
// re.FullMatch("ruby:1234", NULL, &i);
//
// Example: integer overflow causes failure
// !re.FullMatch("ruby:1234567891234", NULL, &i);
//
// Example: fails because there aren't enough sub-patterns:
// !pcrecpp::RE("\\w+:\\d+").FullMatch("ruby:1234", &s);
//
// Example: fails because string cannot be stored in integer
// !pcrecpp::RE("(.*)").FullMatch("ruby", &i);
//
// The provided pointer arguments can be pointers to any scalar numeric
// type, or one of
// string (matched piece is copied to string)
// StringPiece (StringPiece is mutated to point to matched piece)
// T (where "bool T::ParseFrom(const char*, int)" exists)
// NULL (the corresponding matched sub-pattern is not copied)
//
// CAVEAT: An optional sub-pattern that does not exist in the matched
// string is assigned the empty string. Therefore, the following will
// return false (because the empty string is not a valid number):
// int number;
// pcrecpp::RE::FullMatch("abc", "[a-z]+(\\d+)?", &number);
//
// -----------------------------------------------------------------------
// DO_MATCH
//
// The matching interface supports at most 16 arguments per call.
// If you need more, consider using the more general interface
// pcrecpp::RE::DoMatch(). See pcrecpp.h for the signature for DoMatch.
//
// -----------------------------------------------------------------------
// PARTIAL MATCHES
//
// You can use the "PartialMatch" operation when you want the pattern
// to match any substring of the text.
//
// Example: simple search for a string:
// pcrecpp::RE("ell").PartialMatch("hello");
//
// Example: find first number in a string:
// int number;
// pcrecpp::RE re("(\\d+)");
// re.PartialMatch("x*100 + 20", &number);
// assert(number == 100);
//
// -----------------------------------------------------------------------
// UTF-8 AND THE MATCHING INTERFACE:
//
// By default, pattern and text are plain text, one byte per character.
// The UTF8 flag, passed to the constructor, causes both pattern
// and string to be treated as UTF-8 text, still a byte stream but
// potentially multiple bytes per character. In practice, the text
// is likelier to be UTF-8 than the pattern, but the match returned
// may depend on the UTF8 flag, so always use it when matching
// UTF8 text. E.g., "." will match one byte normally but with UTF8
// set may match up to three bytes of a multi-byte character.
//
// Example:
// pcrecpp::RE_Options options;
// options.set_utf8();
// pcrecpp::RE re(utf8_pattern, options);
// re.FullMatch(utf8_string);
//
// Example: using the convenience function UTF8():
// pcrecpp::RE re(utf8_pattern, pcrecpp::UTF8());
// re.FullMatch(utf8_string);
//
// NOTE: The UTF8 option is ignored if pcre was not configured with the
// --enable-utf8 flag.
//
// -----------------------------------------------------------------------
// PASSING MODIFIERS TO THE REGULAR EXPRESSION ENGINE
//
// PCRE defines some modifiers to change the behavior of the regular
// expression engine.
// The C++ wrapper defines an auxiliary class, RE_Options, as a vehicle
// to pass such modifiers to a RE class.
//
// Currently, the following modifiers are supported
//
// modifier description Perl corresponding
//
// PCRE_CASELESS case insensitive match /i
// PCRE_MULTILINE multiple lines match /m
// PCRE_DOTALL dot matches newlines /s
// PCRE_DOLLAR_ENDONLY $ matches only at end N/A
// PCRE_EXTRA strict escape parsing N/A
// PCRE_EXTENDED ignore whitespaces /x
// PCRE_UTF8 handles UTF8 chars built-in
// PCRE_UNGREEDY reverses * and *? N/A
// PCRE_NO_AUTO_CAPTURE disables matching parens N/A (*)
//
// (For a full account on how each modifier works, please check the
// PCRE API reference manual).
//
// (*) Both Perl and PCRE allow non matching parentheses by means of the
// "?:" modifier within the pattern itself. e.g. (?:ab|cd) does not
// capture, while (ab|cd) does.
//
// For each modifier, there are two member functions whose name is made
// out of the modifier in lowercase, without the "PCRE_" prefix. For
// instance, PCRE_CASELESS is handled by
// bool caseless(),
// which returns true if the modifier is set, and
// RE_Options & set_caseless(bool),
// which sets or unsets the modifier.
//
// Moreover, PCRE_EXTRA_MATCH_LIMIT can be accessed through the
// set_match_limit() and match_limit() member functions.
// Setting match_limit to a non-zero value will limit the executation of
// pcre to keep it from doing bad things like blowing the stack or taking
// an eternity to return a result. A value of 5000 is good enough to stop
// stack blowup in a 2MB thread stack. Setting match_limit to zero will
// disable match limiting. Alternately, you can set match_limit_recursion()
// which uses PCRE_EXTRA_MATCH_LIMIT_RECURSION to limit how much pcre
// recurses. match_limit() caps the number of matches pcre does;
// match_limit_recrusion() caps the depth of recursion.
//
// Normally, to pass one or more modifiers to a RE class, you declare
// a RE_Options object, set the appropriate options, and pass this
// object to a RE constructor. Example:
//
// RE_options opt;
// opt.set_caseless(true);
//
// if (RE("HELLO", opt).PartialMatch("hello world")) ...
//
// RE_options has two constructors. The default constructor takes no
// arguments and creates a set of flags that are off by default.
//
// The optional parameter 'option_flags' is to facilitate transfer
// of legacy code from C programs. This lets you do
// RE(pattern, RE_Options(PCRE_CASELESS|PCRE_MULTILINE)).PartialMatch(str);
//
// But new code is better off doing
// RE(pattern,
// RE_Options().set_caseless(true).set_multiline(true)).PartialMatch(str);
// (See below)
//
// If you are going to pass one of the most used modifiers, there are some
// convenience functions that return a RE_Options class with the
// appropriate modifier already set:
// CASELESS(), UTF8(), MULTILINE(), DOTALL(), EXTENDED()
//
// If you need to set several options at once, and you don't want to go
// through the pains of declaring a RE_Options object and setting several
// options, there is a parallel method that give you such ability on the
// fly. You can concatenate several set_xxxxx member functions, since each
// of them returns a reference to its class object. e.g.: to pass
// PCRE_CASELESS, PCRE_EXTENDED, and PCRE_MULTILINE to a RE with one
// statement, you may write
//
// RE(" ^ xyz \\s+ .* blah$", RE_Options()
// .set_caseless(true)
// .set_extended(true)
// .set_multiline(true)).PartialMatch(sometext);
//
// -----------------------------------------------------------------------
// SCANNING TEXT INCREMENTALLY
//
// The "Consume" operation may be useful if you want to repeatedly
// match regular expressions at the front of a string and skip over
// them as they match. This requires use of the "StringPiece" type,
// which represents a sub-range of a real string. Like RE, StringPiece
// is defined in the pcrecpp namespace.
//
// Example: read lines of the form "var = value" from a string.
// string contents = ...; // Fill string somehow
// pcrecpp::StringPiece input(contents); // Wrap in a StringPiece
//
// string var;
// int value;
// pcrecpp::RE re("(\\w+) = (\\d+)\n");
// while (re.Consume(&input, &var, &value)) {
// ...;
// }
//
// Each successful call to "Consume" will set "var/value", and also
// advance "input" so it points past the matched text.
//
// The "FindAndConsume" operation is similar to "Consume" but does not
// anchor your match at the beginning of the string. For example, you
// could extract all words from a string by repeatedly calling
// pcrecpp::RE("(\\w+)").FindAndConsume(&input, &word)
//
// -----------------------------------------------------------------------
// PARSING HEX/OCTAL/C-RADIX NUMBERS
//
// By default, if you pass a pointer to a numeric value, the
// corresponding text is interpreted as a base-10 number. You can
// instead wrap the pointer with a call to one of the operators Hex(),
// Octal(), or CRadix() to interpret the text in another base. The
// CRadix operator interprets C-style "0" (base-8) and "0x" (base-16)
// prefixes, but defaults to base-10.
//
// Example:
// int a, b, c, d;
// pcrecpp::RE re("(.*) (.*) (.*) (.*)");
// re.FullMatch("100 40 0100 0x40",
// pcrecpp::Octal(&a), pcrecpp::Hex(&b),
// pcrecpp::CRadix(&c), pcrecpp::CRadix(&d));
// will leave 64 in a, b, c, and d.
//
// -----------------------------------------------------------------------
// REPLACING PARTS OF STRINGS
//
// You can replace the first match of "pattern" in "str" with
// "rewrite". Within "rewrite", backslash-escaped digits (\1 to \9)
// can be used to insert text matching corresponding parenthesized
// group from the pattern. \0 in "rewrite" refers to the entire
// matching text. E.g.,
//
// string s = "yabba dabba doo";
// pcrecpp::RE("b+").Replace("d", &s);
//
// will leave "s" containing "yada dabba doo". The result is true if
// the pattern matches and a replacement occurs, or false otherwise.
//
// GlobalReplace() is like Replace(), except that it replaces all
// occurrences of the pattern in the string with the rewrite.
// Replacements are not subject to re-matching. E.g.,
//
// string s = "yabba dabba doo";
// pcrecpp::RE("b+").GlobalReplace("d", &s);
//
// will leave "s" containing "yada dada doo". It returns the number
// of replacements made.
//
// Extract() is like Replace(), except that if the pattern matches,
// "rewrite" is copied into "out" (an additional argument) with
// substitutions. The non-matching portions of "text" are ignored.
// Returns true iff a match occurred and the extraction happened
// successfully. If no match occurs, the string is left unaffected.
#include <string>
#include <pcre.h>
#include <pcrecpparg.h> // defines the Arg class
// This isn't technically needed here, but we include it
// anyway so folks who include pcrecpp.h don't have to.
#include <pcre_stringpiece.h>
namespace pcrecpp {
#define PCRE_SET_OR_CLEAR(b, o) \
if (b) all_options_ |= (o); else all_options_ &= ~(o); \
return *this
#define PCRE_IS_SET(o) \
(all_options_ & o) == o
/***** Compiling regular expressions: the RE class *****/
// RE_Options allow you to set options to be passed along to pcre,
// along with other options we put on top of pcre.
// Only 9 modifiers, plus match_limit and match_limit_recursion,
// are supported now.
class PCRECPP_EXP_DEFN RE_Options {
public:
// constructor
RE_Options() : match_limit_(0), match_limit_recursion_(0), all_options_(0) {}
// alternative constructor.
// To facilitate transfer of legacy code from C programs
//
// This lets you do
// RE(pattern, RE_Options(PCRE_CASELESS|PCRE_MULTILINE)).PartialMatch(str);
// But new code is better off doing
// RE(pattern,
// RE_Options().set_caseless(true).set_multiline(true)).PartialMatch(str);
RE_Options(int option_flags) : match_limit_(0), match_limit_recursion_(0),
all_options_(option_flags) {}
// we're fine with the default destructor, copy constructor, etc.
// accessors and mutators
int match_limit() const { return match_limit_; };
RE_Options &set_match_limit(int limit) {
match_limit_ = limit;
return *this;
}
int match_limit_recursion() const { return match_limit_recursion_; };
RE_Options &set_match_limit_recursion(int limit) {
match_limit_recursion_ = limit;
return *this;
}
bool caseless() const {
return PCRE_IS_SET(PCRE_CASELESS);
}
RE_Options &set_caseless(bool x) {
PCRE_SET_OR_CLEAR(x, PCRE_CASELESS);
}
bool multiline() const {
return PCRE_IS_SET(PCRE_MULTILINE);
}
RE_Options &set_multiline(bool x) {
PCRE_SET_OR_CLEAR(x, PCRE_MULTILINE);
}
bool dotall() const {
return PCRE_IS_SET(PCRE_DOTALL);
}
RE_Options &set_dotall(bool x) {
PCRE_SET_OR_CLEAR(x, PCRE_DOTALL);
}
bool extended() const {
return PCRE_IS_SET(PCRE_EXTENDED);
}
RE_Options &set_extended(bool x) {
PCRE_SET_OR_CLEAR(x, PCRE_EXTENDED);
}
bool dollar_endonly() const {
return PCRE_IS_SET(PCRE_DOLLAR_ENDONLY);
}
RE_Options &set_dollar_endonly(bool x) {
PCRE_SET_OR_CLEAR(x, PCRE_DOLLAR_ENDONLY);
}
bool extra() const {
return PCRE_IS_SET(PCRE_EXTRA);
}
RE_Options &set_extra(bool x) {
PCRE_SET_OR_CLEAR(x, PCRE_EXTRA);
}
bool ungreedy() const {
return PCRE_IS_SET(PCRE_UNGREEDY);
}
RE_Options &set_ungreedy(bool x) {
PCRE_SET_OR_CLEAR(x, PCRE_UNGREEDY);
}
bool utf8() const {
return PCRE_IS_SET(PCRE_UTF8);
}
RE_Options &set_utf8(bool x) {
PCRE_SET_OR_CLEAR(x, PCRE_UTF8);
}
bool no_auto_capture() const {
return PCRE_IS_SET(PCRE_NO_AUTO_CAPTURE);
}
RE_Options &set_no_auto_capture(bool x) {
PCRE_SET_OR_CLEAR(x, PCRE_NO_AUTO_CAPTURE);
}
RE_Options &set_all_options(int opt) {
all_options_ = opt;
return *this;
}
int all_options() const {
return all_options_ ;
}
// TODO: add other pcre flags
private:
int match_limit_;
int match_limit_recursion_;
int all_options_;
};
// These functions return some common RE_Options
static inline RE_Options UTF8() {
return RE_Options().set_utf8(true);
}
static inline RE_Options CASELESS() {
return RE_Options().set_caseless(true);
}
static inline RE_Options MULTILINE() {
return RE_Options().set_multiline(true);
}
static inline RE_Options DOTALL() {
return RE_Options().set_dotall(true);
}
static inline RE_Options EXTENDED() {
return RE_Options().set_extended(true);
}
// Interface for regular expression matching. Also corresponds to a
// pre-compiled regular expression. An "RE" object is safe for
// concurrent use by multiple threads.
class PCRECPP_EXP_DEFN RE {
public:
// We provide implicit conversions from strings so that users can
// pass in a string or a "const char*" wherever an "RE" is expected.
RE(const string& pat) { Init(pat, NULL); }
RE(const string& pat, const RE_Options& option) { Init(pat, &option); }
RE(const char* pat) { Init(pat, NULL); }
RE(const char* pat, const RE_Options& option) { Init(pat, &option); }
RE(const unsigned char* pat) {
Init(reinterpret_cast<const char*>(pat), NULL);
}
RE(const unsigned char* pat, const RE_Options& option) {
Init(reinterpret_cast<const char*>(pat), &option);
}
// Copy constructor & assignment - note that these are expensive
// because they recompile the expression.
RE(const RE& re) { Init(re.pattern_, &re.options_); }
const RE& operator=(const RE& re) {
if (this != &re) {
Cleanup();
// This is the code that originally came from Google
// Init(re.pattern_.c_str(), &re.options_);
// This is the replacement from Ari Pollak
Init(re.pattern_, &re.options_);
}
return *this;
}
~RE();
// The string specification for this RE. E.g.
// RE re("ab*c?d+");
// re.pattern(); // "ab*c?d+"
const string& pattern() const { return pattern_; }
// If RE could not be created properly, returns an error string.
// Else returns the empty string.
const string& error() const { return *error_; }
/***** The useful part: the matching interface *****/
// This is provided so one can do pattern.ReplaceAll() just as
// easily as ReplaceAll(pattern-text, ....)
bool FullMatch(const StringPiece& text,
const Arg& ptr1 = no_arg,
const Arg& ptr2 = no_arg,
const Arg& ptr3 = no_arg,
const Arg& ptr4 = no_arg,
const Arg& ptr5 = no_arg,
const Arg& ptr6 = no_arg,
const Arg& ptr7 = no_arg,
const Arg& ptr8 = no_arg,
const Arg& ptr9 = no_arg,
const Arg& ptr10 = no_arg,
const Arg& ptr11 = no_arg,
const Arg& ptr12 = no_arg,
const Arg& ptr13 = no_arg,
const Arg& ptr14 = no_arg,
const Arg& ptr15 = no_arg,
const Arg& ptr16 = no_arg) const;
bool PartialMatch(const StringPiece& text,
const Arg& ptr1 = no_arg,
const Arg& ptr2 = no_arg,
const Arg& ptr3 = no_arg,
const Arg& ptr4 = no_arg,
const Arg& ptr5 = no_arg,
const Arg& ptr6 = no_arg,
const Arg& ptr7 = no_arg,
const Arg& ptr8 = no_arg,
const Arg& ptr9 = no_arg,
const Arg& ptr10 = no_arg,
const Arg& ptr11 = no_arg,
const Arg& ptr12 = no_arg,
const Arg& ptr13 = no_arg,
const Arg& ptr14 = no_arg,
const Arg& ptr15 = no_arg,
const Arg& ptr16 = no_arg) const;
bool Consume(StringPiece* input,
const Arg& ptr1 = no_arg,
const Arg& ptr2 = no_arg,
const Arg& ptr3 = no_arg,
const Arg& ptr4 = no_arg,
const Arg& ptr5 = no_arg,
const Arg& ptr6 = no_arg,
const Arg& ptr7 = no_arg,
const Arg& ptr8 = no_arg,
const Arg& ptr9 = no_arg,
const Arg& ptr10 = no_arg,
const Arg& ptr11 = no_arg,
const Arg& ptr12 = no_arg,
const Arg& ptr13 = no_arg,
const Arg& ptr14 = no_arg,
const Arg& ptr15 = no_arg,
const Arg& ptr16 = no_arg) const;
bool FindAndConsume(StringPiece* input,
const Arg& ptr1 = no_arg,
const Arg& ptr2 = no_arg,
const Arg& ptr3 = no_arg,
const Arg& ptr4 = no_arg,
const Arg& ptr5 = no_arg,
const Arg& ptr6 = no_arg,
const Arg& ptr7 = no_arg,
const Arg& ptr8 = no_arg,
const Arg& ptr9 = no_arg,
const Arg& ptr10 = no_arg,
const Arg& ptr11 = no_arg,
const Arg& ptr12 = no_arg,
const Arg& ptr13 = no_arg,
const Arg& ptr14 = no_arg,
const Arg& ptr15 = no_arg,
const Arg& ptr16 = no_arg) const;
bool Replace(const StringPiece& rewrite,
string *str) const;
int GlobalReplace(const StringPiece& rewrite,
string *str) const;
bool Extract(const StringPiece &rewrite,
const StringPiece &text,
string *out) const;
// Escapes all potentially meaningful regexp characters in
// 'unquoted'. The returned string, used as a regular expression,
// will exactly match the original string. For example,
// 1.5-2.0?
// may become:
// 1\.5\-2\.0\?
// Note QuoteMeta behaves the same as perl's QuoteMeta function,
// *except* that it escapes the NUL character (\0) as backslash + 0,
// rather than backslash + NUL.
static string QuoteMeta(const StringPiece& unquoted);
/***** Generic matching interface *****/
// Type of match (TODO: Should be restructured as part of RE_Options)
enum Anchor {
UNANCHORED, // No anchoring
ANCHOR_START, // Anchor at start only
ANCHOR_BOTH // Anchor at start and end
};
// General matching routine. Stores the length of the match in
// "*consumed" if successful.
bool DoMatch(const StringPiece& text,
Anchor anchor,
int* consumed,
const Arg* const* args, int n) const;
// Return the number of capturing subpatterns, or -1 if the
// regexp wasn't valid on construction.
int NumberOfCapturingGroups() const;
// The default value for an argument, to indicate the end of the argument
// list. This must be used only in optional argument defaults. It should NOT
// be passed explicitly. Some people have tried to use it like this:
//
// FullMatch(x, y, &z, no_arg, &w);
//
// This is a mistake, and will not work.
static Arg no_arg;
private:
void Init(const string& pattern, const RE_Options* options);
void Cleanup();
// Match against "text", filling in "vec" (up to "vecsize" * 2/3) with
// pairs of integers for the beginning and end positions of matched
// text. The first pair corresponds to the entire matched text;
// subsequent pairs correspond, in order, to parentheses-captured
// matches. Returns the number of pairs (one more than the number of
// the last subpattern with a match) if matching was successful
// and zero if the match failed.
// I.e. for RE("(foo)|(bar)|(baz)") it will return 2, 3, and 4 when matching
// against "foo", "bar", and "baz" respectively.
// When matching RE("(foo)|hello") against "hello", it will return 1.
// But the values for all subpattern are filled in into "vec".
int TryMatch(const StringPiece& text,
int startpos,
Anchor anchor,
bool empty_ok,
int *vec,
int vecsize) const;
// Append the "rewrite" string, with backslash subsitutions from "text"
// and "vec", to string "out".
bool Rewrite(string *out,
const StringPiece& rewrite,
const StringPiece& text,
int *vec,
int veclen) const;
// internal implementation for DoMatch
bool DoMatchImpl(const StringPiece& text,
Anchor anchor,
int* consumed,
const Arg* const args[],
int n,
int* vec,
int vecsize) const;
// Compile the regexp for the specified anchoring mode
pcre* Compile(Anchor anchor);
string pattern_;
RE_Options options_;
pcre* re_full_; // For full matches
pcre* re_partial_; // For partial matches
const string* error_; // Error indicator (or points to empty string)
};
} // namespace pcrecpp
#endif /* _PCRECPP_H */
@@ -1,71 +0,0 @@
/*************************************************
* Perl-Compatible Regular Expressions *
*************************************************/
/*
Copyright (c) 2005, Google Inc.
All rights reserved.
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the University of Cambridge nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
-----------------------------------------------------------------------------
*/
#ifndef PCRECPP_INTERNAL_H
#define PCRECPP_INTERNAL_H
/* When compiling a DLL for Windows, the exported symbols have to be declared
using some MS magic. I found some useful information on this web page:
http://msdn2.microsoft.com/en-us/library/y4h7bcy6(VS.80).aspx. According to the
information there, using __declspec(dllexport) without "extern" we have a
definition; with "extern" we have a declaration. The settings here override the
setting in pcre.h. We use:
PCRECPP_EXP_DECL for declarations
PCRECPP_EXP_DEFN for definitions of exported functions
*/
#ifndef PCRECPP_EXP_DECL
# ifdef _WIN32
# ifndef PCRE_STATIC
# define PCRECPP_EXP_DECL extern __declspec(dllexport)
# define PCRECPP_EXP_DEFN __declspec(dllexport)
# else
# define PCRECPP_EXP_DECL extern
# define PCRECPP_EXP_DEFN
# endif
# else
# define PCRECPP_EXP_DECL extern
# define PCRECPP_EXP_DEFN
# endif
#endif
#endif /* PCRECPP_INTERNAL_H */
/* End of pcrecpp_internal.h */
-175
View File
@@ -1,175 +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
#ifndef _PCRECPPARG_H
#define _PCRECPPARG_H
#include <cstdlib> // Custom
#include <stdlib.h> // for NULL
#include <string>
#include <pcre.h>
namespace pcrecpp {
class StringPiece;
// Hex/Octal/Binary?
// Special class for parsing into objects that define a ParseFrom() method
template <class T>
class _RE_MatchObject {
public:
static inline bool Parse(const char* str, int n, void* dest) {
if (dest == NULL) return true;
T* object = reinterpret_cast<T*>(dest);
return object->ParseFrom(str, n);
}
};
class PCRECPP_EXP_DEFN Arg {
public:
// Empty constructor so we can declare arrays of Arg
Arg();
// Constructor specially designed for NULL arguments
Arg(void*);
typedef bool (*Parser)(const char* str, int n, void* dest);
// Type-specific parsers
#define PCRE_MAKE_PARSER(type,name) \
Arg(type* p) : arg_(p), parser_(name) { } \
Arg(type* p, Parser parser) : arg_(p), parser_(parser) { }
PCRE_MAKE_PARSER(char, parse_char);
PCRE_MAKE_PARSER(unsigned char, parse_uchar);
PCRE_MAKE_PARSER(short, parse_short);
PCRE_MAKE_PARSER(unsigned short, parse_ushort);
PCRE_MAKE_PARSER(int, parse_int);
PCRE_MAKE_PARSER(unsigned int, parse_uint);
PCRE_MAKE_PARSER(long, parse_long);
PCRE_MAKE_PARSER(unsigned long, parse_ulong);
#if 1
PCRE_MAKE_PARSER(long long, parse_longlong);
#endif
#if 1
PCRE_MAKE_PARSER(unsigned long long, parse_ulonglong);
#endif
PCRE_MAKE_PARSER(float, parse_float);
PCRE_MAKE_PARSER(double, parse_double);
PCRE_MAKE_PARSER(std::string, parse_string);
PCRE_MAKE_PARSER(StringPiece, parse_stringpiece);
#undef PCRE_MAKE_PARSER
// Generic constructor
template <class T> Arg(T*, Parser parser);
// Generic constructor template
template <class T> Arg(T* p)
: arg_(p), parser_(_RE_MatchObject<T>::Parse) {
}
// Parse the data
bool Parse(const char* str, int n) const;
private:
void* arg_;
Parser parser_;
static bool parse_null (const char* str, int n, void* dest);
static bool parse_char (const char* str, int n, void* dest);
static bool parse_uchar (const char* str, int n, void* dest);
static bool parse_float (const char* str, int n, void* dest);
static bool parse_double (const char* str, int n, void* dest);
static bool parse_string (const char* str, int n, void* dest);
static bool parse_stringpiece (const char* str, int n, void* dest);
#define PCRE_DECLARE_INTEGER_PARSER(name) \
private: \
static bool parse_ ## name(const char* str, int n, void* dest); \
static bool parse_ ## name ## _radix( \
const char* str, int n, void* dest, int radix); \
public: \
static bool parse_ ## name ## _hex(const char* str, int n, void* dest); \
static bool parse_ ## name ## _octal(const char* str, int n, void* dest); \
static bool parse_ ## name ## _cradix(const char* str, int n, void* dest)
PCRE_DECLARE_INTEGER_PARSER(short);
PCRE_DECLARE_INTEGER_PARSER(ushort);
PCRE_DECLARE_INTEGER_PARSER(int);
PCRE_DECLARE_INTEGER_PARSER(uint);
PCRE_DECLARE_INTEGER_PARSER(long);
PCRE_DECLARE_INTEGER_PARSER(ulong);
PCRE_DECLARE_INTEGER_PARSER(longlong);
PCRE_DECLARE_INTEGER_PARSER(ulonglong);
#undef PCRE_DECLARE_INTEGER_PARSER
};
inline Arg::Arg() : arg_(NULL), parser_(parse_null) { }
inline Arg::Arg(void* p) : arg_(p), parser_(parse_null) { }
inline bool Arg::Parse(const char* str, int n) const {
return (*parser_)(str, n, arg_);
}
// This part of the parser, appropriate only for ints, deals with bases
#define MAKE_INTEGER_PARSER(type, name) \
inline Arg Hex(type* ptr) { \
return Arg(ptr, Arg::parse_ ## name ## _hex); } \
inline Arg Octal(type* ptr) { \
return Arg(ptr, Arg::parse_ ## name ## _octal); } \
inline Arg CRadix(type* ptr) { \
return Arg(ptr, Arg::parse_ ## name ## _cradix); }
MAKE_INTEGER_PARSER(short, short) /* */
MAKE_INTEGER_PARSER(unsigned short, ushort) /* */
MAKE_INTEGER_PARSER(int, int) /* Don't use semicolons */
MAKE_INTEGER_PARSER(unsigned int, uint) /* after these statement */
MAKE_INTEGER_PARSER(long, long) /* because they can cause */
MAKE_INTEGER_PARSER(unsigned long, ulong) /* compiler warnings if */
#if 1 /* the checking level is */
MAKE_INTEGER_PARSER(long long, longlong) /* turned up high enough. */
#endif /* */
#if 1 /* */
MAKE_INTEGER_PARSER(unsigned long long, ulonglong) /* */
#endif
#undef PCRE_IS_SET
#undef PCRE_SET_OR_CLEAR
#undef MAKE_INTEGER_PARSER
} // namespace pcrecpp
#endif /* _PCRECPPARG_H */

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