patman 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.
@@ -0,0 +1,112 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4
+ <head>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
6
+ <title>
7
+ Top Level Namespace
8
+
9
+ &mdash; Documentation by YARD 0.8.7.6
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
16
+
17
+ <script type="text/javascript" charset="utf-8">
18
+ hasFrames = window.top.frames.main ? true : false;
19
+ relpath = '';
20
+ framesUrl = "frames.html#!top-level-namespace.html";
21
+ </script>
22
+
23
+
24
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
25
+
26
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
27
+
28
+
29
+ </head>
30
+ <body>
31
+ <div id="header">
32
+ <div id="menu">
33
+
34
+ <a href="_index.html">Index</a> &raquo;
35
+
36
+
37
+ <span class="title">Top Level Namespace</span>
38
+
39
+
40
+ <div class="noframes"><span class="title">(</span><a href="." target="_top">no frames</a><span class="title">)</span></div>
41
+ </div>
42
+
43
+ <div id="search">
44
+
45
+ <a class="full_list_link" id="class_list_link"
46
+ href="class_list.html">
47
+ Class List
48
+ </a>
49
+
50
+ <a class="full_list_link" id="method_list_link"
51
+ href="method_list.html">
52
+ Method List
53
+ </a>
54
+
55
+ <a class="full_list_link" id="file_list_link"
56
+ href="file_list.html">
57
+ File List
58
+ </a>
59
+
60
+ </div>
61
+ <div class="clear"></div>
62
+ </div>
63
+
64
+ <iframe id="search_frame"></iframe>
65
+
66
+ <div id="content"><h1>Top Level Namespace
67
+
68
+
69
+
70
+ </h1>
71
+
72
+ <dl class="box">
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+ </dl>
82
+ <div class="clear"></div>
83
+
84
+ <h2>Defined Under Namespace</h2>
85
+ <p class="children">
86
+
87
+
88
+
89
+
90
+ <strong class="classes">Classes:</strong> <span class='object_link'><a href="Patman.html" title="Patman (class)">Patman</a></span>
91
+
92
+
93
+ </p>
94
+
95
+
96
+
97
+
98
+
99
+
100
+
101
+
102
+
103
+ </div>
104
+
105
+ <div id="footer">
106
+ Generated on Sat Dec 16 23:10:27 2017 by
107
+ <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
108
+ 0.8.7.6 (ruby-2.3.3).
109
+ </div>
110
+
111
+ </body>
112
+ </html>
data/lib/patman.rb ADDED
@@ -0,0 +1,525 @@
1
+ # = Patman
2
+ #
3
+ # == Introduction
4
+ #
5
+ # {Patman} (Patch Manipulator) is a library for text file patching. It
6
+ # can also be used to extract information from files.
7
+ #
8
+ # Typical {Patman} script opens a file for editing. The file is read
9
+ # into the library. User finds the place for editing either with
10
+ # Regexp searches or with direct line numbers. The file content is
11
+ # edited by adding, removing, or replacing lines. When all edits are
12
+ # done, the updated file content is written to disk.
13
+ #
14
+ # All editing commands refer to the "current position". Current
15
+ # position is returned by "line" method. Positions refer to lines that
16
+ # have content. If user wants append to the end of file, then user
17
+ # should jump to last line, with "lastline" method, and then issue
18
+ # "append". It is also possible to jump to arbitrary lines, {Patman}
19
+ # does not prevent this. The line positions are just used as an index
20
+ # to Array. For example negative line number will refer from end
21
+ # towards beginning in content.
22
+ #
23
+ # Position can be explicitly changed with "step", "firstline", or
24
+ # "lastline" methods (commands). "find" changes position if the
25
+ # pattern is found in selected direction. "append" changes position
26
+ # implicitly with every call.
27
+ #
28
+ # Current line content is returned by "get" and it can be set with
29
+ # "set" method. Current line content can be replaced with "sub".
30
+ #
31
+ # {Patman} includes many query commands: line, lines, [], get, find,
32
+ # get_range, get_for. They all return the queried item. All other
33
+ # methods return {Patman} object itself, hence many {Patman} methods
34
+ # can be "chained".
35
+ #
36
+ # Block commands perform commands over a range of lines. Block
37
+ # commands are: do_all, do_range, and do_for. These retain the
38
+ # original position, but the final position is stored (actually one
39
+ # after) and it can be activated by calling "blockline" method.
40
+ #
41
+ # Block commands take a pre-defined number of lines to process. Note
42
+ # that, if user deletes lines in block action, the outcome is most
43
+ # likely not what the user expects.
44
+ #
45
+ # Mark feature can be used if user wants to return back to original
46
+ # position after changes. Mark features includes a "default mark" and
47
+ # "named marks".
48
+ #
49
+ # For debugging purposes it is good to see line content. "view" and
50
+ # "view_ln" can be used to view line content either without or with
51
+ # line numbers respectively.
52
+ #
53
+ # No changes are stored to disk unless "write" is called. If user want
54
+ # to create a "backup" of the edited file, the "copy" method can be
55
+ # used before any editing commands have been applied.
56
+ #
57
+ # == Example session
58
+ #
59
+ # # Open file for reading.
60
+ # r = Patman.read( "report.txt" )
61
+ #
62
+ # # Backup file and find next line with "cpp", method chaining.
63
+ # r.copy( "report.txt.org" ).find( /cpp/ )
64
+ #
65
+ # # Collect some lines.
66
+ # data = 4.times.collect do |i|
67
+ # r.ref( r.line + i )
68
+ # end
69
+ #
70
+ # # Duplicate the lines collected.
71
+ # r.insert( data )
72
+ #
73
+ # # Move to line 9.
74
+ # r.line 9
75
+ #
76
+ # # Append " Hello" to the end of current line.
77
+ # r.set( r.get + " Hello" )
78
+ #
79
+ # # Save changes.
80
+ # r.write
81
+ #
82
+
83
+ class Patman
84
+
85
+ # {Patman} exception base class.
86
+ class PatmanError < RuntimeError; end
87
+
88
+ # {Patman} searching error (for find functions).
89
+ class PatmanSearchError < PatmanError; end
90
+
91
+ # {Patman} file error.
92
+ class PatmanFileError < PatmanError; end
93
+
94
+
95
+ attr_accessor :marks
96
+
97
+
98
+ # Create editing session with file.
99
+ def Patman.read( file )
100
+ p = Patman.new( file )
101
+ p.read
102
+ p
103
+ end
104
+
105
+ # Create {Patman} object.
106
+ def initialize( file )
107
+ @file = file
108
+ @lines = []
109
+ @line = 0
110
+ @mark = nil
111
+ @marks = {}
112
+ @blockline = nil
113
+ @edited = false
114
+ end
115
+
116
+ # Read file in.
117
+ def read
118
+ if File.exist?( @file )
119
+ @lines = read_clean( @file )
120
+ else
121
+ raise PatmanFileError
122
+ end
123
+ end
124
+
125
+ # Write {Patman} content to disk.
126
+ def write( file = @file )
127
+ return unless @edited
128
+ fh = File.open( file, 'w' )
129
+ @lines.each do |line|
130
+ if line
131
+ fh.puts line
132
+ else
133
+ fh.puts ""
134
+ end
135
+ end
136
+ fh.close
137
+ @edited = false
138
+ self
139
+ end
140
+
141
+ # Copy {Patman} content to file.
142
+ def copy( file )
143
+ write( file )
144
+ self
145
+ end
146
+
147
+ # Return or set line.
148
+ def line( arg = nil )
149
+ if arg
150
+ @line = (arg-1)
151
+ self
152
+ else
153
+ @line+1
154
+ end
155
+ end
156
+
157
+ # Step forward or backward current position.
158
+ def step( dir = 1 )
159
+ @line = @line + dir
160
+ self
161
+ end
162
+
163
+ # Jump to first line.
164
+ def firstline
165
+ @line = 0
166
+ self
167
+ end
168
+
169
+ # Jump to last line.
170
+ def lastline
171
+ @line = @lines.length-1
172
+ self
173
+ end
174
+
175
+ # Jump to line after block.
176
+ def blockline
177
+ if @blockline
178
+ @line = @blockline
179
+ end
180
+ self
181
+ end
182
+
183
+ # Get or set all {Patman} content.
184
+ def lines( arg = nil )
185
+ if arg
186
+ @edited = true
187
+ @lines = arg
188
+ else
189
+ @lines
190
+ end
191
+ end
192
+
193
+ # Reference {Patman} content by range.
194
+ def []( range )
195
+ @lines[ range ]
196
+ end
197
+
198
+ # Get current line or lines by count.
199
+ def get( count = 1 )
200
+ if count == 1
201
+ @lines[ @line ]
202
+ else
203
+ @lines[ @line .. (@line+count-1) ]
204
+ end
205
+ end
206
+
207
+ # Get current line or any line.
208
+ def ref( line = nil )
209
+ if line
210
+ @lines[ line-1 ]
211
+ else
212
+ @lines[ @line ]
213
+ end
214
+ end
215
+
216
+ # Set current line.
217
+ def set( text )
218
+ @edited = true
219
+ @lines[ @line ] = text
220
+ self
221
+ end
222
+
223
+ # Substitution in current line.
224
+ def sub( from, to )
225
+ @edited = true
226
+ @lines[ @line ] = @lines[ @line ].sub( from, to )
227
+ self
228
+ end
229
+
230
+ # Update current line content (i.e. get&set) with the return value
231
+ # of the given block. Hence last stmt should include the new line
232
+ # content.
233
+ #
234
+ # @example
235
+ # r.update do |c|
236
+ # c.sub!( /foo/, 'bar' )
237
+ # c
238
+ # end
239
+ #
240
+ def update( &blk )
241
+ @edited = true
242
+ set( yield( get ) )
243
+ self
244
+ end
245
+
246
+ # Insert line or lines (Array) to current position.
247
+ def insert( text = nil )
248
+ @edited = true
249
+ if text.kind_of? Array
250
+ line = @line
251
+ step -1
252
+ text.each do |txt|
253
+ append( txt )
254
+ end
255
+ @line = line
256
+ else
257
+ @lines.insert( @line, text )
258
+ end
259
+ self
260
+ end
261
+
262
+ # Append after current position.
263
+ def append( text = nil )
264
+ @edited = true
265
+ if text.kind_of? Array
266
+ text.each do |txt|
267
+ append( txt )
268
+ end
269
+ else
270
+ step
271
+ @lines.insert( @line, text )
272
+ end
273
+ self
274
+ end
275
+
276
+ # Delete current line.
277
+ def delete( count = 1 )
278
+ @edited = true
279
+ count.times do |i|
280
+ @lines.delete_at( @line )
281
+ end
282
+ self
283
+ end
284
+
285
+ # Insert file to current position.
286
+ def insertfile( file )
287
+ @edited = true
288
+ step -1
289
+ read_clean( file ).each do |line|
290
+ append line
291
+ end
292
+ self
293
+ end
294
+
295
+ # Clear {Patman} content and reset current line.
296
+ def clear
297
+ @edited = true
298
+ @lines = []
299
+ @line = 0
300
+ self
301
+ end
302
+
303
+ # Find Regexp or literal string forwards or backwards. Return true
304
+ # on success.
305
+ def find( re_or_str, forward = true )
306
+ begin
307
+ @line = search_with_exception( re_or_str, forward )
308
+ true
309
+ rescue
310
+ false
311
+ end
312
+ end
313
+
314
+ # Search Regexp or literal string forwards or backwards. Fail with
315
+ # expection (PatmanSearchError) if not found.
316
+ def search( re_or_str, forward = true )
317
+ @line = search_with_exception( re_or_str, forward )
318
+ self
319
+ end
320
+
321
+ # Return line count of {Patman} content.
322
+ def length
323
+ @lines.length
324
+ end
325
+
326
+ # Return {Patman} file name.
327
+ def filename
328
+ @file
329
+ end
330
+
331
+ # Mark content modified (explicit).
332
+ def edit
333
+ @edited = true
334
+ end
335
+
336
+ # Return true if content is modified.
337
+ def edited?
338
+ @edited
339
+ end
340
+
341
+ # Execute block, retain current position, and return block value.
342
+ def excursion( &blk )
343
+ line = @line
344
+ ret = instance_eval( &blk )
345
+ @line = line
346
+ ret
347
+ end
348
+
349
+ # Mark (store) current position to default or to named mark.
350
+ def mark( tag = nil )
351
+ if tag
352
+ @marks[ tag ] = @line+1
353
+ self
354
+ else
355
+ @mark = @line+1
356
+ self
357
+ end
358
+ end
359
+
360
+ # Unmark (restore) current position from default or from named
361
+ # mark.
362
+ def unmark( tag = nil )
363
+ if tag && @marks[ tag ]
364
+ @line = @marks[ tag ]-1
365
+ self
366
+ elsif @mark
367
+ @line = @mark-1
368
+ @mark = nil
369
+ self
370
+ else
371
+ self
372
+ end
373
+ end
374
+
375
+ # Execute given block for all lines, i.e. all positions. Block
376
+ # parameter is {Patman}.
377
+ def do_all( &blk )
378
+ do_for( 1, length-1, &blk )
379
+ end
380
+
381
+ # Execute given block between start and stop positions, and update
382
+ # position.
383
+ def do_range( start, stop, &blk )
384
+ do_for( start, (stop-start+1), &blk )
385
+ end
386
+
387
+ # Execute given block starting from start by count, and update
388
+ # position.
389
+ def do_for( start, count, &blk )
390
+ line = @line
391
+ @line = start-1
392
+ count.times do
393
+ yield self
394
+ @line += 1
395
+ end
396
+ @blockline = @line
397
+ @line = line
398
+ self
399
+ end
400
+
401
+ # Get lines between start and stop positions inclusive.
402
+ def get_range( start, stop )
403
+ @lines[ (start-1) .. (stop-1) ]
404
+ end
405
+
406
+ # Get lines starting from start by count.
407
+ def get_for( start, count )
408
+ @lines[ (start-1) ... (start-1+count) ]
409
+ end
410
+
411
+ # View line content around current position (by count).
412
+ def peek( count = 0 )
413
+ view_range( @line-count, @line+count+1 )
414
+ nil
415
+ end
416
+
417
+ # View line content with line numbers around current position (by
418
+ # count).
419
+ def peek_ln( count = 0 )
420
+ view_range( @line-count, @line+count+1, true )
421
+ nil
422
+ end
423
+
424
+ # View line content.
425
+ #
426
+ # * no args: view all
427
+ # * one arg: view from current onwards by count
428
+ # * two args: view given range
429
+ def view( arg1 = nil, arg2 = nil )
430
+ if !arg1 && !arg2
431
+ view_range( 0, length )
432
+ elsif arg1 && !arg2
433
+ view_range( @line, @line+arg1 )
434
+ elsif arg1 && arg2
435
+ view_range( arg1-1, arg1-1+arg2 )
436
+ end
437
+ nil
438
+ end
439
+
440
+ # View line content with line numbers.
441
+ #
442
+ # * no args: view all
443
+ # * one arg: view from current onwards by count
444
+ # * two args: view given range
445
+ def view_ln( arg1 = nil, arg2 = nil )
446
+ if !arg1 && !arg2
447
+ view_range( 0, length, true )
448
+ elsif arg1 && !arg2
449
+ view_range( @line, @line+arg1, true )
450
+ elsif arg1 && arg2
451
+ view_range( arg1-1, arg1-1+arg2, true )
452
+ end
453
+ nil
454
+ end
455
+
456
+
457
+
458
+ private
459
+
460
+ def read_clean( file ) # :nodoc:
461
+ File.readlines( file ).map{|line| line.chomp}.map{|line| line.empty? ? nil : line}
462
+ end
463
+
464
+ def view_range( first, last, ln = false ) # :nodoc:
465
+ if ln
466
+ ln = first+1
467
+ @lines[first...last].each do |line|
468
+ puts format( "%-4s #{line}", "#{ln.to_s}:" )
469
+ ln += 1
470
+ end
471
+ else
472
+ @lines[first...last].each do |line|
473
+ puts line
474
+ end
475
+ end
476
+ end
477
+
478
+ def search_with_exception( re_or_str, forward ) # :nodoc:
479
+
480
+ line = @line
481
+ len = @lines.length
482
+
483
+ if re_or_str.kind_of? String
484
+
485
+ if forward
486
+ while line < len
487
+ if @lines[line] && @lines[line].include?( re_or_str )
488
+ return line
489
+ end
490
+ line += 1
491
+ end
492
+ else
493
+ while line >= 0
494
+ if @lines[line] && @lines[line].include?( re_or_str )
495
+ return line
496
+ end
497
+ line -= 1
498
+ end
499
+ end
500
+
501
+ else
502
+
503
+ if forward
504
+ while line < len
505
+ if @lines[line] && re_or_str.match( @lines[line] )
506
+ return line
507
+ end
508
+ line += 1
509
+ end
510
+ else
511
+ while line >= 0
512
+ if @lines[line] && re_or_str.match( @lines[line] )
513
+ return line
514
+ end
515
+ line -= 1
516
+ end
517
+ end
518
+ end
519
+
520
+ raise PatmanSearchError, "#{@file}: Could not find \"#{re_or_str}\"..."
521
+ end
522
+
523
+ end
524
+
525
+ require_relative 'version'
data/lib/version.rb ADDED
@@ -0,0 +1,6 @@
1
+ class Patman
2
+ VERSION = "0.0.1"
3
+ def Patman.version
4
+ Patman::VERSION
5
+ end
6
+ end
@@ -0,0 +1,10 @@
1
+ Line 1
2
+ Line 2
3
+ Line 3
4
+ Line 4
5
+ Line 5
6
+ Line 6
7
+ Line 7
8
+ Line 8
9
+ Line 9
10
+ Line 10