File Coverage

blib/lib/Paranoid/IO.pm
Criterion Covered Total %
statement 361 408 88.4
branch 141 214 65.8
condition 32 60 53.3
subroutine 37 41 90.2
pod 18 18 100.0
total 589 741 79.4


line stmt bran cond sub pod time code
1             # Paranoid::IO -- Paranoid IO support
2             #
3             # $Id: lib/Paranoid/IO.pm, 2.09 2021/12/28 15:46:49 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 57     57   24706 use 5.008;
  57         203  
35              
36 57     57   311 use strict;
  57         117  
  57         1254  
37 57     57   318 use warnings;
  57         107  
  57         2106  
38 57     57   302 use vars qw($VERSION @EXPORT @EXPORT_OK %EXPORT_TAGS);
  57         162  
  57         4239  
39 57     57   452 use base qw(Exporter);
  57         180  
  57         5174  
40 57     57   380 use Cwd qw(realpath);
  57         117  
  57         3447  
41 57     57   345 use Fcntl qw(:DEFAULT :flock :mode :seek);
  57         125  
  57         31745  
42 57     57   652 use Paranoid;
  57         138  
  57         2955  
43 57     57   7176 use Paranoid::Debug qw(:all);
  57         138  
  57         9581  
44 57     57   20510 use Paranoid::Input;
  57         135  
  57         4092  
45 57     57   37889 use IO::Handle;
  57         386726  
  57         6854  
46              
47             ($VERSION) = ( q$Revision: 2.09 $ =~ /(\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 57     57   494 use constant PDEFPERM => 0666;
  57         120  
  57         4973  
55 57     57   381 use constant PDEFMODE => O_CREAT | O_RDWR;
  57         152  
  57         3117  
56 57     57   381 use constant PDEFBLKSZ => 4096;
  57         120  
  57         2843  
57 57     57   365 use constant PDEFFILESZ => 65536;
  57         497  
  57         3653  
58 57     57   495 use constant PFLMASK => LOCK_SH | LOCK_EX | LOCK_UN;
  57         144  
  57         3473  
59 57     57   338 use constant PIGNMFLAGS => O_TRUNC | O_CREAT | O_EXCL;
  57         97  
  57         219953  
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
76              
77 844     844 1 2630 $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: PIOBLKSIZE
87              
88 523     523 1 1764 $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
99              
100 32840     32840 1 76883 $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   369 my $fh = shift;
121 248         353 my $rv;
122              
123 248         613 pdebug( 'entering w/%s', PDLEVEL2, $fh );
124 248         699 pIn();
125              
126 248 50 33     1120 if ( defined $fh and ref $fh eq 'GLOB' ) {
127 248         852 foreach ( keys %files ) {
128 138 50       505 if ( $files{$_}{fh} eq $fh ) {
129 138 50       510 $rv = $_ and last;
130             }
131             }
132             }
133              
134 248         652 pOut();
135 248         597 pdebug( 'leaving w/rv: %s', PDLEVEL2, $rv );
136              
137 248         552 return $rv;
138             }
139              
140             sub pclose {
141              
142             # Purpose: Closes a cached file handle
143             # Returns: Boolean
144             # Usage: $rv = plcose($filename)
145             # Usage: $rv = plcose($fh)
146              
147 268     268 1 4875 my $filename = shift;
148 268         1673 my $rv = 1;
149 268         599 my $fh;
150              
151 268         1088 pdebug( 'entering w/%s', PDLEVEL2, $filename );
152 268         905 pIn();
153              
154 268 50       784 if ( defined $filename ) {
155              
156             # Get the missing variable
157 268 100       1303 if ( ref $filename eq 'GLOB' ) {
158 10         30 $fh = $filename;
159 10         28 $filename = _pfFhind($fh);
160             } else {
161 258 100       1223 $fh = $files{$filename}{fh} if exists $files{$filename};
162             }
163              
164             # Close the filehandle
165 268 100 100     1891 if ( defined $fh and fileno $fh ) {
166 221         3197 flock $fh, LOCK_UN;
167 221         4345 $rv = close $fh;
168             }
169              
170             # Clean up internal data structures
171 268 100       1025 if ( defined $filename ) {
172 262         1824 delete $files{$filename};
173 262         639 delete $lstack{$filename};
174             }
175              
176             Paranoid::ERROR =
177 268 50       746 pdebug( 'error closing file handle: %s', PDLEVEL1, $! )
178             unless $rv;
179             }
180              
181 268         2081 pOut();
182 268         2704 pdebug( 'leaving w/rv: %s', PDLEVEL2, $rv );
183              
184 268         1526 return $rv;
185             }
186              
187             sub pcloseAll {
188              
189             # Purpose: Closes all filehandles
190             # Returns: Boolean
191             # Usage: $rv = pcloseAll();
192              
193 57     57 1 387 my @files = @_;
194 57         455 my $rv = 1;
195              
196 57         723 pdebug( 'entering', PDLEVEL3 );
197 57         504 pIn();
198              
199 57 50       779 @files = keys %files unless @files;
200 57         637 foreach (@files) {
201 16 50       134 $rv = 0 unless pclose($_);
202             }
203              
204 57         700 pOut();
205 57         407 pdebug( 'leaving w/rv: %s', PDLEVEL3, $rv );
206              
207 57         2972 return $rv;
208             }
209              
210             sub _open {
211              
212             # Purpose: Performs the sysopen call
213             # Returns: rv of sysopen
214             # Usage: $rv = _open($filename);
215             # Usage: $rv = _open($filename, $mode);
216             # Usage: $rv = _open($filename, $mode, $perms);
217              
218 259     259   739 my $filename = shift;
219 259         642 my $mode = shift;
220 259         491 my $perms = shift;
221 259         573 my ( %tmp, $f, $fh, $rv );
222              
223 259         795 pdebug( 'entering w/(%s)(%s)(%s)',
224             PDLEVEL3, $filename, $mode, $perms );
225 259         734 pIn();
226              
227 259 50       674 if ( defined $filename ) {
228              
229             # Detaint mode/perms
230 259         683 $rv = 1;
231 259 100       654 $mode = PDEFMODE unless defined $mode;
232 259 100       772 $perms = PDEFPERM unless defined $perms;
233 259 50       1719 unless ( detaint( $mode, 'int' ) ) {
234 0         0 $rv = 0;
235 0         0 Paranoid::ERROR =
236             pdebug( 'invalid mode passed: %s', PDLEVEL1, $mode );
237             }
238 259 50       891 unless ( detaint( $perms, 'int' ) ) {
239 0         0 $rv = 0;
240 0         0 Paranoid::ERROR =
241             pdebug( 'invalid perm passed: %s', PDLEVEL1, $perms );
242             }
243              
244             # Prep file record
245             %tmp = (
246 259         2152 mode => $mode,
247             perms => $perms,
248             pid => $$,
249             ltype => LOCK_UN,
250             );
251              
252             # Detaint filename
253 259 50       907 if ($rv) {
254 259 50       918 if ( detaint( $filename, 'filename', $f ) ) {
255              
256             # Attempt to open the fila
257             $rv =
258             ( $tmp{mode} & O_CREAT )
259             ? sysopen $fh, $f, $tmp{mode}, $tmp{perms}
260             : sysopen $fh,
261 259 100       21481 $f, $tmp{mode};
262 259 100       1607 if ($rv) {
263 219         675 $tmp{fh} = $fh;
264 219         9099 $tmp{real} = realpath($filename);
265 219         2337 $files{$filename} = {%tmp};
266             } else {
267 40         241 Paranoid::ERROR = pdebug( 'failed to open %s: %s',
268             PDLEVEL1, $filename, $! );
269             }
270              
271             } else {
272 0         0 Paranoid::ERROR =
273             pdebug( 'failed to detaint %s', PDLEVEL1, $filename );
274             }
275             }
276             }
277              
278 259         1963 pOut();
279 259         813 pdebug( 'leaving w/rv: %s', PDLEVEL3, $rv );
280              
281 259         1569 return $rv;
282             }
283              
284             sub _reopen {
285              
286             # Purpose: Reopens an open file handle
287             # Returns: rv of _open
288             # Usage: $rv = _reopen($filename);
289             # Usage: $rv = _reopen($fh);
290              
291 22     22   314 my $filename = shift;
292 22         279 my ( %tmp, $fh, $pos, $rv, $af );
293              
294 22         422 pdebug( 'entering w/(%s)', PDLEVEL3, $filename );
295 22         351 pIn();
296              
297 22 50 33     1029 if ( defined $filename and exists $files{$filename} ) {
298              
299             # Get a copy of the file record
300 22         178 %tmp = %{ $files{$filename} };
  22         9117  
301 22         285 $fh = $tmp{fh};
302              
303             # Get the current cursor position
304 22 50       496 $pos = fileno $fh ? sysseek $fh, 0, SEEK_CUR : 0;
305 22         2476 $af = $fh->autoflush;
306              
307             # Close the old file handle
308 22         5638 $tmp{fh} = $fh = undef;
309 22 50       432 if ( pclose($filename) ) {
310              
311             # Reopen should ignore O_TRUNC, O_CREAT, and O_EXCL on reopens
312 22 50       2381 $tmp{mode} &= ~PIGNMFLAGS if $tmp{mode} & PIGNMFLAGS;
313              
314             # Open the file and move the cursor back where it was
315 22         2179 $rv = _open( @tmp{qw(real mode perms)} );
316 22 50       706 if ($rv) {
317              
318             # Move the cursor back to where it was
319 22         140 $fh = $files{ $tmp{real} }{fh};
320 22         717 $fh->autoflush($af);
321 22         1511 $rv = sysseek $fh, $pos, SEEK_SET;
322              
323             # Move the record over to the original file name
324 22         178 $files{$filename} = { %{ $files{ $tmp{real} } } };
  22         415  
325 22 50       255 delete $files{ $tmp{real} } if $filename ne $tmp{real};
326              
327             # Delete any existing lock stack
328 22         91 delete $lstack{$filename};
329             }
330             }
331             }
332              
333 22         131 pOut();
334 22         198 pdebug( 'leaving w/rv: %s', PDLEVEL3, $rv );
335              
336 22         151 return $rv;
337             }
338              
339             sub popen {
340              
341             # Purpose: Performs a sysopen with file descriptor caching
342             # Returns: file handle
343             # Usage: $fh = popen($filename, $mode, $perms);
344              
345 57586     57586 1 92864 my $filename = shift;
346 57586         66635 my $mode = shift;
347 57586         75834 my $perms = shift;
348 57586         80374 my ( %tmp, $fh, $f, $pos, $rv );
349              
350 57586         123577 pdebug( 'entering w/(%s)(%s)(%s)',
351             PDLEVEL2, $filename, $mode, $perms );
352 57586         127377 pIn();
353              
354             # Make sure we weren't passed a file handle, but if we
355             # were attempt to find the actual filename
356 57586 50       96709 if ( defined $filename ) {
357 57586 100       98369 if ( ref $filename eq 'GLOB' ) {
358 182         344 $fh = $filename;
359 182         506 $filename = _pfFhind($filename);
360             } else {
361 57404 100       145756 $fh = $files{$filename}{fh} if exists $files{$filename};
362             }
363             }
364              
365 57586 100 100     186845 if ( defined $filename and exists $files{$filename} ) {
    100 33        
    50          
366              
367             # Make sure pid is the same
368 57293 100       153961 if ( $files{$filename}{pid} == $$ ) {
369              
370 57271 50       127144 if ( fileno $fh ) {
371              
372             # Return existing filehandle
373 57271         123505 pdebug( 'returning cached file handle', PDLEVEL2 );
374 57271         89064 $rv = $fh;
375              
376             } else {
377              
378             # Reopen a filehandle that was closed outside
379             # of this module
380 0         0 pdebug( 'reopening closed file handle', PDLEVEL2 );
381 0 0       0 $rv = $files{$filename}{fh} if _reopen($filename);
382             }
383              
384             } else {
385              
386 22         734 pdebug( 'reopening inherited file handle in child',
387             PDLEVEL2 );
388 22 50       603 $rv = $files{$filename}{fh} if _reopen($filename);
389              
390             }
391              
392             } elsif ( defined $filename ) {
393              
394 237         924 pdebug( 'opening new file handle', PDLEVEL2 );
395 237 100       1118 $rv = $files{$filename}{fh} if _open( $filename, $mode, $perms );
396              
397             } elsif ( !defined $filename and defined $fh ) {
398 56         148 Paranoid::ERROR =
399             pdebug( 'popen called with an unmanaged file handle',
400             PDLEVEL1 );
401 56 100       161 $rv = fileno $fh ? $fh : undef;
402             } else {
403 0         0 Paranoid::ERROR =
404             pdebug( 'attempted to open a file with an undefined name',
405             PDLEVEL1 );
406             }
407              
408 57586         123407 pOut();
409 57586         126323 pdebug( 'leaving w/rv: %s', PDLEVEL2, $rv );
410              
411 57586         127756 return $rv;
412             }
413              
414             sub preopen {
415              
416             # Purpose: Reopens either the named files or all
417             # Returns: Boolean
418             # Usage: $rv = preopen();
419             # Usage: $rv = preopen(@filenames);
420              
421 0     0 1 0 my @files = @_;
422 0         0 my $rv = 1;
423              
424 0         0 pdebug( 'entering w/%s', PDLEVEL2, @files );
425 0         0 pIn();
426              
427 0 0       0 @files = keys %files unless @files;
428 0 0       0 foreach (@files) { $rv = 0 unless _reopen($_) }
  0         0  
429              
430 0         0 pOut();
431 0         0 pdebug( 'leaving w/rv: %s', PDLEVEL2, $rv );
432              
433 0         0 return $rv;
434             }
435              
436             sub _pflock {
437              
438             # Purpose: Performs file-locking operations on the passed filename
439             # Returns: Boolean
440             # Usage: $rv = _pflock($filename, LOCK_EX);
441              
442 4106     4106   6321 my $filename = shift;
443 4106         5154 my $lock = shift;
444 4106         6769 my ( $rv, $fh, $rl );
445 4106         16940 local $!;
446              
447 4106         10627 pdebug( 'entering w/(%s)(%s)', PDLEVEL2, $filename, $lock );
448 4106         10296 pIn();
449              
450 4106 50       8582 if ( defined $filename ) {
451              
452             # Get the missing variable
453 4106 100       7905 if ( ref $filename eq 'GLOB' ) {
454 36         51 $fh = $filename;
455 36         68 $filename = _pfFhind($fh);
456             } else {
457 4070 50       10895 $fh = $files{$filename}{fh} if exists $files{$filename};
458             }
459              
460 4106 50       8300 if ( defined $fh ) {
461              
462             # Apply the lock
463 4106         6608 $rl = $lock & PFLMASK;
464 4106         106074332 $rv = flock $fh, $lock;
465              
466             # Record change to internal state if we're tracking this file
467 4106 50       14717 if ($rv) {
468 4106 100 66     18790 if ( defined $filename and exists $files{$filename} ) {
469 4074         9822 $files{$filename}{ltype} = $rl;
470             } else {
471 32         128 pdebug(
472             'flock succeeded on file opened outside of the'
473             . ' Paranoid::IO framework (%s)',
474             PDLEVEL1, $filename
475             );
476             }
477             } else {
478 0 0       0 pdebug(
479             ( ( $lock & LOCK_NB ) ? 'non-blocking' : '' )
480             . 'flock attempt failed on %s',
481             PDLEVEL1, $filename
482             );
483             }
484             }
485             }
486              
487 4106         15768 pOut();
488 4106         10656 pdebug( 'leaving w/rv: %s', PDLEVEL2, $rv );
489              
490 4106         15295 return $rv;
491             }
492              
493             sub _plsflock {
494              
495 31144     31144   42309 my $filename = shift;
496 31144         41004 my $lock = shift;
497 31144         41146 my ( $fh, $stack, $rl, $ll, $lsl, $rv );
498              
499 31144         66215 pdebug( 'entering w/(%s)(%s)', PDLEVEL2, $filename, $lock );
500 31144         67060 pIn();
501              
502             # Var Key:
503             # lock: lock passed to function (can include LOCK_NB)
504             # rl: real lock (stripping LOCK_NB)
505             # ll: last lock (as performed by last _pflock()
506             # lsl: last lock recorded in the lock stack
507              
508             # Translate glob to filename for lock stack tracking purposes
509 31144         41070 $fh = $filename;
510 31144 100       55469 $filename = _pfFhind($filename) if ref $filename eq 'GLOB';
511              
512             # Get the current lock state
513             $ll = $files{$filename}{ltype}
514 31144 50 66     117173 if defined $filename and exists $files{$filename};
515 31144 100       48857 if ( defined $ll ) {
516              
517             # Get the real lock level for comparison
518 31128         44996 $rl = $lock & PFLMASK;
519              
520             # File has been opened, at least, with popen, and has a locktype
521             # entry
522 31128 100       60505 $lstack{$filename} = [] unless exists $lstack{$filename};
523 31128         40728 $stack = $lstack{$filename};
524 31128         41365 $lsl = $$stack[-1];
525              
526             #warn "lock: $lock\nrl: $rl\nll: $ll\nlsl: $lsl\n";
527 31128 50 66     91672 pdebug(
528             'something has gone awry during lock tracking.'
529             . 'll: %s lsl: %s',
530             PDLEVEL1, $ll, $lsl
531             )
532             if defined $lsl
533             and $lsl != $ll;
534              
535             # Adjust as necessary
536 31128 100       64976 if ( $rl == LOCK_UN ) {
    100          
    50          
537              
538             # Remove a lock from the stack
539 15301         20985 pop @$stack;
540              
541 15301 100       25103 if ( scalar @$stack ) {
542              
543             # Still have locks in the stack that must not be degraded
544 14047         16779 $rv = 1;
545 14047 50       29135 if ( $ll != $$stack[-1] ) {
546              
547             # Apply the new level
548 0         0 $rv = _pflock( $filename, $$stack[-1] );
549             }
550              
551             } else {
552              
553             # No locks in the stack to preserve, so go ahead and
554             # release the lock
555 1254         2480 $rv = _pflock( $filename, LOCK_UN );
556              
557             }
558              
559             } elsif ( $rl == LOCK_SH ) {
560              
561             # Upgrade lock to preserve previous exclusive lock on the
562             # stack, if necessary
563 11982 100 100     36634 if ( defined $lsl and $lsl == LOCK_EX ) {
564 10280         15900 $lock = ( LOCK_EX | ( $lock & LOCK_NB ) );
565 10280         13891 $rl = LOCK_EX;
566             }
567              
568 11982 100       20236 $rv = $ll == $rl ? 1 : _pflock( $filename, $lock );
569 11982 50       27834 push @$stack, $rl if $rv;
570              
571             } elsif ( $rl == LOCK_EX ) {
572 3845         7049 push @$stack, $rl;
573 3845 100       7464 $rv = $ll == $rl ? 1 : _pflock( $filename, $lock );
574             } else {
575 0         0 pdebug( 'unknown lock type: %x', PDLEVEL2, $lock );
576             }
577              
578             # Report some diagnostics
579 31128 100       51846 if ( scalar @$stack ) {
580 29874         68990 pdebug( 'lock stack depth: %s', PDLEVEL4, scalar @$stack );
581 29874 100       61477 if ( $ll == $$stack[-1] ) {
582 28598         52415 pdebug( 'preserved lock at %s', PDLEVEL4, $ll );
583             } else {
584 1276         2700 pdebug( 'switched lock from %s to %s',
585             PDLEVEL4, $ll, $$stack[-1] );
586             }
587             } else {
588 1254         2688 pdebug( 'no locks remaining', PDLEVEL4 );
589             }
590              
591             # Delete empty stacks to avoid memory leaks
592 31128 100       72181 delete $lstack{$filename} unless scalar @$stack;
593              
594             } else {
595 16 50 33     57 if ( defined $fh and !defined $filename ) {
596 16         29 $rv = _pflock( $fh, $lock );
597             } else {
598 0         0 pdebug( 'file %s is unknown to Paranoid::IO so far',
599             PDLEVEL2, $filename );
600             }
601             }
602              
603 31144         63293 pOut();
604 31144         63242 pdebug( 'leaving w/rv: %s', PDLEVEL2, $rv );
605              
606 31144         62699 return $rv;
607             }
608              
609             sub pflock {
610              
611             # Purpose: Performs file-locking operations on the passed filename
612             # Returns: Boolean
613             # Usage: $rv = pflock($filename, LOCK_EX);
614              
615 32704     32704 1 48901 my $filename = shift;
616 32704         38403 my $lock = shift;
617 32704         44638 my ( $rv, $fh );
618              
619 32704         68734 pdebug( 'entering w/(%s)(%s)', PDLEVEL2, $filename );
620 32704         75705 pIn();
621              
622             # NOTE: retrieving the file handle might seem silly, but if a process
623             # is forked, and the first thing they do on a file is apply an flock,
624             # the first I/O operation will close and reopen the file to avoid
625             # confusion with the parent process and, therefore, losing the lock.
626             #
627             # End sum, this is a necessary evil in order to preserve locks a
628             # before any effective I/O is done in the child.
629 32704 50       56359 if ( defined $filename ) {
630 32704         55185 $fh = popen($filename);
631 32704 100       56172 $rv =
632             PIOLOCKSTACK()
633             ? _plsflock( $filename, $lock )
634             : _pflock( $filename, $lock );
635             }
636              
637 32704         71425 pOut();
638 32704         64010 pdebug( 'leaving w/rv: %s', PDLEVEL2, $rv );
639              
640 32704         91656 return $rv;
641             }
642              
643             sub plockstat {
644              
645             # Purpose: Returns the the status of the last lock applied via
646             # pflock()
647             # Returns: LOCK_*
648             # Usage: $lock = plockstat($filename);
649              
650 0     0 1 0 my $filename = shift;
651 0         0 my $rv;
652              
653 0         0 pdebug( 'entering w/(%s)(%s)', PDLEVEL2, $filename );
654 0         0 pIn();
655              
656 0 0       0 if ( defined $filename ) {
657              
658             # Get the missing variable
659 0 0       0 $filename = _pfFhind($filename) if ref $filename eq 'GLOB';
660 0 0 0     0 if ( defined $filename and exists $files{$filename} ) {
661 0         0 $rv = $files{$filename}{ltype};
662             } else {
663 0         0 pdebug(
664             'attempted to retrieve lock status for file not opened'
665             . ' with the Paranoid::IO framework (%s)',
666             PDLEVEL1, $filename
667             );
668             }
669             }
670              
671 0         0 pOut();
672 0         0 pdebug( 'leaving w/rv: %s', PDLEVEL2, $rv );
673              
674 0         0 return $rv;
675             }
676             }
677              
678             sub ptell {
679              
680             # Purpose: Returns the cursor position in the file handle
681             # Returns: Integer
682             # Usage: $pos = ptell($filename);
683              
684 592     592 1 3922 my $filename = shift;
685 592         1006 my ( $rv, $fh );
686 592         2672 local $!;
687              
688 592         1703 pdebug( 'entering w/%s', PDLEVEL2, $filename );
689 592         1547 pIn();
690              
691 592 50       1372 if ( defined $filename ) {
692              
693 592         1856 $fh = popen( $filename, O_RDWR );
694 592 100       1604 if ( defined $fh ) {
695 588         4939 $rv = sysseek $fh, 0, SEEK_CUR;
696 588 50       2547 Paranoid::ERROR =
697             pdebug( 'error attempting to ptell: %s', PDLEVEL1, $! )
698             unless $rv;
699             }
700             }
701              
702 592         2293 pOut();
703 592         1800 pdebug( 'leaving w/rv: %s', PDLEVEL2, $rv );
704              
705 592         3398 return $rv;
706             }
707              
708             sub pseek {
709              
710             # Purpose: Performs a sysseek
711             # Returns: Integer/undef
712             # Usage: $cur = pseek($filename, $curpos, $whence);
713              
714 11755     11755 1 1458860 my $filename = shift;
715 11755         18898 my $setpos = shift;
716 11755         16463 my $whence = shift;
717 11755         16843 my ( $rv, $fh );
718 11755         34151 local $!;
719              
720 11755         30809 pdebug( 'entering w/(%s)(%s)(%s)', PDLEVEL2, $filename, $setpos,
721             $whence );
722 11755         27383 pIn();
723              
724 11755 50       27142 if ( defined $filename ) {
725              
726 11755         24151 $fh = popen( $filename, O_RDWR );
727 11755 100       25854 if ( defined $fh ) {
728 11751 50       22882 $whence = SEEK_SET unless defined $whence;
729 11751         117976 $rv = sysseek $fh, $setpos, $whence;
730 11751 100       46191 Paranoid::ERROR =
731             pdebug( 'error attempting to pseek: %s', PDLEVEL1, $! )
732             unless $rv;
733             }
734             }
735              
736 11755         40269 pOut();
737 11755         27692 pdebug( 'leaving w/rv: %s', PDLEVEL2, $rv );
738              
739 11755         63337 return $rv;
740             }
741              
742             sub pwrite {
743              
744             # Purpose: Performs a syswrite w/locking
745             # Returns: Integer/undef
746             # Usage: $bytes = pwrite($filename, $text);
747             # Usage: $bytes = pwrite($filename, $text, $length);
748             # Usage: $bytes = pwrite($filename, $text, $length, $offset);
749             # Usage: $bytes = pwrite($filename, $text, $length, $offset, $nolock);
750              
751 2324     2324 1 17265 my $filename = shift;
752 2324         3944 my $out = shift;
753 2324         3878 my $wlen = shift;
754 2324         3818 my $offset = shift;
755 2324         3682 my $nolock = shift;
756 2324 100       6064 my $bytes = defined $out ? length $out : 0;
757 2324         3691 my ( $fh, $rv );
758              
759 2324         5921 pdebug( 'entering w/(%s)(%s bytes)(%s)(%s)(%s)',
760             PDLEVEL2, $filename, $bytes, $wlen, $offset, $nolock );
761 2324         6860 pIn();
762              
763 2324 100 66     14242 if ( defined $filename and defined $out and length $out ) {
      100        
764              
765             # Opportunistically open a file handle if needed,
766             # otherwise, just retrieve the existing file handle
767 2316         6589 $fh = popen( $filename, O_WRONLY | O_CREAT );
768              
769             # Smoke 'em if you got'em...
770 2316 50       5034 if ( defined $fh ) {
771 2316 50 33     6835 if ( $nolock or pflock( $filename, LOCK_EX ) ) {
772 2316 100       5567 $wlen = length $out unless defined $wlen;
773 2316 100       5126 $offset = 0 unless defined $offset;
774 2316         64546 $rv = syswrite $fh, $out, $wlen, $offset;
775 2316 50       9345 if ( defined $rv ) {
776 2316         8267 pdebug( 'wrote %d bytes', PDLEVEL2, $rv );
777             } else {
778 0         0 Paranoid::ERROR =
779             pdebug( 'failed to write to file handle: %s',
780             PDLEVEL1, $! );
781             }
782 2316 50       7333 pflock( $filename, LOCK_UN ) unless $nolock;
783             }
784             }
785             }
786              
787 2324         5450 pOut();
788 2324         4807 pdebug( 'leaving w/rv: %s', PDLEVEL2, $rv );
789              
790 2324         8506 return $rv;
791             }
792              
793             sub pnlwrite {
794              
795             # Purpose: Wrapper for pwrite w/o internal flocking
796             # Returns: RV of pwrite
797             # Usage: $bytes = pnlwrite($filename, $text, $length);
798             # Usage: $bytes = pnlwrite($filename, $text, $length, $offset);
799              
800 0     0 1 0 my $filename = shift;
801 0         0 my $out = shift;
802 0         0 my $wlen = shift;
803 0         0 my $offset = shift;
804              
805 0         0 return pwrite( $filename, $out, $wlen, $offset, 1 );
806             }
807              
808             sub pappend {
809              
810             # Purpose: Appends the data to the end of the file,
811             # but does not move the file cursor
812             # Returns: Integer/undef
813             # Usage: $rv = pappend($filename, $content);
814             # Usage: $rv = pappend($filename, $content, $length);
815             # Usage: $rv = pappend($filename, $content, $length, $offset);
816              
817 520     520 1 1215 my $filename = shift;
818 520         1063 my $out = shift;
819 520         940 my $wlen = shift;
820 520         804 my $offset = shift;
821 520         867 my $nolock = shift;
822 520         1027 my ( $fh, $pos, $rv );
823              
824 520         1753 pdebug( 'entering w/(%s)(%s)(%s)(%s)(%s)',
825             PDLEVEL2, $filename, $out, $wlen, $offset, $nolock );
826 520         1595 pIn();
827              
828 520 50 33     4217 if ( defined $filename and defined $out and length $out ) {
      33        
829              
830             # Opportunistically open a file handle in append mode
831 520         1727 $fh = popen( $filename, O_WRONLY | O_CREAT | O_APPEND );
832              
833             # Smoke 'em if you got'em...
834 520 50       1534 if ( defined $fh ) {
835              
836             # Lock the file
837 520 50 33     1944 if ( $nolock or pflock( $filename, LOCK_EX ) ) {
838              
839             # Save the current position
840 520         5304 $pos = sysseek $fh, 0, SEEK_CUR;
841              
842             # Seek to the end of the file
843 520 50 33     6016 if ( $pos and sysseek $fh, 0, SEEK_END ) {
844              
845             # write the content
846 520 50       2572 $wlen = length $out unless defined $wlen;
847 520 50       1480 $offset = 0 unless defined $offset;
848 520         13883 $rv = syswrite $fh, $out, $wlen, $offset;
849 520 50       2319 if ( defined $rv ) {
850 520         2099 pdebug( 'wrote %d bytes', PDLEVEL2, $rv );
851             } else {
852 0         0 Paranoid::ERROR =
853             pdebug( 'failed to write to file handle: %s',
854             PDLEVEL1, $! );
855             }
856             }
857              
858             # Seek back to original position
859 520         5002 sysseek $fh, $pos, SEEK_SET;
860              
861             # Unlock the file handle
862 520 50       2744 pflock( $filename, LOCK_UN ) unless $nolock;
863             }
864             }
865             }
866              
867 520         1392 pOut();
868 520         1314 pdebug( 'leaving w/rv: %s', PDLEVEL2, $rv );
869              
870 520         2260 return $rv;
871             }
872              
873             sub pnlappend {
874              
875             # Purpose: Wrapper for pappend w/o internal flocking
876             # Returns: RV of pappend
877             # Usage: $bytes = pnlappend($filename, $text, $length);
878             # Usage: $bytes = pnlappend($filename, $text, $length, $offset);
879              
880 0     0 1 0 my $filename = shift;
881 0         0 my $out = shift;
882 0         0 my $wlen = shift;
883 0         0 my $offset = shift;
884              
885 0         0 return pappend( $filename, $out, $wlen, $offset, 1 );
886             }
887              
888             sub pread ($\$;@) {
889              
890             # Purpose: Performs a sysread w/locking
891             # Returns: Integer/undef
892             # Usage: $bytes = pread($filename, $text, $length);
893             # Usage: $bytes = pread($filename, $text, $length, $offset);
894              
895 9408     9408 1 17683 my $filename = shift;
896 9408         11503 my $sref = shift;
897 9408         14174 my $rlen = shift;
898 9408         13668 my $offset = shift;
899 9408         11561 my $nolock = shift;
900 9408         13296 my ( $fh, $rv );
901              
902 9408         22040 pdebug( 'entering w/(%s)(%s)(%s)(%s)',
903             PDLEVEL2, $filename, $sref, $rlen, $offset );
904 9408         22357 pIn();
905              
906 9408 50       16706 if ( defined $filename ) {
907              
908             # Opportunistically open a file handle if needed,
909             # otherwise, just retrieve the existing file handle
910 9408         18977 $fh = popen( $filename, O_RDONLY );
911              
912             # Smoke 'em if you got'em...
913 9408 100       19168 if ( defined $fh ) {
914 9402 50 66     25502 if ( $nolock or pflock( $filename, LOCK_SH ) ) {
915 9402 100       20212 $rlen = PIOBLKSIZE unless defined $rlen;
916 9402 50       19333 $offset = 0 unless defined $offset;
917 9402         140996 $rv = sysread $fh, $$sref, $rlen, $offset;
918 9402 100       34527 if ( defined $rv ) {
919 9398         30420 pdebug( 'read %d bytes', PDLEVEL2, $rv );
920             } else {
921 4         24 Paranoid::ERROR =
922             pdebug( 'failed to read from file handle: %s',
923             PDLEVEL1, $! );
924             }
925 9402 100       26478 pflock( $filename, LOCK_UN ) unless $nolock;
926             }
927             }
928             }
929              
930 9408         22500 pOut();
931 9408         21241 pdebug( 'leaving w/rv: %s', PDLEVEL2, $rv );
932              
933 9408         32200 return $rv;
934             }
935              
936             sub pnlread ($\$;@) {
937              
938             # Purpose: Wrapper for pread w/o internal flocking
939             # Returns: RV of pread
940             # Usage: $bytes = pnlread($filename, $text, $length);
941             # Usage: $bytes = pnlread($filename, $text, $length, $offset);
942              
943 2     2 1 5 my $filename = shift;
944 2         5 my $sref = shift;
945 2         4 my $rlen = shift;
946 2         6 my $offset = shift;
947              
948 2         10 return pread( $filename, $$sref, $rlen, $offset, 1 );
949             }
950              
951             sub ptruncate {
952              
953             # Purpose: Truncates the specified file
954             # Returns: RV of truncate
955             # Usage: $rv = ptruncate($filename);
956             # Usage: $rv = ptruncate($filename, $pos);
957              
958 2     2 1 5 my $filename = shift;
959 2         4 my $pos = shift;
960 2         4 my $nolock = shift;
961 2         5 my ( $rv, $fh, $cpos );
962              
963 2         8 pdebug( 'entering w/(%s)(%s)', PDLEVEL2, $filename, $pos );
964 2         8 pIn();
965              
966 2 50       8 if ( defined $filename ) {
967 2 50       8 $pos = 0 unless defined $pos;
968 2         7 $fh = popen( $filename, O_RDWR | O_CREAT );
969              
970             # Smoke 'em if you got'em...
971 2 50       28 if ( defined $fh ) {
972 2 50 33     19 if ( $nolock or pflock( $filename, LOCK_EX ) ) {
973 2         19 $cpos = sysseek $fh, 0, SEEK_CUR;
974 2         133 $rv = truncate $fh, $pos;
975 2 50       13 if ($rv) {
976 2 50       10 sysseek $fh, $pos, SEEK_SET if $cpos > $pos;
977             } else {
978 0         0 Paranoid::ERROR =
979             pdebug( 'failed to truncate file: %s', PDLEVEL1, $! );
980             }
981 2 50       25 pflock( $filename, LOCK_UN ) unless $nolock;
982             }
983             }
984             }
985              
986 2         9 pOut();
987 2         8 pdebug( 'leaving w/rv: %s', PDLEVEL2, $rv );
988              
989 2         10 return $rv;
990             }
991              
992             END {
993              
994             # Attempt to clean close all filehandles
995 57     57   56044871 pcloseAll();
996             }
997              
998             1;
999              
1000             __END__