mirror of
https://github.com/ashaduri/gsmartcontrol.git
synced 2026-09-27 06:15:45 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97e95fc01d |
@@ -23,6 +23,10 @@ examples are covered under the Unlicense. See LICENSE_unlicense.txt for
|
||||
details.
|
||||
|
||||
|
||||
Portions of this software are copyright Google Inc. See LICENSE_pcrecpp.txt
|
||||
for details.
|
||||
|
||||
|
||||
This product includes the HZ library (or portions of it). See LICENSE_hz.txt
|
||||
for details.
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
Copyright (c) 2005 - 2010, Google Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+2
-2
@@ -22,6 +22,7 @@ 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
|
||||
|
||||
@@ -50,7 +51,7 @@ src-rpm: dist
|
||||
|
||||
# NSIS installer support.
|
||||
# Requires installed NSIS, smartctl-nc.exe, smartctl.exe, update-smart-drivedb.exe.
|
||||
# Gtkmm and pcre dlls are required if linking against them.
|
||||
# Gtkmm dlls and pcre dlls are required if linking against them.
|
||||
# dos2unix on build machine.
|
||||
# Execute only with win32 build present.
|
||||
|
||||
@@ -221,7 +222,6 @@ win-dist-prepare: all
|
||||
# other
|
||||
cp -p "@WINDOWS_SYSROOT@"/bin/libgcc_s_*.dll win-dist/
|
||||
cp -p "@WINDOWS_SYSROOT@"/bin/libpcre-1.dll win-dist/
|
||||
cp -p "@WINDOWS_SYSROOT@"/bin/libpcrecpp-0.dll win-dist/
|
||||
|
||||
|
||||
nsis-dist-prepare: win-dist-prepare
|
||||
|
||||
@@ -1,23 +1,3 @@
|
||||
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.
|
||||
|
||||
+24
-32
@@ -4,28 +4,17 @@ About GSmartControl
|
||||
GSmartControl - Hard disk drive and SSD health inspection tool.
|
||||
|
||||
GSmartControl is a graphical user interface for smartctl (from smartmontools
|
||||
package, see https://www.smartmontools.org/), which is a tool for
|
||||
package, see http://smartmontools.sourceforge.net), which is a tool for
|
||||
querying and controlling SMART (Self-Monitoring, Analysis, and Reporting
|
||||
Technology) data on modern hard disk and solid-state drives. It allows you to
|
||||
inspect the drive's SMART data to determine its health, as well as run various
|
||||
tests on it.
|
||||
|
||||
GSmartControl supports ATA drives (both PATA and SATA), various USB to
|
||||
ATA bridges and drives behind some RAID controllers:
|
||||
* Adaptec (Linux, some models only)
|
||||
* Areca (Linux, Windows)
|
||||
* HP CCISS (Linux)
|
||||
* HP hpsa / hpahcisr (Linux)
|
||||
* Intel Matrix Storage (CSMI) (Linux, Windows, FreeBSD)
|
||||
* LSI 3ware (Linux, Windows)
|
||||
* LSI MegaRAID (Windows)
|
||||
Note: 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).
|
||||
|
||||
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/
|
||||
http://gsmartcontrol.sourceforge.net
|
||||
|
||||
|
||||
|
||||
@@ -43,8 +32,8 @@ impact on performance;
|
||||
|
||||
* performs SMART self-tests;
|
||||
|
||||
* displays drive identity information, capabilities, attributes,
|
||||
self-test/error logs, device statistics, etc...;
|
||||
* displays drive identity information, capabilities, attributes, and
|
||||
self-test/error logs;
|
||||
|
||||
* can read in smartctl output from a saved file, interpreting it as a
|
||||
read-only virtual device;
|
||||
@@ -102,13 +91,11 @@ 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 https://www.smartmontools.org/ .
|
||||
* smartmontools - see http://smartmontools.sourceforge.net .
|
||||
|
||||
* GTK+, version 3.4 or higher - see http://www.gtk.org .
|
||||
|
||||
@@ -131,6 +118,9 @@ 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:
|
||||
|
||||
@@ -149,10 +139,10 @@ to work without any issues.
|
||||
Windows 8.1, Windows 10. The Windows port uses pd0, pd1, etc...
|
||||
for physical drives 0, 1, etc... .
|
||||
|
||||
* Mac OS X.
|
||||
|
||||
* Solaris.
|
||||
|
||||
* Mac OS X - 3rd-party testing by the MacPorts and Fink projects.
|
||||
|
||||
* QNX - Code written but no testing has been performed yet.
|
||||
|
||||
|
||||
@@ -191,9 +181,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 https://www.smartmontools.org/browser/trunk/smartmontools/smartctl.8.in
|
||||
See http://smartmontools.sourceforge.net/man/smartctl.8.html
|
||||
|
||||
Additional information is available at https://www.smartmontools.org/
|
||||
Additional information is available at http://smartmontools.sourceforge.net
|
||||
|
||||
|
||||
|
||||
@@ -202,7 +192,9 @@ 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.
|
||||
similar arguments won't have any effect. However, --verbose will still
|
||||
increase the verbosity of a log saved via "Options -> View Execution Log ->
|
||||
Save All".
|
||||
|
||||
The most important parameters are:
|
||||
|
||||
@@ -327,18 +319,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 (as root, using sudo or su):
|
||||
terminal emulator (e.g., xterm, konsole or gnome-terminal):
|
||||
|
||||
smartctl -x /dev/sda
|
||||
smartctl -a /dev/sda
|
||||
|
||||
Note: If using Windows, the device name should be /dev/pd1 for the second
|
||||
physical drive, etc... . Run cmd as administrator first.
|
||||
physical drive, etc... .
|
||||
|
||||
If you still think it's a GSmartControl issue, please collect the following
|
||||
information about your system. Without it, it may be very hard or impossible
|
||||
to fix the bug.
|
||||
|
||||
* Which operating system you use (for example, openSUSE Leap 42.3).
|
||||
* Which operating system you use (for example, openSUSE Linux 11.4).
|
||||
|
||||
* Which version of GTK and Gtkmm you have installed. Finding this out is very
|
||||
distribution-specific. For example, on openSUSE it would be
|
||||
@@ -349,9 +341,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 -v
|
||||
gsmartcontrol-root auto -v
|
||||
|
||||
Note: On Windows, -v switch is on by default.
|
||||
Note: On Windows, run gsmartcontrol.exe with "-v" switch as Administrator.
|
||||
|
||||
Perform the steps needed to reproduce the bug, then go to
|
||||
"Options -> View Execution Log", and click "Save All".
|
||||
|
||||
@@ -15,39 +15,229 @@ local scope destruction also works if using stack variables.
|
||||
}
|
||||
|
||||
|
||||
A treeview (also iconview, combobox) from glade comes
|
||||
with existing model, which should be unset.
|
||||
|
||||
|
||||
Bugs / patches:
|
||||
Patch pangomm for Pango::strip_markup().
|
||||
|
||||
|
||||
|
||||
--------------------------------------------------------
|
||||
|
||||
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.
|
||||
|
||||
?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).
|
||||
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)
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
+17
-13
@@ -1,5 +1,5 @@
|
||||
|
||||
AC_INIT([GSmartControl],[1.1.0],[],[gsmartcontrol])
|
||||
AC_INIT([GSmartControl],[1.0.2],[],[gsmartcontrol])
|
||||
|
||||
AC_CONFIG_SRCDIR([configure.ac])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
@@ -128,6 +128,9 @@ APP_CXX_EXTERN_C_OVERLOAD
|
||||
# This may be needed for pch support
|
||||
AC_CHECK_HEADERS([stdc++.h])
|
||||
|
||||
# pcrecpp needs this (break on first function found)
|
||||
AC_CHECK_FUNCS([strtoll _strtoi64], [break])
|
||||
|
||||
# Non-C99 systems don't have strtof and strtold. some have strtodf for strtof,
|
||||
# but let's ignore it. Note: They may be supported by system libc,
|
||||
# but not enabled by default (e.g. by glibc feature macros). In that case,
|
||||
@@ -209,27 +212,27 @@ LIBS="$GTKMM_LIBS $LIBS"
|
||||
# -------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
PCRECPP_CFLAGS="";
|
||||
PCRECPP_LIBS="";
|
||||
PCRE_CFLAGS="";
|
||||
PCRE_LIBS="";
|
||||
|
||||
# try pkg-config first
|
||||
PKG_CHECK_MODULES(PCRECPP, [libpcrecpp], [pcrecpp_found=yes], [pcrecpp_found=no])
|
||||
PKG_CHECK_MODULES(PCRE, [libpcre], [pcre_found=yes], [pcre_found=no])
|
||||
|
||||
# if not found, try pcre-config (solaris had it but not pkg-config).
|
||||
if test "x$pcrecpp_found" = "xno"; then
|
||||
if test "x$pcre_found" = "xno"; then
|
||||
AC_PATH_PROG(pcre_config_binary, [pcre-config])
|
||||
if test "x$pcre_config_binary" != "x"; then
|
||||
PCRE_CFLAGS="`pcre-config --cflags`"
|
||||
PCRE_LIBS="`pcre-config --libs-cpp`"
|
||||
PCRE_LIBS="`pcre-config --libs`"
|
||||
else
|
||||
# AC_MSG_ERROR([Could not find pcrecpp through pkg-config and pcre-config. Make sure the pcre and pcrecpp libraries are installed])
|
||||
AC_MSG_ERROR([$PCRECPP_PKG_ERRORS])
|
||||
# AC_MSG_ERROR([Could not find pcre through pkg-config and pcre-config. Make sure the pcre libraries are installed])
|
||||
AC_MSG_ERROR([$PCRE_PKG_ERRORS])
|
||||
fi
|
||||
fi
|
||||
|
||||
CFLAGS="$CFLAGS $PCRECPP_CFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $PCRECPP_CFLAGS"
|
||||
LIBS="$LIBS $PCRECPP_LIBS"
|
||||
CFLAGS="$CFLAGS $PCRE_CFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $PCRE_CFLAGS"
|
||||
LIBS="$LIBS $PCRE_LIBS"
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------------
|
||||
@@ -386,7 +389,8 @@ CXXFLAGS="$CXXFLAGS $ADDITIONAL_FLAGS"
|
||||
# Add global configuration file. Note: It must be searched in top_builddir
|
||||
# first (that's why -I is the first flag), for precompiled headers to work.
|
||||
|
||||
ADDITIONAL_INCLUDES="-I\$(top_builddir) -I\$(top_srcdir)/src"
|
||||
# Add pcrecpp too, because its files use #<file> on each other.
|
||||
ADDITIONAL_INCLUDES="-I\$(top_builddir) -I\$(top_srcdir)/src -I\$(top_srcdir)/src/pcrecpp"
|
||||
|
||||
# -include works with gcc, intel, pathscale and sunstudio since 12u1, but doesn't work
|
||||
# with pgi and older sunstudio (the one in solaris10, for example).
|
||||
@@ -482,7 +486,7 @@ AC_CONFIG_FILES([data/gsmartcontrol-root], [chmod +x data/gsmartcontrol-root])
|
||||
# these are all the makefiles to generate
|
||||
|
||||
AC_CONFIG_FILES([Makefile src/Makefile src/applib/Makefile src/res/Makefile src/hz/Makefile \
|
||||
src/libdebug/Makefile src/rconfig/Makefile src/rmn/Makefile \
|
||||
src/libdebug/Makefile src/pcrecpp/Makefile src/rconfig/Makefile src/rmn/Makefile \
|
||||
data/Makefile data/16/Makefile data/22/Makefile data/24/Makefile data/32/Makefile \
|
||||
data/48/Makefile data/64/Makefile data/128/Makefile data/256/Makefile data/nsis/Makefile \
|
||||
debian.dist/Makefile])
|
||||
|
||||
+1
-5
@@ -44,10 +44,6 @@ desktop_DATA = gsmartcontrol.desktop
|
||||
metainfodir = $(datadir)/metainfo
|
||||
metainfo_DATA = gsmartcontrol.appdata.xml
|
||||
|
||||
# PolKit files
|
||||
polkitdir = $(datadir)/polkit-1/actions
|
||||
polkit_DATA = org.gsmartcontrol.policy
|
||||
|
||||
|
||||
# Application pixmap (fallback icon for desktop files).
|
||||
# Same as 48/gsmartcontrol.png.
|
||||
@@ -57,7 +53,7 @@ pixmaps_DATA = 48/gsmartcontrol.png gsmartcontrol.xpm
|
||||
|
||||
|
||||
# Put everything inside the distribution
|
||||
EXTRA_DIST = $(appdata_DATA) $(polkit_DATA) gsmartcontrol.ico gsmartcontrol.xpm
|
||||
EXTRA_DIST = $(appdata_DATA) gsmartcontrol.ico gsmartcontrol.xpm
|
||||
|
||||
|
||||
bin_SCRIPTS = gsmartcontrol-root
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# Run gsmartcontrol with root, asking for root password first.
|
||||
# export GSMARTCONTROL_SU to override a su command (e.g. "kdesu -c").
|
||||
|
||||
EXEC_BIN="@prefix@/sbin/gsmartcontrol";
|
||||
EXEC_BIN="@prefix@/bin/gsmartcontrol";
|
||||
prog_name="gsmartcontrol"
|
||||
|
||||
|
||||
@@ -71,12 +71,11 @@ fi
|
||||
|
||||
|
||||
# They're basically the same, only the order is different.
|
||||
# pkexec is for PolKit
|
||||
# sux requires xterm to ask for the password.
|
||||
# xdg-su is basically like this script, except worse :)
|
||||
# su-to-root is a debian/ubuntu official method (although gksu is available).
|
||||
gnome_sus="pkexec su-to-root gnomesu gksu kdesu beesu xdg-su sux";
|
||||
kde_sus="pkexec su-to-root kdesu gnomesu gksu beesu xdg-su sux";
|
||||
gnome_sus="su-to-root gnomesu gksu kdesu beesu xdg-su sux";
|
||||
kde_sus="su-to-root kdesu gnomesu gksu beesu xdg-su sux";
|
||||
other_sus="$gnome_sus";
|
||||
|
||||
|
||||
@@ -137,9 +136,6 @@ 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">https://gsmartcontrol.sourceforge.io</url>
|
||||
<url type="homepage">http://gsmartcontrol.sourceforge.net</url>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<caption>Main window</caption>
|
||||
<image>https://gsmartcontrol.sourceforge.io/home/images/main_ok.png</image>
|
||||
<image>http://gsmartcontrol.sourceforge.net/home/images/main_ok.png</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<caption>Drive identity information</caption>
|
||||
<image>https://gsmartcontrol.sourceforge.io/home/images/info_identity.png</image>
|
||||
<image>http://gsmartcontrol.sourceforge.net/home/images/info_identity.png</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<caption>Attribute list of a failing drive</caption>
|
||||
<image>https://gsmartcontrol.sourceforge.io/home/images/info_failing.png</image>
|
||||
<image>http://gsmartcontrol.sourceforge.net/home/images/info_failing.png</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
</component>
|
||||
|
||||
@@ -4,7 +4,7 @@ Version @VERSION@
|
||||
|
||||
The latest version of this distribution, as well as the source code of
|
||||
GSmartControl, can be found at
|
||||
https://gsmartcontrol.sourceforge.io
|
||||
http://gsmartcontrol.sourceforge.net
|
||||
and
|
||||
https://sourceforge.net/projects/gsmartcontrol/files/
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
!define PRODUCT_NAME "GSmartControl"
|
||||
!define PRODUCT_NAME_SMALL "gsmartcontrol"
|
||||
!define PRODUCT_PUBLISHER "Alexander Shaduri"
|
||||
!define PRODUCT_WEB_SITE "https://gsmartcontrol.sourceforge.io"
|
||||
!define PRODUCT_WEB_SITE "http://gsmartcontrol.sourceforge.net"
|
||||
|
||||
;!define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\AppMainExe.exe"
|
||||
!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
<?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>
|
||||
+3
-2
@@ -1,7 +1,7 @@
|
||||
Source: gsmartcontrol
|
||||
Section: utils
|
||||
Priority: extra
|
||||
Homepage: https://gsmartcontrol.sourceforge.io/
|
||||
Homepage: http://gsmartcontrol.sourceforge.net/
|
||||
Maintainer: Alexander Shaduri <ashaduriREMOVETHIS@gmail.com>
|
||||
Build-Depends: debhelper (>= 5), autotools-dev,
|
||||
libpcre3-dev, libgtkmm-3.0-dev (>= 3.4.0)
|
||||
@@ -9,7 +9,8 @@ Standards-Version: 3.7.3
|
||||
|
||||
Package: gsmartcontrol
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, smartmontools (>= 5.43), xterm, menu
|
||||
#Depends: ${shlibs:Depends}, ${misc:Depends}, smartmontools, menu
|
||||
Depends: ${shlibs:Depends}, smartmontools, menu
|
||||
Description: Hard disk drive and SSD health inspection tool
|
||||
GSmartControl is a graphical user interface for smartctl (from smartmontools
|
||||
package), which is a tool for querying and controlling SMART
|
||||
|
||||
@@ -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 <https://gsmartcontrol.sourceforge.io/>
|
||||
It was downloaded from <http://gsmartcontrol.sourceforge.net/>
|
||||
|
||||
Copyright:
|
||||
|
||||
|
||||
+68
-14
@@ -1,30 +1,34 @@
|
||||
|
||||
# This spec file is for openSUSE Build Service.
|
||||
# Supported distributions: openSUSE, Fedora, CentOS, RHEL.
|
||||
|
||||
Name: gsmartcontrol
|
||||
Version: @VERSION@
|
||||
Release: 0
|
||||
Release: 1as
|
||||
License: GPL-2.0 AND GPL-3.0
|
||||
Url: https://gsmartcontrol.sourceforge.io/
|
||||
Url: http://gsmartcontrol.sourceforge.net/
|
||||
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 >= 5.43, polkit, bash, xterm
|
||||
Requires: smartmontools
|
||||
BuildRequires: gcc-c++, libstdc++-devel, pcre-devel, gtkmm3-devel >= 3.4.0
|
||||
BuildRequires: update-desktop-files
|
||||
BuildRequires: fdupes
|
||||
@@ -35,9 +39,16 @@ BuildRequires: fdupes
|
||||
# Fedora, CentOS, RHEL
|
||||
%if 0%{?fedora_version} || 0%{?rhel_version} || 0%{?centos_version}
|
||||
|
||||
Requires: smartmontools >= 5.43, polkit, bash, xterm
|
||||
# I'm not sure about centos and rhel versions
|
||||
Requires: smartmontools
|
||||
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
|
||||
|
||||
|
||||
@@ -86,6 +97,43 @@ 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
|
||||
|
||||
@@ -93,8 +141,12 @@ rm -rf %buildroot
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
|
||||
%attr(0755,root,root) %{_bindir}/gsmartcontrol-root
|
||||
%attr(0755,root,root) %{_sbindir}/gsmartcontrol
|
||||
# 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(0644,root,root) %%config(noreplace) %%{_sysconfdir}/*
|
||||
|
||||
@@ -105,11 +157,13 @@ 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
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
AM_CPPFLAGS = $(all_includes)
|
||||
METASOURCES = AUTO
|
||||
|
||||
SUBDIRS = libdebug hz rmn rconfig applib res
|
||||
SUBDIRS = libdebug hz rmn rconfig pcrecpp applib res
|
||||
|
||||
|
||||
if TARGET_OS_KERNEL_FAMILY_WINDOWS
|
||||
@@ -24,10 +24,10 @@ noinst_DATA = gsc_winres.rc.in gsmartcontrol.exe.manifest.in
|
||||
# endif
|
||||
|
||||
|
||||
sbin_PROGRAMS = gsmartcontrol
|
||||
bin_PROGRAMS = gsmartcontrol
|
||||
|
||||
gsmartcontrol_LDADD = $(top_builddir)/src/applib/libapplib.a \
|
||||
$(top_builddir)/src/libdebug/libdebug.a \
|
||||
$(top_builddir)/src/pcrecpp/libpcrecpp.a $(top_builddir)/src/libdebug/libdebug.a \
|
||||
$(top_builddir)/src/res/libres.a $(WINRES_OBJ)
|
||||
|
||||
noinst_HEADERS = global_macros.h gsc_about_dialog.h gsc_add_device_window.h gsc_executor_error_dialog.h \
|
||||
|
||||
@@ -26,16 +26,16 @@ smartctl_executor_test_LDADD = libapplib.a \
|
||||
|
||||
smartctl_parser_test_SOURCES = smartctl_parser_test.cpp
|
||||
smartctl_parser_test_LDADD = libapplib.a \
|
||||
$(top_builddir)/src/libdebug/libdebug.a
|
||||
$(top_builddir)/src/pcrecpp/libpcrecpp.a $(top_builddir)/src/libdebug/libdebug.a
|
||||
|
||||
storage_detector_test_SOURCES = storage_detector_test.cpp
|
||||
storage_detector_test_LDADD = libapplib.a \
|
||||
$(top_builddir)/src/libdebug/libdebug.a
|
||||
$(top_builddir)/src/pcrecpp/libpcrecpp.a $(top_builddir)/src/libdebug/libdebug.a
|
||||
|
||||
spawn_test_SOURCES = spawn_test.cpp
|
||||
|
||||
# Build them first
|
||||
SUBDIRS=../libdebug
|
||||
SUBDIRS=../libdebug ../pcrecpp
|
||||
|
||||
|
||||
# we don't list them in a separate variable because otherwise kdevelop won't see them.
|
||||
|
||||
@@ -12,11 +12,18 @@
|
||||
#ifndef APP_PCRECPP_H
|
||||
#define APP_PCRECPP_H
|
||||
|
||||
// A wrapper header for pcrecpp
|
||||
// A wrapper header for bundled pcrecpp
|
||||
|
||||
// disable all that declspec noise
|
||||
#ifndef PCRE_STATIC
|
||||
#define PCRE_STATIC
|
||||
#endif
|
||||
|
||||
|
||||
#include "pcrecpp/pcrecpp_internal.h"
|
||||
#include "pcrecpp/pcrecpp.h"
|
||||
|
||||
#include <pcrecpp.h>
|
||||
#include <string>
|
||||
|
||||
#include "hz/debug.h"
|
||||
|
||||
|
||||
|
||||
@@ -375,7 +375,7 @@ void Cmdex::stopped_cleanup()
|
||||
// Called when child exits
|
||||
void Cmdex::on_child_watch_handler(GPid arg_pid, int waitpid_status, gpointer data)
|
||||
{
|
||||
// DBG_FUNCTION_ENTER_MSG;
|
||||
DBG_FUNCTION_ENTER_MSG;
|
||||
Cmdex* self = static_cast<Cmdex*>(data);
|
||||
|
||||
g_timer_stop(self->timer_); // stop the timer
|
||||
@@ -426,7 +426,7 @@ void Cmdex::on_child_watch_handler(GPid arg_pid, int waitpid_status, gpointer da
|
||||
if (self->exited_callback_)
|
||||
self->exited_callback_(self->exited_callback_data_);
|
||||
|
||||
// DBG_FUNCTION_EXIT_MSG;
|
||||
DBG_FUNCTION_EXIT_MSG;
|
||||
}
|
||||
|
||||
|
||||
@@ -436,7 +436,7 @@ void Cmdex::on_child_watch_handler(GPid arg_pid, int waitpid_status, gpointer da
|
||||
gboolean Cmdex::on_channel_io(GIOChannel* channel,
|
||||
GIOCondition cond, Cmdex* self, channel_t type)
|
||||
{
|
||||
// DBG_FUNCTION_ENTER_MSG;
|
||||
DBG_FUNCTION_ENTER_MSG;
|
||||
// debug_out_dump("app", "Cmdex::on_channel_io("
|
||||
// << (type == channel_type_stdout ? "STDOUT" : "STDERR") << ") " << int(cond) << "\n");
|
||||
|
||||
@@ -481,7 +481,7 @@ gboolean Cmdex::on_channel_io(GIOChannel* channel,
|
||||
}
|
||||
} while (g_io_channel_get_buffer_condition(channel) & G_IO_IN);
|
||||
|
||||
// DBG_FUNCTION_EXIT_MSG;
|
||||
DBG_FUNCTION_EXIT_MSG;
|
||||
|
||||
// false if the source should be removed, true otherwise.
|
||||
return continue_events;
|
||||
|
||||
@@ -108,7 +108,7 @@ std::string SelfTest::start(hz::intrusive_ptr<CmdexSync> smartctl_ex)
|
||||
return "Invalid test specified";
|
||||
|
||||
std::string output;
|
||||
std::string error_msg = drive_->execute_device_smartctl("--test=" + test_param, smartctl_ex, output);
|
||||
std::string error_msg = drive_->execute_device_smartctl("-t " + test_param, smartctl_ex, output); // --test=
|
||||
|
||||
if (!error_msg.empty()) // checks for empty output too
|
||||
return error_msg;
|
||||
@@ -168,7 +168,7 @@ std::string SelfTest::force_stop(hz::intrusive_ptr<CmdexSync> smartctl_ex)
|
||||
|
||||
// To abort non-captive short, long and conveyance tests, use "--abort".
|
||||
std::string output;
|
||||
std::string error_msg = drive_->execute_device_smartctl("--abort", smartctl_ex, output);
|
||||
std::string error_msg = drive_->execute_device_smartctl("-X", smartctl_ex, output); // --abort
|
||||
|
||||
if (!error_msg.empty()) // checks for empty output too
|
||||
return error_msg;
|
||||
@@ -207,8 +207,8 @@ std::string SelfTest::update(hz::intrusive_ptr<CmdexSync> smartctl_ex)
|
||||
return "Invalid drive given.";
|
||||
|
||||
std::string output;
|
||||
// std::string error_msg = drive_->execute_device_smartctl("--log=selftest", smartctl_ex, output);
|
||||
std::string error_msg = drive_->execute_device_smartctl("--capabilities", smartctl_ex, output);
|
||||
// std::string error_msg = drive_->execute_device_smartctl("-l selftest", smartctl_ex, output); // --log=
|
||||
std::string error_msg = drive_->execute_device_smartctl("-c", smartctl_ex, output); // --capabilities
|
||||
|
||||
if (!error_msg.empty()) // checks for empty output too
|
||||
return error_msg;
|
||||
|
||||
+74
-852
File diff suppressed because it is too large
Load Diff
@@ -34,7 +34,7 @@ class SmartctlParser {
|
||||
SmartctlParser();
|
||||
|
||||
|
||||
/// Parse full "smartctl -x" output
|
||||
/// Parse full "smartctl -a" output
|
||||
bool parse_full(const std::string& s, StorageAttribute::DiskType disk_type);
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ class SmartctlParser {
|
||||
|
||||
|
||||
/// Check that the version of smartctl output can be parsed with this parser.
|
||||
static bool check_parsed_version(const std::string& version_str, const std::string& version_full_str);
|
||||
static bool check_version(const std::string& version_str, const std::string& version_full_str);
|
||||
|
||||
|
||||
/// Convert e.g. "1,000,204,886,016 bytes" to 1.00 TiB [931.51 GB, 1000204886016 bytes]
|
||||
@@ -57,8 +57,7 @@ class SmartctlParser {
|
||||
bool parse_section(const std::string& header, const std::string& body);
|
||||
|
||||
|
||||
/// Parse the info section (without "===" header).
|
||||
/// This includes --info and --get=all.
|
||||
/// Parse the info section (without "===" header)
|
||||
bool parse_section_info(const std::string& body);
|
||||
|
||||
/// Parse a component (one line) of the info section
|
||||
@@ -72,14 +71,9 @@ class SmartctlParser {
|
||||
bool parse_section_data_subsection_health(const std::string& sub);
|
||||
bool parse_section_data_subsection_capabilities(const std::string& sub);
|
||||
bool parse_section_data_subsection_attributes(const std::string& sub);
|
||||
bool parse_section_data_subsection_directory_log(const std::string& sub);
|
||||
bool parse_section_data_subsection_error_log(const std::string& sub);
|
||||
bool parse_section_data_subsection_selftest_log(const std::string& sub);
|
||||
bool parse_section_data_subsection_selective_selftest_log(const std::string& sub);
|
||||
bool parse_section_data_subsection_scttemp_log(const std::string& sub);
|
||||
bool parse_section_data_subsection_scterc_log(const std::string& sub);
|
||||
bool parse_section_data_subsection_devstat(const std::string& sub);
|
||||
bool parse_section_data_subsection_sataphy(const std::string& sub);
|
||||
|
||||
/// Check the capabilities for internal properties we can use.
|
||||
bool parse_section_data_internal_capabilities(StorageProperty& cap);
|
||||
@@ -138,21 +132,21 @@ class SmartctlParser {
|
||||
void add_property(StorageProperty p);
|
||||
|
||||
|
||||
/// Set "full" data ("smartctl -x" output)
|
||||
/// Set "full" data ("smartctl -a" output)
|
||||
void set_data_full(const std::string& s)
|
||||
{
|
||||
data_full_ = s;
|
||||
}
|
||||
|
||||
|
||||
/// Set "info" section data ("smartctl -i" output, or the first part of "smartctl -x" output)
|
||||
/// Set "info" section data ("smartctl -i" output, or the first part of "smartctl -a" output)
|
||||
void set_data_section_info(const std::string& s)
|
||||
{
|
||||
data_section_info_ = s;
|
||||
}
|
||||
|
||||
|
||||
/// Parse "data" section data (the second part of "smartctl -x" output).
|
||||
/// Parse "data" section data (the second part of "smartctl -a" output).
|
||||
void set_data_section_data(const std::string& s)
|
||||
{
|
||||
data_section_data_ = s;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "libdebug/libdebug.h"
|
||||
#include "hz/debug.h"
|
||||
#include "hz/fs_file.h"
|
||||
#include "storage_property.h"
|
||||
#include "smartctl_parser.h"
|
||||
@@ -33,8 +33,6 @@ int main(int argc, char** argv)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
debug_register_domain("app");
|
||||
|
||||
std::string file_str = argv[1];
|
||||
hz::File file(file_str);
|
||||
|
||||
|
||||
@@ -17,9 +17,6 @@
|
||||
#include <windows.h> // CreateFileA(), CloseHandle(), etc...
|
||||
#include <glibmm.h>
|
||||
#include <set>
|
||||
#include <bitset>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "hz/win32_tools.h"
|
||||
#include "hz/string_sprintf.h"
|
||||
@@ -58,12 +55,6 @@ Newer (added after 5.39.1) smartctl supports --scan-open, which will give us:
|
||||
and remove pd0 from PhysicalDrive-detected list.
|
||||
Running smartctl on sda gives almost the same result as on sda,0.
|
||||
|
||||
smartctl --scan-open output for win32 with 3ware RAID:
|
||||
------------------------------------------------------------------
|
||||
/dev/sda,0 -d ata (opened)
|
||||
/dev/sda,1 -d ata (opened)
|
||||
------------------------------------------------------------------
|
||||
|
||||
|
||||
Intel Matrix RAID (since smartmontools SVN version on 2011-02-04):
|
||||
Call as: "/dev/csmi[0-9],N" where N is the port behind the logical
|
||||
@@ -84,6 +75,12 @@ The drives may be duplicated as pdX (with X and N being unrelated).
|
||||
We filter out pdX devices using serial numbers (unless "-q noserial"
|
||||
is given to smartctl), and prefer csmi to pd (since csmi provides more features).
|
||||
|
||||
|
||||
smartctl --scan-open output for win32 with 3ware RAID:
|
||||
------------------------------------------------------------------
|
||||
/dev/sda,0 -d ata (opened)
|
||||
/dev/sda,1 -d ata (opened)
|
||||
------------------------------------------------------------------
|
||||
</pre>
|
||||
*/
|
||||
|
||||
@@ -91,65 +88,12 @@ The drives may be duplicated as pdX (with X and N being unrelated).
|
||||
namespace {
|
||||
|
||||
|
||||
/// Check which physical drives each drive letter (C, D, ...) spans across.
|
||||
std::map<char, std::set<int> > win32_get_drive_letter_map()
|
||||
{
|
||||
std::map<char, std::set<int> > drive_letter_map;
|
||||
|
||||
std::bitset<32> drives(GetLogicalDrives());
|
||||
|
||||
// Check which drives are fixed
|
||||
std::vector<char> good_drives;
|
||||
for (char c = 'A'; c <= 'Z'; ++c) {
|
||||
if (drives[c - 'A']) { // drive is present
|
||||
debug_out_dump("app", "Windows drive found: " << c << ".\n");
|
||||
if (GetDriveType((c + string(":\\")).c_str()) == DRIVE_FIXED) {
|
||||
debug_out_dump("app", "Windows drive " << c << " is fixed.\n");
|
||||
good_drives.push_back(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to open each drive, check its disk extents
|
||||
for (std::size_t i = 0; i < good_drives.size(); ++i) {
|
||||
char drive = good_drives[i];
|
||||
string drive_str = string("\\\\.\\") + drive + ":";
|
||||
HANDLE h = CreateFileA(
|
||||
drive_str.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_RANDOM_ACCESS, NULL);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
debug_out_warn("app", "Windows drive " << drive << " cannot be opened.\n");
|
||||
continue;
|
||||
}
|
||||
DWORD bytesReturned = 0;
|
||||
VOLUME_DISK_EXTENTS vde;
|
||||
if (!DeviceIoControl(
|
||||
h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
|
||||
NULL, 0, &vde, sizeof(vde), &bytesReturned, NULL)) {
|
||||
debug_out_warn("app", "Windows drive " << drive << " is not mapped to any physical drives.\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
std::set<int> drive_numbers;
|
||||
for (int i = 0; i < vde.NumberOfDiskExtents; ++i) {
|
||||
drive_numbers.insert(vde.Extents[i].DiskNumber);
|
||||
debug_out_dump("app", "Windows drive " << drive << " corresponds to physical drive " << vde.Extents[i].DiskNumber << ".\n");
|
||||
}
|
||||
drive_letter_map[drive] = drive_numbers;
|
||||
}
|
||||
|
||||
return drive_letter_map;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Run "smartctl --scan-open" and pick the devices which have
|
||||
/// a port parameter. We don't pick the others because the may
|
||||
/// conflict with pd* devices, and we like pd* better than sd*.
|
||||
std::string get_scan_open_multiport_devices(std::vector<StorageDeviceRefPtr>& drives,
|
||||
ExecutorFactoryRefPtr ex_factory,
|
||||
const std::map<char, std::set<int> >& drive_letter_map,
|
||||
std::set<int>& equivalent_pds)
|
||||
ExecutorFactoryRefPtr ex_factory, std::set<int>& equivalent_pds)
|
||||
{
|
||||
debug_out_info("app", "Getting multi-port devices through smartctl --scan-open...\n");
|
||||
|
||||
@@ -204,26 +148,14 @@ std::string get_scan_open_multiport_devices(std::vector<StorageDeviceRefPtr>& dr
|
||||
for (std::size_t i = 0; i < lines.size(); ++i) {
|
||||
std::string dev, port_str, type;
|
||||
if (port_re.PartialMatch(hz::string_trim_copy(lines.at(i)), &dev, &port_str, &type)) {
|
||||
std::string sd_letter;
|
||||
int drive_num = -1;
|
||||
if (dev_re.PartialMatch(dev, &sd_letter)) {
|
||||
std::string letter;
|
||||
if (dev_re.PartialMatch(dev, &letter)) {
|
||||
// don't use pd* devices equivalent to these sd* devices.
|
||||
drive_num = sd_letter.at(0) - 'a';
|
||||
equivalent_pds.insert(drive_num);
|
||||
equivalent_pds.insert(letter.at(0) - 'a');
|
||||
}
|
||||
|
||||
std::string full_dev = dev + "," + port_str;
|
||||
StorageDeviceRefPtr drive(new StorageDevice(full_dev, type));
|
||||
|
||||
std::vector<char> drive_letters;
|
||||
for (std::map<char, std::set<int> >::const_iterator iter = drive_letter_map.begin(); iter != drive_letter_map.end(); ++iter) {
|
||||
if (iter->second.count(drive_num) > 0) {
|
||||
drive_letters.push_back(iter->first);
|
||||
}
|
||||
}
|
||||
drive->set_drive_letters(drive_letters);
|
||||
|
||||
drives.push_back(drive);
|
||||
drives.push_back(StorageDeviceRefPtr(new StorageDevice(full_dev, type)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -630,18 +562,12 @@ std::string detect_drives_win32(std::vector<StorageDeviceRefPtr>& drives, Execut
|
||||
std::vector<std::string> error_msgs;
|
||||
std::string error_msg;
|
||||
|
||||
// Construct drive letter map
|
||||
debug_out_info("app", "Checking which drive corresponds to which \\\\.\\PhysicalDriveN device...\n");
|
||||
std::map<char, std::set<int> > drive_letter_map = win32_get_drive_letter_map();
|
||||
|
||||
|
||||
hz::intrusive_ptr<CmdexSync> smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl);
|
||||
|
||||
// Fetch multiport devices using --scan-open.
|
||||
// Note that this may return duplicates (e.g. /dev/sda and /dev/csmi0,0)
|
||||
|
||||
std::set<int> used_pds;
|
||||
error_msg = get_scan_open_multiport_devices(drives, ex_factory, drive_letter_map, used_pds);
|
||||
error_msg = get_scan_open_multiport_devices(drives, ex_factory, used_pds);
|
||||
if (!error_msg.empty()) {
|
||||
error_msgs.push_back(error_msg);
|
||||
}
|
||||
@@ -650,7 +576,7 @@ std::string detect_drives_win32(std::vector<StorageDeviceRefPtr>& drives, Execut
|
||||
bool areca_open_found = false; // whether areca devices were found at --scan-open time.
|
||||
|
||||
// Find out their serial numbers and whether there are Arecas there.
|
||||
std::map<std::string, StorageDeviceRefPtr> serials;
|
||||
std::set<std::string> serials;
|
||||
for (std::size_t i = 0; i < drives.size(); ++i) {
|
||||
std::string local_error = drives.at(i)->fetch_basic_data_and_parse(smartctl_ex);
|
||||
if (!local_error.empty()) {
|
||||
@@ -659,8 +585,7 @@ std::string detect_drives_win32(std::vector<StorageDeviceRefPtr>& drives, Execut
|
||||
}
|
||||
if (!drives.at(i)->get_serial_number().empty()) {
|
||||
// add model as well, who knows, there may be duplicates across vendors
|
||||
std::string drive_serial_id = drives.at(i)->get_model_name() + "_" + drives.at(i)->get_serial_number();
|
||||
serials[drive_serial_id] = drives.at(i);
|
||||
serials.insert(drives.at(i)->get_model_name() + "_" + drives.at(i)->get_serial_number());
|
||||
}
|
||||
|
||||
// See if there are any areca devices. This is not implemented yet by smartctl (as of 6.0),
|
||||
@@ -672,7 +597,7 @@ std::string detect_drives_win32(std::vector<StorageDeviceRefPtr>& drives, Execut
|
||||
}
|
||||
|
||||
|
||||
// Scan PhysicalDriveN entries
|
||||
// Scan PhysicalDrive entries
|
||||
|
||||
debug_out_info("app", "Starting sequential scan of \\\\.\\PhysicalDriveN devices...\n");
|
||||
|
||||
@@ -711,15 +636,6 @@ std::string detect_drives_win32(std::vector<StorageDeviceRefPtr>& drives, Execut
|
||||
|
||||
StorageDeviceRefPtr drive(new StorageDevice(hz::string_sprintf("pd%d", drive_num)));
|
||||
|
||||
std::vector<char> letters;
|
||||
for (std::map<char, std::set<int> >::iterator iter = drive_letter_map.begin(); iter != drive_letter_map.end(); ++iter) {
|
||||
if (iter->second.count(drive_num) > 0) {
|
||||
letters.push_back(iter->first);
|
||||
}
|
||||
}
|
||||
drive->set_drive_letters(letters);
|
||||
debug_out_dump("app", "Drive letters for: " << drive->get_device() << ": " << drive->format_drive_letters() << ".\n");
|
||||
|
||||
// Sometimes, a single physical drive may be accessible from both "/.//PhysicalDriveN"
|
||||
// and "/.//Scsi2" (e.g. pd0 and csmi2,1). Prefer the port-having ones (which is from --scan-open),
|
||||
// they contain more information.
|
||||
@@ -730,14 +646,11 @@ std::string detect_drives_win32(std::vector<StorageDeviceRefPtr>& drives, Execut
|
||||
debug_out_info("app", "Smartctl returned with an error: " << local_error << "\n");
|
||||
// Don't exit, just report it.
|
||||
}
|
||||
|
||||
std::string drive_serial_id = drive->get_model_name() + "_" + drive->get_serial_number();
|
||||
// A serial may be empty if "-q noserial" was given to smartctl.
|
||||
if (!drive->get_serial_number().empty() && serials.count(drive_serial_id) > 0) {
|
||||
if (!drive->get_serial_number().empty()
|
||||
&& serials.count(drive->get_model_name() + "_" + drive->get_serial_number()) > 0) {
|
||||
debug_out_info("app", "Skipping drive due to duplicate S/N: model: \"" << drive->get_model_name()
|
||||
<< "\", S/N: \"" << drive->get_serial_number() << "\".\n");
|
||||
// Copy the drive letters over to previously detected one (since we can't detect drive letters there).
|
||||
serials[drive_serial_id]->set_drive_letters(drive->get_drive_letters());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +162,8 @@ std::string StorageDevice::fetch_basic_data_and_parse(hz::intrusive_ptr<CmdexSyn
|
||||
|
||||
// We don't use "--all" - it may cause really screwed up the output (tests, etc...).
|
||||
// This looks just like "--info" only on non-smart devices.
|
||||
std::string error_msg = execute_device_smartctl("--info --health --capabilities", smartctl_ex, this->info_output_, true); // set type to invalid if needed
|
||||
// --info --health --capabilities
|
||||
std::string error_msg = execute_device_smartctl("-i -H -c", smartctl_ex, this->info_output_, true); // set type to invalid if needed
|
||||
|
||||
// Smartctl 5.39 cvs/svn version defaults to usb type on at least linux and windows.
|
||||
// This means that the old SCSI identify command isn't executed by default,
|
||||
@@ -319,15 +320,15 @@ std::string StorageDevice::fetch_data_and_parse(hz::intrusive_ptr<CmdexSync> sma
|
||||
std::string output;
|
||||
std::string error_msg;
|
||||
|
||||
// instead of -x, we use all the individual options -x encompasses, so that
|
||||
// an addition to default -x output won't affect us.
|
||||
// instead of -a, we use all the individual options -a encompasses, so that
|
||||
// an addition to default -a output won't affect us.
|
||||
if (this->get_type_argument() == "scsi") { // not sure about correctness... FIXME probably fails with RAID/scsi
|
||||
// This doesn't do much yet, but just in case...
|
||||
// SCSI equivalent of -x:
|
||||
error_msg = execute_device_smartctl("--health --info --attributes --log=error --log=selftest --log=background --log=sasphy", smartctl_ex, output);
|
||||
// SCSI equivalent of -a:
|
||||
error_msg = execute_device_smartctl("-H -i -A -l error -l selftest", smartctl_ex, output);
|
||||
} else {
|
||||
// ATA equivalent of -x:
|
||||
error_msg = execute_device_smartctl("--health --info --get=all --capabilities --attributes --format=brief --log=xerror,50,error --log=xselftest,50,selftest --log=selective --log=directory --log=scttemp --log=scterc --log=devstat --log=sataphy",
|
||||
// ATA equivalent of -a:
|
||||
error_msg = execute_device_smartctl("-H -i -c -A -l error -l selftest -l selective",
|
||||
smartctl_ex, output, true); // set type to invalid if needed
|
||||
}
|
||||
// See notes above (in fetch_basic_data_and_parse()).
|
||||
@@ -422,7 +423,8 @@ A mandatory SMART command failed: exiting. To continue, add one or more '-T perm
|
||||
*/
|
||||
|
||||
std::string output;
|
||||
std::string error_msg = execute_device_smartctl((b ? "--smart=on --saveauto=on" : "--smart=off"), smartctl_ex, output);
|
||||
// --smart=on --saveauto=on, --smart=off
|
||||
std::string error_msg = execute_device_smartctl((b ? "-s on -S on" : "-s off"), smartctl_ex, output);
|
||||
if (!error_msg.empty())
|
||||
return error_msg;
|
||||
|
||||
@@ -456,7 +458,8 @@ SMART Automatic Offline Testing Disabled.
|
||||
A mandatory SMART command failed: exiting. To continue, add one or more '-T permissive' options.
|
||||
*/
|
||||
std::string output;
|
||||
std::string error_msg = execute_device_smartctl((b ? "--offlineauto=on" : "--offlineauto=off"), smartctl_ex, output);
|
||||
// --offlineauto=on, --offlineauto=off
|
||||
std::string error_msg = execute_device_smartctl((b ? "-o on" : "-o off"), smartctl_ex, output);
|
||||
if (!error_msg.empty())
|
||||
return error_msg;
|
||||
|
||||
@@ -651,31 +654,6 @@ std::string StorageDevice::get_extra_arguments() const
|
||||
|
||||
|
||||
|
||||
void StorageDevice::set_drive_letters(const std::vector< char >& letters)
|
||||
{
|
||||
drive_letters_ = letters;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const std::vector< char >& StorageDevice::get_drive_letters() const
|
||||
{
|
||||
return drive_letters_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string StorageDevice::format_drive_letters() const
|
||||
{
|
||||
std::vector<std::string> drive_letters_decorated;
|
||||
for (std::size_t i = 0; i < drive_letters_.size(); ++i) {
|
||||
drive_letters_decorated.push_back(std::string() + (char)std::toupper(drive_letters_[i]) + ":");
|
||||
}
|
||||
return hz::string_join(drive_letters_decorated, ", ");
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool StorageDevice::get_is_virtual() const
|
||||
{
|
||||
return is_virtual_;
|
||||
|
||||
@@ -165,16 +165,6 @@ class StorageDevice : public hz::intrusive_ptr_referenced {
|
||||
std::string get_extra_arguments() const;
|
||||
|
||||
|
||||
/// Set windows drive letters for this drive
|
||||
void set_drive_letters(const std::vector<char>& letters);
|
||||
|
||||
/// Get windows drive letters for this drive
|
||||
const std::vector<char>& get_drive_letters() const;
|
||||
|
||||
/// Get comma-separated win32 drive letters (if present)
|
||||
std::string format_drive_letters() const;
|
||||
|
||||
|
||||
/// Get "virtual" status
|
||||
bool get_is_virtual() const;
|
||||
|
||||
@@ -271,12 +261,10 @@ class StorageDevice : public hz::intrusive_ptr_referenced {
|
||||
std::string info_output_; ///< "smartctl --info" output
|
||||
std::string full_output_; ///< "smartctl --all" output
|
||||
|
||||
std::string device_; ///< e.g. /dev/sda or pd0. empty if virtual.
|
||||
std::string device_; ///< e.g. /dev/sda. empty if virtual.
|
||||
std::string type_arg_; ///< Device type (for -d smartctl parameter), as specified when adding the device.
|
||||
std::string extra_args_; ///< Extra parameters for smartctl, as specified when adding the device.
|
||||
|
||||
std::vector<char> drive_letters_; ///< Windows drive letters (if detected)
|
||||
|
||||
bool is_virtual_; ///< If true, then this is not a real device - merely a loaded description of it.
|
||||
std::string virtual_file_; ///< A file (smartctl data) the virtual device was loaded from
|
||||
bool is_manually_added_; ///< StorageDevice doesn't use it, but it's useful for its users.
|
||||
|
||||
+70
-144
@@ -11,15 +11,11 @@
|
||||
|
||||
#include <map>
|
||||
#include <ostream> // not iosfwd - it doesn't work
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <locale>
|
||||
|
||||
#include "hz/string_num.h" // number_to_string
|
||||
#include "hz/stream_cast.h" // stream_cast<>
|
||||
#include "hz/format_unit.h" // format_time_length
|
||||
#include "hz/string_algo.h" // string_join
|
||||
#include "hz/string_num.h" // number_to_string
|
||||
|
||||
#include "storage_property.h"
|
||||
|
||||
@@ -38,20 +34,6 @@ std::ostream& operator<< (std::ostream& os, const StorageCapability& p)
|
||||
|
||||
|
||||
|
||||
std::string StorageAttribute::format_raw_value() const
|
||||
{
|
||||
// If it's fully a number, format it with commas
|
||||
if (hz::number_to_string(raw_value_int) == raw_value) {
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale(""));
|
||||
ss << std::fixed << raw_value_int;
|
||||
return ss.str();
|
||||
}
|
||||
return raw_value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::ostream& operator<< (std::ostream& os, const StorageAttribute& p)
|
||||
{
|
||||
// os << p.name << ": "
|
||||
@@ -60,15 +42,7 @@ std::ostream& operator<< (std::ostream& os, const StorageAttribute& p)
|
||||
} else {
|
||||
os << "-";
|
||||
}
|
||||
os << " (" << p.format_raw_value() << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const StorageStatistic& p)
|
||||
{
|
||||
os << p.value;
|
||||
os << " (" << p.raw_value_int << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
@@ -76,23 +50,22 @@ std::ostream& operator<<(std::ostream& os, const StorageStatistic& p)
|
||||
|
||||
std::string StorageErrorBlock::get_readable_error_types(const std::vector<std::string>& types)
|
||||
{
|
||||
static std::map<std::string, std::string> m;
|
||||
if (m.empty()) {
|
||||
m["ABRT"] = "Command aborted";
|
||||
m["AMNF"] = "Address mark not found";
|
||||
m["CCTO"] = "Command completion timed out";
|
||||
m["EOM"] = "End of media";
|
||||
m["ICRC"] = "Interface CRC error";
|
||||
m["IDNF"] = "Identity not found";
|
||||
m["ILI"] = "(Packet command-set specific)";
|
||||
m["MC"] = "Media changed";
|
||||
m["MCR"] = "Media change request";
|
||||
m["NM"] = "No media";
|
||||
m["obs"] = "Obsolete";
|
||||
m["TK0NF"] = "Track 0 not found";
|
||||
m["UNC"] = "Uncorrectable error in data";
|
||||
m["WP"] = "Media is write protected";
|
||||
}
|
||||
std::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) {
|
||||
@@ -108,44 +81,6 @@ std::string StorageErrorBlock::get_readable_error_types(const std::vector<std::s
|
||||
|
||||
|
||||
|
||||
int StorageErrorBlock::get_warning_level_for_error_type(std::string& type)
|
||||
{
|
||||
static std::map<std::string, StorageProperty::warning_t> m;
|
||||
if (m.empty()) {
|
||||
m["ABRT"] = StorageProperty::warning_none;
|
||||
m["AMNF"] = StorageProperty::warning_alert;
|
||||
m["CCTO"] = StorageProperty::warning_warn;
|
||||
m["EOM"] = StorageProperty::warning_warn;
|
||||
m["ICRC"] = StorageProperty::warning_warn;
|
||||
m["IDNF"] = StorageProperty::warning_alert;
|
||||
m["ILI"] = StorageProperty::warning_notice;
|
||||
m["MC"] = StorageProperty::warning_none;
|
||||
m["MCR"] = StorageProperty::warning_none;
|
||||
m["NM"] = StorageProperty::warning_none;
|
||||
m["obs"] = StorageProperty::warning_none;
|
||||
m["TK0NF"] = StorageProperty::warning_alert;
|
||||
m["UNC"] = StorageProperty::warning_alert;
|
||||
m["WP"] = StorageProperty::warning_none;
|
||||
}
|
||||
|
||||
if (m.find(type) != m.end()) {
|
||||
return int(m[type]);
|
||||
}
|
||||
return StorageProperty::warning_none; // unknown error
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string StorageErrorBlock::format_lifetime_hours() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale(""));
|
||||
ss << std::fixed << lifetime_hours;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::ostream& operator<< (std::ostream& os, const StorageErrorBlock& b)
|
||||
{
|
||||
os << "Error number " << b.error_num << ": "
|
||||
@@ -156,16 +91,6 @@ std::ostream& operator<< (std::ostream& os, const StorageErrorBlock& b)
|
||||
|
||||
|
||||
|
||||
std::string StorageSelftestEntry::format_lifetime_hours() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale(""));
|
||||
ss << std::fixed << lifetime_hours;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::ostream& operator<< (std::ostream& os, const StorageSelftestEntry& b)
|
||||
{
|
||||
os << "Test entry " << b.test_num << ": "
|
||||
@@ -189,38 +114,34 @@ void StorageProperty::dump(std::ostream& os, int internal_offset) const
|
||||
// if (!readable_value.empty())
|
||||
// os << readable_value;
|
||||
|
||||
switch(value_type) {
|
||||
case StorageProperty::value_type_unknown:
|
||||
os << "[empty]";
|
||||
break;
|
||||
case StorageProperty::value_type_string:
|
||||
os << "\"" << value_string << "\"";
|
||||
break;
|
||||
case StorageProperty::value_type_integer:
|
||||
os << value_integer << " [" << reported_value << "]";
|
||||
break;
|
||||
case StorageProperty::value_type_bool:
|
||||
os << value_bool << " [" << reported_value << "]";
|
||||
break;
|
||||
case StorageProperty::value_type_time_length:
|
||||
os << value_time_length << " [" << reported_value << "]";
|
||||
break;
|
||||
case StorageProperty::value_type_capability:
|
||||
os << value_capability;
|
||||
break;
|
||||
case StorageProperty::value_type_attribute:
|
||||
os << value_attribute;
|
||||
break;
|
||||
case StorageProperty::value_type_statistic:
|
||||
os << value_statistic;
|
||||
break;
|
||||
case StorageProperty::value_type_error_block:
|
||||
os << value_error_block;
|
||||
break;
|
||||
case StorageProperty::value_type_selftest_entry:
|
||||
os << value_selftest_entry;
|
||||
break;
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -230,27 +151,32 @@ std::string StorageProperty::format_value(bool add_reported_too) const
|
||||
if (!readable_value.empty())
|
||||
return readable_value;
|
||||
|
||||
switch(value_type) {
|
||||
case StorageProperty::value_type_unknown:
|
||||
return "[unknown]";
|
||||
case StorageProperty::value_type_string:
|
||||
return value_string;
|
||||
case StorageProperty::value_type_integer:
|
||||
return hz::number_to_string(value_integer) + (add_reported_too ? (" [" + reported_value + "]") : "");
|
||||
case StorageProperty::value_type_bool:
|
||||
return std::string(value_bool ? "Yes" : "No") + (add_reported_too ? (" [" + reported_value + "]") : "");
|
||||
case StorageProperty::value_type_time_length:
|
||||
return hz::format_time_length(value_time_length) + (add_reported_too ? (" [" + reported_value + "]") : "");
|
||||
case StorageProperty::value_type_capability:
|
||||
return hz::stream_cast<std::string>(value_capability);
|
||||
case StorageProperty::value_type_attribute:
|
||||
return hz::stream_cast<std::string>(value_attribute);
|
||||
case StorageProperty::value_type_statistic:
|
||||
return hz::stream_cast<std::string>(value_statistic);
|
||||
case StorageProperty::value_type_error_block:
|
||||
return hz::stream_cast<std::string>(value_error_block);
|
||||
case StorageProperty::value_type_selftest_entry:
|
||||
return hz::stream_cast<std::string>(value_selftest_entry);
|
||||
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]";
|
||||
|
||||
@@ -124,12 +124,8 @@ class StorageAttribute {
|
||||
{ }
|
||||
|
||||
|
||||
/// Format raw value with commas (if it's a number)
|
||||
std::string format_raw_value() const;
|
||||
|
||||
|
||||
int32_t id; ///< Attribute ID (most vendors agree on this)
|
||||
std::string flag; ///< "Old" format is "0xXXXX", "brief" format is "PO--C-".
|
||||
std::string flag; ///< Some have it in 0xXXXX format, others in "PO--C-" format (some WD drives?).
|
||||
hz::OptionalValue<uint8_t> value; ///< Normalized value. May be unset ("---").
|
||||
hz::OptionalValue<uint8_t> worst; ///< Worst ever value. May be unset ("---").
|
||||
hz::OptionalValue<uint8_t> threshold; ///< Threshold for normalized value. May be unset ("---").
|
||||
@@ -148,35 +144,6 @@ std::ostream& operator<< (std::ostream& os, const StorageAttribute& p);
|
||||
|
||||
|
||||
|
||||
/// Holds one line of "devstat" subsection
|
||||
class StorageStatistic {
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
StorageStatistic() : is_header(false), value_int(0), page(0), offset(0)
|
||||
{ }
|
||||
|
||||
/// Whether the normalization flag is present
|
||||
bool is_normalized() const
|
||||
{
|
||||
return flags.find('N') != flags.npos;
|
||||
}
|
||||
|
||||
bool is_header; ///< If the line is a header
|
||||
std::string flags; ///< Flags in "NDC" / "---" format
|
||||
std::string value; ///< Value as a string, as presented by smartctl (formatted).
|
||||
int64_t value_int; ///< Same as value, but parsed as int64.
|
||||
int64_t page; ///< Page
|
||||
int64_t offset; ///< Offset in page
|
||||
};
|
||||
|
||||
|
||||
/// Output operator for debug purposes
|
||||
std::ostream& operator<< (std::ostream& os, const StorageStatistic& p);
|
||||
|
||||
|
||||
|
||||
|
||||
/// Holds one error block of "error log" subsection
|
||||
class StorageErrorBlock {
|
||||
public:
|
||||
@@ -188,12 +155,6 @@ class StorageErrorBlock {
|
||||
/// Get readable error types from reported types
|
||||
static std::string get_readable_error_types(const std::vector<std::string>& types);
|
||||
|
||||
/// Get warning level (warning_t) for an error type
|
||||
static int get_warning_level_for_error_type(std::string& type);
|
||||
|
||||
/// Format lifetime hours with comma
|
||||
std::string format_lifetime_hours() const;
|
||||
|
||||
uint32_t error_num; ///< Error number
|
||||
uint32_t lifetime_hours; ///< When the error occurred (in lifetime hours)
|
||||
std::string device_state; ///< Device state during the error - "active or idle", standby, etc...
|
||||
@@ -288,17 +249,13 @@ class StorageSelftestEntry {
|
||||
}
|
||||
|
||||
|
||||
/// Format lifetime hours with comma
|
||||
std::string format_lifetime_hours() const;
|
||||
|
||||
|
||||
uint32_t test_num; ///< Test number. always starts from 1. larger means older or newer, depending on model. 0 for capability.
|
||||
std::string type; ///< Extended offline, Short offline, Conveyance offline, etc... . capability: unused.
|
||||
std::string status_str; ///< Self-test routine in progress, Completed without error, etc... (as reported by log or capability)
|
||||
status_t status; ///< same as status_str, but from enum
|
||||
int8_t remaining_percent; ///< Remaining %. 0% for completed, 90% for started. -1 if n/a.
|
||||
uint32_t lifetime_hours; ///< When the test happened (in lifetime hours). capability: unused.
|
||||
std::string lba_of_first_error; ///< LBA of the first error. "-" or value (format? usually hex). capability: unused.
|
||||
std::string lba_of_first_error; ///< LBA of the first error. "-" or value (format?). capability: unused.
|
||||
};
|
||||
|
||||
|
||||
@@ -322,7 +279,6 @@ class StorageProperty {
|
||||
|
||||
value_type_capability, ///< For "capabilities" subsection (non-time-interval blocks only)
|
||||
value_type_attribute, ///< For "attributes" subsection
|
||||
value_type_statistic, ///< For "devstat" subsection
|
||||
value_type_error_block, // For "error_log" subsection
|
||||
value_type_selftest_entry // For "selftest_log" subsection
|
||||
};
|
||||
@@ -338,7 +294,6 @@ class StorageProperty {
|
||||
case value_type_time_length: return "time_length";
|
||||
case value_type_capability: return "capability";
|
||||
case value_type_attribute: return "attribute";
|
||||
case value_type_statistic: return "statistic";
|
||||
case value_type_error_block: return "error_block";
|
||||
case value_type_selftest_entry: return "selftest_entry";
|
||||
}
|
||||
@@ -373,14 +328,9 @@ class StorageProperty {
|
||||
subsection_health, ///< Overall-health (-H, --health)
|
||||
subsection_capabilities, ///< General SMART Values, aka Capabilities (-c, --capabilities)
|
||||
subsection_attributes, ///< Attributes (-A, --attributes). These need decoding.
|
||||
subsection_devstat, ///< Device statistics (--log=devstat). These need decoding.
|
||||
subsection_error_log, ///< Error Log (--log=error)
|
||||
subsection_selftest_log, ///< Self-test log (--log=selftest)
|
||||
subsection_selective_selftest_log, ///< Selective self-test log (--log=selective)
|
||||
subsection_temperature_log, ///< SCT temperature (current and history) (--log=scttemp)
|
||||
subsection_erc_log, ///< SCT Error Recovery Control settings (--log=scterc)
|
||||
subsection_phy_log, ///< Phy log (--log=sataphy)
|
||||
subsection_directory_log, ///< Directory log (--log=directory)
|
||||
subsection_error_log, ///< Error Log (-l error)
|
||||
subsection_selftest_log, ///< Self-test log (-l selftest)
|
||||
subsection_selective_selftest_log ///< Selective self-test log and settings
|
||||
};
|
||||
|
||||
/// Get displayable subsection type name
|
||||
@@ -391,14 +341,9 @@ class StorageProperty {
|
||||
case subsection_health: return "health";
|
||||
case subsection_capabilities: return "capabilities";
|
||||
case subsection_attributes: return "attributes";
|
||||
case subsection_devstat: return "devstat";
|
||||
case subsection_error_log: return "error_log";
|
||||
case subsection_selftest_log: return "selftest_log";
|
||||
case subsection_selective_selftest_log: return "selective_selftest_log";
|
||||
case subsection_temperature_log: return "temperature_log";
|
||||
case subsection_erc_log: return "erc_log";
|
||||
case subsection_phy_log: return "phy_log";
|
||||
case subsection_directory_log: return "directory_log";
|
||||
}
|
||||
return "[error]";
|
||||
}
|
||||
@@ -463,6 +408,7 @@ class StorageProperty {
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string reported_name; ///< Property name as reported by smartctl.
|
||||
std::string generic_name; ///< Generic (internal) name. May be same as reported_name, or something more program-identifiable.
|
||||
std::string readable_name; ///< Readable property name. May be same as reported_name, or something more user-readable. Possibly translatable.
|
||||
@@ -490,7 +436,6 @@ class StorageProperty {
|
||||
|
||||
StorageCapability value_capability; ///< Value (if it's a capability)
|
||||
StorageAttribute value_attribute; ///< Value (if it's an attribute)
|
||||
StorageStatistic value_statistic; ///< Value (if it's a statistic from devstat)
|
||||
StorageErrorBlock value_error_block; ///< Value (if it's a error block)
|
||||
StorageSelftestEntry value_selftest_entry; ///< Value (if it's a self-test entry)
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -35,6 +35,11 @@ Additionally, it may be included through compiler's "-include" option
|
||||
|
||||
// this is either defined to 1 (by autoconf), or undefined
|
||||
#ifdef HAVE_LONG_LONG_INT
|
||||
// pcrecpp uses this
|
||||
#ifndef HAVE_LONG_LONG
|
||||
#define HAVE_LONG_LONG
|
||||
#endif
|
||||
|
||||
#define DISABLE_LL_INT 0
|
||||
|
||||
#else // there is no long long int
|
||||
@@ -46,6 +51,11 @@ Additionally, it may be included through compiler's "-include" option
|
||||
|
||||
// this is either defined to 1 (by autoconf), or undefined
|
||||
#ifdef HAVE_UNSIGNED_LONG_LONG_INT
|
||||
// pcrecpp uses this
|
||||
#ifndef HAVE_UNSIGNED_LONG_LONG
|
||||
#define HAVE_UNSIGNED_LONG_LONG
|
||||
#endif
|
||||
|
||||
#define DISABLE_ULL_INT 0
|
||||
|
||||
#else // there is no long long int
|
||||
|
||||
@@ -40,7 +40,7 @@ GscAboutDialog::GscAboutDialog(BaseObjectType* gtkcobj, const app_ui_res_ref_t&
|
||||
set_version(VERSION);
|
||||
|
||||
// set these properties here (after setting hooks) to make the links work.
|
||||
set_website("https://gsmartcontrol.sourceforge.io/");
|
||||
set_website("http://gsmartcontrol.sourceforge.net/");
|
||||
|
||||
set_license(LicenseTextResData().get_string());
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "applib/app_gtkmm_utils.h" // app_gtkmm_create_tree_view_column
|
||||
#include "applib/app_gtkmm_features.h"
|
||||
#include "hz/scoped_ptr.h"
|
||||
#include "rconfig/rconfig_mini.h"
|
||||
|
||||
#include "gsc_executor_log_window.h"
|
||||
#include "gsc_init.h" // app_get_debug_buffer_str()
|
||||
@@ -193,33 +192,17 @@ void GscExecutorLogWindow::on_window_save_current_button_clicked()
|
||||
CmdexSyncCommandInfoRefPtr entry = (*iter)[col_entry];
|
||||
|
||||
static std::string last_dir;
|
||||
if (last_dir.empty()) {
|
||||
rconfig::get_data("gui/drive_data_open_save_dir", last_dir);
|
||||
}
|
||||
int result = 0;
|
||||
|
||||
Glib::RefPtr<Gtk::FileFilter> specific_filter = Gtk::FileFilter::create();
|
||||
specific_filter->set_name("Text Files");
|
||||
specific_filter->add_pattern("*.txt");
|
||||
|
||||
Glib::RefPtr<Gtk::FileFilter> all_filter = Gtk::FileFilter::create();
|
||||
all_filter->set_name("All Files");
|
||||
all_filter->add_pattern("*");
|
||||
|
||||
#if GTK_CHECK_VERSION(3, 20, 0)
|
||||
hz::scoped_ptr<GtkFileChooserNative> dialog(gtk_file_chooser_native_new(
|
||||
"Save Data As...", this->gobj(), GTK_FILE_CHOOSER_ACTION_SAVE, NULL, NULL), g_object_unref);
|
||||
|
||||
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog.get()), true);
|
||||
|
||||
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog.get()), specific_filter->gobj());
|
||||
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog.get()), all_filter->gobj());
|
||||
|
||||
if (!last_dir.empty())
|
||||
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog.get()), last_dir.c_str());
|
||||
|
||||
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog.get()), ".txt");
|
||||
|
||||
result = gtk_native_dialog_run(GTK_NATIVE_DIALOG(dialog.get()));
|
||||
|
||||
#else
|
||||
@@ -232,14 +215,9 @@ void GscExecutorLogWindow::on_window_save_current_button_clicked()
|
||||
|
||||
dialog.set_do_overwrite_confirmation(true);
|
||||
|
||||
dialog.add_filter(specific_filter);
|
||||
dialog.add_filter(all_filter);
|
||||
|
||||
if (!last_dir.empty())
|
||||
dialog.set_current_folder(last_dir);
|
||||
|
||||
dialog.set_current_name(".txt");
|
||||
|
||||
// Show the dialog and wait for a user response
|
||||
result = dialog.run(); // the main cycle blocks here
|
||||
#endif
|
||||
@@ -256,11 +234,6 @@ void GscExecutorLogWindow::on_window_save_current_button_clicked()
|
||||
file = dialog.get_filename(); // in fs encoding
|
||||
last_dir = dialog.get_current_folder(); // save for the future
|
||||
#endif
|
||||
rconfig::set_data("gui/drive_data_open_save_dir", last_dir);
|
||||
|
||||
if (file.rfind(".txt") != (file.size() - std::strlen(".txt"))) {
|
||||
file += ".txt";
|
||||
}
|
||||
|
||||
hz::File f(file);
|
||||
if (!f.put_contents(entry->std_output)) {
|
||||
@@ -310,33 +283,17 @@ void GscExecutorLogWindow::on_window_save_all_button_clicked()
|
||||
|
||||
|
||||
static std::string last_dir;
|
||||
if (last_dir.empty()) {
|
||||
rconfig::get_data("gui/drive_data_open_save_dir", last_dir);
|
||||
}
|
||||
int result = 0;
|
||||
|
||||
Glib::RefPtr<Gtk::FileFilter> specific_filter = Gtk::FileFilter::create();
|
||||
specific_filter->set_name("Text Files");
|
||||
specific_filter->add_pattern("*.txt");
|
||||
|
||||
Glib::RefPtr<Gtk::FileFilter> all_filter = Gtk::FileFilter::create();
|
||||
all_filter->set_name("All Files");
|
||||
all_filter->add_pattern("*");
|
||||
|
||||
#if GTK_CHECK_VERSION(3, 20, 0)
|
||||
hz::scoped_ptr<GtkFileChooserNative> dialog(gtk_file_chooser_native_new(
|
||||
"Save Data As...", this->gobj(), GTK_FILE_CHOOSER_ACTION_SAVE, NULL, NULL), g_object_unref);
|
||||
|
||||
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog.get()), true);
|
||||
|
||||
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog.get()), specific_filter->gobj());
|
||||
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog.get()), all_filter->gobj());
|
||||
|
||||
if (!last_dir.empty())
|
||||
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog.get()), last_dir.c_str());
|
||||
|
||||
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog.get()), ".txt");
|
||||
|
||||
result = gtk_native_dialog_run(GTK_NATIVE_DIALOG(dialog.get()));
|
||||
|
||||
#else
|
||||
@@ -349,14 +306,9 @@ void GscExecutorLogWindow::on_window_save_all_button_clicked()
|
||||
|
||||
dialog.set_do_overwrite_confirmation(true);
|
||||
|
||||
dialog.add_filter(specific_filter);
|
||||
dialog.add_filter(all_filter);
|
||||
|
||||
if (!last_dir.empty())
|
||||
dialog.set_current_folder(last_dir);
|
||||
|
||||
dialog.set_current_name(".txt");
|
||||
|
||||
// Show the dialog and wait for a user response
|
||||
result = dialog.run(); // the main cycle blocks here
|
||||
#endif
|
||||
@@ -373,11 +325,6 @@ void GscExecutorLogWindow::on_window_save_all_button_clicked()
|
||||
file = dialog.get_filename(); // in fs encoding
|
||||
last_dir = dialog.get_current_folder(); // save for the future
|
||||
#endif
|
||||
rconfig::set_data("gui/drive_data_open_save_dir", last_dir);
|
||||
|
||||
if (file.rfind(".txt") != (file.size() - std::strlen(".txt"))) {
|
||||
file += ".txt";
|
||||
}
|
||||
|
||||
hz::File f(file);
|
||||
if (!f.put_contents(exss.str())) {
|
||||
|
||||
+448
-990
File diff suppressed because it is too large
Load Diff
+7
-25
@@ -13,7 +13,6 @@
|
||||
#define GSC_INFO_WINDOW_H
|
||||
|
||||
#include <gtkmm.h>
|
||||
#include <map>
|
||||
|
||||
#include "applib/app_ui_res_utils.h"
|
||||
#include "applib/storage_device.h"
|
||||
@@ -35,10 +34,8 @@ class GscInfoWindow : public AppUIResWidget<GscInfoWindow, true> {
|
||||
GscInfoWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui);
|
||||
|
||||
/// Virtual destructor
|
||||
virtual ~GscInfoWindow();
|
||||
|
||||
// Overridden from InstanceManager
|
||||
virtual void obj_destroy();
|
||||
virtual ~GscInfoWindow()
|
||||
{ }
|
||||
|
||||
|
||||
/// Set the drive to show
|
||||
@@ -100,15 +97,9 @@ class GscInfoWindow : public AppUIResWidget<GscInfoWindow, true> {
|
||||
void on_test_stop_button_clicked();
|
||||
|
||||
|
||||
/// Callback attached to StorageDevice change signal.
|
||||
// Callback attached to StorageDevice change signal.
|
||||
void on_drive_changed(StorageDevice* pdrive);
|
||||
|
||||
/// Callback
|
||||
bool on_treeview_button_press_event(GdkEventButton* button_event, Gtk::Menu* menu, Gtk::TreeView* treeview);
|
||||
|
||||
/// Callback
|
||||
void on_treeview_menu_copy_clicked(Gtk::TreeView* treeview);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
@@ -123,22 +114,13 @@ class GscInfoWindow : public AppUIResWidget<GscInfoWindow, true> {
|
||||
|
||||
// --------- Data members
|
||||
|
||||
std::map<std::string, Gtk::Menu*> treeview_menus; ///< Context menus
|
||||
|
||||
// tab headers, to perform their coloration
|
||||
Glib::ustring tab_identity_name; ///< Tab header name
|
||||
Glib::ustring tab_attributes_name; ///< Tab header name
|
||||
Glib::ustring tab_statistics_name; ///< Tab header name
|
||||
Glib::ustring tab_test_name; ///< Tab header name
|
||||
Glib::ustring tab_error_log_name; ///< Tab header name
|
||||
Glib::ustring tab_temperature_name; ///< Tab header name
|
||||
Glib::ustring tab_advanced_name; ///< Tab header name
|
||||
|
||||
Glib::ustring tab_capabilities_name; ///< Tab header name
|
||||
Glib::ustring tab_erc_name; ///< Tab header name
|
||||
Glib::ustring tab_selective_selftest_name; ///< Tab header name
|
||||
Glib::ustring tab_phy_name; ///< Tab header name
|
||||
Glib::ustring tab_directory_name; ///< Tab header name
|
||||
Glib::ustring tab_attributes_name; ///< Tab header name
|
||||
Glib::ustring tab_error_log_name; ///< Tab header name
|
||||
Glib::ustring tab_selftest_log_name; ///< Tab header name
|
||||
Glib::ustring tab_test_name; ///< Tab header name
|
||||
|
||||
Gtk::Label* device_name_label; ///< Top label
|
||||
|
||||
|
||||
@@ -512,6 +512,7 @@ bool app_init_and_loop(int& argc, char**& argv)
|
||||
m.run();
|
||||
debug_out_info("app", "Main loop exited.\n");
|
||||
|
||||
|
||||
// close the main window and delete its object
|
||||
GscMainWindow::destroy();
|
||||
|
||||
|
||||
+23
-146
@@ -56,29 +56,9 @@ GscMainWindow::GscMainWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& re
|
||||
// iconview, gtkuimanager stuff (menus), custom labels
|
||||
create_widgets();
|
||||
|
||||
// Size
|
||||
{
|
||||
int def_size_w = 0, def_size_h = 0;
|
||||
rconfig::get_data("gui/main_window/default_size_w", def_size_w);
|
||||
rconfig::get_data("gui/main_window/default_size_h", def_size_h);
|
||||
if (def_size_w > 0 && def_size_h > 0) {
|
||||
set_default_size(def_size_w, def_size_h);
|
||||
}
|
||||
}
|
||||
|
||||
// show the window first, scan later
|
||||
show();
|
||||
|
||||
// Position (after the window has been shown)
|
||||
{
|
||||
int pos_x = 0, pos_y = 0;
|
||||
rconfig::get_data("gui/main_window/default_pos_x", pos_x);
|
||||
rconfig::get_data("gui/main_window/default_pos_y", pos_y);
|
||||
if (pos_x > 0 && pos_y > 0) { // to avoid situations where positions are not supported
|
||||
this->move(pos_x, pos_y);
|
||||
}
|
||||
}
|
||||
|
||||
while (Gtk::Main::events_pending()) // allow the window to show
|
||||
Gtk::Main::iteration();
|
||||
|
||||
@@ -129,18 +109,6 @@ GscMainWindow::GscMainWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& re
|
||||
break;
|
||||
}
|
||||
|
||||
{
|
||||
// We require this version at runtime to support --get=all.
|
||||
const double minimum_req_version = 5.43;
|
||||
double version_double = 0;
|
||||
if (hz::string_is_numeric<double>(version, version_double, false)) {
|
||||
if (version_double < minimum_req_version) {
|
||||
error_msg = "Smartctl version " + version + " found, " + hz::number_to_string(minimum_req_version) + " required.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} while (false);
|
||||
|
||||
bool smartctl_valid = error_msg.empty();
|
||||
@@ -262,7 +230,6 @@ bool GscMainWindow::create_widgets()
|
||||
|
||||
" <menu action='options_menu'>"
|
||||
" <menuitem action='" APP_ACTION_NAME(action_executor_log) "' />"
|
||||
" <menuitem action='" APP_ACTION_NAME(action_update_drivedb) "' />"
|
||||
" <menuitem action='" APP_ACTION_NAME(action_preferences) "' />"
|
||||
" </menu>"
|
||||
|
||||
@@ -370,10 +337,6 @@ bool GscMainWindow::create_widgets()
|
||||
actiongroup_main->add((action_map[action_executor_log] = action),
|
||||
sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_executor_log));
|
||||
|
||||
action = Gtk::Action::create(APP_ACTION_NAME(action_update_drivedb), "Update Drive Database");
|
||||
actiongroup_main->add((action_map[action_update_drivedb] = action),
|
||||
sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_update_drivedb));
|
||||
|
||||
action = Gtk::Action::create(APP_ACTION_NAME(action_preferences), Gtk::Stock::PREFERENCES);
|
||||
actiongroup_main->add((action_map[action_preferences] = action), Gtk::AccelKey("<alt>P"),
|
||||
sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_preferences));
|
||||
@@ -539,7 +502,13 @@ void GscMainWindow::on_action_activated(GscMainWindow::action_t action_type)
|
||||
|
||||
switch (action_type) {
|
||||
case action_quit:
|
||||
quit_requested();
|
||||
// if at least one drive is having a test performed, disallow.
|
||||
if (this->testing_active()) {
|
||||
if (!ask_about_quit_on_test(*this)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
app_quit(); // ends the main loop
|
||||
break;
|
||||
|
||||
case action_view_details:
|
||||
@@ -607,12 +576,6 @@ void GscMainWindow::on_action_activated(GscMainWindow::action_t action_type)
|
||||
break;
|
||||
}
|
||||
|
||||
case action_update_drivedb:
|
||||
{
|
||||
run_update_drivedb();
|
||||
break;
|
||||
}
|
||||
|
||||
case action_preferences:
|
||||
{
|
||||
GscPreferencesWindow* win = GscPreferencesWindow::create(); // destroyed on close
|
||||
@@ -987,19 +950,16 @@ void GscMainWindow::update_status_widgets()
|
||||
return;
|
||||
}
|
||||
|
||||
std::string device = Glib::Markup::escape_text(drive->get_is_virtual() ? ("Virtual: " + drive->get_virtual_filename()) : drive->get_device_with_type());
|
||||
std::string size = Glib::Markup::escape_text(drive->get_device_size_str());
|
||||
std::string model = Glib::Markup::escape_text(drive->get_model_name().empty() ? std::string("Unknown model") : drive->get_model_name());
|
||||
std::string family = Glib::Markup::escape_text(drive->get_family_name().empty() ? "Unknown" : drive->get_family_name());
|
||||
std::string family_fallback = Glib::Markup::escape_text(drive->get_family_name().empty() ? model : drive->get_family_name());
|
||||
std::string drive_letters_str = Glib::Markup::escape_text(drive->format_drive_letters());
|
||||
std::string device = (drive->get_is_virtual() ? ("Virtual: " + drive->get_virtual_filename()) : drive->get_device_with_type());
|
||||
std::string size = drive->get_device_size_str();
|
||||
std::string model = (drive->get_model_name().empty() ? std::string("Unknown model") : drive->get_model_name());
|
||||
std::string family = (drive->get_family_name().empty() ? "Unknown" : drive->get_family_name());
|
||||
std::string family_fallback = (drive->get_family_name().empty() ? model : drive->get_family_name());
|
||||
|
||||
std::string info_str = device
|
||||
+ (drive_letters_str.empty() ? "" : (" (<b>" + drive_letters_str + "</b>)"))
|
||||
+ (size.empty() ? "" : (", " + size))
|
||||
+ (model.empty() ? "" : (", " + model));
|
||||
|
||||
std::string info_str = device + (size.empty() ? "" : (", " + size)) + (model.empty() ? "" : (", " + model));
|
||||
if (name_label) {
|
||||
name_label->set_markup(info_str);
|
||||
name_label->set_text(info_str);
|
||||
app_gtkmm_set_widget_tooltip(*name_label, info_str, false); // in case it doesn't fit
|
||||
}
|
||||
|
||||
@@ -1136,35 +1096,6 @@ void GscMainWindow::rescan_devices()
|
||||
|
||||
|
||||
|
||||
void GscMainWindow::run_update_drivedb()
|
||||
{
|
||||
std::string smartctl_binary = get_smartctl_binary();
|
||||
|
||||
if (smartctl_binary.empty()) {
|
||||
gui_show_error_dialog("Error Updating Drive Database", "Smartctl binary is not specified in configuration.", this);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string update_binary;
|
||||
hz::FsPath path(smartctl_binary);
|
||||
if (path.is_absolute()) {
|
||||
update_binary = path.get_dirname() + "/";
|
||||
}
|
||||
update_binary += "update-smart-drivedb";
|
||||
update_binary = Glib::shell_quote(update_binary);
|
||||
|
||||
#ifndef _WIN32
|
||||
update_binary = "xterm -hold -e " + update_binary;
|
||||
#endif
|
||||
|
||||
hz::scoped_ptr<GError> spawn_error(0, g_error_free);
|
||||
if (!g_spawn_command_line_async(update_binary.c_str(), &spawn_error.get_ref())) {
|
||||
gui_show_error_dialog("Error Updating Drive Database", spawn_error->message, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool GscMainWindow::add_device(const std::string& file, const std::string& type_arg, const std::string& extra_args)
|
||||
{
|
||||
#ifndef _WIN32 // win32 doesn't have device files, so skip the check
|
||||
@@ -1346,28 +1277,12 @@ void GscMainWindow::show_add_device_chooser()
|
||||
void GscMainWindow::show_load_virtual_file_chooser()
|
||||
{
|
||||
static std::string last_dir;
|
||||
if (last_dir.empty()) {
|
||||
rconfig::get_data("gui/drive_data_open_save_dir", last_dir);
|
||||
}
|
||||
int result = 0;
|
||||
|
||||
Glib::RefPtr<Gtk::FileFilter> specific_filter = Gtk::FileFilter::create();
|
||||
specific_filter->set_name("Text Files");
|
||||
specific_filter->add_pattern("*.txt");
|
||||
|
||||
Glib::RefPtr<Gtk::FileFilter> all_filter = Gtk::FileFilter::create();
|
||||
all_filter->set_name("All Files");
|
||||
all_filter->add_pattern("*");
|
||||
|
||||
#if GTK_CHECK_VERSION(3, 20, 0)
|
||||
hz::scoped_ptr<GtkFileChooserNative> dialog(gtk_file_chooser_native_new(
|
||||
"Load Data From...", this->gobj(), GTK_FILE_CHOOSER_ACTION_OPEN, NULL, NULL), g_object_unref);
|
||||
|
||||
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog.get()), specific_filter->gobj());
|
||||
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog.get()), all_filter->gobj());
|
||||
|
||||
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog.get()), true);
|
||||
|
||||
if (!last_dir.empty()) {
|
||||
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog.get()), last_dir.c_str());
|
||||
}
|
||||
@@ -1382,11 +1297,6 @@ void GscMainWindow::show_load_virtual_file_chooser()
|
||||
dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
|
||||
dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT);
|
||||
|
||||
dialog.add_filter(specific_filter);
|
||||
dialog.add_filter(all_filter);
|
||||
|
||||
dialog.set_select_multiple(true);
|
||||
|
||||
if (!last_dir.empty())
|
||||
dialog.set_current_folder(last_dir);
|
||||
|
||||
@@ -1398,27 +1308,16 @@ void GscMainWindow::show_load_virtual_file_chooser()
|
||||
switch (result) {
|
||||
case Gtk::RESPONSE_ACCEPT:
|
||||
{
|
||||
std::vector<std::string> files;
|
||||
std::string file;
|
||||
|
||||
#if GTK_CHECK_VERSION(3, 20, 0)
|
||||
hz::scoped_ptr<GSList> file_slist(gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog.get())), g_slist_free);
|
||||
GSList* iterator = file_slist.get();
|
||||
while(iterator) {
|
||||
files.push_back(app_ustring_from_gchar((gchar*)iterator->data));
|
||||
iterator = g_slist_next(iterator);
|
||||
}
|
||||
file = app_ustring_from_gchar(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog.get())));
|
||||
last_dir = hz::path_get_dirname(file);
|
||||
#else
|
||||
files = dialog.get_filenames(); // in fs encoding
|
||||
file = dialog.get_filename(); // in fs encoding
|
||||
last_dir = dialog.get_current_folder(); // save for the future
|
||||
#endif
|
||||
if (!files.empty()) {
|
||||
last_dir = hz::path_get_dirname(files.front());
|
||||
}
|
||||
rconfig::set_data("gui/drive_data_open_save_dir", last_dir);
|
||||
for (size_t i = 0; i < files.size(); ++i) {
|
||||
if (hz::File(files[i]).is_file()) { // file chooser returns selected directories as well, ignore them.
|
||||
this->add_virtual_drive(files[i]);
|
||||
}
|
||||
}
|
||||
this->add_virtual_drive(file);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1434,7 +1333,8 @@ void GscMainWindow::show_load_virtual_file_chooser()
|
||||
|
||||
|
||||
|
||||
bool GscMainWindow::quit_requested()
|
||||
// by default, delete_event calls hide().
|
||||
bool GscMainWindow::on_delete_event_before(GdkEventAny* e)
|
||||
{
|
||||
// if at least one drive is having a test performed, disallow.
|
||||
if (this->testing_active()) {
|
||||
@@ -1442,35 +1342,12 @@ bool GscMainWindow::quit_requested()
|
||||
return true; // handled
|
||||
}
|
||||
}
|
||||
|
||||
// window size / pos
|
||||
{
|
||||
int window_w = 0, window_h = 0;
|
||||
get_size(window_w, window_h);
|
||||
rconfig::set_data("gui/main_window/default_size_w", window_w);
|
||||
rconfig::set_data("gui/main_window/default_size_h", window_h);
|
||||
|
||||
int pos_x = 0, pos_y = 0;
|
||||
get_position(pos_x, pos_y);
|
||||
rconfig::set_data("gui/main_window/default_pos_x", pos_x);
|
||||
rconfig::set_data("gui/main_window/default_pos_y", pos_y);
|
||||
}
|
||||
|
||||
app_quit(); // ends the main loop
|
||||
|
||||
return true; // event handled, don't call default virtual handler
|
||||
}
|
||||
|
||||
|
||||
|
||||
// by default, delete_event calls hide().
|
||||
bool GscMainWindow::on_delete_event_before(GdkEventAny* e)
|
||||
{
|
||||
return quit_requested();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -53,10 +53,6 @@ class GscMainWindow : public AppUIResWidget<GscMainWindow, false> {
|
||||
void rescan_devices();
|
||||
|
||||
|
||||
/// Execute update-smart-drivedb
|
||||
void run_update_drivedb();
|
||||
|
||||
|
||||
/// Manually add device file to icon list
|
||||
bool add_device(const std::string& file, const std::string& type_arg, const std::string& extra_args);
|
||||
|
||||
@@ -94,7 +90,6 @@ class GscMainWindow : public AppUIResWidget<GscMainWindow, false> {
|
||||
action_rescan_devices,
|
||||
|
||||
action_executor_log,
|
||||
action_update_drivedb,
|
||||
action_preferences,
|
||||
|
||||
action_general_help,
|
||||
@@ -128,10 +123,6 @@ class GscMainWindow : public AppUIResWidget<GscMainWindow, false> {
|
||||
void show_load_virtual_file_chooser();
|
||||
|
||||
|
||||
/// Called when quit has been requested (by delete event or Quit action)
|
||||
bool quit_requested();
|
||||
|
||||
|
||||
// -------------------- callbacks
|
||||
|
||||
// ---------- overriden virtual methods
|
||||
|
||||
@@ -247,7 +247,6 @@ class GscMainWindowIconView : public Gtk::IconView {
|
||||
if (rconfig::get_data<bool>("gui/icons_show_device_name")) {
|
||||
if (!drive->get_is_virtual()) {
|
||||
name += "\n" + Glib::Markup::escape_text(drive->get_device_with_type());
|
||||
name += " (" + Glib::Markup::escape_text(drive->format_drive_letters()) + ")";
|
||||
} else if (!drive->get_virtual_filename().empty()) {
|
||||
name += "\n" + Glib::Markup::escape_text(drive->get_virtual_filename());
|
||||
}
|
||||
@@ -255,28 +254,15 @@ class GscMainWindowIconView : public Gtk::IconView {
|
||||
if (rconfig::get_data<bool>("gui/icons_show_serial_number") && !drive->get_serial_number().empty()) {
|
||||
name += "\n" + Glib::Markup::escape_text(drive->get_serial_number());
|
||||
}
|
||||
StorageProperty scan_time_prop;
|
||||
if (drive->get_is_virtual()) {
|
||||
scan_time_prop = drive->lookup_property("scan_time");
|
||||
if (!scan_time_prop.value_string.empty()) {
|
||||
name += "\n" + Glib::Markup::escape_text(scan_time_prop.value_string);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> tooltip_strs;
|
||||
|
||||
if (drive->get_is_virtual()) {
|
||||
std::string vfile = drive->get_virtual_filename();
|
||||
tooltip_strs.push_back("Loaded from: " + (vfile.empty() ? "[empty]" : Glib::Markup::escape_text(vfile)));
|
||||
if (!scan_time_prop.value_string.empty()) {
|
||||
tooltip_strs.push_back("Scanned on: " + Glib::Markup::escape_text(scan_time_prop.value_string));
|
||||
}
|
||||
} else {
|
||||
tooltip_strs.push_back("Device: <b>" + Glib::Markup::escape_text(drive->get_device_with_type()) + "</b>");
|
||||
}
|
||||
if (!drive->format_drive_letters().empty()) {
|
||||
tooltip_strs.push_back("Drive letters: <b>" + Glib::Markup::escape_text(drive->format_drive_letters()) + "</b>");
|
||||
}
|
||||
if (!drive->get_serial_number().empty()) {
|
||||
tooltip_strs.push_back("Serial number: <b>" + Glib::Markup::escape_text(drive->get_serial_number()) + "</b>");
|
||||
}
|
||||
|
||||
@@ -576,16 +576,6 @@ void GscPreferencesWindow::on_smartctl_binary_browse_button_clicked()
|
||||
|
||||
int result = 0;
|
||||
|
||||
#ifdef _WIN32
|
||||
Glib::RefPtr<Gtk::FileFilter> specific_filter = Gtk::FileFilter::create();
|
||||
specific_filter->set_name("Executable Files");
|
||||
specific_filter->add_pattern("*.exe");
|
||||
|
||||
Glib::RefPtr<Gtk::FileFilter> all_filter = Gtk::FileFilter::create();
|
||||
all_filter->set_name("All Files");
|
||||
all_filter->add_pattern("*");
|
||||
#endif
|
||||
|
||||
#if GTK_CHECK_VERSION(3, 20, 0)
|
||||
hz::scoped_ptr<GtkFileChooserNative> dialog(gtk_file_chooser_native_new(
|
||||
"Choose Smartctl Binary...", this->gobj(), GTK_FILE_CHOOSER_ACTION_OPEN, NULL, NULL), g_object_unref);
|
||||
@@ -593,11 +583,6 @@ void GscPreferencesWindow::on_smartctl_binary_browse_button_clicked()
|
||||
if (path.is_absolute())
|
||||
gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog.get()), path.c_str());
|
||||
|
||||
#ifdef _WIN32
|
||||
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog.get()), specific_filter->gobj());
|
||||
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog.get()), all_filter->gobj());
|
||||
#endif
|
||||
|
||||
result = gtk_native_dialog_run(GTK_NATIVE_DIALOG(dialog.get()));
|
||||
|
||||
#else
|
||||
@@ -608,11 +593,6 @@ void GscPreferencesWindow::on_smartctl_binary_browse_button_clicked()
|
||||
dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
|
||||
dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT);
|
||||
|
||||
#ifdef _WIN32
|
||||
dialog.add_filter(specific_filter);
|
||||
dialog.add_filter(all_filter);
|
||||
#endif
|
||||
|
||||
// Note: This works on absolute paths only (otherwise it's gtk warning).
|
||||
if (path.is_absolute())
|
||||
dialog.set_filename(path.str()); // change to its dir and select it if exists.
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "hz/debug.h"
|
||||
#include "hz/fs_file.h"
|
||||
#include "hz/scoped_ptr.h"
|
||||
#include "rconfig/rconfig_mini.h"
|
||||
|
||||
#include "applib/app_gtkmm_features.h"
|
||||
#include "applib/app_ui_res_utils.h"
|
||||
@@ -142,28 +141,14 @@ class GscTextWindow : public AppUIResWidget<GscTextWindow<InstanceSwitch>, Insta
|
||||
void on_save_as_button_clicked()
|
||||
{
|
||||
static std::string last_dir;
|
||||
if (last_dir.empty()) {
|
||||
rconfig::get_data("gui/drive_data_open_save_dir", last_dir);
|
||||
}
|
||||
int result = 0;
|
||||
|
||||
Glib::RefPtr<Gtk::FileFilter> specific_filter = Gtk::FileFilter::create();
|
||||
specific_filter->set_name("Text Files");
|
||||
specific_filter->add_pattern("*.txt");
|
||||
|
||||
Glib::RefPtr<Gtk::FileFilter> all_filter = Gtk::FileFilter::create();
|
||||
all_filter->set_name("All Files");
|
||||
all_filter->add_pattern("*");
|
||||
|
||||
#if GTK_CHECK_VERSION(3, 20, 0)
|
||||
hz::scoped_ptr<GtkFileChooserNative> dialog(gtk_file_chooser_native_new(
|
||||
"Save Data As...", this->gobj(), GTK_FILE_CHOOSER_ACTION_SAVE, NULL, NULL), g_object_unref);
|
||||
|
||||
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog.get()), true);
|
||||
|
||||
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog.get()), specific_filter->gobj());
|
||||
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog.get()), all_filter->gobj());
|
||||
|
||||
if (!last_dir.empty())
|
||||
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog.get()), last_dir.c_str());
|
||||
|
||||
@@ -182,9 +167,6 @@ class GscTextWindow : public AppUIResWidget<GscTextWindow<InstanceSwitch>, Insta
|
||||
|
||||
dialog.set_do_overwrite_confirmation(true);
|
||||
|
||||
dialog.add_filter(specific_filter);
|
||||
dialog.add_filter(all_filter);
|
||||
|
||||
if (!last_dir.empty())
|
||||
dialog.set_current_folder(last_dir);
|
||||
|
||||
@@ -207,12 +189,6 @@ class GscTextWindow : public AppUIResWidget<GscTextWindow<InstanceSwitch>, Insta
|
||||
file = dialog.get_filename(); // in fs encoding
|
||||
last_dir = dialog.get_current_folder(); // save for the future
|
||||
#endif
|
||||
rconfig::set_data("gui/drive_data_open_save_dir", last_dir);
|
||||
|
||||
if (file.rfind(".txt") != (file.size() - std::strlen(".txt"))) {
|
||||
file += ".txt";
|
||||
}
|
||||
|
||||
hz::File f(file);
|
||||
if (!f.put_contents(this->contents_)) { // this will send to debug_ too.
|
||||
gui_show_error_dialog("Cannot save data to file", f.get_error_utf8(), this);
|
||||
|
||||
@@ -184,17 +184,9 @@ T ascii_strtoi(const char* nptr, char** endptr, int base)
|
||||
// overflow.
|
||||
|
||||
if (std::numeric_limits<T>::is_signed) {
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wint-in-bool-context"
|
||||
#pragma GCC diagnostic ignored "-Woverflow"
|
||||
#endif
|
||||
cutoff = static_cast<unsigned_type>( neg ?
|
||||
( static_cast<unsigned_type>(-(std::numeric_limits<T>::min() + std::numeric_limits<T>::max()))
|
||||
+ std::numeric_limits<T>::max() ) : std::numeric_limits<T>::max() );
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
cutlim = static_cast<signed_type>(cutoff % base);
|
||||
cutoff = static_cast<unsigned_type>(cutoff / base);
|
||||
} else {
|
||||
|
||||
@@ -125,14 +125,7 @@ class scoped_ptr { // non-copyable
|
||||
cleaner->cleanup(ptr);
|
||||
delete cleaner;
|
||||
} else {
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdelete-incomplete"
|
||||
#endif
|
||||
delete ptr;
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,11 +38,7 @@ namespace debug_internal {
|
||||
/// Constructor
|
||||
DebugCmdArgs() :
|
||||
// defaults
|
||||
#ifdef _WIN32
|
||||
verbose(TRUE),
|
||||
#else
|
||||
verbose(FALSE),
|
||||
#endif
|
||||
quiet(FALSE),
|
||||
#ifdef DEBUG_BUILD
|
||||
verbosity_level(5), // all
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
AM_CPPFLAGS = $(all_includes)
|
||||
METASOURCES = AUTO
|
||||
noinst_LIBRARIES = libpcrecpp.a
|
||||
libpcrecpp_a_SOURCES = pcre_scanner.cpp pcre_stringpiece.cpp pcrecpp.cpp
|
||||
noinst_HEADERS = pcre_scanner.h pcre_stringpiece.h pcrecpp.h pcrecpp_internal.h \
|
||||
pcrecpparg.h
|
||||
|
||||
# Distribute original, unpatched files too. Append .orig_dist to prevent conflict
|
||||
# with debian .orig files.
|
||||
EXTRA_DIST = pcrecpp.cc.orig_dist
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
Part of PCRE library, version 8.01.
|
||||
|
||||
pcrecpparg.h and pcre_stringpiece.h generated using:
|
||||
|
||||
./configure \
|
||||
--with-link-size=2 \
|
||||
--with-match-limit=10000000 \
|
||||
--enable-newline-is-lf \
|
||||
--enable-utf8 \
|
||||
--enable-unicode-properties
|
||||
|
||||
The system was OpenSUSE 11.1, with gcc-4.3-34.243 and glibc-2.9-2.12.1.
|
||||
@@ -0,0 +1,200 @@
|
||||
// Copyright (c) 2005, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: Sanjay Ghemawat
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <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
|
||||
@@ -0,0 +1,173 @@
|
||||
// Copyright (c) 2005, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: Sanjay Ghemawat
|
||||
//
|
||||
// Regular-expression based scanner for parsing an input stream.
|
||||
//
|
||||
// Example 1: parse a sequence of "var = number" entries from input:
|
||||
//
|
||||
// Scanner scanner(input);
|
||||
// string var;
|
||||
// int number;
|
||||
// scanner.SetSkipExpression("\\s+"); // Skip any white space we encounter
|
||||
// while (scanner.Consume("(\\w+) = (\\d+)", &var, &number)) {
|
||||
// ...;
|
||||
// }
|
||||
|
||||
#ifndef _PCRE_SCANNER_H
|
||||
#define _PCRE_SCANNER_H
|
||||
|
||||
#include <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 */
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2005, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: wilsonh@google.com (Wilson Hsieh)
|
||||
//
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include "pcrecpp_internal.h"
|
||||
#include "pcre_stringpiece.h"
|
||||
|
||||
std::ostream& operator<<(std::ostream& o, const pcrecpp::StringPiece& piece) {
|
||||
return (o << piece.as_string());
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
// Copyright (c) 2005, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: Sanjay Ghemawat
|
||||
//
|
||||
// A string like object that points into another piece of memory.
|
||||
// Useful for providing an interface that allows clients to easily
|
||||
// pass in either a "const char*" or a "string".
|
||||
//
|
||||
// Arghh! I wish C++ literals were automatically of type "string".
|
||||
|
||||
#ifndef _PCRE_STRINGPIECE_H
|
||||
#define _PCRE_STRINGPIECE_H
|
||||
|
||||
#include <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 */
|
||||
@@ -0,0 +1,920 @@
|
||||
// Copyright (c) 2010, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: Sanjay Ghemawat
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <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
|
||||
@@ -0,0 +1,965 @@
|
||||
// Copyright (c) 2010, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: Sanjay Ghemawat
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
// Custom:
|
||||
#include <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
|
||||
@@ -0,0 +1,710 @@
|
||||
// Copyright (c) 2005, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: Sanjay Ghemawat
|
||||
// Support for PCRE_XXX modifiers added by Giuseppe Maxia, July 2005
|
||||
|
||||
#ifndef _PCRECPP_H
|
||||
#define _PCRECPP_H
|
||||
|
||||
// C++ interface to the pcre regular-expression library. RE supports
|
||||
// Perl-style regular expressions (with extensions like \d, \w, \s,
|
||||
// ...).
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// REGEXP SYNTAX:
|
||||
//
|
||||
// This module is part of the pcre library and hence supports its syntax
|
||||
// for regular expressions.
|
||||
//
|
||||
// The syntax is pretty similar to Perl's. For those not familiar
|
||||
// with Perl's regular expressions, here are some examples of the most
|
||||
// commonly used extensions:
|
||||
//
|
||||
// "hello (\\w+) world" -- \w matches a "word" character
|
||||
// "version (\\d+)" -- \d matches a digit
|
||||
// "hello\\s+world" -- \s matches any whitespace character
|
||||
// "\\b(\\w+)\\b" -- \b matches empty string at a word boundary
|
||||
// "(?i)hello" -- (?i) turns on case-insensitive matching
|
||||
// "/\\*(.*?)\\*/" -- .*? matches . minimum no. of times possible
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// MATCHING INTERFACE:
|
||||
//
|
||||
// The "FullMatch" operation checks that supplied text matches a
|
||||
// supplied pattern exactly.
|
||||
//
|
||||
// Example: successful match
|
||||
// pcrecpp::RE re("h.*o");
|
||||
// re.FullMatch("hello");
|
||||
//
|
||||
// Example: unsuccessful match (requires full match):
|
||||
// pcrecpp::RE re("e");
|
||||
// !re.FullMatch("hello");
|
||||
//
|
||||
// Example: creating a temporary RE object:
|
||||
// pcrecpp::RE("h.*o").FullMatch("hello");
|
||||
//
|
||||
// You can pass in a "const char*" or a "string" for "text". The
|
||||
// examples below tend to use a const char*.
|
||||
//
|
||||
// You can, as in the different examples above, store the RE object
|
||||
// explicitly in a variable or use a temporary RE object. The
|
||||
// examples below use one mode or the other arbitrarily. Either
|
||||
// could correctly be used for any of these examples.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// MATCHING WITH SUB-STRING EXTRACTION:
|
||||
//
|
||||
// You can supply extra pointer arguments to extract matched subpieces.
|
||||
//
|
||||
// Example: extracts "ruby" into "s" and 1234 into "i"
|
||||
// int i;
|
||||
// string s;
|
||||
// pcrecpp::RE re("(\\w+):(\\d+)");
|
||||
// re.FullMatch("ruby:1234", &s, &i);
|
||||
//
|
||||
// Example: does not try to extract any extra sub-patterns
|
||||
// re.FullMatch("ruby:1234", &s);
|
||||
//
|
||||
// Example: does not try to extract into NULL
|
||||
// re.FullMatch("ruby:1234", NULL, &i);
|
||||
//
|
||||
// Example: integer overflow causes failure
|
||||
// !re.FullMatch("ruby:1234567891234", NULL, &i);
|
||||
//
|
||||
// Example: fails because there aren't enough sub-patterns:
|
||||
// !pcrecpp::RE("\\w+:\\d+").FullMatch("ruby:1234", &s);
|
||||
//
|
||||
// Example: fails because string cannot be stored in integer
|
||||
// !pcrecpp::RE("(.*)").FullMatch("ruby", &i);
|
||||
//
|
||||
// The provided pointer arguments can be pointers to any scalar numeric
|
||||
// type, or one of
|
||||
// string (matched piece is copied to string)
|
||||
// StringPiece (StringPiece is mutated to point to matched piece)
|
||||
// T (where "bool T::ParseFrom(const char*, int)" exists)
|
||||
// NULL (the corresponding matched sub-pattern is not copied)
|
||||
//
|
||||
// CAVEAT: An optional sub-pattern that does not exist in the matched
|
||||
// string is assigned the empty string. Therefore, the following will
|
||||
// return false (because the empty string is not a valid number):
|
||||
// int number;
|
||||
// pcrecpp::RE::FullMatch("abc", "[a-z]+(\\d+)?", &number);
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// DO_MATCH
|
||||
//
|
||||
// The matching interface supports at most 16 arguments per call.
|
||||
// If you need more, consider using the more general interface
|
||||
// pcrecpp::RE::DoMatch(). See pcrecpp.h for the signature for DoMatch.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// PARTIAL MATCHES
|
||||
//
|
||||
// You can use the "PartialMatch" operation when you want the pattern
|
||||
// to match any substring of the text.
|
||||
//
|
||||
// Example: simple search for a string:
|
||||
// pcrecpp::RE("ell").PartialMatch("hello");
|
||||
//
|
||||
// Example: find first number in a string:
|
||||
// int number;
|
||||
// pcrecpp::RE re("(\\d+)");
|
||||
// re.PartialMatch("x*100 + 20", &number);
|
||||
// assert(number == 100);
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// UTF-8 AND THE MATCHING INTERFACE:
|
||||
//
|
||||
// By default, pattern and text are plain text, one byte per character.
|
||||
// The UTF8 flag, passed to the constructor, causes both pattern
|
||||
// and string to be treated as UTF-8 text, still a byte stream but
|
||||
// potentially multiple bytes per character. In practice, the text
|
||||
// is likelier to be UTF-8 than the pattern, but the match returned
|
||||
// may depend on the UTF8 flag, so always use it when matching
|
||||
// UTF8 text. E.g., "." will match one byte normally but with UTF8
|
||||
// set may match up to three bytes of a multi-byte character.
|
||||
//
|
||||
// Example:
|
||||
// pcrecpp::RE_Options options;
|
||||
// options.set_utf8();
|
||||
// pcrecpp::RE re(utf8_pattern, options);
|
||||
// re.FullMatch(utf8_string);
|
||||
//
|
||||
// Example: using the convenience function UTF8():
|
||||
// pcrecpp::RE re(utf8_pattern, pcrecpp::UTF8());
|
||||
// re.FullMatch(utf8_string);
|
||||
//
|
||||
// NOTE: The UTF8 option is ignored if pcre was not configured with the
|
||||
// --enable-utf8 flag.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// PASSING MODIFIERS TO THE REGULAR EXPRESSION ENGINE
|
||||
//
|
||||
// PCRE defines some modifiers to change the behavior of the regular
|
||||
// expression engine.
|
||||
// The C++ wrapper defines an auxiliary class, RE_Options, as a vehicle
|
||||
// to pass such modifiers to a RE class.
|
||||
//
|
||||
// Currently, the following modifiers are supported
|
||||
//
|
||||
// modifier description Perl corresponding
|
||||
//
|
||||
// PCRE_CASELESS case insensitive match /i
|
||||
// PCRE_MULTILINE multiple lines match /m
|
||||
// PCRE_DOTALL dot matches newlines /s
|
||||
// PCRE_DOLLAR_ENDONLY $ matches only at end N/A
|
||||
// PCRE_EXTRA strict escape parsing N/A
|
||||
// PCRE_EXTENDED ignore whitespaces /x
|
||||
// PCRE_UTF8 handles UTF8 chars built-in
|
||||
// PCRE_UNGREEDY reverses * and *? N/A
|
||||
// PCRE_NO_AUTO_CAPTURE disables matching parens N/A (*)
|
||||
//
|
||||
// (For a full account on how each modifier works, please check the
|
||||
// PCRE API reference manual).
|
||||
//
|
||||
// (*) Both Perl and PCRE allow non matching parentheses by means of the
|
||||
// "?:" modifier within the pattern itself. e.g. (?:ab|cd) does not
|
||||
// capture, while (ab|cd) does.
|
||||
//
|
||||
// For each modifier, there are two member functions whose name is made
|
||||
// out of the modifier in lowercase, without the "PCRE_" prefix. For
|
||||
// instance, PCRE_CASELESS is handled by
|
||||
// bool caseless(),
|
||||
// which returns true if the modifier is set, and
|
||||
// RE_Options & set_caseless(bool),
|
||||
// which sets or unsets the modifier.
|
||||
//
|
||||
// Moreover, PCRE_EXTRA_MATCH_LIMIT can be accessed through the
|
||||
// set_match_limit() and match_limit() member functions.
|
||||
// Setting match_limit to a non-zero value will limit the executation of
|
||||
// pcre to keep it from doing bad things like blowing the stack or taking
|
||||
// an eternity to return a result. A value of 5000 is good enough to stop
|
||||
// stack blowup in a 2MB thread stack. Setting match_limit to zero will
|
||||
// disable match limiting. Alternately, you can set match_limit_recursion()
|
||||
// which uses PCRE_EXTRA_MATCH_LIMIT_RECURSION to limit how much pcre
|
||||
// recurses. match_limit() caps the number of matches pcre does;
|
||||
// match_limit_recrusion() caps the depth of recursion.
|
||||
//
|
||||
// Normally, to pass one or more modifiers to a RE class, you declare
|
||||
// a RE_Options object, set the appropriate options, and pass this
|
||||
// object to a RE constructor. Example:
|
||||
//
|
||||
// RE_options opt;
|
||||
// opt.set_caseless(true);
|
||||
//
|
||||
// if (RE("HELLO", opt).PartialMatch("hello world")) ...
|
||||
//
|
||||
// RE_options has two constructors. The default constructor takes no
|
||||
// arguments and creates a set of flags that are off by default.
|
||||
//
|
||||
// The optional parameter 'option_flags' is to facilitate transfer
|
||||
// of legacy code from C programs. This lets you do
|
||||
// RE(pattern, RE_Options(PCRE_CASELESS|PCRE_MULTILINE)).PartialMatch(str);
|
||||
//
|
||||
// But new code is better off doing
|
||||
// RE(pattern,
|
||||
// RE_Options().set_caseless(true).set_multiline(true)).PartialMatch(str);
|
||||
// (See below)
|
||||
//
|
||||
// If you are going to pass one of the most used modifiers, there are some
|
||||
// convenience functions that return a RE_Options class with the
|
||||
// appropriate modifier already set:
|
||||
// CASELESS(), UTF8(), MULTILINE(), DOTALL(), EXTENDED()
|
||||
//
|
||||
// If you need to set several options at once, and you don't want to go
|
||||
// through the pains of declaring a RE_Options object and setting several
|
||||
// options, there is a parallel method that give you such ability on the
|
||||
// fly. You can concatenate several set_xxxxx member functions, since each
|
||||
// of them returns a reference to its class object. e.g.: to pass
|
||||
// PCRE_CASELESS, PCRE_EXTENDED, and PCRE_MULTILINE to a RE with one
|
||||
// statement, you may write
|
||||
//
|
||||
// RE(" ^ xyz \\s+ .* blah$", RE_Options()
|
||||
// .set_caseless(true)
|
||||
// .set_extended(true)
|
||||
// .set_multiline(true)).PartialMatch(sometext);
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// SCANNING TEXT INCREMENTALLY
|
||||
//
|
||||
// The "Consume" operation may be useful if you want to repeatedly
|
||||
// match regular expressions at the front of a string and skip over
|
||||
// them as they match. This requires use of the "StringPiece" type,
|
||||
// which represents a sub-range of a real string. Like RE, StringPiece
|
||||
// is defined in the pcrecpp namespace.
|
||||
//
|
||||
// Example: read lines of the form "var = value" from a string.
|
||||
// string contents = ...; // Fill string somehow
|
||||
// pcrecpp::StringPiece input(contents); // Wrap in a StringPiece
|
||||
//
|
||||
// string var;
|
||||
// int value;
|
||||
// pcrecpp::RE re("(\\w+) = (\\d+)\n");
|
||||
// while (re.Consume(&input, &var, &value)) {
|
||||
// ...;
|
||||
// }
|
||||
//
|
||||
// Each successful call to "Consume" will set "var/value", and also
|
||||
// advance "input" so it points past the matched text.
|
||||
//
|
||||
// The "FindAndConsume" operation is similar to "Consume" but does not
|
||||
// anchor your match at the beginning of the string. For example, you
|
||||
// could extract all words from a string by repeatedly calling
|
||||
// pcrecpp::RE("(\\w+)").FindAndConsume(&input, &word)
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// PARSING HEX/OCTAL/C-RADIX NUMBERS
|
||||
//
|
||||
// By default, if you pass a pointer to a numeric value, the
|
||||
// corresponding text is interpreted as a base-10 number. You can
|
||||
// instead wrap the pointer with a call to one of the operators Hex(),
|
||||
// Octal(), or CRadix() to interpret the text in another base. The
|
||||
// CRadix operator interprets C-style "0" (base-8) and "0x" (base-16)
|
||||
// prefixes, but defaults to base-10.
|
||||
//
|
||||
// Example:
|
||||
// int a, b, c, d;
|
||||
// pcrecpp::RE re("(.*) (.*) (.*) (.*)");
|
||||
// re.FullMatch("100 40 0100 0x40",
|
||||
// pcrecpp::Octal(&a), pcrecpp::Hex(&b),
|
||||
// pcrecpp::CRadix(&c), pcrecpp::CRadix(&d));
|
||||
// will leave 64 in a, b, c, and d.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// REPLACING PARTS OF STRINGS
|
||||
//
|
||||
// You can replace the first match of "pattern" in "str" with
|
||||
// "rewrite". Within "rewrite", backslash-escaped digits (\1 to \9)
|
||||
// can be used to insert text matching corresponding parenthesized
|
||||
// group from the pattern. \0 in "rewrite" refers to the entire
|
||||
// matching text. E.g.,
|
||||
//
|
||||
// string s = "yabba dabba doo";
|
||||
// pcrecpp::RE("b+").Replace("d", &s);
|
||||
//
|
||||
// will leave "s" containing "yada dabba doo". The result is true if
|
||||
// the pattern matches and a replacement occurs, or false otherwise.
|
||||
//
|
||||
// GlobalReplace() is like Replace(), except that it replaces all
|
||||
// occurrences of the pattern in the string with the rewrite.
|
||||
// Replacements are not subject to re-matching. E.g.,
|
||||
//
|
||||
// string s = "yabba dabba doo";
|
||||
// pcrecpp::RE("b+").GlobalReplace("d", &s);
|
||||
//
|
||||
// will leave "s" containing "yada dada doo". It returns the number
|
||||
// of replacements made.
|
||||
//
|
||||
// Extract() is like Replace(), except that if the pattern matches,
|
||||
// "rewrite" is copied into "out" (an additional argument) with
|
||||
// substitutions. The non-matching portions of "text" are ignored.
|
||||
// Returns true iff a match occurred and the extraction happened
|
||||
// successfully. If no match occurs, the string is left unaffected.
|
||||
|
||||
|
||||
#include <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 */
|
||||
@@ -0,0 +1,71 @@
|
||||
/*************************************************
|
||||
* Perl-Compatible Regular Expressions *
|
||||
*************************************************/
|
||||
|
||||
/*
|
||||
Copyright (c) 2005, Google Inc.
|
||||
All rights reserved.
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Cambridge nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
-----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PCRECPP_INTERNAL_H
|
||||
#define PCRECPP_INTERNAL_H
|
||||
|
||||
/* When compiling a DLL for Windows, the exported symbols have to be declared
|
||||
using some MS magic. I found some useful information on this web page:
|
||||
http://msdn2.microsoft.com/en-us/library/y4h7bcy6(VS.80).aspx. According to the
|
||||
information there, using __declspec(dllexport) without "extern" we have a
|
||||
definition; with "extern" we have a declaration. The settings here override the
|
||||
setting in pcre.h. We use:
|
||||
|
||||
PCRECPP_EXP_DECL for declarations
|
||||
PCRECPP_EXP_DEFN for definitions of exported functions
|
||||
|
||||
*/
|
||||
|
||||
#ifndef PCRECPP_EXP_DECL
|
||||
# ifdef _WIN32
|
||||
# ifndef PCRE_STATIC
|
||||
# define PCRECPP_EXP_DECL extern __declspec(dllexport)
|
||||
# define PCRECPP_EXP_DEFN __declspec(dllexport)
|
||||
# else
|
||||
# define PCRECPP_EXP_DECL extern
|
||||
# define PCRECPP_EXP_DEFN
|
||||
# endif
|
||||
# else
|
||||
# define PCRECPP_EXP_DECL extern
|
||||
# define PCRECPP_EXP_DEFN
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif /* PCRECPP_INTERNAL_H */
|
||||
|
||||
/* End of pcrecpp_internal.h */
|
||||
@@ -0,0 +1,175 @@
|
||||
// Copyright (c) 2005, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: Sanjay Ghemawat
|
||||
|
||||
#ifndef _PCRECPPARG_H
|
||||
#define _PCRECPPARG_H
|
||||
|
||||
#include <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 */
|
||||
+427
-558
File diff suppressed because it is too large
Load Diff
@@ -303,9 +303,6 @@
|
||||
<object class="GtkLabel" id="label3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">3</property>
|
||||
<property name="margin_bottom">3</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="label" translatable="yes">_General</property>
|
||||
<property name="use_underline">True</property>
|
||||
</object>
|
||||
@@ -675,12 +672,8 @@
|
||||
<object class="GtkLabel" id="label4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">6</property>
|
||||
<property name="margin_right">6</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="label" translatable="yes">_Drives</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="width_chars">10</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
|
||||
Reference in New Issue
Block a user