File Coverage

blib/lib/Paranoid/IO.pm
Criterion Covered Total %
statement 331 374 88.5
branch 141 214 65.8
condition 32 60 53.3
subroutine 37 41 90.2
pod 18 18 100.0
total 559 707 79.0


line stmt bran cond sub pod time code
1             # Paranoid::IO -- Paranoid IO support
2             #
3             # $Id: lib/Paranoid/IO.pm, 2.10 2022/03/08 00:01:04 acorliss Exp $
4             #
5             # This software is free software. Similar to Perl, you can redistribute it
6             # and/or modify it under the terms of either:
7             #
8             # a) the GNU General Public License
9             # as published by the
10             # Free Software Foundation ; either version 1
11             # , or any later version
12             # , or
13             # b) the Artistic License 2.0
14             # ,
15             #
16             # subject to the following additional term: No trademark rights to
17             # "Paranoid" have been or are conveyed under any of the above licenses.
18             # However, "Paranoid" may be used fairly to describe this unmodified
19             # software, in good faith, but not as a trademark.
20             #
21             # (c) 2005 - 2021, Arthur Corliss (corliss@digitalmages.com)
22             # (tm) 2008 - 2021, Paranoid Inc. (www.paranoid.com)
23             #
24             #####################################################################
25              
26             #####################################################################
27             #
28             # Environment definitions
29             #
30             #####################################################################
31              
32             package Paranoid::IO;
33              
34 58     58   19223 use 5.008;
  58         195  
35              
36 58     58   756 use strict;
  58         129  
  58         1050  
37 58     58   220 use warnings;
  58         103  
  58         1726  
38 58     58   289 use vars qw($VERSION @EXPORT @EXPORT_OK %EXPORT_TAGS);
  58         103  
  58         3524  
39 58     58   303 use base qw(Exporter);
  58         107  
  58         4029  
40 58     58   332 use Cwd qw(realpath);
  58         79  
  58         2977  
41 58     58   452 use Fcntl qw(:DEFAULT :flock :mode :seek);
  58         227  
  58         31257  
42 58     58   364 use Paranoid;
  58         108  
  58         2589  
43 58     58   5633 use Paranoid::Debug qw(:all);
  58         275  
  58         8234  
44 58     58   14977 use Paranoid::Input;
  58         108  
  58         3010  
45 58     58   27926 use IO::Handle;
  58         304246  
  58         5578  
46              
47             ($VERSION) = ( q$Revision: 2.10 $ =~ /(\d+(?:\.\d+)+)/sm );
48              
49             @EXPORT = qw(pclose pcloseAll popen preopen ptell pseek pflock pread
50             pnlread pwrite pnlwrite pappend pnlappend ptruncate pnltruncate);
51             @EXPORT_OK = ( @EXPORT, qw(PIOBLKSIZE PIOMAXFSIZE PIOLOCKSTACK) );
52             %EXPORT_TAGS = ( all => [@EXPORT_OK] );
53              
54 58     58   398 use constant PDEFPERM => 0666;
  58         165  
  58         4005  
55 58     58   276 use constant PDEFMODE => O_CREAT | O_RDWR;
  58         131  
  58         2537  
56 58     58   314 use constant PDEFBLKSZ => 4096;
  58         88  
  58         2161  
57 58     58   356 use constant PDEFFILESZ => 65536;
  58         80  
  58         2625  
58 58     58   328 use constant PFLMASK => LOCK_SH | LOCK_EX | LOCK_UN;
  58         72  
  58         2942  
59 58     58   322 use constant PIGNMFLAGS => O_TRUNC | O_CREAT | O_EXCL;
  58         122  
  58         174486  
60              
61             #####################################################################
62             #
63             # Module code follows
64             #
65             #####################################################################
66              
67             {
68              
69             my $mblksz = PDEFBLKSZ;
70              
71             sub PIOBLKSIZE : lvalue {
72              
73             # Purpose: Gets/sets default block size for I/O
74             # Returns: $mblksz
75             # Usage: PIOBLKSIZE = $bytes;
76              
77 844     844 1 2013 $mblksz;
78             }
79              
80             my $mfsz = PDEFFILESZ;
81              
82             sub PIOMAXFSIZE : lvalue {
83              
84             # Purpose: Gets/sets default max file size for I/O
85             # Returns: $mfsz
86             # Usage: PIOMAXFSIZE = bytes;
87              
88 523     523 1 1421 $mfsz;
89             }
90              
91             my %lstack;
92             my $lsflag = 0;
93              
94             sub PIOLOCKSTACK : lvalue {
95              
96             # Purpose: Enables/disables the flock lock stack
97             # Returns: $lsflag
98             # Usage: PIOLOCKSTACK = 1;
99              
100 32882     32882 1 80320 $lsflag;
101             }
102              
103             # %files: {name} => {
104             # pid => $pid,
105             # mode => $mode,
106             # perms => $perms,
107             # fh => $fh,
108             # real => $realpath,
109             # ltype => $lock,
110             # }
111             my %files;
112              
113             sub _pfFhind ($) {
114              
115             # Purpose: Searches for a filename based on the
116             # current file handle
117             # Returns: String/undefined
118             # Usage: $rv = _pfFhind($fh);
119              
120 248     248   338 my $fh = shift;
121 248         282 my $rv;
122              
123 248         529 subPreamble( PDLEVEL4, '$', $fh );
124              
125 248 50 33     861 if ( defined $fh and ref $fh eq 'GLOB' ) {
126 248         643 foreach ( keys %files ) {
127 138 50       421 if ( $files{$_}{fh} eq $fh ) {
128 138 50       407 $rv = $_ and last;
129             }
130             }
131             }
132              
133 248         636 subPostamble( PDLEVEL4, '$', $rv );
134              
135 248         401 return $rv;
136             }
137              
138             sub pclose ($) {
139              
140             # Purpose: Closes a cached file handle
141             # Returns: Boolean
142             # Usage: $rv = plcose($filename)
143             # Usage: $rv = plcose($fh)
144              
145 270     270 1 3914 my $filename = shift;
146 270         582 my $rv = 1;
147 270         452 my $fh;
148              
149 270         882 subPreamble( PDLEVEL1, '$', $filename );
150              
151 270 50       769 if ( defined $filename ) {
152              
153             # Get the missing variable
154 270 100       912 if ( ref $filename eq 'GLOB' ) {
155 10         24 $fh = $filename;
156 10         25 $filename = _pfFhind($fh);
157             } else {
158 260 100       1052 $fh = $files{$filename}{fh} if exists $files{$filename};
159             }
160              
161             # Close the filehandle
162 270 100 100     1670 if ( defined $fh and fileno $fh ) {
163 223         2298 flock $fh, LOCK_UN;
164 223         4060 $rv = close $fh;
165             }
166              
167             # Clean up internal data structures
168 270 100       904 if ( defined $filename ) {
169 264         1801 delete $files{$filename};
170 264         529 delete $lstack{$filename};
171             }
172              
173             Paranoid::ERROR =
174 270 50       664 pdebug( 'error closing file handle: %s', PDLEVEL1, $! )
175             unless $rv;
176             }
177              
178 270         1098 subPostamble( PDLEVEL1, '$', $rv );
179              
180 270         1144 return $rv;
181             }
182              
183             sub pcloseAll {
184              
185             # Purpose: Closes all filehandles
186             # Returns: Boolean
187             # Usage: $rv = pcloseAll();
188              
189 58     58 1 471 my @files = @_;
190 58         418 my $rv = 1;
191              
192 58         938 subPreamble( PDLEVEL1, '@', @files );
193              
194 58 50       712 @files = keys %files unless @files;
195 58         474 foreach (@files) {
196 16 50       138 $rv = 0 unless pclose($_);
197             }
198              
199 58         736 subPostamble( PDLEVEL1, '$', $rv );
200              
201 58         2716 return $rv;
202             }
203              
204             sub _open {
205              
206             # Purpose: Performs the sysopen call
207             # Returns: rv of sysopen
208             # Usage: $rv = _open($filename);
209             # Usage: $rv = _open($filename, $mode);
210             # Usage: $rv = _open($filename, $mode, $perms);
211              
212 261     261   535 my $filename = shift;
213 261         380 my $mode = shift;
214 261         463 my $perms = shift;
215 261         444 my ( %tmp, $f, $fh, $rv );
216              
217 261         877 subPreamble( PDLEVEL3, '$;$$', $filename, $mode, $perms );
218              
219 261 50       721 if ( defined $filename ) {
220              
221             # Detaint mode/perms
222 261         685 $rv = 1;
223 261 100       629 $mode = PDEFMODE unless defined $mode;
224 261 100       530 $perms = PDEFPERM unless defined $perms;
225 261 50       1537 unless ( detaint( $mode, 'int' ) ) {
226 0         0 $rv = 0;
227 0         0 Paranoid::ERROR =
228             pdebug( 'invalid mode passed: %s', PDLEVEL1, $mode );
229             }
230 261 50       727 unless ( detaint( $perms, 'int' ) ) {
231 0         0 $rv = 0;
232 0         0 Paranoid::ERROR =
233             pdebug( 'invalid perm passed: %s', PDLEVEL1, $perms );
234             }
235              
236             # Prep file record
237             %tmp = (
238 261         1798 mode => $mode,
239             perms => $perms,
240             pid => $$,
241             ltype => LOCK_UN,
242             );
243              
244             # Detaint filename
245 261 50       890 if ($rv) {
246 261 50       775 if ( detaint( $filename, 'filename', $f ) ) {
247              
248             # Attempt to open the fila
249             $rv =
250             ( $tmp{mode} & O_CREAT )
251             ? sysopen $fh, $f, $tmp{mode}, $tmp{perms}
252             : sysopen $fh,
253 261 100       18990 $f, $tmp{mode};
254 261 100       1339 if ($rv) {
255 221         683 $tmp{fh} = $fh;
256 221         6412 $tmp{real} = realpath($filename);
257 221         1815 $files{$filename} = {%tmp};
258             } else {
259 40         156 Paranoid::ERROR = pdebug( 'failed to open %s: %s',
260             PDLEVEL1, $filename, $! );
261             }
262              
263             } else {
264 0         0 Paranoid::ERROR =
265             pdebug( 'failed to detaint %s', PDLEVEL1, $filename );
266             }
267             }
268             }
269              
270 261         1160 subPostamble( PDLEVEL3, '$', $rv );
271              
272 261         1197 return $rv;
273             }
274              
275             sub _reopen {
276              
277             # Purpose: Reopens an open file handle
278             # Returns: rv of _open
279             # Usage: $rv = _reopen($filename);
280             # Usage: $rv = _reopen($fh);
281              
282 22     22   201 my $filename = shift;
283 22         294 my ( %tmp, $fh, $pos, $rv, $af );
284              
285 22         860 subPreamble( PDLEVEL3, '$', $filename );
286              
287 22 50 33     766 if ( defined $filename and exists $files{$filename} ) {
288              
289             # Get a copy of the file record
290 22         327 %tmp = %{ $files{$filename} };
  22         988  
291 22         185 $fh = $tmp{fh};
292              
293             # Get the current cursor position
294 22 50       1056 $pos = fileno $fh ? sysseek $fh, 0, SEEK_CUR : 0;
295 22         1639 $af = $fh->autoflush;
296              
297             # Close the old file handle
298 22         3996 $tmp{fh} = $fh = undef;
299 22 50       337 if ( pclose($filename) ) {
300              
301             # Reopen should ignore O_TRUNC, O_CREAT, and O_EXCL on reopens
302 22 50       284 $tmp{mode} &= ~PIGNMFLAGS if $tmp{mode} & PIGNMFLAGS;
303              
304             # Open the file and move the cursor back where it was
305 22         261 $rv = _open( @tmp{qw(real mode perms)} );
306 22 50       110 if ($rv) {
307              
308             # Move the cursor back to where it was
309 22         146 $fh = $files{ $tmp{real} }{fh};
310 22         326 $fh->autoflush($af);
311 22         980 $rv = sysseek $fh, $pos, SEEK_SET;
312              
313             # Move the record over to the original file name
314 22         153 $files{$filename} = { %{ $files{ $tmp{real} } } };
  22         255  
315 22 50       332 delete $files{ $tmp{real} } if $filename ne $tmp{real};
316              
317             # Delete any existing lock stack
318 22         195 delete $lstack{$filename};
319             }
320             }
321             }
322              
323 22         273 subPostamble( PDLEVEL3, '$', $rv );
324              
325 22         138 return $rv;
326             }
327              
328             sub popen {
329              
330             # Purpose: Performs a sysopen with file descriptor caching
331             # Returns: file handle
332             # Usage: $fh = popen($filename, $mode, $perms);
333              
334 57659     57659 1 75801 my $filename = shift;
335 57659         63298 my $mode = shift;
336 57659         60212 my $perms = shift;
337 57659         70559 my ( %tmp, $fh, $f, $pos, $rv );
338              
339 57659         116315 subPreamble( PDLEVEL2, '$;$$', $filename, $mode, $perms );
340              
341             # Make sure we weren't passed a file handle, but if we
342             # were attempt to find the actual filename
343 57659 50       96449 if ( defined $filename ) {
344 57659 100       100650 if ( ref $filename eq 'GLOB' ) {
345 182         246 $fh = $filename;
346 182         395 $filename = _pfFhind($filename);
347             } else {
348 57477 100       131466 $fh = $files{$filename}{fh} if exists $files{$filename};
349             }
350             }
351              
352 57659 100 100     163648 if ( defined $filename and exists $files{$filename} ) {
    100 33        
    50          
353              
354             # Make sure pid is the same
355 57364 100       132508 if ( $files{$filename}{pid} == $$ ) {
356              
357 57342 50       121659 if ( fileno $fh ) {
358              
359             # Return existing filehandle
360 57342         121597 pdebug( 'returning cached file handle', PDLEVEL2 );
361 57342         90175 $rv = $fh;
362              
363             } else {
364              
365             # Reopen a filehandle that was closed outside
366             # of this module
367 0         0 pdebug( 'reopening closed file handle', PDLEVEL2 );
368 0 0       0 $rv = $files{$filename}{fh} if _reopen($filename);
369             }
370              
371             } else {
372              
373 22         563 pdebug( 'reopening inherited file handle in child',
374             PDLEVEL2 );
375 22 50       441 $rv = $files{$filename}{fh} if _reopen($filename);
376              
377             }
378              
379             } elsif ( defined $filename ) {
380              
381 239         1120 pdebug( 'opening new file handle', PDLEVEL2 );
382 239 100       829 $rv = $files{$filename}{fh} if _open( $filename, $mode, $perms );
383              
384             } elsif ( !defined $filename and defined $fh ) {
385 56         99 Paranoid::ERROR =
386             pdebug( 'popen called with an unmanaged file handle',
387             PDLEVEL1 );
388 56 100       132 $rv = fileno $fh ? $fh : undef;
389             } else {
390 0         0 Paranoid::ERROR =
391             pdebug( 'attempted to open a file with an undefined name',
392             PDLEVEL1 );
393             }
394              
395 57659         127463 subPostamble( PDLEVEL2, '$', $rv );
396              
397 57659         104816 return $rv;
398             }
399              
400             sub preopen {
401              
402             # Purpose: Reopens either the named files or all
403             # Returns: Boolean
404             # Usage: $rv = preopen();
405             # Usage: $rv = preopen(@filenames);
406              
407 0     0 1 0 my @files = @_;
408 0         0 my $rv = 1;
409              
410 0         0 subPreamble( PDLEVEL2, '@', @files );
411              
412 0 0       0 @files = keys %files unless @files;
413 0 0       0 foreach (@files) { $rv = 0 unless _reopen($_) }
  0         0  
414              
415 0         0 subPostamble( PDLEVEL2, '$', $rv );
416              
417 0         0 return $rv;
418             }
419              
420             sub _pflock {
421              
422             # Purpose: Performs file-locking operations on the passed filename
423             # Returns: Boolean
424             # Usage: $rv = _pflock($filename, LOCK_EX);
425              
426 4148     4148   5369 my $filename = shift;
427 4148         5128 my $lock = shift;
428 4148         5326 my ( $rv, $fh, $rl );
429 4148         13888 local $!;
430              
431 4148         9787 subPreamble( PDLEVEL3, '$$', $filename, $lock );
432              
433 4148 50       7444 if ( defined $filename ) {
434              
435             # Get the missing variable
436 4148 100       7355 if ( ref $filename eq 'GLOB' ) {
437 36         50 $fh = $filename;
438 36         48 $filename = _pfFhind($fh);
439             } else {
440 4112 50       9591 $fh = $files{$filename}{fh} if exists $files{$filename};
441             }
442              
443 4148 50       6971 if ( defined $fh ) {
444              
445             # Apply the lock
446 4148         5388 $rl = $lock & PFLMASK;
447 4148         163452557 $rv = flock $fh, $lock;
448              
449             # Record change to internal state if we're tracking this file
450 4148 50       13176 if ($rv) {
451 4148 100 66     15152 if ( defined $filename and exists $files{$filename} ) {
452 4116         8098 $files{$filename}{ltype} = $rl;
453             } else {
454 32         113 pdebug(
455             'flock succeeded on file opened outside of the'
456             . ' Paranoid::IO framework (%s)',
457             PDLEVEL1, $filename
458             );
459             }
460             } else {
461 0 0       0 pdebug(
462             ( ( $lock & LOCK_NB ) ? 'non-blocking' : '' )
463             . 'flock attempt failed on %s',
464             PDLEVEL1, $filename
465             );
466             }
467             }
468             }
469              
470 4148         13575 subPostamble( PDLEVEL3, '$', $rv );
471              
472 4148         15092 return $rv;
473             }
474              
475             sub _plsflock {
476              
477 31144     31144   40477 my $filename = shift;
478 31144         33092 my $lock = shift;
479 31144         39706 my ( $fh, $stack, $rl, $ll, $lsl, $rv );
480              
481 31144         65343 subPreamble( PDLEVEL3, '$$', $filename, $lock );
482              
483             # Var Key:
484             # lock: lock passed to function (can include LOCK_NB)
485             # rl: real lock (stripping LOCK_NB)
486             # ll: last lock (as performed by last _pflock()
487             # lsl: last lock recorded in the lock stack
488              
489             # Translate glob to filename for lock stack tracking purposes
490 31144         39800 $fh = $filename;
491 31144 100       55114 $filename = _pfFhind($filename) if ref $filename eq 'GLOB';
492              
493             # Get the current lock state
494             $ll = $files{$filename}{ltype}
495 31144 50 66     100907 if defined $filename and exists $files{$filename};
496 31144 100       49583 if ( defined $ll ) {
497              
498             # Get the real lock level for comparison
499 31128         41613 $rl = $lock & PFLMASK;
500              
501             # File has been opened, at least, with popen, and has a locktype
502             # entry
503 31128 100       51659 $lstack{$filename} = [] unless exists $lstack{$filename};
504 31128         38426 $stack = $lstack{$filename};
505 31128         37897 $lsl = $$stack[-1];
506              
507             #warn "lock: $lock\nrl: $rl\nll: $ll\nlsl: $lsl\n";
508 31128 50 66     81074 pdebug(
509             'something has gone awry during lock tracking.'
510             . 'll: %s lsl: %s',
511             PDLEVEL1, $ll, $lsl
512             )
513             if defined $lsl
514             and $lsl != $ll;
515              
516             # Adjust as necessary
517 31128 100       65371 if ( $rl == LOCK_UN ) {
    100          
    50          
518              
519             # Remove a lock from the stack
520 15301         22460 pop @$stack;
521              
522 15301 100       25965 if ( scalar @$stack ) {
523              
524             # Still have locks in the stack that must not be degraded
525 14047         15887 $rv = 1;
526 14047 50       27527 if ( $ll != $$stack[-1] ) {
527              
528             # Apply the new level
529 0         0 $rv = _pflock( $filename, $$stack[-1] );
530             }
531              
532             } else {
533              
534             # No locks in the stack to preserve, so go ahead and
535             # release the lock
536 1254         2376 $rv = _pflock( $filename, LOCK_UN );
537              
538             }
539              
540             } elsif ( $rl == LOCK_SH ) {
541              
542             # Upgrade lock to preserve previous exclusive lock on the
543             # stack, if necessary
544 11982 100 100     35629 if ( defined $lsl and $lsl == LOCK_EX ) {
545 10280         15274 $lock = ( LOCK_EX | ( $lock & LOCK_NB ) );
546 10280         13207 $rl = LOCK_EX;
547             }
548              
549 11982 100       19653 $rv = $ll == $rl ? 1 : _pflock( $filename, $lock );
550 11982 50       25146 push @$stack, $rl if $rv;
551              
552             } elsif ( $rl == LOCK_EX ) {
553 3845         6684 push @$stack, $rl;
554 3845 100       7019 $rv = $ll == $rl ? 1 : _pflock( $filename, $lock );
555             } else {
556 0         0 pdebug( 'unknown lock type: %x', PDLEVEL1, $lock );
557             }
558              
559             # Report some diagnostics
560 31128 100       48452 if ( scalar @$stack ) {
561 29874         67714 pdebug( 'lock stack depth: %s', PDLEVEL4, scalar @$stack );
562 29874 100       63032 if ( $ll == $$stack[-1] ) {
563 28598         53060 pdebug( 'preserved lock at %s', PDLEVEL4, $ll );
564             } else {
565 1276         2382 pdebug( 'switched lock from %s to %s',
566             PDLEVEL4, $ll, $$stack[-1] );
567             }
568             } else {
569 1254         2198 pdebug( 'no locks remaining', PDLEVEL4 );
570             }
571              
572             # Delete empty stacks to avoid memory leaks
573 31128 100       74816 delete $lstack{$filename} unless scalar @$stack;
574              
575             } else {
576 16 50 33     60 if ( defined $fh and !defined $filename ) {
577 16         26 $rv = _pflock( $fh, $lock );
578             } else {
579 0         0 pdebug( 'file %s is unknown to Paranoid::IO so far',
580             PDLEVEL1, $filename );
581             }
582             }
583              
584 31144         67141 subPostamble( PDLEVEL3, '$', $rv );
585              
586 31144         57873 return $rv;
587             }
588              
589             sub pflock {
590              
591             # Purpose: Performs file-locking operations on the passed filename
592             # Returns: Boolean
593             # Usage: $rv = pflock($filename, LOCK_EX);
594              
595 32746     32746 1 43495 my $filename = shift;
596 32746         36072 my $lock = shift;
597 32746         40672 my ( $rv, $fh );
598              
599 32746         73505 subPreamble( PDLEVEL2, '$$', $filename, $lock );
600              
601             # NOTE: retrieving the file handle might seem silly, but if a process
602             # is forked, and the first thing they do on a file is apply an flock,
603             # the first I/O operation will close and reopen the file to avoid
604             # confusion with the parent process and, therefore, losing the lock.
605             #
606             # End sum, this is a necessary evil in order to preserve locks a
607             # before any effective I/O is done in the child.
608 32746 50       56731 if ( defined $filename ) {
609 32746         52137 $fh = popen($filename);
610 32746 100       52235 $rv =
611             PIOLOCKSTACK()
612             ? _plsflock( $filename, $lock )
613             : _pflock( $filename, $lock );
614             }
615              
616 32746         74238 subPostamble( PDLEVEL2, '$', $rv );
617              
618 32746         83457 return $rv;
619             }
620              
621             sub plockstat {
622              
623             # Purpose: Returns the the status of the last lock applied via
624             # pflock()
625             # Returns: LOCK_*
626             # Usage: $lock = plockstat($filename);
627              
628 0     0 1 0 my $filename = shift;
629 0         0 my $rv;
630              
631 0         0 subPreamble( PDLEVEL2, '$', $filename );
632              
633 0 0       0 if ( defined $filename ) {
634              
635             # Get the missing variable
636 0 0       0 $filename = _pfFhind($filename) if ref $filename eq 'GLOB';
637 0 0 0     0 if ( defined $filename and exists $files{$filename} ) {
638 0         0 $rv = $files{$filename}{ltype};
639             } else {
640 0         0 pdebug(
641             'attempted to retrieve lock status for file not opened'
642             . ' with the Paranoid::IO framework (%s)',
643             PDLEVEL1, $filename
644             );
645             }
646             }
647              
648 0         0 subPostamble( PDLEVEL2, '$', $rv );
649              
650 0         0 return $rv;
651             }
652             }
653              
654             sub ptell {
655              
656             # Purpose: Returns the cursor position in the file handle
657             # Returns: Integer
658             # Usage: $pos = ptell($filename);
659              
660 597     597 1 3066 my $filename = shift;
661 597         939 my ( $rv, $fh );
662 597         2108 local $!;
663              
664 597         1604 subPreamble( PDLEVEL2, '$', $filename );
665              
666 597 50       1302 if ( defined $filename ) {
667              
668 597         1337 $fh = popen( $filename, O_RDWR );
669 597 100       1498 if ( defined $fh ) {
670 593         4061 $rv = sysseek $fh, 0, SEEK_CUR;
671 593 50       1989 Paranoid::ERROR =
672             pdebug( 'error attempting to ptell: %s', PDLEVEL1, $! )
673             unless $rv;
674             }
675             }
676              
677 597         2164 subPostamble( PDLEVEL2, '$', $rv );
678              
679 597         2820 return $rv;
680             }
681              
682             sub pseek {
683              
684             # Purpose: Performs a sysseek
685             # Returns: Integer/undef
686             # Usage: $cur = pseek($filename, $curpos, $whence);
687              
688 11758     11758 1 1346405 my $filename = shift;
689 11758         16945 my $setpos = shift;
690 11758         15900 my $whence = shift;
691 11758         15981 my ( $rv, $fh );
692 11758         33001 local $!;
693              
694 11758         28034 subPreamble( PDLEVEL2, '$$;$', $filename, $setpos, $whence );
695              
696 11758 50       21308 if ( defined $filename ) {
697              
698 11758         22039 $fh = popen( $filename, O_RDWR );
699 11758 100       21579 if ( defined $fh ) {
700 11754 50       22640 $whence = SEEK_SET unless defined $whence;
701 11754         112617 $rv = sysseek $fh, $setpos, $whence;
702 11754 100       43824 Paranoid::ERROR =
703             pdebug( 'error attempting to pseek: %s', PDLEVEL1, $! )
704             unless $rv;
705             }
706             }
707              
708 11758         37678 subPostamble( PDLEVEL2, '$', $rv );
709              
710 11758         55654 return $rv;
711             }
712              
713             sub pwrite {
714              
715             # Purpose: Performs a syswrite w/locking
716             # Returns: Integer/undef
717             # Usage: $bytes = pwrite($filename, $text);
718             # Usage: $bytes = pwrite($filename, $text, $length);
719             # Usage: $bytes = pwrite($filename, $text, $length, $offset);
720             # Usage: $bytes = pwrite($filename, $text, $length, $offset, $nolock);
721              
722 2329     2329 1 14271 my $filename = shift;
723 2329         3431 my $out = shift;
724 2329         3899 my $wlen = shift;
725 2329         3386 my $offset = shift;
726 2329         3110 my $nolock = shift;
727 2329 100       5788 my $bytes = defined $out ? length $out : 0;
728 2329         3819 my ( $fh, $rv );
729              
730 2329         5809 subPreamble( PDLEVEL2, '$$;$$$', $filename, $bytes, $wlen, $offset,
731             $nolock );
732              
733 2329 100 66     14064 if ( defined $filename and defined $out and length $out ) {
      100        
734              
735             # Opportunistically open a file handle if needed,
736             # otherwise, just retrieve the existing file handle
737 2321         5019 $fh = popen( $filename, O_WRONLY | O_CREAT );
738              
739             # Smoke 'em if you got'em...
740 2321 50       5495 if ( defined $fh ) {
741 2321 50 33     6569 if ( $nolock or pflock( $filename, LOCK_EX ) ) {
742 2321 100       5220 $wlen = length $out unless defined $wlen;
743 2321 100       5127 $offset = 0 unless defined $offset;
744 2321         62967 $rv = syswrite $fh, $out, $wlen, $offset;
745 2321 50       8734 if ( defined $rv ) {
746 2321         7899 pdebug( 'wrote %d bytes', PDLEVEL2, $rv );
747             } else {
748 0         0 Paranoid::ERROR =
749             pdebug( 'failed to write to file handle: %s',
750             PDLEVEL1, $! );
751             }
752 2321 50       7139 pflock( $filename, LOCK_UN ) unless $nolock;
753             }
754             }
755             }
756              
757 2329         5675 subPostamble( PDLEVEL2, '$', $rv );
758              
759 2329         8354 return $rv;
760             }
761              
762             sub pnlwrite {
763              
764             # Purpose: Wrapper for pwrite w/o internal flocking
765             # Returns: RV of pwrite
766             # Usage: $bytes = pnlwrite($filename, $text, $length);
767             # Usage: $bytes = pnlwrite($filename, $text, $length, $offset);
768              
769 0     0 1 0 my $filename = shift;
770 0         0 my $out = shift;
771 0         0 my $wlen = shift;
772 0         0 my $offset = shift;
773              
774 0         0 return pwrite( $filename, $out, $wlen, $offset, 1 );
775             }
776              
777             sub pappend {
778              
779             # Purpose: Appends the data to the end of the file,
780             # but does not move the file cursor
781             # Returns: Integer/undef
782             # Usage: $rv = pappend($filename, $content);
783             # Usage: $rv = pappend($filename, $content, $length);
784             # Usage: $rv = pappend($filename, $content, $length, $offset);
785              
786 520     520 1 1030 my $filename = shift;
787 520         933 my $out = shift;
788 520         865 my $wlen = shift;
789 520         725 my $offset = shift;
790 520         742 my $nolock = shift;
791 520         823 my ( $fh, $pos, $rv );
792              
793 520         1699 subPreamble( PDLEVEL2, '$$;$$', $filename, $out, $wlen, $offset,
794             $nolock );
795              
796 520 50 33     3951 if ( defined $filename and defined $out and length $out ) {
      33        
797              
798             # Opportunistically open a file handle in append mode
799 520         1518 $fh = popen( $filename, O_WRONLY | O_CREAT | O_APPEND );
800              
801             # Smoke 'em if you got'em...
802 520 50       1110 if ( defined $fh ) {
803              
804             # Lock the file
805 520 50 33     1668 if ( $nolock or pflock( $filename, LOCK_EX ) ) {
806              
807             # Save the current position
808 520         4580 $pos = sysseek $fh, 0, SEEK_CUR;
809              
810             # Seek to the end of the file
811 520 50 33     5215 if ( $pos and sysseek $fh, 0, SEEK_END ) {
812              
813             # write the content
814 520 50       2327 $wlen = length $out unless defined $wlen;
815 520 50       1323 $offset = 0 unless defined $offset;
816 520         12653 $rv = syswrite $fh, $out, $wlen, $offset;
817 520 50       1946 if ( defined $rv ) {
818 520         1984 pdebug( 'wrote %d bytes', PDLEVEL2, $rv );
819             } else {
820 0         0 Paranoid::ERROR =
821             pdebug( 'failed to write to file handle: %s',
822             PDLEVEL1, $! );
823             }
824             }
825              
826             # Seek back to original position
827 520         4414 sysseek $fh, $pos, SEEK_SET;
828              
829             # Unlock the file handle
830 520 50       2248 pflock( $filename, LOCK_UN ) unless $nolock;
831             }
832             }
833             }
834              
835 520         1409 subPostamble( PDLEVEL2, '$', $rv );
836              
837 520         1936 return $rv;
838             }
839              
840             sub pnlappend {
841              
842             # Purpose: Wrapper for pappend w/o internal flocking
843             # Returns: RV of pappend
844             # Usage: $bytes = pnlappend($filename, $text, $length);
845             # Usage: $bytes = pnlappend($filename, $text, $length, $offset);
846              
847 0     0 1 0 my $filename = shift;
848 0         0 my $out = shift;
849 0         0 my $wlen = shift;
850 0         0 my $offset = shift;
851              
852 0         0 return pappend( $filename, $out, $wlen, $offset, 1 );
853             }
854              
855             sub pread ($\$;@) {
856              
857             # Purpose: Performs a sysread w/locking
858             # Returns: Integer/undef
859             # Usage: $bytes = pread($filename, $text, $length);
860             # Usage: $bytes = pread($filename, $text, $length, $offset);
861              
862 9423     9423 1 16492 my $filename = shift;
863 9423         10869 my $sref = shift;
864 9423         14244 my $rlen = shift;
865 9423         12879 my $offset = shift;
866 9423         11064 my $nolock = shift;
867 9423         12296 my ( $fh, $rv );
868              
869 9423         23278 subPreamble( PDLEVEL2, '$\$;$$$', $filename, $sref, $rlen, $offset,
870             $nolock );
871              
872 9423 50       18276 if ( defined $filename ) {
873              
874             # Opportunistically open a file handle if needed,
875             # otherwise, just retrieve the existing file handle
876 9423         17173 $fh = popen( $filename, O_RDONLY );
877              
878             # Smoke 'em if you got'em...
879 9423 100       21398 if ( defined $fh ) {
880 9417 50 66     27998 if ( $nolock or pflock( $filename, LOCK_SH ) ) {
881 9417 100       20149 $rlen = PIOBLKSIZE unless defined $rlen;
882 9417 50       17103 $offset = 0 unless defined $offset;
883 9417         138597 $rv = sysread $fh, $$sref, $rlen, $offset;
884 9417 100       33945 if ( defined $rv ) {
885 9413         27969 pdebug( 'read %d bytes', PDLEVEL2, $rv );
886             } else {
887 4         20 Paranoid::ERROR =
888             pdebug( 'failed to read from file handle: %s',
889             PDLEVEL1, $! );
890             }
891 9417 100       27519 pflock( $filename, LOCK_UN ) unless $nolock;
892             }
893             }
894             }
895              
896 9423         24809 subPostamble( PDLEVEL2, '$', $rv );
897              
898 9423         33715 return $rv;
899             }
900              
901             sub pnlread ($\$;@) {
902              
903             # Purpose: Wrapper for pread w/o internal flocking
904             # Returns: RV of pread
905             # Usage: $bytes = pnlread($filename, $text, $length);
906             # Usage: $bytes = pnlread($filename, $text, $length, $offset);
907              
908 2     2 1 11 my $filename = shift;
909 2         6 my $sref = shift;
910 2         8 my $rlen = shift;
911 2         6 my $offset = shift;
912              
913 2         16 return pread( $filename, $$sref, $rlen, $offset, 1 );
914             }
915              
916             sub ptruncate {
917              
918             # Purpose: Truncates the specified file
919             # Returns: RV of truncate
920             # Usage: $rv = ptruncate($filename);
921             # Usage: $rv = ptruncate($filename, $pos);
922             # Usage: $rv = ptruncate($filename, $pos, 1);
923              
924 3     3 1 9 my $filename = shift;
925 3         8 my $pos = shift;
926 3         12 my $nolock = shift;
927 3         9 my ( $rv, $fh, $cpos );
928              
929 3         13 subPreamble( PDLEVEL2, '$;$$', $filename, $pos, $nolock );
930              
931 3 50       12 if ( defined $filename ) {
932 3 50       17 $pos = 0 unless defined $pos;
933 3         10 $fh = popen( $filename, O_RDWR | O_CREAT );
934              
935             # Smoke 'em if you got'em...
936 3 50       14 if ( defined $fh ) {
937 3 50 33     19 if ( $nolock or pflock( $filename, LOCK_EX ) ) {
938 3         30 $cpos = sysseek $fh, 0, SEEK_CUR;
939 3         203 $rv = truncate $fh, $pos;
940 3 50       16 if ($rv) {
941 3 50       15 sysseek $fh, $pos, SEEK_SET if $cpos > $pos;
942             } else {
943 0         0 Paranoid::ERROR =
944             pdebug( 'failed to truncate file: %s', PDLEVEL1, $! );
945             }
946 3 50       25 pflock( $filename, LOCK_UN ) unless $nolock;
947             }
948             }
949             }
950              
951 3         13 subPostamble( PDLEVEL2, '$', $rv );
952              
953 3         12 return $rv;
954             }
955              
956             END {
957              
958             # Attempt to clean close all filehandles
959 58     58   56040952 pcloseAll();
960             }
961              
962             1;
963              
964             __END__