Compare commits

...

76 Commits

Author SHA1 Message Date
rakshasa 6154d16987 Bumped to version 0.9.8. 2019-07-19 13:38:33 +02:00
rakshasa a5e8631cba Added log.close. 2019-07-17 19:41:23 +02:00
rakshasa 07dcd04357 Revert to only using ax_with_curses.m4, and upgrade it to version 16 which seems to be the last unborked version. 2019-07-13 14:36:05 +02:00
rakshasa 863de44c18 Added d.tracker_announce.force. 2019-07-11 17:46:35 +02:00
Jari Sundell 24135ed48e Merge pull request #763 from MindTooth/travis_path_fix
travis: Fix test in wrong path, and jobs parallelization
2019-07-09 21:24:12 +02:00
rakshasa 83709d28c9 Merge branch 'toff-master' 2019-07-08 21:40:02 +02:00
rakshasa 859d5a9578 Merge branch 'master' of https://github.com/toff/rtorrent into toff-master 2019-07-08 21:39:31 +02:00
rakshasa adc9b1dec5 Merge branch 'zetafunction-autoconf' 2019-07-08 20:30:09 +02:00
rakshasa 285972a20d Fixed configure.ac. (zetafunction) 2019-07-08 20:29:43 +02:00
Jari Sundell 3ec2f52db5 Merge pull request #857 from skangas/view-keys-man
Update change view keys to 0-9 in man page
2019-07-08 19:41:20 +02:00
rakshasa ac64fc3f63 Added _GNU_SOURCE to fallocate test. (neheb) 2019-07-08 19:37:17 +02:00
rakshasa b26dc42f3a Merge branch 'ffontaine-master' 2019-06-10 01:18:32 +09:00
rakshasa 6572088aec Merge branch 'master' of https://github.com/ffontaine/rtorrent into ffontaine-master 2019-06-09 23:51:22 +09:00
rakshasa 31a0ea1a2d Merge branch 'master' of github.com:rakshasa/rtorrent 2019-06-09 17:10:17 +09:00
rakshasa 2d72a0d2c0 Merge branch 'chros73-input_history_with_categories' 2019-06-09 17:09:59 +09:00
rakshasa a3a384b049 Fixed minor issues. 2019-06-09 17:09:35 +09:00
Jari Sundell f37dd0d5c2 Merge pull request #722 from g0tmi1k/config
Update rtorrent.rc
2019-06-09 16:27:53 +09:00
Jari Sundell 78c25011cd Merge pull request #824 from Irishsmurf/patch-1
Fix typo in README
2019-06-09 16:25:07 +09:00
rakshasa 35d98c91e0 Merge branch 'master' of github.com:rakshasa/rtorrent 2019-06-09 16:24:18 +09:00
rakshasa 27811db659 Merge branch 'ss23-749_ncurses_tinfo' 2019-06-09 16:24:00 +09:00
rakshasa 4e73ce6bfd Merge branch '749_ncurses_tinfo' of https://github.com/ss23/rtorrent into ss23-749_ncurses_tinfo 2019-06-09 15:43:54 +09:00
Jari Sundell 8c8e73bb72 Merge pull request #794 from waveletlet/master
Edit getsession() for 0.9 config format
2019-06-09 15:17:18 +09:00
Jari Sundell 0d5b344d2a Switch to C++11 MRT RNG for random bytes
Switching to a better RNG for generating strings will prevent the common peerID collisions the rTorrent client has been seeing for YEARS in #440 and #318.
2019-06-08 16:22:47 +09:00
Fabrice Fontaine 703cac9a42 Fix cross compilation, based on OpenWRT patch.
[Vincent: tweak the patch for version 0.13.6]
[Bernd: tweak the patch for version 0.13.7]

Signed-off-by: Gustavo Zacarias <gustavo@zacarias.com.ar>
Signed-off-by: Vicente Olivert Riera <Vincent.Riera@imgtec.com>
Signed-off-by: Bernd Kuhls <bernd.kuhls@t-online.de>
[Retrieved from:
https://git.buildroot.net/buildroot/tree/package/libtorrent/0001-Fix-cross-compilation-based-on-OpenWRT-patch.patch]
Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
2019-04-20 21:37:26 +02:00
Stefan Kangas e6c008b2b7 Update change view keys to 0-9 in man page 2019-04-08 10:03:15 +02:00
Daniel Cheng 81de60e0ae Update global CFLAGS/CXXFLAGS/LIBS at the end of configure.ac
Changes to CFLAGS/CXXFLAGS/LIBS affect following lines in configure.ac.
As originally written, this would cause random libraries, such as
libtorrent, to be linked into the autoconf test program. Occasionally,
this would cause the things like the pkgconfig checks to fail with
mysterious linker errors (e.g. rakshasa/rtorrent#156).

Instead, only populate these global flags with the additional flags for
dependencies at the very end, before the invocation of AC_OUTPUT.
2019-01-13 23:37:36 -08:00
David K c361d6010e Fix typo in README
s/nessesary/necessary
2019-01-03 21:10:09 +00:00
Birger J. Nordølum 724dc088ca travis: Fix test in wrong path, and jobs parallelization 2018-11-14 22:05:19 +01:00
rakshasa c62fa6a756 Merge branch 'pyroscope-cmd-d_multicall_filtered' 2018-10-20 22:00:21 +09:00
rakshasa 656d735de7 Merge branch 'cmd-d_multicall_filtered' of https://github.com/pyroscope/rtorrent into pyroscope-cmd-d_multicall_filtered 2018-10-20 21:47:36 +09:00
rakshasa 408013478c Merge branch 'pyroscope-cmd-compare' 2018-10-20 21:38:39 +09:00
rakshasa fa61b8c11c Merge branch 'cmd-compare' of https://github.com/pyroscope/rtorrent into pyroscope-cmd-compare 2018-10-20 21:38:28 +09:00
rakshasa 1339a3ace9 Merge branch 'chros73-event_system_commands' 2018-10-20 21:35:35 +09:00
rakshasa 4ba128163f Merge branch 'event_system_commands' of https://github.com/chros73/rtorrent into chros73-event_system_commands 2018-10-20 21:30:39 +09:00
rakshasa 908a99751f Merge branch 'chros73-list_type_method_fix' 2018-10-20 21:22:13 +09:00
waveletlet c115640934 Edit getsession() for 0.9 config format 2018-10-18 19:39:32 -03:00
waveletlet 9043c46815 Edit getsession() for 0.9 config format 2018-10-18 19:36:49 -03:00
chros 1f418366d4 Fix 'list' type method (See #25) 2018-10-01 23:27:58 +01:00
toff 30c78c7034 Merge branch 'master' into master 2018-09-29 11:31:06 +02:00
chros 74435f6c40 Add 'event.system.startup_done/shutdown' commands/events (Closes #24) 2018-09-26 15:12:20 +01:00
Jari Sundell a4b281afa4 Merge pull request #753 from ss23/cross_compiling-execinfo
Use AC_COMPILE instead of AC_RUN to check for execinfo.h
2018-06-27 18:36:21 +09:00
Stephen Shkardoon f84670dd63 Use AC_COMPILE instead of AC_RUN to check for execinfo.h
This way enables cross compiling, since we don't need to run anything during the configure script.
2018-06-25 20:14:43 +12:00
rakshasa f0207ce654 Added "try" command that catches input_errors and logs them on rpc_events. 2018-06-23 17:01:34 +09:00
Stephen Shkardoon 9cce5a0bd1 Fix compiling if ncurses was built using --with-terminfo
Resolves #749
Patch from https://bugs.gentoo.org/658390
2018-06-20 01:50:45 +12:00
g0tmi1k 95cff549c6 Removed stop_untied at @pyroscope's request 2018-06-19 10:56:45 +01:00
g0tmi1k 92898a9831 Use session.path for rpc.socket in rtorrent.rc 2018-06-19 10:56:36 +01:00
g0tmi1k fcfbf0e79f Fix typos in rtorrent.rc 2018-06-19 10:51:35 +01:00
g0tmi1k 680b47c197 Add example rtorrent.rc
Source: https://github.com/rakshasa/rtorrent/wiki/CONFIG-Template
2018-06-19 10:48:25 +01:00
Jari Sundell 7804ed3009 Merge pull request #748 from pyroscope/d_custom_if_z-empty-val
d.custom.if_z: return default also for existing but empty value
2018-06-19 18:11:00 +09:00
pyroscope d9b39f5ac6 d.custom.if_z: return default also for existing but empty value 2018-06-18 19:41:53 +02:00
Jari Sundell 08aa287cfc Merge pull request #614 from chros73/adjustable_global_throttle_steps
Make global throttle steps adjustable
2018-06-19 00:08:32 +09:00
Jari Sundell 851d1902a0 Merge pull request #738 from pyroscope/d_custom_if_z+keys+items
add d.custom.if_z + d.custom.keys + d.custom.items
2018-06-19 00:06:23 +09:00
Jari Sundell b600ff0c0e Merge pull request #742 from pyroscope/cmd-event-view-change
add 'event.view.hide/show' commands / events
2018-06-16 18:48:10 +09:00
Jari Sundell a639461c4a Merge pull request #743 from pyroscope/fix-log-xmlrpc-close
fix: properly close the XMLRPC log, resetting the handle after close
2018-06-16 18:46:11 +09:00
Jari Sundell c1057d5ac7 Merge pull request #745 from pyroscope/cmd-value
add 'value' command / conversion op
2018-06-16 18:33:39 +09:00
pyroscope 2ac65ad7e1 add 'value' command / conversion op 2018-06-16 06:54:39 +02:00
pyroscope a4939cb9fd add 'compare' command / operator 2018-06-15 22:36:55 +02:00
pyroscope 3d88ac2cc8 fix: properly close the XMLRPC log, resetting the handle after close 2018-06-15 19:38:12 +02:00
pyroscope f37fefcc54 add 'event.view.hide/show' commands / events 2018-06-15 18:36:01 +02:00
pyroscope 885836f68b add 'd.multicall.filtered' command 2018-06-15 00:40:00 +02:00
pyroscope ca4c3bee8d add d.custom.if_z + d.custom.keys + d.custom.items 2018-06-14 20:32:28 +02:00
Jari Sundell 6c278668e4 Merge pull request #587 from pyroscope/add-position-display-in-title
add '[ X of Y ]' to title bar
2018-06-11 23:15:21 +09:00
Jari Sundell 1665437588 Merge pull request #576 from chros73/simple_math_commands
Add support for basic arithmetic operators
2018-06-11 23:06:37 +09:00
chros 6dc87e499a Fix bugs in 'math.*' commands (See #14) 2018-06-01 11:51:42 +01:00
chros 6c11a1dbb0 Fix error message in as_vector method (See #14) 2018-03-03 09:08:28 +00:00
chros 29d57e858a Make global throttle steps adjustable (Closes #17) 2017-06-06 09:54:01 +01:00
Toff 7ec9b6685b Merge pyroscope and chros73 suggestion: Command name uniformisation.
ui.console.log.tempfilter => view.temp_filter.log
2017-05-06 11:17:25 +02:00
Toff d5de8a91d1 Merge chros73 suggestions (settings to exclude views and hide info logs)
- Certain views can be excluded from subfiltering via view.temp_filter.excluded config option
    - its default value: "default,started,stopped"
    - example usage in config: view.temp_filter.excluded.set="default,started,stopped,leeching"

- Log messages can be displayed when temp filtering occured
    - disabled by default
    - can be enabled with: ui.console.log.tempfilter.set=1
2017-04-17 14:56:11 +02:00
pyroscope 4d6f6fe335 add '[ X of Y ]' to title bar 2017-04-01 12:30:10 +02:00
chros 13676f06e5 Add median command (See #14) 2017-03-11 11:40:08 +00:00
chros 6cb176ada3 Add more info to throw message in math commands (See #14) 2017-03-07 13:12:18 +00:00
chros 8b6a1ad079 Add count and average commands (See #14) 2017-03-07 12:57:31 +00:00
chros 3002c95b2c Rename couple of arithmetic commands (See #14) 2017-03-06 19:41:49 +00:00
chros 9590949fa7 Add support for basic arithmetic operators (See #14) 2017-03-06 13:06:59 +00:00
Toff 6ee61ee6b5 Merge chros73 suggestions (filter indicator, "start/stopped" view case)
- Suffix filtered view name with "(filtered)"
- Add a check to not filter "start" and "stopped" view
2017-01-08 19:45:46 +01:00
Toff e15270271d Add a temporary name filter for the main view.
By using 'F' in the main view you can enter a regex
that will be used to filter the view based on the torrent name.
To remove the filter set the input to empty.
This is adding a 'view.temp_filter' property and a 'match{}' function.
For example with Ctrl+X you could do:
command> view.temp_filter=main,"match={d.name=,.*linux.*iso}"
2016-10-31 12:19:41 +01:00
38 changed files with 1158 additions and 256 deletions
+13 -3
View File
@@ -1,5 +1,9 @@
language: cpp
env:
global:
- MAKEFLAGS="-j 12"
matrix:
include:
- compiler: clang
@@ -67,8 +71,14 @@ matrix:
# TODO: Use the same branch name if libtorrent has it.
before_install:
- git clone https://github.com/rakshasa/libtorrent.git
- cd libtorrent && ./autogen.sh && CXX="$COMPILER" ./configure && make -j12 && sudo make install
- |
pushd libtorrent \
&& ./autogen.sh \
&& CXX="$COMPILER" ./configure --prefix=/usr \
&& make \
&& sudo make install \
&& popd
script:
- ./autogen.sh && CXX="$COMPILER" ./configure && make -j12
- if [ ! $SKIP_CHECK ]; then make -j12 check; fi
- ./autogen.sh && CXX="$COMPILER" ./configure && make
- if [ ! $SKIP_CHECK ]; then make check; fi
+1 -1
View File
@@ -1,7 +1,7 @@
BUILDING
Run "./autogen.sh" to generate the configure scripts if
nessesary. The man page "doc/rtorrent.1" must be generated with
necessary. The man page "doc/rtorrent.1" must be generated with
"docbook2man rtorrent.1.xml" if it is missing.
Note that rtorrent follows the development of libtorrent closely, and
+10 -8
View File
@@ -1,6 +1,6 @@
AC_INIT(rtorrent, 0.9.7, sundell.software@gmail.com)
AC_INIT(rtorrent, 0.9.8, sundell.software@gmail.com)
AC_DEFINE(API_VERSION, 9, api version)
AC_DEFINE(API_VERSION, 10, api version)
AM_INIT_AUTOMAKE
AC_CONFIG_HEADERS(config.h)
@@ -38,13 +38,9 @@ if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then
AC_MSG_ERROR([requires either NcursesW or Ncurses library])
fi
PKG_CHECK_MODULES([LIBCURL], [libcurl], , [LIBCURL_CHECK_CONFIG])
PKG_CHECK_MODULES([LIBCURL], [libcurl],, [LIBCURL_CHECK_CONFIG])
PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"])
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.13.7])
LIBS="$PTHREAD_LIBS $CURSES_LIB $CPPUNIT_LIBS $LIBCURL $LIBCURL_LIBS $DEPENDENCIES_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS $CPPUNIT_CFLAGS $LIBCURL_CPPFLAGS $LIBCURL_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS"
CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $CPPUNIT_CFLAGS $LIBCURL_CPPFLAGS $LIBCURL_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS"
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.13.8])
AC_LANG_PUSH(C++)
TORRENT_WITH_XMLRPC_C
@@ -62,6 +58,12 @@ CC_ATTRIBUTE_UNUSED(
AC_DEFINE([__UNUSED], [], [Null-wrapper if unused attribute is unsupported])
)
dnl Only update global build variables immediately before generating the output,
dnl to avoid affecting the global build environment for other autoconf checks.
LIBS="$PTHREAD_LIBS $CURSES_LIB $CURSES_LIBS $CPPUNIT_LIBS $LIBCURL $LIBCURL_LIBS $DEPENDENCIES_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS $CPPUNIT_CFLAGS $LIBCURL_CPPFLAGS $LIBCURL_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS"
CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $CPPUNIT_CFLAGS $LIBCURL_CPPFLAGS $LIBCURL_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS"
AC_OUTPUT([
Makefile
doc/Makefile
+4 -1
View File
@@ -52,7 +52,7 @@ Decrease the download throttle by 1/5/50 KB.
\fB->\fR
View download.
.TP
\fB1 - 7\fR
\fB0 - 9\fR
Change view.
.TP
\fB^S\fR
@@ -99,6 +99,9 @@ Delete the file the torrent is tied to, and clear the association.
.TP
\fBI\fR
Toggle whether torrent ignores ratio settings.
.TP
\fBF\fR
Add a temporary name based regex filter to the current view.
.SS "DOWNLOAD VIEW KEYS"
.TP
\fB->\fR
+8 -1
View File
@@ -122,7 +122,7 @@ View download.
</varlistentry>
<varlistentry>
<term>1 - 7</term>
<term>0 - 9</term>
<listitem><para>
Change view.
</para></listitem>
@@ -225,6 +225,13 @@ Toggle whether torrent ignores ratio settings.
</para></listitem>
</varlistentry>
<varlistentry>
<term>F</term>
<listitem><para>
Add a temporary name based regex filter to the current view.
</para></listitem>
</varlistentry>
</variablelist>
</refsect2>
+39 -23
View File
@@ -1,22 +1,36 @@
# This is an example resource file for rTorrent. Copy to
# ~/.rtorrent.rc and enable/modify the options as needed. Remember to
# uncomment the options you wish to enable.
#############################################################################
# This is an (old) example resource file for rTorrent.
# Copy to ~/.rtorrent.rc and enable/modify the options as needed.
# Remember to uncomment the options you wish to enable.
#
# See 'rtorrent.rc-example' for a newer, basic configuration.
#
# Sample: https://github.com/rakshasa/rtorrent/wiki/CONFIG-Template
# Complete: https://rtorrent-docs.readthedocs.io/en/latest/cmd-ref.html
# Useful: https://rtorrent-docs.readthedocs.io/en/latest/use-cases.html
# Manual: https://rtorrent-docs.readthedocs.io/en/latest/
# Convert: https://github.com/rakshasa/rtorrent/wiki/rTorrent-0.9-Comprehensive-Command-list-(WIP)
# Handbook: https://media.readthedocs.org/pdf/rtorrent-docs/latest/rtorrent-docs.pdf
# File: https://github.com/rakshasa/rtorrent/blob/master/doc/rtorrent.rc
#############################################################################
# Maximum and minimum number of peers to connect to per torrent.
#
#throttle.min_peers.normal.set = 40
#throttle.max_peers.normal.set = 100
# Same as above but for seeding completed torrents (-1 = same as downloading)
# Same as above but for seeding completed torrents.
# "-1" = same as downloading.
#
#throttle.min_peers.seed.set = 10
#throttle.max_peers.seed.set = 50
# Maximum number of simultanious uploads per torrent.
# Maximum number of simultaneous uploads per torrent.
#
#throttle.max_uploads.set = 15
# Global upload and download rate in KiB. "0" for unlimited.
# Global upload and download rate in KiB.
# "0" for unlimited.
#
#throttle.global_down.max_rate.set_kb = 0
#throttle.global_up.max_rate.set_kb = 0
@@ -26,7 +40,7 @@
#directory.default.set = ./
# Default session directory. Make sure you don't run multiple instance
# of rtorrent using the same session directory. Perhaps using a
# of rTorrent using the same session directory. Perhaps using a
# relative path?
#
#session.path.set = ./session
@@ -35,18 +49,17 @@
# deleted.
#
#schedule2 = watch_directory,5,5,load.start=./watch/*.torrent
#schedule2 = untied_directory,5,5,stop_untied=
# Close torrents when diskspace is low.
# Close torrents when disk-space is low.
#
#schedule2 = low_diskspace,5,60,close_low_diskspace=100M
# The ip address reported to the tracker.
# The IP address reported to the tracker.
#
#network.local_address.set = 127.0.0.1
#network.local_address.set = rakshasa.no
# The ip address the listening socket and outgoing connections is
# The IP address the listening socket and outgoing connections is
# bound to.
#
#network.bind_address.set = 127.0.0.1
@@ -60,8 +73,8 @@
#
#network.port_random.set = no
# Check hash for finished torrents. Might be usefull until the bug is
# fixed that causes lack of diskspace not to be properly reported.
# Check hash for finished torrents. Might be useful until the bug is
# fixed that causes lack of disk-space not to be properly reported.
#
#pieces.hash.on_completion.set = no
@@ -69,7 +82,7 @@
#
#trackers.use_udp.set = yes
# Alternative calls to bind and ip that should handle dynamic ip's.
# Alternative calls to bind and IP that should handle dynamic IP's.
#
#schedule2 = ip_tick,0,1800,ip=rakshasa
#schedule2 = bind_tick,0,1800,bind=rakshasa
@@ -79,7 +92,7 @@
#
# The example value allows incoming encrypted connections, starts unencrypted
# outgoing connections but retries with encryption if they fail, preferring
# plaintext to RC4 encryption after the encrypted handshake
# plain-text to RC4 encryption after the encrypted handshake.
#
# protocol.encryption.set = allow_incoming,enable_retry,prefer_plaintext
@@ -87,26 +100,29 @@
# May be set to "disable" (completely disable DHT), "off" (do not start DHT),
# "auto" (start and stop DHT as needed), or "on" (start DHT immediately).
# The default is "off". For DHT to work, a session directory must be defined.
#
#
# dht.mode.set = auto
# UDP port to use for DHT.
# UDP port to use for DHT.
#
#dht.port.set = 6881
# Enable peer exchange (for torrents not marked private)
# Enable peer exchange (for torrents not marked private).
#
#protocol.pex.set = yes
# Set downlad list layout style. ("full", "compact")
# Set download list layout style ("full", "compact").
#
#ui.torrent_list.layout.set = "full"
# SCGI Connectivity (for alternative rtorrent interfaces, XMLRPC)
# Run rTorrent as a daemon, controlled via XMLRPC.
#
#system.daemon.set = false
# SCGI Connectivity (for alternative rtorrent interfaces, XMLRPC)
# Use a IP socket with scgi_port, or a Unix socket with scgi_local.
# schedule can be used to set permissions on the unix socket.
#
#scgi_port = 127.0.0.1:5000
#scgi_local = /home/user/rtorrent/rpc.socket
#schedule = scgi_permission,0,0,"execute.nothrow=chmod,\"g+w,o=\",/home/user/rtorrent/rpc.socket"
#network.scgi.open_port = "127.0.0.1:5000"
#network.scgi.open_local = (cat,(session.path),/rpc.sock)
#schedule2 = socket_chmod, 0, 0, "execute.nothrow=chmod,770,(cat,(session.path),/rpc.sock)"
+106
View File
@@ -0,0 +1,106 @@
#############################################################################
# A minimal rTorrent configuration that provides the basic features
# you want to have in addition to the built-in defaults.
#
# See https://github.com/rakshasa/rtorrent/wiki/CONFIG-Template
# for an up-to-date version.
#############################################################################
# Instance layout (base paths)
method.insert = cfg.basedir, private|const|string, (cat,"/home/USERNAME/rtorrent/")
method.insert = cfg.download, private|const|string, (cat,(cfg.basedir),"download/")
method.insert = cfg.logs, private|const|string, (cat,(cfg.basedir),"log/")
method.insert = cfg.logfile, private|const|string, (cat,(cfg.logs),"rtorrent-",(system.time),".log")
method.insert = cfg.session, private|const|string, (cat,(cfg.basedir),".session/")
method.insert = cfg.watch, private|const|string, (cat,(cfg.basedir),"watch/")
# Create instance directories
execute.throw = sh, -c, (cat,\
"mkdir -p \"",(cfg.download),"\" ",\
"\"",(cfg.logs),"\" ",\
"\"",(cfg.session),"\" ",\
"\"",(cfg.watch),"/load\" ",\
"\"",(cfg.watch),"/start\" ")
# Listening port for incoming peer traffic (fixed; you can also randomize it)
network.port_range.set = 50000-50000
network.port_random.set = no
# Tracker-less torrent and UDP tracker support
# (conservative settings for 'private' trackers, change for 'public')
dht.mode.set = disable
protocol.pex.set = no
trackers.use_udp.set = no
# Peer settings
throttle.max_uploads.set = 100
throttle.max_uploads.global.set = 250
throttle.min_peers.normal.set = 20
throttle.max_peers.normal.set = 60
throttle.min_peers.seed.set = 30
throttle.max_peers.seed.set = 80
trackers.numwant.set = 80
protocol.encryption.set = allow_incoming,try_outgoing,enable_retry
# Limits for file handle resources, this is optimized for
# an `ulimit` of 1024 (a common default). You MUST leave
# a ceiling of handles reserved for rTorrent's internal needs!
network.http.max_open.set = 50
network.max_open_files.set = 600
network.max_open_sockets.set = 300
# Memory resource usage (increase if you have a large number of items loaded,
# and/or the available resources to spend)
pieces.memory.max.set = 1800M
network.xmlrpc.size_limit.set = 4M
# Basic operational settings (no need to change these)
session.path.set = (cat, (cfg.session))
directory.default.set = (cat, (cfg.download))
log.execute = (cat, (cfg.logs), "execute.log")
##log.xmlrpc = (cat, (cfg.logs), "xmlrpc.log")
execute.nothrow = sh, -c, (cat, "echo >",\
(session.path), "rtorrent.pid", " ", (system.pid))
# Other operational settings (check & adapt)
encoding.add = utf8
system.umask.set = 0027
system.cwd.set = (directory.default)
network.http.dns_cache_timeout.set = 25
schedule2 = monitor_diskspace, 15, 60, ((close_low_diskspace, 1000M))
##pieces.hash.on_completion.set = no
##view.sort_current = seeding, greater=d.ratio=
##keys.layout.set = qwerty
##network.http.capath.set = "/etc/ssl/certs"
##network.http.ssl_verify_peer.set = 0
##network.http.ssl_verify_host.set = 0
# Some additional values and commands
method.insert = system.startup_time, value|const, (system.time)
method.insert = d.data_path, simple,\
"if=(d.is_multi_file),\
(cat, (d.directory), /),\
(cat, (d.directory), /, (d.name))"
method.insert = d.session_file, simple, "cat=(session.path), (d.hash), .torrent"
# Watch directories (add more as you like, but use unique schedule names)
schedule2 = watch_start, 10, 10, ((load.start_verbose, (cat, (cfg.watch), "start/*.torrent")))
schedule2 = watch_load, 11, 10, ((load.verbose, (cat, (cfg.watch), "load/*.torrent")))
# Run the rTorrent process as a daemon in the background
# (and control via XMLRPC sockets)
#system.daemon.set = true
#network.scgi.open_local = (cat,(session.path),rtorrent.sock)
#execute.nothrow = chmod,770,(cat,(session.path),rtorrent.sock)
# Logging:
# Levels = critical error warn notice info debug
# Groups = connection_* dht_* peer_* rpc_* storage_* thread_* tracker_* torrent_*
print = (cat, "Logging to ", (cfg.logfile))
log.open_file = "log", (cfg.logfile)
log.add_output = "info", "log"
##log.add_output = "tracker_debug", "log"
### END of rtorrent.rc ###
+1 -1
View File
@@ -130,7 +130,7 @@ d_stop() {
}
getsession() {
session=$(cat "$1" | grep "^[[:space:]]*session[[:space:]]*=" | sed "s/^[[:space:]]*session[[:space:]]*=[[:space:]]*//" )
session=$(cat "$1" | grep "^[[:space:]]*session.path.set[[:space:]]*=" | sed "s/^[[:space:]]*session.path.set[[:space:]]*=[[:space:]]*//" )
#session=${session/#~/`getent passwd ${user}|cut -d: -f6`}
echo $session
}
+1 -1
View File
@@ -104,7 +104,7 @@ d_stop() {
}
getsession() {
session=`cat "$1" | grep "^[[:space:]]*session[[:space:]]*=" | sed "s/^[[:space:]]*session[[:space:]]*=[[:space:]]*//" `
session=`cat "$1" | grep "^[[:space:]]*session.path.set[[:space:]]*=" | sed "s/^[[:space:]]*session.path.set[[:space:]]*=[[:space:]]*//" `
echo $session
}
+20
View File
@@ -176,6 +176,26 @@ inline int popcount_wrapper(T t) {
#endif
}
// Get the median of an unordered set of numbers of arbitrary
// type by modifing the underlying dataset
template <typename T = double, typename _InputIter>
T median(_InputIter __first, _InputIter __last) {
T __med;
unsigned int __size = __last - __first;
unsigned int __middle = __size / 2;
_InputIter __target1 = __first + __middle;
std::nth_element(__first, __target1, __last);
__med = *__target1;
if (__size % 2 == 0) {
_InputIter __target2 = std::max_element(__first, __target1);
__med = (__med + *__target2) / 2.0;
}
return __med;
}
}
#endif
+54 -3
View File
@@ -39,9 +39,13 @@
#include <algorithm>
#include <cctype>
#include <climits>
#include <cstdlib>
#include <functional>
#include <iterator>
#include <locale>
#include <random>
namespace rak {
@@ -312,11 +316,13 @@ transform_hex_str(const Sequence& seq) {
template <typename Sequence>
Sequence
generate_random(size_t length) {
std::random_device rd;
std::mt19937 mt(rd());
using bytes_randomizer = std::independent_bits_engine<std::mt19937, CHAR_BIT, uint8_t>;
bytes_randomizer bytes(mt);
Sequence s;
s.reserve(length);
std::generate_n(std::back_inserter(s), length, &::random);
std::generate_n(std::back_inserter(s), length, std::ref(bytes));
return s;
}
@@ -371,6 +377,51 @@ is_all_name(const Sequence& src) {
return is_all_name(src.begin(), src.end());
}
template <typename Iterator>
std::string
sanitize(Iterator first, Iterator last) {
std::string dest;
for (; first != last; ++first) {
if (std::isprint(*first) && *first != '\r' && *first != '\n' && *first != '\t')
dest += *first;
else
dest += " ";
}
return dest;
}
template <typename Sequence>
std::string
sanitize(const Sequence& src) {
return trim(sanitize(src.begin(), src.end()));
}
template <typename Iterator>
std::string striptags(Iterator first, Iterator last) {
bool copychar = true;
std::string dest;
for (; first != last; ++first) {
if (std::isprint(*first) && *first == '<') {
copychar = false;
} else if (std::isprint(*first) && *first == '>') {
copychar = true;
continue;
}
if (copychar)
dest += *first;
}
return dest;
}
template <typename Sequence>
std::string striptags(const Sequence& src) {
return striptags(src.begin(), src.end());
}
}
#endif
+87 -55
View File
@@ -12,7 +12,9 @@
# present, along with the associated header file. The NcursesW
# (wide-character) library is searched for first, followed by Ncurses,
# then the system-default plain Curses. The first library found is the
# one returned.
# one returned. Finding libraries will first be attempted by using
# pkg-config, and should the pkg-config files not be available, will
# fallback to combinations of known flags itself.
#
# The following options are understood: --with-ncursesw, --with-ncurses,
# --without-ncursesw, --without-ncurses. The "--with" options force the
@@ -52,23 +54,25 @@
#
# (These preprocessor symbols are discussed later in this document.)
#
# The following output variable is defined by this macro; it is precious
# and may be overridden on the ./configure command line:
# The following output variables are defined by this macro; they are
# precious and may be overridden on the ./configure command line:
#
# CURSES_LIB - library to add to xxx_LDADD
# CURSES_CPPFLAGS - include paths to add to xxx_CPPFLAGS
#
# The library listed in CURSES_LIB is NOT added to LIBS by default. You
# need to add CURSES_LIB to the appropriate xxx_LDADD line in your
# Makefile.am. For example:
# Neither the library listed in CURSES_LIB, nor the flags in
# CURSES_CPPFLAGS are added to LIBS, respectively CPPFLAGS, by default.
# You need to add both to the appropriate xxx_LDADD/xxx_CPPFLAGS line in
# your Makefile.am. For example:
#
# prog_LDADD = @CURSES_LIB@
# prog_CPPFLAGS = @CURSES_CPPFLAGS@
#
# If CURSES_LIB is set on the configure command line (such as by running
# "./configure CURSES_LIB=-lmycurses"), then the only header searched for
# is <curses.h>. The user may use the CPPFLAGS precious variable to
# override the standard #include search path. If the user needs to
# specify an alternative path for a library (such as for a non-standard
# NcurseW), the user should use the LDFLAGS variable.
# is <curses.h>. If the user needs to specify an alternative path for a
# library (such as for a non-standard NcurseW), the user should use the
# LDFLAGS variable.
#
# The following shell variables may be defined by this macro:
#
@@ -88,7 +92,7 @@
#
# AX_WITH_CURSES
# if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then
# AX_MSG_ERROR([requires either NcursesW or Ncurses library])
# AC_MSG_ERROR([requires either NcursesW or Ncurses library])
# fi
#
# If any Curses library will do (but one must be present and must support
@@ -182,33 +186,85 @@
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 13
#serial 16
# internal function to factorize common code that is used by both ncurses
# and ncursesw
AC_DEFUN([_FIND_CURSES_FLAGS], [
AC_MSG_CHECKING([for $1 via pkg-config])
_PKG_CONFIG([_ax_cv_$1_lib], [libs], [$1])
_PKG_CONFIG([_ax_cv_$1_cppflags], [cflags], [$1])
AS_IF([test "x$pkg_failed" = "xyes" || test "x$pkg_failed" = "xuntried"],[
AC_MSG_RESULT([no])
# No suitable .pc file found, have to find flags via fallback
AC_CACHE_CHECK([for $1 via fallback], [ax_cv_$1], [
AS_ECHO()
pkg_cv__ax_cv_$1_lib="-l$1"
pkg_cv__ax_cv_$1_cppflags="$CURSES_CPPFLAGS"
LIBS="$ax_saved_LIBS $pkg_cv__ax_cv_$1_lib"
CPPFLAGS="$ax_saved_CPPFLAGS $pkg_cv__ax_cv_$1_cppflags"
AC_MSG_CHECKING([for initscr() with $pkg_cv__ax_cv_$1_lib])
AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])],
[
AC_MSG_RESULT([yes])
AC_MSG_CHECKING([for nodelay() with $pkg_cv__ax_cv_$1_lib])
AC_LINK_IFELSE([AC_LANG_CALL([], [nodelay])],[
ax_cv_$1=yes
],[
AC_MSG_RESULT([no])
m4_if(
[$1],[ncursesw],[pkg_cv__ax_cv_$1_lib="$pkg_cv__ax_cv_$1_lib -ltinfow"],
[$1],[ncurses],[pkg_cv__ax_cv_$1_lib="$pkg_cv__ax_cv_$1_lib -ltinfo"]
)
LIBS="$ax_saved_LIBS $pkg_cv__ax_cv_$1_lib"
AC_MSG_CHECKING([for nodelay() with $pkg_cv__ax_cv_$1_lib])
AC_LINK_IFELSE([AC_LANG_CALL([], [nodelay])],[
ax_cv_$1=yes
],[
ax_cv_$1=no
])
])
],[
ax_cv_$1=no
])
])
],[
AC_MSG_RESULT([yes])
# Found .pc file, using its information
LIBS="$ax_saved_LIBS $pkg_cv__ax_cv_$1_lib"
CPPFLAGS="$ax_saved_CPPFLAGS $pkg_cv__ax_cv_$1_cppflags"
ax_cv_$1=yes
])
])
AU_ALIAS([MP_WITH_CURSES], [AX_WITH_CURSES])
AC_DEFUN([AX_WITH_CURSES], [
AC_ARG_VAR([CURSES_LIB], [linker library for Curses, e.g. -lcurses])
AC_ARG_VAR([CURSES_CPPFLAGS], [preprocessor flags for Curses, e.g. -I/usr/include/ncursesw])
AC_ARG_WITH([ncurses], [AS_HELP_STRING([--with-ncurses],
[force the use of Ncurses or NcursesW])],
[], [with_ncurses=check])
AC_ARG_WITH([ncursesw], [AS_HELP_STRING([--without-ncursesw],
[do not use NcursesW (wide character support)])],
[], [with_ncursesw=check])
AC_REQUIRE([PKG_PROG_PKG_CONFIG])
ax_saved_LIBS=$LIBS
ax_saved_CPPFLAGS=$CPPFLAGS
AS_IF([test "x$with_ncurses" = xyes || test "x$with_ncursesw" = xyes],
[ax_with_plaincurses=no], [ax_with_plaincurses=check])
ax_cv_curses_which=no
# Test for NcursesW
AS_IF([test "x$CURSES_LIB" = x && test "x$with_ncursesw" != xno], [
LIBS="$ax_saved_LIBS -lncursesw"
_FIND_CURSES_FLAGS([ncursesw])
AC_CACHE_CHECK([for NcursesW wide-character library], [ax_cv_ncursesw], [
AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])],
[ax_cv_ncursesw=yes], [ax_cv_ncursesw=no])
])
AS_IF([test "x$ax_cv_ncursesw" = xno && test "x$with_ncursesw" = xyes], [
AC_MSG_ERROR([--with-ncursesw specified but could not find NcursesW library])
])
@@ -216,7 +272,8 @@ AC_DEFUN([AX_WITH_CURSES], [
AS_IF([test "x$ax_cv_ncursesw" = xyes], [
ax_cv_curses=yes
ax_cv_curses_which=ncursesw
CURSES_LIB="-lncursesw"
CURSES_LIB="$pkg_cv__ax_cv_ncursesw_lib"
CURSES_CPPFLAGS="$pkg_cv__ax_cv_ncursesw_cppflags"
AC_DEFINE([HAVE_NCURSESW], [1], [Define to 1 if the NcursesW library is present])
AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present])
@@ -316,46 +373,15 @@ AC_DEFUN([AX_WITH_CURSES], [
AS_IF([test "x$ax_cv_header_ncursesw_curses_h" = xno && test "x$ax_cv_header_ncursesw_h" = xno && test "x$ax_cv_header_ncurses_h_with_ncursesw" = xno], [
AC_MSG_WARN([could not find a working ncursesw/curses.h, ncursesw.h or ncurses.h])
])
dnl Test if we need to explicitly link against -ltinfow.
AC_CACHE_CHECK([if curses tinfo library is linked properly], [ax_cv_ncurses_compiled], [
LIBS="$ax_saved_LIBS $CURSES_LIB"
AC_LINK_IFELSE([AC_LANG_CALL([], [keypad])], [ax_cv_ncurses_compiled=yes], [ax_cv_ncurses_compiled=no])
LIBS="$ax_saved_LIBS $CURSES_LIB -ltinfo"
AC_LINK_IFELSE([AC_LANG_CALL([], [keypad])], [ax_cv_tinfo_compiled=yes], [ax_cv_tinfo_compiled=no])
LIBS="$ax_saved_LIBS $CURSES_LIB -ltinfow"
AC_LINK_IFELSE([AC_LANG_CALL([], [keypad])], [ax_cv_tinfow_compiled=yes], [ax_cv_tinfow_compiled=no])
LIBS=$ax_saved_LIBS
])
AS_IF([test "x$ax_cv_ncurses_compiled" = xno], [
AS_IF(
[test "x$ax_cv_tinfo_compiled" = xyes], [
AC_MSG_RESULT([adding libtinfo])
CURSES_LIB="$CURSES_LIB -ltinfo"
],
[test "x$ax_cv_tinfow_compiled" = xyes], [
AC_MSG_RESULT([adding libtinfow])
CURSES_LIB="$CURSES_LIB -ltinfow"
], [
AC_MSG_ERROR([no])
])
])
])
])
unset pkg_cv__ax_cv_ncursesw_lib
unset pkg_cv__ax_cv_ncursesw_cppflags
# Test for Ncurses
AS_IF([test "x$CURSES_LIB" = x && test "x$with_ncurses" != xno && test "x$ax_cv_curses_which" = xno], [
LIBS="$ax_saved_LIBS -lncurses"
_FIND_CURSES_FLAGS([ncurses])
AC_CACHE_CHECK([for Ncurses library], [ax_cv_ncurses], [
AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])],
[ax_cv_ncurses=yes], [ax_cv_ncurses=no])
])
AS_IF([test "x$ax_cv_ncurses" = xno && test "x$with_ncurses" = xyes], [
AC_MSG_ERROR([--with-ncurses specified but could not find Ncurses library])
])
@@ -363,7 +389,8 @@ AC_DEFUN([AX_WITH_CURSES], [
AS_IF([test "x$ax_cv_ncurses" = xyes], [
ax_cv_curses=yes
ax_cv_curses_which=ncurses
CURSES_LIB="-lncurses"
CURSES_LIB="$pkg_cv__ax_cv_ncurses_lib"
CURSES_CPPFLAGS="$pkg_cv__ax_cv_ncurses_cppflags"
AC_DEFINE([HAVE_NCURSES], [1], [Define to 1 if the Ncurses library is present])
AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present])
@@ -418,9 +445,10 @@ AC_DEFUN([AX_WITH_CURSES], [
])
])
])
unset pkg_cv__ax_cv_ncurses_lib
unset pkg_cv__ax_cv_ncurses_cppflags
# Test for plain Curses (or if CURSES_LIB was set by user)
AS_IF([test "x$with_plaincurses" != xno && test "x$ax_cv_curses_which" = xno], [
AS_IF([test "x$CURSES_LIB" != x], [
LIBS="$ax_saved_LIBS $CURSES_LIB"
@@ -543,4 +571,8 @@ AC_DEFUN([AX_WITH_CURSES], [
AS_IF([test "x$ax_cv_curses_obsolete" != xyes], [ax_cv_curses_obsolete=no])
LIBS=$ax_saved_LIBS
CPPFLAGS=$ax_saved_CPPFLAGS
unset ax_saved_LIBS
unset ax_saved_CPPFLAGS
])dnl
+3 -3
View File
@@ -96,7 +96,7 @@ AC_DEFUN([TORRENT_CHECK_KQUEUE], [
AC_DEFUN([TORRENT_CHECK_KQUEUE_SOCKET_ONLY], [
AC_MSG_CHECKING(whether kqueue supports pipes and ptys)
AC_RUN_IFELSE([AC_LANG_SOURCE([
AC_LINK_IFELSE([AC_LANG_SOURCE([
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
@@ -174,8 +174,8 @@ AC_DEFUN([TORRENT_WITHOUT_VARIABLE_FDSET], [
AC_DEFUN([TORRENT_CHECK_FALLOCATE], [
AC_MSG_CHECKING(for fallocate)
AC_TRY_LINK([#include <fcntl.h>
#include <linux/falloc.h>
AC_TRY_LINK([#define _GNU_SOURCE
#include <fcntl.h>
],[ fallocate(0, FALLOC_FL_KEEP_SIZE, 0, 0); return 0;
],
[
+2 -2
View File
@@ -153,7 +153,7 @@ dnl Need to fix this so that it uses the stuff defined by the system.
AC_DEFUN([TORRENT_CHECK_EXECINFO], [
AC_MSG_CHECKING(for execinfo.h)
AC_RUN_IFELSE([AC_LANG_SOURCE([
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
#include <execinfo.h>
int main() { backtrace((void**)0, 0); backtrace_symbols((char**)0, 0); return 0;}
])],
@@ -168,7 +168,7 @@ AC_DEFUN([TORRENT_CHECK_EXECINFO], [
AC_DEFUN([TORRENT_CHECK_ALIGNED], [
AC_MSG_CHECKING(the byte alignment)
AC_RUN_IFELSE([AC_LANG_SOURCE([
AC_LINK_IFELSE([AC_LANG_SOURCE([
#include <inttypes.h>
int main() {
char buf@<:@8@:>@ = { 0, 0, 0, 0, 1, 0, 0, 0 };
+39
View File
@@ -274,6 +274,41 @@ retrieve_d_custom_throw(core::Download* download, const std::string& key) {
}
}
torrent::Object
retrieve_d_custom_if_z(core::Download* download, const torrent::Object::list_type& args) {
torrent::Object::list_const_iterator itr = args.begin();
if (itr == args.end())
throw torrent::bencode_error("d.custom.if_z: Missing key argument.");
const std::string& key = (itr++)->as_string();
if (key.empty())
throw torrent::bencode_error("d.custom.if_z: Empty key argument.");
if (itr == args.end())
throw torrent::bencode_error("d.custom.if_z: Missing default argument.");
try {
const std::string& val = download->bencode()->get_key("rtorrent").get_key("custom").get_key_string(key);
return val.empty() ? itr->as_string() : val;
} catch (torrent::bencode_error& e) {
return itr->as_string();
}
}
torrent::Object
retrieve_d_custom_map(core::Download* download, bool keys_only, const torrent::Object::list_type& args) {
if (args.begin() != args.end())
throw torrent::bencode_error("d.custom.keys/items takes no arguments.");
torrent::Object result = keys_only ? torrent::Object::create_list() : torrent::Object::create_map();
torrent::Object::map_type& entries = download->bencode()->get_key("rtorrent").get_key("custom").as_map();
for (torrent::Object::map_type::const_iterator itr = entries.begin(), last = entries.end(); itr != last; itr++) {
if (keys_only) result.as_list().push_back(itr->first);
else result.as_map()[itr->first] = itr->second;
}
return result;
}
torrent::Object
retrieve_d_bitfield(core::Download* download) {
const torrent::Bitfield* bitField = download->download()->file_list()->bitfield();
@@ -710,6 +745,9 @@ initialize_command_download() {
CMD2_DL_STRING("d.custom", std::bind(&retrieve_d_custom, std::placeholders::_1, std::placeholders::_2));
CMD2_DL_STRING("d.custom_throw", std::bind(&retrieve_d_custom_throw, std::placeholders::_1, std::placeholders::_2));
CMD2_DL_LIST ("d.custom.set", std::bind(&apply_d_custom, std::placeholders::_1, std::placeholders::_2));
CMD2_DL_LIST ("d.custom.if_z", std::bind(&retrieve_d_custom_if_z, std::placeholders::_1, std::placeholders::_2));
CMD2_DL_LIST ("d.custom.keys", std::bind(&retrieve_d_custom_map, std::placeholders::_1, true, std::placeholders::_2));
CMD2_DL_LIST ("d.custom.items", std::bind(&retrieve_d_custom_map, std::placeholders::_1, false, std::placeholders::_2));
CMD2_DL_VAR_STRING_PUBLIC("d.custom1", "rtorrent", "custom1");
CMD2_DL_VAR_STRING_PUBLIC("d.custom2", "rtorrent", "custom2");
@@ -835,6 +873,7 @@ initialize_command_download() {
CMD2_DL ("d.wanted_chunks", CMD2_ON_DATA(wanted_chunks));
CMD2_DL_V ("d.tracker_announce", std::bind(&torrent::Download::manual_request, CMD2_BIND_DL, false));
CMD2_DL_V ("d.tracker_announce.force", std::bind(&torrent::Download::manual_request, CMD2_BIND_DL, true));
CMD2_DL ("d.tracker_numwant", std::bind(&torrent::TrackerList::numwant, CMD2_BIND_TL));
CMD2_DL_VALUE_V ("d.tracker_numwant.set", std::bind(&torrent::TrackerList::set_numwant, CMD2_BIND_TL, std::placeholders::_2));
// TODO: Deprecate 'd.tracker_focus'.
+23 -5
View File
@@ -139,6 +139,8 @@ system_method_insert_object(const torrent::Object::list_type& args, int flags) {
case rpc::object_storage::flag_function_type:
system_method_generate_command2(&value, itrArgs, args.end());
break;
case rpc::object_storage::flag_list_type:
break;
case rpc::object_storage::flag_multi_type:
break;
default:
@@ -152,7 +154,17 @@ system_method_insert_object(const torrent::Object::list_type& args, int flags) {
if (!(flags & rpc::object_storage::flag_private))
cmd_flags |= rpc::CommandMap::flag_public_xmlrpc;
control->object_storage()->insert_str(rawKey, value, flags);
if ((flags & rpc::object_storage::mask_type) == rpc::object_storage::flag_list_type) {
torrent::Object valueList = torrent::Object::create_list();
torrent::Object::list_type& valueListType = valueList.as_list();
if ((itrArgs)->is_list())
valueListType = (itrArgs)->as_list();
control->object_storage()->insert_str(rawKey, valueList, flags);
} else {
control->object_storage()->insert_str(rawKey, value, flags);
}
if ((flags & rpc::object_storage::mask_type) == rpc::object_storage::flag_function_type ||
(flags & rpc::object_storage::mask_type) == rpc::object_storage::flag_multi_type) {
@@ -208,6 +220,13 @@ system_method_insert_object(const torrent::Object::list_type& args, int flags) {
&rpc::command_base_call_string<rpc::target_type>,
cmd_flags, NULL, NULL);
break;
case rpc::object_storage::flag_list_type:
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call_list<rpc::target_type> >::type>
(create_new_key<5>(rawKey, ".set"),
std::bind(&rpc::object_storage::set_str_list, control->object_storage(), rawKey, std::placeholders::_2),
&rpc::command_base_call_list<rpc::target_type>,
cmd_flags, NULL, NULL);
break;
case rpc::object_storage::flag_function_type:
case rpc::object_storage::flag_multi_type:
default: break;
@@ -310,10 +329,6 @@ system_method_insert(const torrent::Object::list_type& args) {
new_flags = rpc::object_storage::flag_string_type;
else if (options.find("list") != std::string::npos)
new_flags = rpc::object_storage::flag_list_type;
else if (options.find("simple") != std::string::npos)
new_flags = rpc::object_storage::flag_function_type;
else
throw torrent::input_error("No support for 'list' variable type.");
if (options.find("static") != std::string::npos)
new_flags |= rpc::object_storage::flag_static;
@@ -447,6 +462,9 @@ initialize_command_dynamic() {
CMD2_ANY_LIST ("method.insert", std::bind(&system_method_insert, std::placeholders::_2));
CMD2_ANY_LIST ("method.insert.value", std::bind(&system_method_insert_object, std::placeholders::_2, rpc::object_storage::flag_value_type));
CMD2_ANY_LIST ("method.insert.bool", std::bind(&system_method_insert_object, std::placeholders::_2, rpc::object_storage::flag_bool_type));
CMD2_ANY_LIST ("method.insert.string", std::bind(&system_method_insert_object, std::placeholders::_2, rpc::object_storage::flag_string_type));
CMD2_ANY_LIST ("method.insert.list", std::bind(&system_method_insert_object, std::placeholders::_2, rpc::object_storage::flag_list_type));
CMD2_METHOD_INSERT("method.insert.simple", rpc::object_storage::flag_function_type);
CMD2_METHOD_INSERT("method.insert.c_simple", rpc::object_storage::flag_constant | rpc::object_storage::flag_function_type);
+37
View File
@@ -307,6 +307,42 @@ d_multicall(const torrent::Object::list_type& args) {
return resultRaw;
}
torrent::Object
d_multicall_filtered(const torrent::Object::list_type& args) {
if (args.size() < 2)
throw torrent::input_error("d.multicall.filtered requires at least 2 arguments.");
torrent::Object::list_const_iterator arg = args.begin();
// Find the given view
core::ViewManager* viewManager = control->view_manager();
core::ViewManager::iterator viewItr = viewManager->find(arg->as_string().empty() ? "default" : arg->as_string());
if (viewItr == viewManager->end())
throw torrent::input_error("Could not find view '" + arg->as_string() + "'.");
// Make a filtered copy of the current item list
core::View::base_type dlist;
(*viewItr)->filter_by(*++arg, dlist);
// Generate result by iterating over all items
torrent::Object resultRaw = torrent::Object::create_list();
torrent::Object::list_type& result = resultRaw.as_list();
++arg; // skip to first command
for (core::View::iterator item = dlist.begin(); item != dlist.end(); ++item) {
// Add empty row to result
torrent::Object::list_type& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
// Call the provided commands and assemble their results
for (torrent::Object::list_const_iterator command = arg; command != args.end(); command++) {
const std::string& cmdstr = command->as_string();
row.push_back(rpc::parse_command(rpc::make_target(*item), cmdstr.c_str(), cmdstr.c_str() + cmdstr.size()).first);
}
}
return resultRaw;
}
static void
call_watch_command(const std::string& command, const std::string& path) {
rpc::commands.call_catch(command.c_str(), rpc::make_target(), path);
@@ -359,6 +395,7 @@ initialize_command_events() {
CMD2_ANY_LIST ("download_list", std::bind(&apply_download_list, std::placeholders::_2));
CMD2_ANY_LIST ("d.multicall2", std::bind(&d_multicall, std::placeholders::_2));
CMD2_ANY_LIST ("d.multicall.filtered", std::bind(&d_multicall_filtered, std::placeholders::_2));
CMD2_ANY_LIST ("directory.watch.added", std::bind(&directory_watch_added, std::placeholders::_2));
}
+3 -37
View File
@@ -1,39 +1,3 @@
// rTorrent - BitTorrent client
// Copyright (C) 2005-2011, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <jaris@ifi.uio.no>
//
// Skomakerveien 33
// 3185 Skoppum, NORWAY
#include "config.h"
#include <fcntl.h>
@@ -167,7 +131,9 @@ initialize_command_logging() {
CMD2_ANY_LIST ("log.open_file_pid", std::bind(&apply_log_open, log_flag_append_pid, std::placeholders::_2));
CMD2_ANY_LIST ("log.open_gz_file_pid", std::bind(&apply_log_open, log_flag_append_pid | log_flag_use_gz, std::placeholders::_2));
CMD2_ANY_LIST ("log.add_output", std::bind(&apply_log_add_output, std::placeholders::_2));
CMD2_ANY_STRING_V("log.close", std::bind(&torrent::log_close_output_str, std::placeholders::_2));
CMD2_ANY_LIST ("log.add_output", std::bind(&apply_log_add_output, std::placeholders::_2));
CMD2_ANY_STRING ("log.execute", std::bind(&apply_log, std::placeholders::_2, 0));
CMD2_ANY_STRING ("log.vmmap.dump", std::bind(&log_vmmap_dump, std::placeholders::_2));
+2 -4
View File
@@ -167,8 +167,7 @@ apply_scgi(const std::string& arg, int type) {
sa.sa_inet()->clear();
saPtr = &sa;
lt_log_print(torrent::LOG_RPC_EVENTS,
"The SCGI socket has not been bound to any address and likely poses a security risk.");
lt_log_print(torrent::LOG_RPC_EVENTS, "SCGI socket is open to any address and is a security risk");
} else if (std::sscanf(arg.c_str(), "%1023[^:]:%i%c", address, &port, &dummy) == 2 ||
std::sscanf(arg.c_str(), "[%64[^]]]:%i%c", address, &port, &dummy) == 2) { // [xx::xx]:port format
@@ -177,8 +176,7 @@ apply_scgi(const std::string& arg, int type) {
saPtr = ai->address();
lt_log_print(torrent::LOG_RPC_EVENTS,
"The SCGI socket is bound to a specific network device yet may still pose a security risk, consider using 'scgi_local'.");
lt_log_print(torrent::LOG_RPC_EVENTS, "SCGI socket is bound to an address and might be a security risk");
} else {
throw torrent::input_error("Could not parse address.");
+286 -2
View File
@@ -39,8 +39,12 @@
#include <sys/types.h>
#include <ctime>
#include <regex>
#include <rak/algorithm.h>
#include <rak/functional.h>
#include <rak/functional_fun.h>
#include <torrent/utils/log.h>
#include "core/manager.h"
#include "core/view_manager.h"
@@ -147,6 +151,41 @@ apply_cat(rpc::target_type target, const torrent::Object& rawArgs) {
return result;
}
torrent::Object
apply_value(rpc::target_type target, const torrent::Object::list_type& args) {
if (args.size() < 1)
throw torrent::input_error("'value' takes at least a number argument!");
if (args.size() > 2)
throw torrent::input_error("'value' takes at most two arguments!");
torrent::Object::value_type val = 0;
if (args.front().is_value()) {
val = args.front().as_value();
} else {
int base = args.size() > 1 ? args.back().is_value() ?
args.back().as_value() : strtol(args.back().as_string().c_str(), NULL, 10) : 10;
char* endptr = 0;
val = strtoll(args.front().as_string().c_str(), &endptr, base);
while (*endptr == ' ' || *endptr == '\n') ++endptr;
if (*endptr)
throw torrent::input_error("Junk at end of number: " + args.front().as_string());
}
return val;
}
torrent::Object
apply_try(rpc::target_type target, const torrent::Object& args) {
try {
return rpc::call_object(args, target);
} catch (torrent::input_error& e) {
lt_log_print(torrent::LOG_RPC_EVENTS, "try command caught input_error: %s", e.what());
}
return torrent::Object();
}
// Move these boolean operators to a new file.
inline bool
@@ -282,6 +321,103 @@ torrent::Object apply_equal(rpc::target_type target, const torrent::Object::list
return result.is_value() ? result.as_value() == 0 : (int64_t)false;
}
torrent::Object
apply_compare(rpc::target_type target, const torrent::Object::list_type& args) {
if (!rpc::is_target_pair(target))
throw torrent::input_error("Can only compare a target pair.");
if (args.size() < 2)
throw torrent::input_error("Need at least order and one field.");
torrent::Object::list_const_iterator itr = args.begin();
std::string order = (itr++)->as_string();
const char* current = order.c_str();
torrent::Object result1;
torrent::Object result2;
for (torrent::Object::list_const_iterator last = args.end(); itr != last; itr++) {
std::string field = itr->as_string();
result1 = rpc::parse_command_single(rpc::get_target_left(target), field);
result2 = rpc::parse_command_single(rpc::get_target_right(target), field);
if (result1.type() != result2.type())
throw torrent::input_error(std::string("Type mismatch in compare of ") + field);
bool descending = *current == 'd' || *current == 'D' || *current == '-';
if (*current) {
if (!descending && !(*current == 'a' || *current == 'A' || *current == '+'))
throw torrent::input_error(std::string("Bad order '") + *current + "' in " + order);
++current;
}
switch (result1.type()) {
case torrent::Object::TYPE_VALUE:
if (result1.as_value() != result2.as_value())
return (int64_t) (descending ^ (result1.as_value() < result2.as_value()));
break;
case torrent::Object::TYPE_STRING:
if (result1.as_string() != result2.as_string())
return (int64_t) (descending ^ (result1.as_string() < result2.as_string()));
break;
default:
break; // treat unknown types as equal
}
}
// if all else is equal, ensure stable sort order based on memory location
return (int64_t) (target.second < target.third);
}
// Regexp based 'match' function.
// arg1: the text to match.
// arg2: the regexp pattern.
// eg: match{d.name=,.*linux.*iso}
torrent::Object apply_match(rpc::target_type target, const torrent::Object::list_type& args) {
if (args.size() != 2)
throw torrent::input_error("Wrong argument count for 'match': 2 arguments needed.");
// This really should be converted to using args flagged as
// commands, so that we can compare commands and statics values.
torrent::Object result1;
torrent::Object result2;
rpc::target_type target1 = rpc::is_target_pair(target) ? rpc::get_target_left(target) : target;
rpc::target_type target2 = rpc::is_target_pair(target) ? rpc::get_target_right(target) : target;
if (args.front().is_dict_key())
result1 = rpc::commands.call_command(args.front().as_dict_key().c_str(), args.front().as_dict_obj(), target1);
else
result1 = rpc::parse_command_single(target1, args.front().as_string());
if (args.back().is_dict_key())
result2 = rpc::commands.call_command(args.back().as_dict_key().c_str(), args.back().as_dict_obj(), target2);
else
result2 = args.back().as_string();
if (result1.type() != result2.type())
throw torrent::input_error("Type mismatch for 'match' arguments.");
std::string text = result1.as_string();
std::string pattern = result2.as_string();
std::transform(text.begin(), text.end(), text.begin(), ::tolower);
std::transform(pattern.begin(), pattern.end(), pattern.begin(), ::tolower);
bool isAMatch = false;
try {
std::regex re(pattern);
isAMatch = std::regex_match(text, re);
} catch (const std::regex_error& exc) {
control->core()->push_log_std("regex_error: " + std::string(exc.what()));
}
return isAMatch ? (int64_t)true : (int64_t)false;
}
torrent::Object
apply_to_time(const torrent::Object& rawArgs, int flags) {
std::tm *u;
@@ -519,6 +655,128 @@ apply_elapsed_greater(const torrent::Object::list_type& args) {
return (int64_t)(start_time != 0 && rak::timer::current_seconds() - start_time > rpc::convert_to_value(args.back()));
}
inline std::vector<int64_t>
as_vector(const torrent::Object::list_type& args) {
if (args.size() == 0)
throw torrent::input_error("Wrong argument count in as_vector.");
std::vector<int64_t> result;
for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) {
if (itr->is_value()) {
result.push_back(itr->as_value());
} else if (itr->is_string()) {
result.push_back(rpc::convert_to_value(itr->as_string()));
} else if (itr->is_list()) {
std::vector<int64_t> subResult = as_vector(itr->as_list());
result.insert(result.end(), subResult.begin(), subResult.end());
} else {
throw torrent::input_error("Wrong type supplied to as_vector.");
}
}
return result;
}
int64_t
apply_math_basic(const char* name, const std::function<int64_t(int64_t,int64_t)> op, const torrent::Object::list_type& args) {
int64_t val = 0, rhs = 0;
bool divides = !strcmp(name, "math.div") || !strcmp(name, "math.mod");
if (args.size() == 0)
throw torrent::input_error(std::string(name) + ": No arguments provided!");
for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) {
if (itr->is_value()) {
rhs = itr->as_value();
} else if (itr->is_string()) {
rhs = rpc::convert_to_value(itr->as_string());
} else if (itr->is_list()) {
rhs = apply_math_basic(name, op, itr->as_list());
} else {
throw torrent::input_error(std::string(name) + ": Wrong argument type");
}
if (divides && !rhs && itr != args.begin())
throw torrent::input_error(std::string(name) + ": Division by zero!");
val = itr == args.begin() ? rhs : op(val, rhs);
}
return val;
}
int64_t
apply_arith_basic(const std::function<int64_t(int64_t,int64_t)> op, const torrent::Object::list_type& args) {
if (args.size() == 0)
throw torrent::input_error("Wrong argument count in apply_arith_basic.");
int64_t val = 0;
for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) {
if (itr->is_value()) {
val = itr == args.begin() ? itr->as_value() : (op(val, itr->as_value()) ? val : itr->as_value());
} else if (itr->is_string()) {
int64_t cval = rpc::convert_to_value(itr->as_string());
val = itr == args.begin() ? cval : (op(val, cval) ? val : cval);
} else if (itr->is_list()) {
int64_t fval = apply_arith_basic(op, itr->as_list());
val = itr == args.begin() ? fval : (op(val, fval) ? val : fval);
} else {
throw torrent::input_error("Wrong type supplied to apply_arith_basic.");
}
}
return val;
}
int64_t
apply_arith_count(const torrent::Object::list_type& args) {
if (args.size() == 0)
throw torrent::input_error("Wrong argument count in apply_arith_count.");
int64_t val = 0;
for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) {
switch (itr->type()) {
case torrent::Object::TYPE_VALUE:
case torrent::Object::TYPE_STRING:
val++;
break;
case torrent::Object::TYPE_LIST:
val += apply_arith_count(itr->as_list());
break;
default:
throw torrent::input_error("Wrong type supplied to apply_arith_count.");
}
}
return val;
}
int64_t
apply_arith_other(const char* op, const torrent::Object::list_type& args) {
if (args.size() == 0)
throw torrent::input_error("Wrong argument count in apply_arith_other.");
if (strcmp(op, "average") == 0) {
return (int64_t)(apply_math_basic(op, std::plus<int64_t>(), args) / apply_arith_count(args));
} else if (strcmp(op, "median") == 0) {
std::vector<int64_t> result = as_vector(args);
return (int64_t)rak::median(result.begin(), result.end());
} else {
throw torrent::input_error("Wrong operation supplied to apply_arith_other.");
}
}
void
initialize_command_ui() {
CMD2_VAR_STRING("keys.layout", "qwerty");
@@ -528,8 +786,11 @@ initialize_command_ui() {
CMD2_ANY_L ("view.list", std::bind(&apply_view_list));
CMD2_ANY_LIST("view.set", std::bind(&apply_view_set, std::placeholders::_2));
CMD2_ANY_LIST("view.filter", std::bind(&apply_view_event, &core::ViewManager::set_filter, std::placeholders::_2));
CMD2_ANY_LIST("view.filter_on", std::bind(&apply_view_filter_on, std::placeholders::_2));
CMD2_ANY_LIST ("view.filter", std::bind(&apply_view_event, &core::ViewManager::set_filter, std::placeholders::_2));
CMD2_ANY_LIST ("view.filter_on", std::bind(&apply_view_filter_on, std::placeholders::_2));
CMD2_ANY_LIST ("view.filter.temp", std::bind(&apply_view_event, &core::ViewManager::set_filter_temp, std::placeholders::_2));
CMD2_VAR_STRING("view.filter.temp.excluded", "default,started,stopped");
CMD2_VAR_BOOL ("view.filter.temp.log", 0);
CMD2_ANY_LIST("view.sort", std::bind(&apply_view_sort, std::placeholders::_2));
CMD2_ANY_LIST("view.sort_new", std::bind(&apply_view_event, &core::ViewManager::set_sort_new, std::placeholders::_2));
@@ -555,12 +816,22 @@ initialize_command_ui() {
CMD2_ANY ("ui.current_view", std::bind(&cmd_ui_current_view));
CMD2_ANY_STRING("ui.current_view.set", std::bind(&cmd_ui_set_view, std::placeholders::_2));
CMD2_ANY ("ui.input.history.size", std::bind(&ui::Root::get_input_history_size, control->ui()));
CMD2_ANY_VALUE_V("ui.input.history.size.set", std::bind(&ui::Root::set_input_history_size, control->ui(), std::placeholders::_2));
CMD2_ANY_V ("ui.input.history.clear", std::bind(&ui::Root::clear_input_history, control->ui()));
CMD2_VAR_VALUE ("ui.throttle.global.step.small", 5);
CMD2_VAR_VALUE ("ui.throttle.global.step.medium", 50);
CMD2_VAR_VALUE ("ui.throttle.global.step.large", 500);
// TODO: Add 'option_string' for rtorrent-specific options.
CMD2_VAR_STRING("ui.torrent_list.layout", "full");
// Move.
CMD2_ANY("print", &apply_print);
CMD2_ANY("cat", &apply_cat);
CMD2_ANY_LIST("value", &apply_value);
CMD2_ANY("try", &apply_try);
CMD2_ANY("if", std::bind(&apply_if, std::placeholders::_1, std::placeholders::_2, 0));
CMD2_ANY("not", &apply_not);
CMD2_ANY("false", &apply_false);
@@ -574,6 +845,8 @@ initialize_command_ui() {
CMD2_ANY_LIST("less", &apply_less);
CMD2_ANY_LIST("greater", &apply_greater);
CMD2_ANY_LIST("equal", &apply_equal);
CMD2_ANY_LIST("compare", &apply_compare);
CMD2_ANY_LIST("match", &apply_match);
CMD2_ANY_VALUE("convert.gm_time", std::bind(&apply_to_time, std::placeholders::_2, 0));
CMD2_ANY_VALUE("convert.gm_date", std::bind(&apply_to_time, std::placeholders::_2, 0x2));
@@ -585,6 +858,17 @@ initialize_command_ui() {
CMD2_ANY_VALUE("convert.xb", std::bind(&apply_to_xb, std::placeholders::_2));
CMD2_ANY_VALUE("convert.throttle", std::bind(&apply_to_throttle, std::placeholders::_2));
CMD2_ANY_LIST("math.add", std::bind(&apply_math_basic, "math.add", std::plus<int64_t>(), std::placeholders::_2));
CMD2_ANY_LIST("math.sub", std::bind(&apply_math_basic, "math.sub", std::minus<int64_t>(), std::placeholders::_2));
CMD2_ANY_LIST("math.mul", std::bind(&apply_math_basic, "math.mul", std::multiplies<int64_t>(), std::placeholders::_2));
CMD2_ANY_LIST("math.div", std::bind(&apply_math_basic, "math.div", std::divides<int64_t>(), std::placeholders::_2));
CMD2_ANY_LIST("math.mod", std::bind(&apply_math_basic, "math.mod", std::modulus<int64_t>(), std::placeholders::_2));
CMD2_ANY_LIST("math.min", std::bind(&apply_arith_basic, std::less<int64_t>(), std::placeholders::_2));
CMD2_ANY_LIST("math.max", std::bind(&apply_arith_basic, std::greater<int64_t>(), std::placeholders::_2));
CMD2_ANY_LIST("math.cnt", std::bind(&apply_arith_count, std::placeholders::_2));
CMD2_ANY_LIST("math.avg", std::bind(&apply_arith_other, "average", std::placeholders::_2));
CMD2_ANY_LIST("math.med", std::bind(&apply_arith_other, "median", std::placeholders::_2));
CMD2_ANY_LIST ("elapsed.less", std::bind(&apply_elapsed_less, std::placeholders::_2));
CMD2_ANY_LIST ("elapsed.greater", std::bind(&apply_elapsed_greater, std::placeholders::_2));
}
+2
View File
@@ -168,6 +168,8 @@ Control::is_shutdown_completed() {
void
Control::handle_shutdown() {
rpc::commands.call_catch("event.system.shutdown", rpc::make_target(), "shutdown", "System shutdown event action failed: ");
if (!m_shutdownQuick) {
// Temporary hack:
if (worker_thread->is_active())
+2
View File
@@ -63,6 +63,7 @@
#include "download.h"
#include "download_list.h"
#include "download_store.h"
#include "ui/root.h"
#define DL_TRIGGER_EVENT(download, event_name) \
rpc::commands.call_catch(event_name, rpc::make_target(download), torrent::Object(), "Event '" event_name "' failed: ");
@@ -93,6 +94,7 @@ DownloadList::session_save() {
lt_log_print(torrent::LOG_ERROR, "Failed to save session torrents.");
control->dht_manager()->save_dht_cache();
control->ui()->save_input_history();
}
DownloadList::iterator
+24 -10
View File
@@ -89,17 +89,21 @@ struct view_downloads_compare : std::binary_function<Download*, Download*, bool>
};
struct view_downloads_filter : std::unary_function<Download*, bool> {
view_downloads_filter(const torrent::Object& cmd) : m_command(cmd) {}
view_downloads_filter(const torrent::Object& cmd, const torrent::Object& cmd2) : m_command(cmd), m_command2(cmd2) {}
bool operator () (Download* d1) const {
if (m_command.is_empty())
return this->evalCmd(m_command, d1) && this->evalCmd(m_command2, d1);
}
bool evalCmd(const torrent::Object& cmd, Download* d1) const {
if (cmd.is_empty())
return true;
try {
torrent::Object result;
if (m_command.is_dict_key()) {
// torrent::Object tmp_command = m_command;
if (cmd.is_dict_key()) {
// torrent::Object tmp_command = cmd;
// uint32_t flags = tmp_command.flags() & torrent::Object::mask_function;
// tmp_command.unset_flags(torrent::Object::mask_function);
@@ -109,10 +113,10 @@ struct view_downloads_filter : std::unary_function<Download*, bool> {
// result = rpc::commands.call_command(tmp_command.as_dict_key().c_str(), tmp_command.as_dict_obj(),
// rpc::make_target(d1));
result = rpc::commands.call_command(m_command.as_dict_key().c_str(), m_command.as_dict_obj(), rpc::make_target(d1));
result = rpc::commands.call_command(cmd.as_dict_key().c_str(), cmd.as_dict_obj(), rpc::make_target(d1));
} else {
result = rpc::parse_command_single(rpc::make_target(d1), m_command.as_string());
result = rpc::parse_command_single(rpc::make_target(d1), cmd.as_string());
}
switch (result.type()) {
@@ -136,6 +140,7 @@ struct view_downloads_filter : std::unary_function<Download*, bool> {
}
const torrent::Object& m_command;
const torrent::Object& m_command2;
};
void
@@ -262,8 +267,8 @@ View::filter() {
return;
// Parition the list in two steps so we know which elements changed.
iterator splitVisible = std::stable_partition(begin_visible(), end_visible(), view_downloads_filter(m_filter));
iterator splitFiltered = std::stable_partition(begin_filtered(), end_filtered(), view_downloads_filter(m_filter));
iterator splitVisible = std::stable_partition(begin_visible(), end_visible(), view_downloads_filter(m_filter, m_temp_filter));
iterator splitFiltered = std::stable_partition(begin_filtered(), end_filtered(), view_downloads_filter(m_filter, m_temp_filter));
base_type changed(splitVisible, splitFiltered);
iterator splitChanged = changed.begin() + std::distance(splitVisible, end_visible());
@@ -295,6 +300,16 @@ View::filter() {
emit_changed();
}
void
View::filter_by(const torrent::Object& condition, View::base_type& result) {
// std::copy_if(begin_visible(), end_visible(), result.begin(), view_downloads_filter(condition));
view_downloads_filter matches = view_downloads_filter(condition, m_temp_filter);
for (iterator itr = begin_visible(); itr != end_visible(); ++itr)
if (matches(*itr))
result.push_back(*itr);
}
void
View::filter_download(core::Download* download) {
iterator itr = std::find(base_type::begin(), base_type::end(), download);
@@ -302,8 +317,7 @@ View::filter_download(core::Download* download) {
if (itr == base_type::end())
throw torrent::internal_error("View::filter_download(...) could not find download.");
if (view_downloads_filter(m_filter)(download)) {
if (view_downloads_filter(m_filter, m_temp_filter)(download)) {
if (itr >= end_visible()) {
erase_internal(itr);
insert_visible(download);
+5 -1
View File
@@ -120,10 +120,13 @@ public:
// Need to explicity trigger filtering.
void filter();
void filter_by(const torrent::Object& condition, base_type& result);
void filter_download(core::Download* download);
const torrent::Object& get_filter() const { return m_filter; }
void set_filter(const torrent::Object& s) { m_filter = s; }
void set_filter(const torrent::Object& s) { m_filter = s; }
const torrent::Object& get_filter_temp() const { return m_temp_filter; }
void set_filter_temp(const torrent::Object& s) { m_temp_filter = s; }
void set_filter_on_event(const std::string& event);
void clear_filter_on();
@@ -172,6 +175,7 @@ private:
torrent::Object m_sortCurrent;
torrent::Object m_filter;
torrent::Object m_temp_filter; // Temporary view filter (eg: name based filter)
torrent::Object m_event_added;
torrent::Object m_event_removed;
+8
View File
@@ -109,6 +109,14 @@ ViewManager::set_filter(const std::string& name, const torrent::Object& cmd) {
(*viewItr)->filter();
}
void
ViewManager::set_filter_temp(const std::string& name, const torrent::Object& cmd) {
iterator viewItr = find_throw(name);
(*viewItr)->set_filter_temp(cmd);
(*viewItr)->filter();
}
void
ViewManager::set_filter_on(const std::string& name, const filter_args& args) {
iterator viewItr = find_throw(name);
+1
View File
@@ -93,6 +93,7 @@ public:
void set_sort_current(const std::string& name, const torrent::Object& cmd) { (*find_throw(name))->set_sort_current(cmd); }
void set_filter(const std::string& name, const torrent::Object& cmd);
void set_filter_temp(const std::string& name, const torrent::Object& cmd);
void set_filter_on(const std::string& name, const filter_args& args);
void set_event_added(const std::string& name, const torrent::Object& cmd) { (*find_throw(name))->set_event_added(cmd); }
-36
View File
@@ -1,39 +1,3 @@
// rTorrent - BitTorrent client
// Copyright (C) 2005-2011, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <jaris@ifi.uio.no>
//
// Skomakerveien 33
// 3185 Skoppum, NORWAY
#ifndef RTORRENT_DISPLAY_ATTRIBUTES_H
#define RTORRENT_DISPLAY_ATTRIBUTES_H
+10 -1
View File
@@ -81,11 +81,20 @@ WindowDownloadList::redraw() {
if (m_view == NULL)
return;
m_canvas->print(0, 0, "%s", ("[View: " + m_view->name() + "]").c_str());
m_canvas->print(0, 0, "%s", ("[View: " + m_view->name() + (m_view->get_filter_temp().is_empty() ? "" : " (filtered)") + "]").c_str());
if (m_view->empty_visible() || m_canvas->width() < 5 || m_canvas->height() < 2)
return;
// show "X of Y"
if (m_canvas->width() > 16 + 8 + m_view->name().length()) {
int item_idx = m_view->focus() - m_view->begin_visible();
if (item_idx == int(m_view->size()))
m_canvas->print(m_canvas->width() - 16, 0, "[ none of %-5d]", m_view->size());
else
m_canvas->print(m_canvas->width() - 16, 0, "[%5d of %-5d]", item_idx + 1, m_view->size());
}
int layout_height;
const std::string layout_name = rpc::call_command_string("ui.torrent_list.layout");
-21
View File
@@ -47,22 +47,6 @@ TextInput::pressed(int key) {
if (m_bindings.pressed(key)) {
return true;
} else if (m_alt) {
m_alt = false;
switch (key) {
// case 'b':
// Base::insert(m_pos, "M^b");
// break;
// case 'f':
// Base::insert(m_pos, "M^f");
// break;
default:
return false;
}
} else if (key >= 0x20 && key < 0x7F) {
Base::insert(m_pos++, 1, key);
@@ -113,11 +97,6 @@ TextInput::pressed(int key) {
Base::erase(m_pos, size()-m_pos);
break;
case 0x1B:
m_alt = true;
break;
default:
return false;
}
+2 -3
View File
@@ -54,7 +54,7 @@ public:
using Base::size_type;
using Base::npos;
TextInput() : m_pos(0), m_alt(false) {}
TextInput() : m_pos(0) {}
virtual ~TextInput() {}
size_type get_pos() { return m_pos; }
@@ -62,7 +62,7 @@ public:
virtual bool pressed(int key);
void clear() { m_pos = 0; m_alt = false; Base::clear(); }
void clear() { m_pos = 0; Base::clear(); }
void slot_dirty(slot_void s) { m_slot_dirty = s; }
void mark_dirty() { if (m_slot_dirty) m_slot_dirty(); }
@@ -74,7 +74,6 @@ public:
private:
size_type m_pos;
bool m_alt;
slot_void m_slot_dirty;
Bindings m_bindings;
+10
View File
@@ -66,6 +66,7 @@
#include "display/window.h"
#include "display/manager.h"
#include "input/bindings.h"
#include "ui/root.h"
#include "rpc/command_scheduler.h"
#include "rpc/command_scheduler_item.h"
@@ -249,6 +250,12 @@ main(int argc, char** argv) {
rpc::parse_command_multiple
(rpc::make_target(),
"method.insert = event.view.hide,multi|rlookup|static\n"
"method.insert = event.view.show,multi|rlookup|static\n"
"method.insert = event.system.startup_done,multi|rlookup|static\n"
"method.insert = event.system.shutdown,multi|rlookup|static\n"
"method.insert = event.download.inserted,multi|rlookup|static\n"
"method.insert = event.download.inserted_new,multi|rlookup|static\n"
"method.insert = event.download.inserted_session,multi|rlookup|static\n"
@@ -460,6 +467,7 @@ main(int argc, char** argv) {
}
control->initialize();
control->ui()->load_input_history();
// Load session torrents and perform scheduled tasks to ensure
// session torrents are loaded before arg torrents.
@@ -477,6 +485,8 @@ main(int argc, char** argv) {
worker_thread->start_thread();
rpc::commands.call_catch("event.system.startup_done", rpc::make_target(), "startup_done", "System startup_done event action failed: ");
torrent::thread_base::event_loop(torrent::main_thread());
control->core()->download_list()->session_save();
+5 -4
View File
@@ -113,14 +113,15 @@ ThreadWorker::change_xmlrpc_log() {
if (scgi() == NULL)
return;
if (scgi()->log_fd() != -1)
if (scgi()->log_fd() != -1) {
::close(scgi()->log_fd());
if (m_xmlrpcLog.empty()) {
scgi()->set_log_fd(-1);
control->core()->push_log("Closed XMLRPC log.");
return;
}
if (m_xmlrpcLog.empty())
return;
scgi()->set_log_fd(open(rak::path_expand(m_xmlrpcLog).c_str(), O_WRONLY | O_APPEND | O_CREAT, 0644));
if (scgi()->log_fd() == -1) {
+13 -12
View File
@@ -54,6 +54,7 @@
#include "display/window_download_statusbar.h"
#include "display/text_element_string.h"
#include "rpc/parse_commands.h"
#include "control.h"
#include "download.h"
@@ -395,20 +396,20 @@ Download::bind_keys() {
const char* keys = control->ui()->get_throttle_keys();
m_bindings[keys[ 0]] = std::bind(&Download::adjust_up_throttle, this, 1);
m_bindings[keys[ 1]] = std::bind(&Download::adjust_up_throttle, this, -1);
m_bindings[keys[ 2]] = std::bind(&Download::adjust_down_throttle, this, 1);
m_bindings[keys[ 3]] = std::bind(&Download::adjust_down_throttle, this, -1);
m_bindings[keys[ 0]] = std::bind(&Download::adjust_up_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.small"));
m_bindings[keys[ 1]] = std::bind(&Download::adjust_up_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.small"));
m_bindings[keys[ 2]] = std::bind(&Download::adjust_down_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.small"));
m_bindings[keys[ 3]] = std::bind(&Download::adjust_down_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.small"));
m_bindings[keys[ 4]] = std::bind(&Download::adjust_up_throttle, this, 5);
m_bindings[keys[ 5]] = std::bind(&Download::adjust_up_throttle, this, -5);
m_bindings[keys[ 6]] = std::bind(&Download::adjust_down_throttle, this, 5);
m_bindings[keys[ 7]] = std::bind(&Download::adjust_down_throttle, this, -5);
m_bindings[keys[ 4]] = std::bind(&Download::adjust_up_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.medium"));
m_bindings[keys[ 5]] = std::bind(&Download::adjust_up_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.medium"));
m_bindings[keys[ 6]] = std::bind(&Download::adjust_down_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.medium"));
m_bindings[keys[ 7]] = std::bind(&Download::adjust_down_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.medium"));
m_bindings[keys[ 8]] = std::bind(&Download::adjust_up_throttle, this, 50);
m_bindings[keys[ 9]] = std::bind(&Download::adjust_up_throttle, this, -50);
m_bindings[keys[10]] = std::bind(&Download::adjust_down_throttle, this, 50);
m_bindings[keys[11]] = std::bind(&Download::adjust_down_throttle, this, -50);
m_bindings[keys[ 8]] = std::bind(&Download::adjust_up_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.large"));
m_bindings[keys[ 9]] = std::bind(&Download::adjust_up_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.large"));
m_bindings[keys[10]] = std::bind(&Download::adjust_down_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.large"));
m_bindings[keys[11]] = std::bind(&Download::adjust_down_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.large"));
}
}
+56 -2
View File
@@ -36,6 +36,8 @@
#include "config.h"
#include <sstream>
#include <rak/functional.h>
#include <rak/string_manip.h>
#include <torrent/exceptions.h>
@@ -259,6 +261,25 @@ DownloadList::receive_view_input(Input type) {
title = "command";
break;
case INPUT_FILTER:
{
// Do not allow to subfilter the defined excluded views
const std::string excluded_views = rpc::call_command_string("view.filter.temp.excluded");
std::stringstream ss(excluded_views);
std::string view_name_var;
while(ss.good()) {
std::getline(ss, view_name_var, ',');
if (current_view()->name() == rak::trim(view_name_var)) {
control->core()->push_log_std("View '" + current_view()->name() + "' can't be filtered.");
return;
}
}
title = "filter";
}
break;
default:
throw torrent::internal_error("DownloadList::receive_view_input(...) Invalid input type.");
}
@@ -274,21 +295,31 @@ DownloadList::receive_view_input(Input type) {
std::placeholders::_1,
std::placeholders::_2));
// reset ESC delay for input prompt
set_escdelay(0);
input->bindings()['\n'] = std::bind(&DownloadList::receive_exit_input, this, type);
input->bindings()[KEY_ENTER] = std::bind(&DownloadList::receive_exit_input, this, type);
input->bindings()['\x07'] = std::bind(&DownloadList::receive_exit_input, this, INPUT_NONE);
input->bindings()['\x07'] = std::bind(&DownloadList::receive_exit_input, this, INPUT_NONE); // ^G
input->bindings()['\x1B'] = std::bind(&DownloadList::receive_exit_input, this, INPUT_NONE); // ESC , ^[
control->ui()->enable_input(title, input);
control->ui()->enable_input(title, input, type);
}
void
DownloadList::receive_exit_input(Input type) {
// set back ESC delay to default
set_escdelay(1000);
input::TextInput* input = control->ui()->current_input();
// We should check that this object is the one holding the input.
if (input == NULL)
return;
if (type != INPUT_NONE && type != INPUT_EOI)
control->ui()->add_to_input_history(type, input->str());
control->ui()->disable_input();
try {
@@ -322,6 +353,28 @@ DownloadList::receive_exit_input(Input type) {
input->str());
break;
case INPUT_FILTER:
if (input->str().empty()) {
if (rpc::call_command_value("view.filter.temp.log"))
control->core()->push_log_std("Clear temporary filter on '" + current_view()->name() + "' view.");
current_view()->set_filter_temp(torrent::Object());
current_view()->filter();
current_view()->sort();
} else {
std::string pattern = input->str();
if (pattern.back() != '$')
pattern = pattern + ".*";
if (pattern.front() != '^')
pattern = ".*" + pattern;
std::transform(pattern.begin(), pattern.end(), pattern.begin(), ::tolower);
std::string temp_filter = "match={d.name=," + pattern + "}";
if (rpc::call_command_value("view.filter.temp.log"))
control->core()->push_log_std("Temporary filter on '" + current_view()->name() + "' view: " + pattern);
current_view()->set_filter_temp(temp_filter);
current_view()->filter();
}
break;
default:
throw torrent::internal_error("DownloadList::receive_exit_input(...) Invalid input type.");
}
@@ -343,6 +396,7 @@ DownloadList::setup_keys() {
m_bindings[KEY_ENTER] = std::bind(&DownloadList::receive_view_input, this, INPUT_LOAD_MODIFIED);
m_bindings['\x0F'] = std::bind(&DownloadList::receive_view_input, this, INPUT_CHANGE_DIRECTORY);
m_bindings['X' - '@'] = std::bind(&DownloadList::receive_view_input, this, INPUT_COMMAND);
m_bindings['F'] = std::bind(&DownloadList::receive_view_input, this, INPUT_FILTER);
m_uiArray[DISPLAY_LOG]->bindings()[KEY_LEFT] =
m_uiArray[DISPLAY_LOG]->bindings()['B' - '@'] =
+3 -1
View File
@@ -86,7 +86,9 @@ public:
INPUT_LOAD_DEFAULT,
INPUT_LOAD_MODIFIED,
INPUT_CHANGE_DIRECTORY,
INPUT_COMMAND
INPUT_COMMAND,
INPUT_FILTER,
INPUT_EOI
} Input;
DownloadList();
+7
View File
@@ -220,7 +220,14 @@ ElementDownloadList::receive_change_view(const std::string& name) {
return;
}
std::string old_name = view() ? view()->name() : "";
if (!old_name.empty())
rpc::commands.call_catch("event.view.hide", rpc::make_target(), name,
"View hide event action failed: ");
set_view(*itr);
if (!old_name.empty())
rpc::commands.call_catch("event.view.show", rpc::make_target(), old_name,
"View show event action failed: ");
}
void
+246 -14
View File
@@ -36,11 +36,14 @@
#include "config.h"
#include <fstream>
#include <stdexcept>
#include <string.h>
#include <rak/string_manip.h>
#include <torrent/throttle.h>
#include <torrent/torrent.h>
#include <torrent/download/resource_manager.h>
#include <torrent/utils/log.h>
#include "core/manager.h"
#include "display/frame.h"
@@ -54,6 +57,7 @@
#include "control.h"
#include "download_list.h"
#include "core/download_store.h"
#include "root.h"
@@ -65,7 +69,17 @@ Root::Root() :
m_windowTitle(NULL),
m_windowHttpQueue(NULL),
m_windowInput(NULL),
m_windowStatusbar(NULL) {
m_windowStatusbar(NULL),
m_input_history_length(99),
m_input_history_last_input(""),
m_input_history_pointer_get(0) {
// Initialise prefilled m_input_history and m_input_history_pointers objects.
for (int type = ui::DownloadList::INPUT_LOAD_DEFAULT; type != ui::DownloadList::INPUT_EOI; type++) {
m_input_history.insert( std::make_pair(type, InputHistoryCategory(m_input_history_length)) );
m_input_history_pointers.insert( std::make_pair(type, 0) );
}
}
void
@@ -140,20 +154,20 @@ Root::setup_keys() {
const char* keys = get_throttle_keys();
m_bindings[keys[ 0]] = std::bind(&Root::adjust_up_throttle, this, 1);
m_bindings[keys[ 1]] = std::bind(&Root::adjust_up_throttle, this, -1);
m_bindings[keys[ 2]] = std::bind(&Root::adjust_down_throttle, this, 1);
m_bindings[keys[ 3]] = std::bind(&Root::adjust_down_throttle, this, -1);
m_bindings[keys[ 0]] = std::bind(&Root::adjust_up_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.small"));
m_bindings[keys[ 1]] = std::bind(&Root::adjust_up_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.small"));
m_bindings[keys[ 2]] = std::bind(&Root::adjust_down_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.small"));
m_bindings[keys[ 3]] = std::bind(&Root::adjust_down_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.small"));
m_bindings[keys[ 4]] = std::bind(&Root::adjust_up_throttle, this, 5);
m_bindings[keys[ 5]] = std::bind(&Root::adjust_up_throttle, this, -5);
m_bindings[keys[ 6]] = std::bind(&Root::adjust_down_throttle, this, 5);
m_bindings[keys[ 7]] = std::bind(&Root::adjust_down_throttle, this, -5);
m_bindings[keys[ 4]] = std::bind(&Root::adjust_up_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.medium"));
m_bindings[keys[ 5]] = std::bind(&Root::adjust_up_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.medium"));
m_bindings[keys[ 6]] = std::bind(&Root::adjust_down_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.medium"));
m_bindings[keys[ 7]] = std::bind(&Root::adjust_down_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.medium"));
m_bindings[keys[ 8]] = std::bind(&Root::adjust_up_throttle, this, 50);
m_bindings[keys[ 9]] = std::bind(&Root::adjust_up_throttle, this, -50);
m_bindings[keys[10]] = std::bind(&Root::adjust_down_throttle, this, 50);
m_bindings[keys[11]] = std::bind(&Root::adjust_down_throttle, this, -50);
m_bindings[keys[ 8]] = std::bind(&Root::adjust_up_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.large"));
m_bindings[keys[ 9]] = std::bind(&Root::adjust_up_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.large"));
m_bindings[keys[10]] = std::bind(&Root::adjust_down_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.large"));
m_bindings[keys[11]] = std::bind(&Root::adjust_down_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.large"));
m_bindings['\x0C'] = std::bind(&display::Manager::force_redraw, m_control->display()); // ^L
m_bindings['\x11'] = std::bind(&Control::receive_normal_shutdown, m_control); // ^Q
@@ -230,7 +244,7 @@ Root::adjust_up_throttle(int throttle) {
}
void
Root::enable_input(const std::string& title, input::TextInput* input) {
Root::enable_input(const std::string& title, input::TextInput* input, ui::DownloadList::Input type) {
if (m_windowInput->input() != NULL)
throw torrent::internal_error("Root::enable_input(...) m_windowInput->input() != NULL.");
@@ -243,8 +257,14 @@ Root::enable_input(const std::string& title, input::TextInput* input) {
m_windowInput->set_title(title);
m_windowInput->set_focus(true);
reset_input_history_attributes(type);
input->bindings()['\x0C'] = std::bind(&display::Manager::force_redraw, m_control->display()); // ^L
input->bindings()['\x11'] = std::bind(&Control::receive_normal_shutdown, m_control); // ^Q
input->bindings()[KEY_UP] = std::bind(&Root::prev_in_input_history, this, type); // UP arrow
input->bindings()['\x10'] = std::bind(&Root::prev_in_input_history, this, type); // ^P
input->bindings()[KEY_DOWN] = std::bind(&Root::next_in_input_history, this, type); // DOWN arrow
input->bindings()['\x0E'] = std::bind(&Root::next_in_input_history, this, type); // ^N
control->input()->set_text_input(input);
control->display()->adjust_layout();
@@ -272,4 +292,216 @@ Root::current_input() {
return m_windowInput->input();
}
void
Root::add_to_input_history(ui::DownloadList::Input type, std::string item) {
InputHistory::iterator itr = m_input_history.find(type);
InputHistoryPointers::iterator pitr = m_input_history_pointers.find(type);
int prev_item_pointer = (pitr->second - 1) < 0 ? (m_input_history_length - 1) : (pitr->second - 1);
// Don't store item if it's empty or the same as the last one in the category.
if (!item.empty() && item != itr->second.at(prev_item_pointer)) {
itr->second.at(pitr->second) = rak::trim(item);
m_input_history_pointers[type] = (pitr->second + 1) % m_input_history_length;
}
}
void
Root::prev_in_input_history(ui::DownloadList::Input type) {
if (m_windowInput->input() == NULL)
throw torrent::internal_error("Root::prev_in_input_history() m_windowInput->input() == NULL.");
InputHistory::iterator itr = m_input_history.find(type);
InputHistoryPointers::const_iterator pitr = m_input_history_pointers.find(type);
if (m_input_history_pointer_get == pitr->second)
m_input_history_last_input = m_windowInput->input()->str();
else
itr->second.at(m_input_history_pointer_get) = m_windowInput->input()->str();
std::string tmp_input = m_input_history_last_input;
int prev_pointer_get = (m_input_history_pointer_get - 1) < 0 ? (m_input_history_length - 1) : (m_input_history_pointer_get - 1);
if (prev_pointer_get != pitr->second && itr->second.at(prev_pointer_get) != "")
m_input_history_pointer_get = prev_pointer_get;
if (m_input_history_pointer_get != pitr->second)
tmp_input = itr->second.at(m_input_history_pointer_get);
m_windowInput->input()->str() = tmp_input;
m_windowInput->input()->set_pos(tmp_input.length());
m_windowInput->input()->mark_dirty();
}
void
Root::next_in_input_history(ui::DownloadList::Input type) {
if (m_windowInput->input() == NULL)
throw torrent::internal_error("Root::next_in_input_history() m_windowInput->input() == NULL.");
InputHistory::iterator itr = m_input_history.find(type);
InputHistoryPointers::const_iterator pitr = m_input_history_pointers.find(type);
if (m_input_history_pointer_get == pitr->second)
m_input_history_last_input = m_windowInput->input()->str();
else
itr->second.at(m_input_history_pointer_get) = m_windowInput->input()->str();
std::string tmp_input = m_input_history_last_input;
if (m_input_history_pointer_get != pitr->second) {
m_input_history_pointer_get = (m_input_history_pointer_get + 1) % m_input_history_length;
tmp_input = (m_input_history_pointer_get == pitr->second) ? m_input_history_last_input : itr->second.at(m_input_history_pointer_get);
}
m_windowInput->input()->str() = tmp_input;
m_windowInput->input()->set_pos(tmp_input.length());
m_windowInput->input()->mark_dirty();
}
void
Root::reset_input_history_attributes(ui::DownloadList::Input type) {
InputHistoryPointers::const_iterator itr = m_input_history_pointers.find(type);
// Clear last_input and set pointer_get to the same as pointer_insert.
m_input_history_last_input = "";
m_input_history_pointer_get = itr->second;
}
void
Root::set_input_history_size(int size) {
if (size < 1)
throw torrent::input_error("Invalid input history size.");
for (InputHistory::iterator itr = m_input_history.begin(), last = m_input_history.end(); itr != last; itr++) {
// Reserve the latest input history entries if new size is smaller than original.
if (size < m_input_history_length) {
int pointer_offset = m_input_history_length - size;
InputHistoryPointers::iterator pitr = m_input_history_pointers.find(itr->first);
InputHistoryCategory input_history_category_tmp = itr->second;
for (int i=0; i != size; i++)
itr->second.at(i) = input_history_category_tmp.at((pitr->second + pointer_offset + i) % m_input_history_length);
m_input_history_pointers[pitr->first] = 0;
}
itr->second.resize(size);
}
m_input_history_length = size;
}
void
Root::load_input_history() {
if (!m_control->core()->download_store()->is_enabled()) {
lt_log_print(torrent::LOG_DEBUG, "ignoring input history file");
return;
}
std::string history_filename = m_control->core()->download_store()->path() + "rtorrent.input_history";
std::fstream history_file(history_filename.c_str(), std::ios::in);
if (history_file.is_open()) {
// Create a temp object of the content since size of history categories can be smaller than this.
InputHistory input_history_tmp;
for (int type = ui::DownloadList::INPUT_LOAD_DEFAULT; type != ui::DownloadList::INPUT_EOI; type++)
input_history_tmp.insert( std::make_pair(type, InputHistoryCategory()) );
std::string line;
while (std::getline(history_file, line)) {
if (!line.empty()) {
int delim_pos = line.find("|");
if (delim_pos != std::string::npos) {
int type = std::atoi(line.substr(0, delim_pos).c_str());
InputHistory::iterator itr = input_history_tmp.find(type);
if (itr != input_history_tmp.end()) {
std::string input_str = rak::trim(line.substr(delim_pos + 1));
if (!input_str.empty())
itr->second.push_back(input_str);
}
}
}
}
if (history_file.bad()) {
lt_log_print(torrent::LOG_DEBUG, "input history file corrupted, discarding (path:%s)", history_filename.c_str());
return;
} else {
lt_log_print(torrent::LOG_DEBUG, "input history file read (path:%s)", history_filename.c_str());
}
for (InputHistory::const_iterator itr = input_history_tmp.begin(), last = input_history_tmp.end(); itr != last; itr++) {
int input_history_tmp_category_length = itr->second.size();
InputHistory::iterator hitr = m_input_history.find(itr->first);
InputHistoryPointers::iterator pitr = m_input_history_pointers.find(itr->first);
if (m_input_history_length < input_history_tmp_category_length) {
int pointer_offset = input_history_tmp_category_length - m_input_history_length;
for (int i=0; i != m_input_history_length; i++)
hitr->second.at(i) = itr->second.at((pointer_offset + i) % input_history_tmp_category_length);
pitr->second = 0;
} else {
hitr->second = itr->second;
hitr->second.resize(m_input_history_length);
pitr->second = input_history_tmp_category_length % m_input_history_length;
}
}
} else {
lt_log_print(torrent::LOG_DEBUG, "could not open input history file (path:%s)", history_filename.c_str());
}
}
void
Root::save_input_history() {
if (!m_control->core()->download_store()->is_enabled())
return;
std::string history_filename = m_control->core()->download_store()->path() + "rtorrent.input_history";
std::string history_filename_tmp = history_filename + ".new";
std::fstream history_file(history_filename_tmp.c_str(), std::ios::out | std::ios::trunc);
if (!history_file.is_open()) {
lt_log_print(torrent::LOG_DEBUG, "could not open input history file for writing (path:%s)", history_filename.c_str());
return;
}
for (InputHistory::const_iterator itr = m_input_history.begin(), last = m_input_history.end(); itr != last; itr++) {
InputHistoryPointers::const_iterator pitr = m_input_history_pointers.find(itr->first);
for (int i=0; i != m_input_history_length; i++)
if (!itr->second.at((pitr->second + i) % m_input_history_length).empty())
history_file << itr->first << "|" + itr->second.at((pitr->second + i) % m_input_history_length) + "\n";
}
if (!history_file.good()) {
lt_log_print(torrent::LOG_DEBUG, "input history file corrupted during writing, discarding (path:%s)", history_filename.c_str());
return;
} else {
lt_log_print(torrent::LOG_DEBUG, "input history file written (path:%s)", history_filename.c_str());
}
history_file.close();
std::rename(history_filename_tmp.c_str(), history_filename.c_str());
}
void
Root::clear_input_history() {
for (int type = ui::DownloadList::INPUT_LOAD_DEFAULT; type != ui::DownloadList::INPUT_EOI; type++) {
InputHistory::iterator itr = m_input_history.find(type);
for (int i=0; i != m_input_history_length; i++)
itr->second.at(i) = "";
m_input_history_pointers[type] = 0;
}
}
}
+25 -1
View File
@@ -39,6 +39,7 @@
#include <inttypes.h>
#include "input/bindings.h"
#include "download_list.h"
class Control;
@@ -65,6 +66,10 @@ public:
typedef display::WindowInput WInput;
typedef display::WindowStatusbar WStatusbar;
typedef std::map<int, int> InputHistoryPointers;
typedef std::vector<std::string> InputHistoryCategory;
typedef std::map<int, InputHistoryCategory> InputHistory;
Root();
void init(Control* c);
@@ -88,11 +93,19 @@ public:
const char* get_throttle_keys();
void enable_input(const std::string& title, input::TextInput* input);
void enable_input(const std::string& title, input::TextInput* input, ui::DownloadList::Input type);
void disable_input();
input::TextInput* current_input();
int get_input_history_size() { return m_input_history_length; }
void set_input_history_size(int size);
void add_to_input_history(ui::DownloadList::Input type, std::string item);
void load_input_history();
void save_input_history();
void clear_input_history();
private:
void setup_keys();
@@ -105,6 +118,17 @@ private:
WStatusbar* m_windowStatusbar;
input::Bindings m_bindings;
int m_input_history_length;
std::string m_input_history_last_input;
int m_input_history_pointer_get;
InputHistory m_input_history;
InputHistoryPointers m_input_history_pointers;
void prev_in_input_history(ui::DownloadList::Input type);
void next_in_input_history(ui::DownloadList::Input type);
void reset_input_history_attributes(ui::DownloadList::Input type);
};
}