diru 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.rdoc +3 -0
- data/LICENSE +20 -0
- data/README.rdoc +260 -0
- data/bin/diru +1010 -0
- data/lib/version.rb +6 -0
- data/test/dir_0/dir_0_0/dir_0_0_0/empty.txt +0 -0
- data/test/dir_0/dir_0_0/dir_0_0_1/empty.txt +0 -0
- data/test/dir_0/dir_0_0/dir_0_0_2/empty.txt +0 -0
- data/test/dir_0/dir_0_1/dir_0_1_0/empty.txt +0 -0
- data/test/dir_0/dir_0_1/dir_0_1_1/empty.txt +0 -0
- data/test/dir_0/dir_0_1/dir_0_1_2/empty.txt +0 -0
- data/test/dir_0/dir_0_2/dir_0_2_0/empty.txt +0 -0
- data/test/dir_0/dir_0_2/dir_0_2_1/empty.txt +0 -0
- data/test/dir_0/dir_0_2/dir_0_2_2/empty.txt +0 -0
- data/test/dir_0/dir_0_3/dir_0_3_0/empty.txt +0 -0
- data/test/dir_0/dir_0_3/dir_0_3_1/empty.txt +0 -0
- data/test/dir_0/dir_0_3/dir_0_3_2/empty.txt +0 -0
- data/test/dir_0/dir_0_4/dir_0_4_0/empty.txt +0 -0
- data/test/dir_0/dir_0_4/dir_0_4_1/empty.txt +0 -0
- data/test/dir_0/dir_0_4/dir_0_4_2/empty.txt +0 -0
- data/test/dir_1/dir_1_0/dir_1_0_0/empty.txt +0 -0
- data/test/dir_1/dir_1_0/dir_1_0_1/empty.txt +0 -0
- data/test/dir_1/dir_1_0/dir_1_0_2/empty.txt +0 -0
- data/test/dir_1/dir_1_1/dir_1_1_0/empty.txt +0 -0
- data/test/dir_1/dir_1_1/dir_1_1_1/empty.txt +0 -0
- data/test/dir_1/dir_1_1/dir_1_1_2/empty.txt +0 -0
- data/test/dir_1/dir_1_2/dir_1_2_0/empty.txt +0 -0
- data/test/dir_1/dir_1_2/dir_1_2_1/empty.txt +0 -0
- data/test/dir_1/dir_1_2/dir_1_2_2/empty.txt +0 -0
- data/test/dir_1/dir_1_3/dir_1_3_0/empty.txt +0 -0
- data/test/dir_1/dir_1_3/dir_1_3_1/empty.txt +0 -0
- data/test/dir_1/dir_1_3/dir_1_3_2/empty.txt +0 -0
- data/test/dir_1/dir_1_4/dir_1_4_0/empty.txt +0 -0
- data/test/dir_1/dir_1_4/dir_1_4_1/empty.txt +0 -0
- data/test/dir_1/dir_1_4/dir_1_4_2/empty.txt +0 -0
- data/test/golden.log +243 -0
- data/test/test_diru.rb +72 -0
- data/test/test_diru.sh +84 -0
- metadata +111 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 909b1ab4f9f1edcafd5b5a7e0e9f025663dc753c
|
4
|
+
data.tar.gz: 2d6cee00599bc252645141e237cf9a51e092ba2c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cba95aeefd39cf86ed3c6ecce6413d8ff995bc2d85431b23f1bf8a32faa1d0ed173a5dc5f8b4569ab9001f74e695151ff4ad49d2c74d5f97feae832435b42586
|
7
|
+
data.tar.gz: f462f73de40bf425a332cacd7ac32d74e227e64a318bb9e97c877872e6e8b5740bb17f2f03f33d4ea0e1c67a88a04a9c28df15bc001735bb4771775ebe22ab5a
|
data/CHANGELOG.rdoc
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2017 tero.isannainen@gmail.com
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,260 @@
|
|
1
|
+
= Diru
|
2
|
+
|
3
|
+
* Introduction
|
4
|
+
* Getting started
|
5
|
+
* Client commands
|
6
|
+
* Configuration
|
7
|
+
* Practical usage
|
8
|
+
|
9
|
+
= Introduction
|
10
|
+
|
11
|
+
Diru is a Change Directory (cd) utility for augmenting Unix Shell
|
12
|
+
functionality. Diru uses server/client architecture, which enables
|
13
|
+
sharing of directory info between terminal sessions.
|
14
|
+
|
15
|
+
Each Server serves one Project, which is a tree of related directories
|
16
|
+
where user wants to jump around and which has a logical root. There
|
17
|
+
can be multiple Servers, if user needs to access multiple Projects
|
18
|
+
concurrently.
|
19
|
+
|
20
|
+
Client queries directory info from Server and directory change is
|
21
|
+
pushed to Shell in order to change the current directory within
|
22
|
+
Shell. Diru is not able to change the Shell directory by itself. User
|
23
|
+
must define a Shell function which can actually change the state of
|
24
|
+
the Shell.
|
25
|
+
|
26
|
+
Diru features:
|
27
|
+
|
28
|
+
* Support for multiple concurrent Projects.
|
29
|
+
* User and Project specific options (configuration).
|
30
|
+
* Jump to Project root.
|
31
|
+
* Find and jump to dir under Project root (regexp match).
|
32
|
+
* Find and jump to dir under current dir (regexp match).
|
33
|
+
* Favorite directories in options (configuration), for permanent
|
34
|
+
favorites.
|
35
|
+
* Bookmark saving and referencing, for current favorites.
|
36
|
+
* Directory change history and history referencing.
|
37
|
+
* Scratch Pad for directory copy/paste.
|
38
|
+
* Peer directory jumping, i.e. peer of current (regexp match).
|
39
|
+
* Online help.
|
40
|
+
|
41
|
+
|
42
|
+
= Getting started
|
43
|
+
|
44
|
+
The first thing to do is to start Diru Hub. Hub is a server process
|
45
|
+
that enables the Project specific Servers to be started. Hub will give
|
46
|
+
each new Server an unique port to operate with, starting from Hub Port
|
47
|
+
plus one.
|
48
|
+
|
49
|
+
Start Hub to port 42323:
|
50
|
+
|
51
|
+
shell> diru --hub --hport 42323
|
52
|
+
|
53
|
+
Start Server for a Project:
|
54
|
+
|
55
|
+
shell> cd <project_root_dir>
|
56
|
+
shell> diru --hport 42323 -s
|
57
|
+
|
58
|
+
Server will be listening to port "42324" (HubPort + 1). Following
|
59
|
+
Servers will get "42325", "42326" etc. Server displays the Port at
|
60
|
+
startup.
|
61
|
+
|
62
|
+
Server is a thread in Hub process. Servers can be started and killed
|
63
|
+
at will. Complete list of running Servers can be seen with:
|
64
|
+
|
65
|
+
shell> diru --hport 42323 -l
|
66
|
+
|
67
|
+
When Server starts, it will detect the Project root. It can be a file
|
68
|
+
or defined as environment variable. If ".diru_root_dir" file or a
|
69
|
+
".diru.yml" file is found from current dir or some dir above, the dir
|
70
|
+
containing either of the files will become the Project root. If no files
|
71
|
+
are found, then DIRU_ROOT environment variable is used.
|
72
|
+
|
73
|
+
Client communicates with the Server in order to get and save directory
|
74
|
+
info. As mentioned above, Diru is more or less useless unless user has
|
75
|
+
defined a Shell function to realize directory changing.
|
76
|
+
|
77
|
+
Here is an example of such function for Bourne Shell based shells:
|
78
|
+
|
79
|
+
dr()
|
80
|
+
{
|
81
|
+
ret=`diru -p 42324 -c $*`
|
82
|
+
if test $? -eq 0; then
|
83
|
+
cd $ret
|
84
|
+
fi
|
85
|
+
}
|
86
|
+
|
87
|
+
Diru Client command is called now "dr". We specify with "-p" that port
|
88
|
+
"42324" is used towards the Server and with "-c" that we want to issue
|
89
|
+
directory "change" commands. In practice only part of the commands
|
90
|
+
will change directory, since some commands are only for queries.
|
91
|
+
|
92
|
+
If you want to see all Diru options, perform:
|
93
|
+
|
94
|
+
shell> diru -h
|
95
|
+
|
96
|
+
To jump to Project root, we do:
|
97
|
+
|
98
|
+
shell> dr r
|
99
|
+
|
100
|
+
If we want to jump to a subdir called "dir_0_0" (under root), we do:
|
101
|
+
|
102
|
+
shell> dr r dir_0_0
|
103
|
+
|
104
|
+
Note that "dir_0_0" does not have to be directly under Project root,
|
105
|
+
the directory is searched from the complete tree of directories under
|
106
|
+
Project root using regexp matching.
|
107
|
+
|
108
|
+
Now that we have changed directory for a couple of times, we can look
|
109
|
+
at the directory change history:
|
110
|
+
|
111
|
+
shell> dr h
|
112
|
+
|
113
|
+
We can reference the history by listed numbers. In order to jump back
|
114
|
+
to previous dir, we do:
|
115
|
+
|
116
|
+
shell> dr h 0
|
117
|
+
|
118
|
+
|
119
|
+
= Client commands
|
120
|
+
|
121
|
+
Client commands either change the current directory or query directory
|
122
|
+
info from Server.
|
123
|
+
|
124
|
+
Search commands:
|
125
|
+
|
126
|
+
* "dr r" - change to Project root dir.
|
127
|
+
* "dr r <dir>" - change to <dir> (somewhere) under Project root dir.
|
128
|
+
* "dr t <dir>" - change to <dir> (somewhere) under current dir.
|
129
|
+
|
130
|
+
Search can match to multiple directories. First match is used and the
|
131
|
+
rest (Left-overs) are displayed to the user. Left-overs are also
|
132
|
+
stored, and they can be referenced and used in order of appearance
|
133
|
+
with simply issuing "dr".
|
134
|
+
|
135
|
+
Search pattern can also be constructed from multiple pieces.
|
136
|
+
|
137
|
+
shell> dr r dir 1_0_1
|
138
|
+
|
139
|
+
See "dr i" for alternatives for single letter commands. For example
|
140
|
+
"r" can be replaced with "/".
|
141
|
+
|
142
|
+
|
143
|
+
Bookmark commands:
|
144
|
+
|
145
|
+
* "dr b" - display bookmarks.
|
146
|
+
* "dr b ." - add current dir to bookmarks.
|
147
|
+
* "dr b !" - reset (clear) bookmarks.
|
148
|
+
* "dr b s <file>" - store bookmarks to <file>.
|
149
|
+
* "dr b l <file>" - load bookmarks from <file>.
|
150
|
+
* "dr b d <num>" - delete bookmark with <num>.
|
151
|
+
* "dr b <num>" - change dir to bookmark <num>.
|
152
|
+
|
153
|
+
History commands:
|
154
|
+
|
155
|
+
* "dr h" - display history.
|
156
|
+
* "dr h ." - add current dir to history.
|
157
|
+
* "dr h !" - reset (clear) historys.
|
158
|
+
* "dr h ," - reference last history item.
|
159
|
+
* "dr h <num>" - change dir to history <num>.
|
160
|
+
|
161
|
+
Scratch Pad commands:
|
162
|
+
|
163
|
+
* "dr s ." - store current dir to Scratch Pad.
|
164
|
+
* "dr s <dir>" - store <dir> to Scratch Pad.
|
165
|
+
* "dr s" - change dir to Scratch Pad dir.
|
166
|
+
|
167
|
+
Misc commands:
|
168
|
+
|
169
|
+
* "dr p" - jump to peer dir, i.e. the peer of current (from options).
|
170
|
+
* "dr c <cmd>" - issue RPC command to server (reset etc., see below).
|
171
|
+
* "dr f" - list favorites dirs (from options).
|
172
|
+
* "dr f <dir>" - change dir to favorite <dir>.
|
173
|
+
* "dr i" - show command info.
|
174
|
+
* "dr <dir>" - change to given dir (must be under current).
|
175
|
+
* "dr" - change to next "Left-over" directory.
|
176
|
+
|
177
|
+
RPC (Remote Procedure Call) commands:
|
178
|
+
|
179
|
+
* "reset" - Reset Server state, i.e. History, Boorkmarks, Left-overs.
|
180
|
+
* "abook" - Add current dir to Bookmarks.
|
181
|
+
* "lbook" - List Bookmarks.
|
182
|
+
* "rbook" - Reset Bookmarks.
|
183
|
+
* "doc" - Show command documentation.
|
184
|
+
|
185
|
+
|
186
|
+
= Configuration
|
187
|
+
|
188
|
+
Diru uses several environment variables, which are optional, and
|
189
|
+
provides thereby backup values for non-specified command line info.
|
190
|
+
|
191
|
+
* DIRU_HUB_PORT - Hub Port, if nothing, 41114 is used.
|
192
|
+
* DIRU_PORT - Server Port, if nothing, ~/.diru.prt is used (Port File).
|
193
|
+
* DIRU_OPTS - options file, if nothing, ~/.diru.yml is used.
|
194
|
+
* DIRU_ROOT - Project root, used if ".diru_root_dir" and ".diru.yml"
|
195
|
+
are missing.
|
196
|
+
|
197
|
+
If Hub is started without "--hport" option, Diru checks if
|
198
|
+
DIRU_HUB_PORT is defined. If not, port 41114 is used.
|
199
|
+
|
200
|
+
If Client is used without "-p" option, Diru checks if DIRU_PORT is
|
201
|
+
defined. If not, Port File, i.e. "~/.diru.prt", is used. Client does
|
202
|
+
not work if no port info is given.
|
203
|
+
|
204
|
+
If Server is started in a directory where (or above) a file called
|
205
|
+
".diru_root_dir" is found, then Project root is the directory
|
206
|
+
containing ".diru_root_dir". If ".diru_root_dir" is not found, but
|
207
|
+
".diru.yml" is found, then Project root is the directory containing
|
208
|
+
".diru.yml", and Options for Project are taken from that file. Last
|
209
|
+
resort for defining Project root, is DIRU_ROOT definition.
|
210
|
+
|
211
|
+
If ".diru_root_dir" or DIRU_ROOT is used to define Project root, then
|
212
|
+
DIRU_OPTS is used to define Options File. However, if not defined,
|
213
|
+
then "~/.diru.yml" is used for options.
|
214
|
+
|
215
|
+
An example Options file can be displayed with:
|
216
|
+
|
217
|
+
shell> diru -t
|
218
|
+
|
219
|
+
|
220
|
+
Available options in Options File:
|
221
|
+
|
222
|
+
* :sync - Options File and Project directory hierarchy polling period
|
223
|
+
(for Server).
|
224
|
+
* :hist - number of entries in history (max).
|
225
|
+
* :favs - tagged favorite dirs.
|
226
|
+
* :peers - <regexp>,<str> pairs for peer matching (String#sub method).
|
227
|
+
|
228
|
+
|
229
|
+
= Practical usage
|
230
|
+
|
231
|
+
After Hub has been started, user can start Server for each
|
232
|
+
Project. Each Server runs on its own Port, and user can either define
|
233
|
+
a Shell function for each Server with specific name, or define
|
234
|
+
environment variable (DIRU_PORT) and change it according to
|
235
|
+
Project. Depending on the usage pattern, user might also want to use
|
236
|
+
the Port File.
|
237
|
+
|
238
|
+
Each Project might benefit from specific Options File, and thus user
|
239
|
+
could mark the Project Root with ".diru.yml" and specify the Project
|
240
|
+
setup there.
|
241
|
+
|
242
|
+
If user is working with a programming project, it is fairly common to
|
243
|
+
have separate sub-directories for source code and build targets. Let's
|
244
|
+
assume user starts with editing source code and has a terminal for
|
245
|
+
accessing files. When program is ready for running, user opens a new
|
246
|
+
terminal for handling the compile-run-debug iterations.
|
247
|
+
|
248
|
+
User can copy source terminal directory to Scratch Pad. Then in the
|
249
|
+
new terminal, user can refer to Scratch Pad and change to same
|
250
|
+
directory as in source terminal. Finally user can change to a Peer of
|
251
|
+
current dir, i.e. where program running and debugging takes place.
|
252
|
+
|
253
|
+
If programming project is big enough, there might be several sub-dirs
|
254
|
+
where source files reside. User might setup favorites to Options File,
|
255
|
+
in order to easily reference the popular source code directories.
|
256
|
+
|
257
|
+
Options File can be edited while Server is running. Server reads the
|
258
|
+
Options File every 5 seconds (configurable with ":sync") and refreshes
|
259
|
+
its internal state. Server also updates its internal cache of Project
|
260
|
+
directory content at each refresh cycle.
|
data/bin/diru
ADDED
@@ -0,0 +1,1010 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# diru is a tool for moving around in the users directory
|
4
|
+
# structure. diru provides input for the shell cd command for entering
|
5
|
+
# a new directory. However some commands do not produce input for cd.
|
6
|
+
#
|
7
|
+
# diru has hub (daemon) which creates new service (server) threads for
|
8
|
+
# diru clients. Before user can start using diru, a server should be
|
9
|
+
# created.
|
10
|
+
#
|
11
|
+
# diru has server side and client side. The server maintains
|
12
|
+
# information about users directories and also the state of the
|
13
|
+
# queries issued by the client. Client is what the end user uses.
|
14
|
+
#
|
15
|
+
# diru client command has to be integrated to the currently used
|
16
|
+
# shell in order to force the shell to change the current
|
17
|
+
# directory. Lets assume that the shell alias/function that user is
|
18
|
+
# actually using is "dr" for now. If diru returns success, then the
|
19
|
+
# diru output should be used as new directory. If diru status is
|
20
|
+
# failure, then directory should not be changed.
|
21
|
+
#
|
22
|
+
# Example of diru use:
|
23
|
+
# shell> dr / build src
|
24
|
+
#
|
25
|
+
# This command means that the client sends to the server a request for
|
26
|
+
# directory search. The search starts from Diru root ("/") and the
|
27
|
+
# rest of the path should include the words "build", and
|
28
|
+
# "src". If path with this spec is found, the shell directory gets
|
29
|
+
# updated.
|
30
|
+
#
|
31
|
+
# Perform:
|
32
|
+
# shell> dr @ doc
|
33
|
+
# or
|
34
|
+
# shell> dr i
|
35
|
+
#
|
36
|
+
# for some documentation.
|
37
|
+
#
|
38
|
+
# Diru setup:
|
39
|
+
#
|
40
|
+
# Start hub (deamon)
|
41
|
+
# * shell> diru --hub
|
42
|
+
#
|
43
|
+
# Open server for client.
|
44
|
+
# * shell> diru -s
|
45
|
+
#
|
46
|
+
# Use client
|
47
|
+
# * shell> diru -p 41115 -c r
|
48
|
+
#
|
49
|
+
#
|
50
|
+
# Feature list:
|
51
|
+
# * User root dir (downwards) search
|
52
|
+
# * Current directory (downwards) dir search
|
53
|
+
# * Bookmarking directories
|
54
|
+
# * Left-over search with multiple matches
|
55
|
+
# * Fast jump to specific common directories
|
56
|
+
# * Peer dir jump
|
57
|
+
# * Server state reset
|
58
|
+
# * Revert mode, unknown commands to cd
|
59
|
+
#
|
60
|
+
|
61
|
+
|
62
|
+
require 'como'
|
63
|
+
include Como
|
64
|
+
require 'drb'
|
65
|
+
require 'yaml'
|
66
|
+
|
67
|
+
# require 'byebug'
|
68
|
+
|
69
|
+
|
70
|
+
Spec.command( 'diru', 'Tero Isannainen', '2017',
|
71
|
+
[
|
72
|
+
[ :switch, 'hub', nil, "H: Start hub." ],
|
73
|
+
[ :opt_single, 'hport', nil, "H: Hub port (default: DIRU_HUB_PORT or 41114)." ],
|
74
|
+
[ :switch, 'hkill', nil, "H: Kill hub." ],
|
75
|
+
[ :switch, 'nodaemon', nil, "H: No daemon for server." ],
|
76
|
+
[ :switch, 'server', '-s', "S: Open server for client." ],
|
77
|
+
[ :opt_single, 'kill', '-k', "S: Close server for client." ],
|
78
|
+
[ :switch, 'list', '-l', "S: List servers." ],
|
79
|
+
[ :opt_single, 'options', '-o', "SC: Options File." ],
|
80
|
+
[ :opt_single, 'port', '-p', "SC: Server port for client (default: DIRU_PORT or ~/.diru.prt)." ],
|
81
|
+
[ :switch, 'change', '-c', "C: Change dir (i.e. target is to change dir)." ],
|
82
|
+
[ :exclusive, 'template', '-t', "C: Display Options File template." ],
|
83
|
+
[ :default, nil, nil, "C: Client commands (try: diru i)." ],
|
84
|
+
] )
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
# ------------------------------------------------------------
|
89
|
+
# Common:
|
90
|
+
# ------------------------------------------------------------
|
91
|
+
|
92
|
+
|
93
|
+
# Diru common features.
|
94
|
+
module Diru
|
95
|
+
|
96
|
+
# Default port.
|
97
|
+
DIRU_HUB_PORT = ENV['DIRU_HUB_PORT'] || 41114
|
98
|
+
|
99
|
+
# Load yaml configuration file (if exists).
|
100
|
+
def Diru.load_conf( conf_file )
|
101
|
+
conf = {}
|
102
|
+
if File.exist?( conf_file )
|
103
|
+
conf = YAML.load( File.read( conf_file ) )
|
104
|
+
end
|
105
|
+
conf
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
# See: Diru.load_conf
|
110
|
+
def load_conf( conf_file )
|
111
|
+
@conf = Diru.load_conf( conf_file )
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
# Error message display.
|
116
|
+
def Diru.error( msg )
|
117
|
+
STDERR.puts "Diru Error: #{msg}"
|
118
|
+
exit( false )
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
# List the dir content (default: pwd).
|
123
|
+
def Diru.list( dir = '.', glob = '*' )
|
124
|
+
Dir.glob( "#{dir}/#{glob}" )
|
125
|
+
end
|
126
|
+
|
127
|
+
# List the dir files (default: pwd).
|
128
|
+
def Diru.list_files( dir = '.', glob = '*' )
|
129
|
+
Diru.list( dir, glob ).select do |i| File.file?(i) == true end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Find file entry from directory hierarchy upwards.
|
133
|
+
def Diru.find_upper_file( file, dir = Dir.pwd )
|
134
|
+
found = Diru.list_files( dir, file )
|
135
|
+
if found.empty?
|
136
|
+
dir = File.dirname( dir )
|
137
|
+
if dir == "/"
|
138
|
+
raise RuntimeError, "Could not find file \"#{file}\"!"
|
139
|
+
else
|
140
|
+
return Diru.find_upper_file( file, dir )
|
141
|
+
end
|
142
|
+
else
|
143
|
+
return found[0]
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
# Find file entry from directory hierarchy upwards, exit if not found.
|
149
|
+
def Diru.must_find_upper_file( file, dir = Dir.pwd )
|
150
|
+
begin
|
151
|
+
Diru.find_upper_file( file, dir )
|
152
|
+
rescue RuntimeError
|
153
|
+
STDERR.puts "Could not find file \"#{file}\"!"
|
154
|
+
exit( false )
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def Diru.get_opts_file( file = nil )
|
159
|
+
opts_file = nil
|
160
|
+
if Opt['options'].given
|
161
|
+
opts_file = Opt['options'].value
|
162
|
+
elsif file
|
163
|
+
opts_file = file
|
164
|
+
elsif ENV['DIRU_OPTS']
|
165
|
+
opts_file = ENV['DIRU_OPTS']
|
166
|
+
else
|
167
|
+
opts_file = "#{ENV['HOME']}/.diru.yml"
|
168
|
+
end
|
169
|
+
|
170
|
+
opts_file
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
# Diru Server State.
|
177
|
+
class Search
|
178
|
+
|
179
|
+
include Diru
|
180
|
+
|
181
|
+
def initialize( root, opts_file = nil )
|
182
|
+
|
183
|
+
# Server root.
|
184
|
+
@root = root
|
185
|
+
|
186
|
+
# Change directory to DIRU root.
|
187
|
+
Dir.chdir( @root )
|
188
|
+
|
189
|
+
# List of old matches.
|
190
|
+
@old = []
|
191
|
+
|
192
|
+
# Numbered bookmarks.
|
193
|
+
@book = []
|
194
|
+
|
195
|
+
# Numbered history.
|
196
|
+
@hist = []
|
197
|
+
|
198
|
+
# Favorites.
|
199
|
+
@fav = {}
|
200
|
+
|
201
|
+
# History limit.
|
202
|
+
@histlimit = 20
|
203
|
+
|
204
|
+
# Sync period.
|
205
|
+
@sync = 5
|
206
|
+
|
207
|
+
# Options File.
|
208
|
+
@opts_file = opts_file
|
209
|
+
|
210
|
+
# Temporary storage (scratch pad).
|
211
|
+
@scratch = nil
|
212
|
+
|
213
|
+
# Glob pattern for DB build.
|
214
|
+
@glob = "**/*"
|
215
|
+
|
216
|
+
@logging = false
|
217
|
+
# if Opt['log'].given
|
218
|
+
# @logging = true
|
219
|
+
# end
|
220
|
+
|
221
|
+
# Initial data update.
|
222
|
+
update_data
|
223
|
+
update_conf
|
224
|
+
|
225
|
+
# Lock for DB access.
|
226
|
+
@datalock = Mutex.new
|
227
|
+
|
228
|
+
# Start periodic DB updates.
|
229
|
+
periodic_update
|
230
|
+
end
|
231
|
+
|
232
|
+
|
233
|
+
def opts_file
|
234
|
+
@opts_file
|
235
|
+
end
|
236
|
+
|
237
|
+
# Return root.
|
238
|
+
def root
|
239
|
+
@root
|
240
|
+
end
|
241
|
+
|
242
|
+
|
243
|
+
# Add bookmark (if not already).
|
244
|
+
def abook( dir )
|
245
|
+
idx = @book.index dir
|
246
|
+
unless idx
|
247
|
+
@book.push dir
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
|
252
|
+
# Delete bookmark.
|
253
|
+
def dbook( idx )
|
254
|
+
@book.delete_at idx
|
255
|
+
end
|
256
|
+
|
257
|
+
|
258
|
+
# Get bookmark.
|
259
|
+
def gbook( idx )
|
260
|
+
@book[ idx ]
|
261
|
+
end
|
262
|
+
|
263
|
+
|
264
|
+
# Get all bookmarks.
|
265
|
+
def book
|
266
|
+
@book
|
267
|
+
end
|
268
|
+
|
269
|
+
|
270
|
+
# Reset bookmarks.
|
271
|
+
def rbook
|
272
|
+
@book = []
|
273
|
+
end
|
274
|
+
|
275
|
+
|
276
|
+
# Save bookmarks to file.
|
277
|
+
def savebook( file )
|
278
|
+
File.write( file, @book.join("\n") + "\n" )
|
279
|
+
end
|
280
|
+
|
281
|
+
|
282
|
+
# Load bookmarks from file.
|
283
|
+
def loadbook( file )
|
284
|
+
@book = File.read( file ).split("\n")
|
285
|
+
end
|
286
|
+
|
287
|
+
|
288
|
+
# Add history.
|
289
|
+
def ahist( dir )
|
290
|
+
if dir != @hist[0]
|
291
|
+
if @hist.length >= @histlimit
|
292
|
+
@hist = [ dir ] + @hist[ 0..(@histlimit-2) ]
|
293
|
+
else
|
294
|
+
@hist = [ dir ] + @hist
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
|
300
|
+
# Get history.
|
301
|
+
def ghist( idx )
|
302
|
+
@hist[ idx ]
|
303
|
+
end
|
304
|
+
|
305
|
+
|
306
|
+
# Get all historys.
|
307
|
+
def hist
|
308
|
+
@hist
|
309
|
+
end
|
310
|
+
|
311
|
+
|
312
|
+
# Reset history.
|
313
|
+
def rhist
|
314
|
+
@hist = []
|
315
|
+
end
|
316
|
+
|
317
|
+
|
318
|
+
# Get (and update) favorite.
|
319
|
+
def gfav( tag )
|
320
|
+
"#{@root}/#{@fav[ tag ]}"
|
321
|
+
end
|
322
|
+
|
323
|
+
|
324
|
+
# Return the whole fav.
|
325
|
+
def fav
|
326
|
+
@fav
|
327
|
+
end
|
328
|
+
|
329
|
+
|
330
|
+
# Update options.
|
331
|
+
def update_conf
|
332
|
+
if @opts_file
|
333
|
+
|
334
|
+
load_conf( @opts_file )
|
335
|
+
|
336
|
+
if @conf[:sync] && @conf[:sync] >= 1
|
337
|
+
@sync = @conf[:sync]
|
338
|
+
end
|
339
|
+
|
340
|
+
if @conf[:hist] && ( @conf[:hist] >= 2 )
|
341
|
+
@histlimit = @conf[:hist]
|
342
|
+
else
|
343
|
+
@histlimit = 20
|
344
|
+
end
|
345
|
+
|
346
|
+
if @conf[ :favs ]
|
347
|
+
@fav = @conf[ :favs ]
|
348
|
+
end
|
349
|
+
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
|
354
|
+
# Set scratch dir.
|
355
|
+
def settmp( dir )
|
356
|
+
@scratch = dir
|
357
|
+
end
|
358
|
+
|
359
|
+
|
360
|
+
# Get scratch dir.
|
361
|
+
def gettmp
|
362
|
+
@scratch
|
363
|
+
end
|
364
|
+
|
365
|
+
|
366
|
+
# Match dir/pattern from DB.
|
367
|
+
def match( dir, pattern )
|
368
|
+
list = []
|
369
|
+
|
370
|
+
if dir
|
371
|
+
rem = "#{@root}/"
|
372
|
+
begin
|
373
|
+
dir[rem] = ''
|
374
|
+
rescue
|
375
|
+
return []
|
376
|
+
end
|
377
|
+
r = dir + '.*' + pattern
|
378
|
+
else
|
379
|
+
r = pattern
|
380
|
+
end
|
381
|
+
|
382
|
+
@datalock.synchronize do
|
383
|
+
@data.each do |i|
|
384
|
+
if i.match( r )
|
385
|
+
list.push "#{@root}/#{i}"
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
# Update old list.
|
391
|
+
@old = list.rotate
|
392
|
+
|
393
|
+
list
|
394
|
+
end
|
395
|
+
|
396
|
+
|
397
|
+
# Get next match from old.
|
398
|
+
def next
|
399
|
+
ret = @old[0]
|
400
|
+
@old.rotate!
|
401
|
+
ret
|
402
|
+
end
|
403
|
+
|
404
|
+
|
405
|
+
# Update directory DB.
|
406
|
+
def update_data
|
407
|
+
@data = Dir.glob( @glob ).select{|ent| File.directory?( ent )}
|
408
|
+
@data = @data.sort
|
409
|
+
end
|
410
|
+
|
411
|
+
|
412
|
+
# Update DB with MT sync.
|
413
|
+
def update_data_sync
|
414
|
+
@datalock.synchronize do
|
415
|
+
update_data
|
416
|
+
update_conf
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
|
421
|
+
# Reset dynamic state.
|
422
|
+
def reset
|
423
|
+
log "reset"
|
424
|
+
@old = []
|
425
|
+
@book = []
|
426
|
+
@hist = []
|
427
|
+
update_data_sync
|
428
|
+
end
|
429
|
+
|
430
|
+
|
431
|
+
# Update DB periodically with background thread.
|
432
|
+
def periodic_update
|
433
|
+
# Spawn More data updates.
|
434
|
+
@th = Thread.new do
|
435
|
+
loop do
|
436
|
+
# puts "data update..."
|
437
|
+
sleep @sync
|
438
|
+
update_data_sync
|
439
|
+
end
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
|
444
|
+
# Enable logging.
|
445
|
+
def logon
|
446
|
+
@logging = true
|
447
|
+
end
|
448
|
+
|
449
|
+
# Disable logging.
|
450
|
+
def logoff
|
451
|
+
@logging = false
|
452
|
+
end
|
453
|
+
|
454
|
+
|
455
|
+
# Log events.
|
456
|
+
def log( msg )
|
457
|
+
if @logging
|
458
|
+
fh = File.open( "#{ENV['HOME']}/.diru.log", "a" )
|
459
|
+
fh.puts "#{Time.timestamp()}: #{msg}"
|
460
|
+
fh.close
|
461
|
+
end
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
|
466
|
+
|
467
|
+
# ------------------------------------------------------------
|
468
|
+
# Server program.
|
469
|
+
# ------------------------------------------------------------
|
470
|
+
|
471
|
+
# Diru hub.
|
472
|
+
class Hub
|
473
|
+
|
474
|
+
# Start in daemon or direct mode.
|
475
|
+
def Hub.start( port, daemon = true )
|
476
|
+
if daemon
|
477
|
+
p = fork do
|
478
|
+
Hub.new( port )
|
479
|
+
end
|
480
|
+
Process.detach( p )
|
481
|
+
else
|
482
|
+
Hub.new( port )
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
|
487
|
+
# Server collection.
|
488
|
+
@@servers = {}
|
489
|
+
|
490
|
+
attr_reader :port
|
491
|
+
|
492
|
+
def initialize( port )
|
493
|
+
@port = port
|
494
|
+
|
495
|
+
# Start the service
|
496
|
+
@hub = DRb.start_service( "druby://localhost:#{@port}", self )
|
497
|
+
DRb.thread.join
|
498
|
+
end
|
499
|
+
|
500
|
+
|
501
|
+
# Kill hub.
|
502
|
+
def kill
|
503
|
+
@th = Thread.new do
|
504
|
+
STDERR.puts "Diru Hub: Exiting sooooon..."
|
505
|
+
sleep 3
|
506
|
+
kill_servers
|
507
|
+
sleep 3
|
508
|
+
STDERR.puts "Diru Hub: Exit done..."
|
509
|
+
@hub.stop_service
|
510
|
+
exit( false )
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
|
515
|
+
# Create new server and return port for the server.
|
516
|
+
def get_server( root, opts_file )
|
517
|
+
|
518
|
+
50.times do |i|
|
519
|
+
if @@servers[ @port+1+i ] == nil
|
520
|
+
port = @port+1+i
|
521
|
+
s = Service.new( root, port, opts_file )
|
522
|
+
@@servers[ port ] = s
|
523
|
+
return port
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
0
|
528
|
+
end
|
529
|
+
|
530
|
+
|
531
|
+
# List all server ports.
|
532
|
+
def list_servers
|
533
|
+
@@servers.keys.map{ |i| [ i, @@servers[i].root ] }
|
534
|
+
end
|
535
|
+
|
536
|
+
|
537
|
+
# Kill all servers.
|
538
|
+
def kill_servers
|
539
|
+
@@servers.keys.each do |s|
|
540
|
+
kill_server( s )
|
541
|
+
end
|
542
|
+
@@servers = {}
|
543
|
+
end
|
544
|
+
|
545
|
+
# Kill server with the given port.
|
546
|
+
def kill_server( s_port )
|
547
|
+
s = @@servers[ s_port ]
|
548
|
+
if s
|
549
|
+
s.kill
|
550
|
+
@@servers.delete( s_port )
|
551
|
+
else
|
552
|
+
nil
|
553
|
+
end
|
554
|
+
end
|
555
|
+
|
556
|
+
end
|
557
|
+
|
558
|
+
|
559
|
+
|
560
|
+
# diru client service (server) thread.
|
561
|
+
class Service
|
562
|
+
|
563
|
+
attr_reader :root
|
564
|
+
|
565
|
+
# Initialize and start service.
|
566
|
+
def initialize( root, port, opts_file )
|
567
|
+
@root = root
|
568
|
+
@port = port
|
569
|
+
@drb = nil
|
570
|
+
@opts_file = opts_file
|
571
|
+
start
|
572
|
+
self
|
573
|
+
end
|
574
|
+
|
575
|
+
# Kill service.
|
576
|
+
def kill
|
577
|
+
@drb.stop_service
|
578
|
+
Thread.kill( @th )
|
579
|
+
end
|
580
|
+
|
581
|
+
# Start service.
|
582
|
+
def start
|
583
|
+
@th = Thread.new do
|
584
|
+
|
585
|
+
# Create "front" object.
|
586
|
+
@search = Search.new( @root, @opts_file )
|
587
|
+
|
588
|
+
# Start the service
|
589
|
+
@drb = DRb::DRbServer.new( "druby://localhost:#{@port}", @search )
|
590
|
+
@drb.thread.join
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
end
|
595
|
+
|
596
|
+
|
597
|
+
if Opt['template'].given
|
598
|
+
puts %q{---
|
599
|
+
:hist: 20
|
600
|
+
:sync: 10
|
601
|
+
:favs:
|
602
|
+
f: dir_0/dir_0_4/dir_0_4_0
|
603
|
+
g: dir_1/dir_1_2
|
604
|
+
:peers:
|
605
|
+
- - "(.*/dir_0/.*)/dir_0_2_0"
|
606
|
+
- "\\1"
|
607
|
+
- - "(.*/dir_0/.*)/dir_0_1_0"
|
608
|
+
- "\\1/dir_0_1_1"
|
609
|
+
}
|
610
|
+
exit( false )
|
611
|
+
end
|
612
|
+
|
613
|
+
|
614
|
+
hport = nil
|
615
|
+
if Opt['hport'].given
|
616
|
+
hport = Opt['hport'].value.to_i
|
617
|
+
else
|
618
|
+
hport = Diru::DIRU_HUB_PORT
|
619
|
+
end
|
620
|
+
|
621
|
+
|
622
|
+
if Opt['hub'].given
|
623
|
+
|
624
|
+
# Hub:
|
625
|
+
h = Hub.start( hport, !Opt['nodaemon'].given )
|
626
|
+
exit( true )
|
627
|
+
end
|
628
|
+
|
629
|
+
|
630
|
+
if false
|
631
|
+
# For hub maintenance (irb):
|
632
|
+
require 'drb'
|
633
|
+
hub = DRbObject.new( nil, "druby://localhost:41114" )
|
634
|
+
hub.list_servers
|
635
|
+
end
|
636
|
+
|
637
|
+
|
638
|
+
if false
|
639
|
+
# Test client
|
640
|
+
require 'drb'
|
641
|
+
@port = 41115
|
642
|
+
@search = DRbObject.new( nil, "druby://localhost:#{@port}" )
|
643
|
+
end
|
644
|
+
|
645
|
+
|
646
|
+
if Opt['hkill'].given
|
647
|
+
hub = DRbObject.new( nil, "druby://localhost:#{hport}" )
|
648
|
+
hub.kill
|
649
|
+
exit( true )
|
650
|
+
end
|
651
|
+
|
652
|
+
|
653
|
+
if Opt['server'].given
|
654
|
+
|
655
|
+
# Setup client server.
|
656
|
+
hub = DRbObject.new( nil, "druby://localhost:#{hport}" )
|
657
|
+
|
658
|
+
root = nil
|
659
|
+
opts_file = nil
|
660
|
+
|
661
|
+
if ENV['DIRU_ROOT']
|
662
|
+
root = ENV['DIRU_ROOT']
|
663
|
+
else
|
664
|
+
# First search for .diru_root_dir file.
|
665
|
+
begin
|
666
|
+
root = File.dirname( Diru.find_upper_file( '.diru_root_dir' ) )
|
667
|
+
rescue
|
668
|
+
root = nil
|
669
|
+
end
|
670
|
+
|
671
|
+
unless root
|
672
|
+
# Next search for .diru.yml file.
|
673
|
+
begin
|
674
|
+
yml_file = Diru.find_upper_file( '.diru.yml' )
|
675
|
+
root = File.dirname( yml_file )
|
676
|
+
opts_file = yml_file
|
677
|
+
rescue
|
678
|
+
Diru.error "Could not find user directory root!"
|
679
|
+
end
|
680
|
+
end
|
681
|
+
end
|
682
|
+
|
683
|
+
opts_file = Diru.get_opts_file( opts_file )
|
684
|
+
|
685
|
+
begin
|
686
|
+
s_port = hub.get_server( root, opts_file )
|
687
|
+
rescue
|
688
|
+
Diru.error "Access to Hub failed!"
|
689
|
+
end
|
690
|
+
|
691
|
+
if s_port == 0
|
692
|
+
Diru.error "Could not start server!"
|
693
|
+
else
|
694
|
+
# File.write( port_file, s_port.to_s ) if port_file
|
695
|
+
puts "Using server port: #{s_port}..."
|
696
|
+
end
|
697
|
+
|
698
|
+
exit( true )
|
699
|
+
end
|
700
|
+
|
701
|
+
|
702
|
+
if Opt['kill'].given
|
703
|
+
|
704
|
+
hub = DRbObject.new( nil, "druby://localhost:#{hport}" )
|
705
|
+
port = Opt['kill'].value.to_i
|
706
|
+
begin
|
707
|
+
hub.kill_server port
|
708
|
+
rescue
|
709
|
+
Diru.error "Access to Hub failed!"
|
710
|
+
end
|
711
|
+
|
712
|
+
exit( true )
|
713
|
+
end
|
714
|
+
|
715
|
+
|
716
|
+
if Opt['list'].given
|
717
|
+
|
718
|
+
hub = DRbObject.new( nil, "druby://localhost:#{hport}" )
|
719
|
+
begin
|
720
|
+
hub.list_servers.each do |i|
|
721
|
+
puts format( "%-12.d %s", i[0], i[1] )
|
722
|
+
end
|
723
|
+
rescue
|
724
|
+
Diru.error "Access to Hub failed!"
|
725
|
+
end
|
726
|
+
|
727
|
+
exit( true )
|
728
|
+
end
|
729
|
+
|
730
|
+
|
731
|
+
|
732
|
+
# ------------------------------------------------------------
|
733
|
+
# Client program.
|
734
|
+
# ------------------------------------------------------------
|
735
|
+
|
736
|
+
|
737
|
+
class Client
|
738
|
+
|
739
|
+
include Diru
|
740
|
+
|
741
|
+
def initialize( port )
|
742
|
+
|
743
|
+
@port = port
|
744
|
+
|
745
|
+
# Create a DRbObject instance that is connected to server. All methods
|
746
|
+
# executed on this object will be executed to the remote one.
|
747
|
+
@search = DRbObject.new( nil, "druby://localhost:#{@port}" )
|
748
|
+
|
749
|
+
load_conf( @search.opts_file )
|
750
|
+
end
|
751
|
+
|
752
|
+
|
753
|
+
# Perform cd (i.e. return true) and store current dir to history.
|
754
|
+
def do_cd
|
755
|
+
@search.ahist( Dir.pwd )
|
756
|
+
true
|
757
|
+
end
|
758
|
+
|
759
|
+
|
760
|
+
# No directory change, i.e. return false.
|
761
|
+
def no_cd
|
762
|
+
false
|
763
|
+
end
|
764
|
+
|
765
|
+
|
766
|
+
# Process command.
|
767
|
+
def command( input )
|
768
|
+
|
769
|
+
if input.empty?
|
770
|
+
|
771
|
+
# Refer to last search list.
|
772
|
+
puts @search.next
|
773
|
+
do_cd
|
774
|
+
|
775
|
+
else
|
776
|
+
|
777
|
+
cmd = input[0]
|
778
|
+
arg = input[1..-1]
|
779
|
+
|
780
|
+
# Shell specials chars to prevent:
|
781
|
+
# * ? [ ] ' " \ $ ; & ( ) | ^ < >
|
782
|
+
|
783
|
+
# Thus allowed:
|
784
|
+
# ! % + , - . / : = @ _ ~
|
785
|
+
# ^ ^ ^ ^ ^ ^ ^ ^ ^
|
786
|
+
|
787
|
+
# Decode user command.
|
788
|
+
ret = \
|
789
|
+
case cmd
|
790
|
+
|
791
|
+
when '/', 'r';
|
792
|
+
if arg.empty?
|
793
|
+
disp @search.root
|
794
|
+
else
|
795
|
+
disp @search.match( nil, arg.join('.*') )
|
796
|
+
end
|
797
|
+
|
798
|
+
when ':', 't'; disp @search.match( Dir.pwd, arg.join('.*') )
|
799
|
+
|
800
|
+
when '.', 'b';
|
801
|
+
if arg[0] == nil
|
802
|
+
lbook( @search.book )
|
803
|
+
no_cd
|
804
|
+
elsif arg[0] == '.'
|
805
|
+
@search.abook( Dir.pwd )
|
806
|
+
no_cd
|
807
|
+
elsif arg[0] == '!'
|
808
|
+
@search.rbook
|
809
|
+
no_cd
|
810
|
+
elsif arg[0] == 's'
|
811
|
+
@search.savebook( arg[1] )
|
812
|
+
no_cd
|
813
|
+
elsif arg[0] == 'l'
|
814
|
+
@search.loadbook( arg[1] )
|
815
|
+
no_cd
|
816
|
+
elsif arg[0] == 'd'
|
817
|
+
@search.dbook( arg[1].to_i )
|
818
|
+
no_cd
|
819
|
+
else
|
820
|
+
disp @search.gbook( arg[0].to_i )
|
821
|
+
end
|
822
|
+
|
823
|
+
when ',', 'h';
|
824
|
+
if arg[0] == nil
|
825
|
+
lbook( @search.hist )
|
826
|
+
no_cd
|
827
|
+
elsif arg[0] == '.'
|
828
|
+
@search.ahist( Dir.pwd )
|
829
|
+
no_cd
|
830
|
+
elsif arg[0] == '!'
|
831
|
+
@search.rhist
|
832
|
+
no_cd
|
833
|
+
elsif arg[0] == ','
|
834
|
+
disp @search.ghist( 0 )
|
835
|
+
else
|
836
|
+
disp @search.ghist( arg[0].to_i )
|
837
|
+
end
|
838
|
+
|
839
|
+
when '_', 's';
|
840
|
+
if arg[0] == nil
|
841
|
+
disp @search.gettmp
|
842
|
+
elsif arg[0]
|
843
|
+
if arg[0] == '.'
|
844
|
+
dir = Dir.pwd
|
845
|
+
else
|
846
|
+
dir = arg[0]
|
847
|
+
end
|
848
|
+
disp @search.settmp( dir )
|
849
|
+
no_cd
|
850
|
+
else
|
851
|
+
no_cd
|
852
|
+
end
|
853
|
+
|
854
|
+
when '=', 'p'; disp peer
|
855
|
+
|
856
|
+
when '@', 'c'; rpc( arg )
|
857
|
+
|
858
|
+
when 'f';
|
859
|
+
if arg[0] == nil
|
860
|
+
@search.fav.each do |k,v|
|
861
|
+
STDERR.puts format( "%-6s %s", k, v )
|
862
|
+
end
|
863
|
+
no_cd
|
864
|
+
else
|
865
|
+
fav_map( arg )
|
866
|
+
end
|
867
|
+
|
868
|
+
when 'i';
|
869
|
+
rpc( [ 'doc' ] )
|
870
|
+
|
871
|
+
else
|
872
|
+
|
873
|
+
if Opt['change'].given
|
874
|
+
all = [cmd] + arg
|
875
|
+
puts "#{all.join(' ')}"
|
876
|
+
do_cd
|
877
|
+
else
|
878
|
+
no_cd
|
879
|
+
end
|
880
|
+
|
881
|
+
end
|
882
|
+
|
883
|
+
exit( ret )
|
884
|
+
end
|
885
|
+
|
886
|
+
end
|
887
|
+
|
888
|
+
|
889
|
+
# Display directories. One to STDOUT and others to STDERR.
|
890
|
+
def disp( resp )
|
891
|
+
if resp == nil
|
892
|
+
no_cd
|
893
|
+
elsif resp.kind_of? Array
|
894
|
+
puts resp[0]
|
895
|
+
if resp.length > 1
|
896
|
+
resp[1..-1].each do |i|
|
897
|
+
STDERR.puts i
|
898
|
+
end
|
899
|
+
end
|
900
|
+
do_cd
|
901
|
+
else
|
902
|
+
puts resp
|
903
|
+
do_cd
|
904
|
+
end
|
905
|
+
end
|
906
|
+
|
907
|
+
|
908
|
+
# Get peer directory. Search list of re-pairs. Match either of the
|
909
|
+
# pair, and switch to the pair dir.
|
910
|
+
#
|
911
|
+
def peer
|
912
|
+
cur = Dir.pwd
|
913
|
+
peers = @conf[ :peers ]
|
914
|
+
peers.each do |pair|
|
915
|
+
re = Regexp.new( pair[0] )
|
916
|
+
if ( re.match( cur ) )
|
917
|
+
return cur.sub( re, pair[1] )
|
918
|
+
end
|
919
|
+
end
|
920
|
+
nil
|
921
|
+
end
|
922
|
+
|
923
|
+
|
924
|
+
# Display short command doc.
|
925
|
+
def doc
|
926
|
+
STDERR.puts "
|
927
|
+
r / - search from root (or to root)
|
928
|
+
t : - search from this (current)
|
929
|
+
b . - bookmark access
|
930
|
+
h , - history access
|
931
|
+
s _ - scratch pad access
|
932
|
+
p = - peer of current
|
933
|
+
c @ - command (reset, doc etc.)
|
934
|
+
f - favorites
|
935
|
+
i - short info
|
936
|
+
"
|
937
|
+
end
|
938
|
+
|
939
|
+
|
940
|
+
# List bookmarks with index number (in reverse order).
|
941
|
+
def lbook( book )
|
942
|
+
idx = book.length-1
|
943
|
+
(0..idx).to_a.reverse.each do |i|
|
944
|
+
STDERR.puts format( "%2d: %s", i, book[i] )
|
945
|
+
end
|
946
|
+
end
|
947
|
+
|
948
|
+
|
949
|
+
# Remove Procedure Call towards server.
|
950
|
+
#
|
951
|
+
# Example:
|
952
|
+
# shell> dr @ rbook
|
953
|
+
#
|
954
|
+
def rpc( arg )
|
955
|
+
case arg[0]
|
956
|
+
when 'reset'; @search.reset
|
957
|
+
when 'abook'; @search.abook( Dir.pwd )
|
958
|
+
when 'lbook'; lbook( @search.book )
|
959
|
+
when 'rbook'; @search.rbook
|
960
|
+
when 'doc'; doc
|
961
|
+
else return no_cd
|
962
|
+
end
|
963
|
+
no_cd
|
964
|
+
end
|
965
|
+
|
966
|
+
|
967
|
+
# Direct directory jump.
|
968
|
+
def fav_map( arg )
|
969
|
+
ret = @search.gfav( arg[0] )
|
970
|
+
if ret
|
971
|
+
disp ret
|
972
|
+
do_cd
|
973
|
+
else
|
974
|
+
no_cd
|
975
|
+
end
|
976
|
+
end
|
977
|
+
|
978
|
+
end
|
979
|
+
|
980
|
+
|
981
|
+
if Opt['port'].given
|
982
|
+
port = Opt['port'].value.to_i
|
983
|
+
elsif ENV['DIRU_PORT']
|
984
|
+
port = ENV['DIRU_PORT'].to_i
|
985
|
+
elsif File.exist?( "#{ENV['HOME']}/.diru.prt" )
|
986
|
+
port = File.read( "#{ENV['HOME']}/.diru.prt" ).to_i
|
987
|
+
else
|
988
|
+
Diru.error "Server port info missing..."
|
989
|
+
end
|
990
|
+
|
991
|
+
|
992
|
+
# User command content.
|
993
|
+
input = Opt[nil].value
|
994
|
+
|
995
|
+
begin
|
996
|
+
client = Client.new( port )
|
997
|
+
rescue
|
998
|
+
Diru.error "Server not available!"
|
999
|
+
exit( false )
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
ret = false
|
1003
|
+
begin
|
1004
|
+
ret = client.command( input )
|
1005
|
+
rescue
|
1006
|
+
Diru.error "Command failure!"
|
1007
|
+
exit( false )
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
exit( ret )
|