Here's another way, featuring flock. It finds the
first "unused" number when adding a new user, so the
gaps are kept to a minimum.
#!/usr/bin/perl -- -*-fundamental-*-
use strict;
use Fcntl qw(:flock);
my $type = shift or die "Need a type!\n";
my $title = shift or die "Need a title or user!\n";
my $desc = shift || "";
my $flatfile = "members.txt";
## Format of file: ID|Title|Description
&AddUser if $type =~ /add/i;
&DeleteUser if $type =~ /delete/i;
die qq[Invalid type of "$type"!\n];
exit;
sub AddUser() {
## Add a new user to the flat file
## Open the file in read/write mode:
open(FF, "+< $flatfile") or die "Could not open $flatfile: $!\n";
## Now we lock it so nobody else messes with it until we're done
flock(FF, LOCK_EX) or die "Could not flock $flatfile: $!\n";
## Now, we read through until we find a "free" number
my $goodnumber=0;
my $newnumber;
my $slurp="";
while(<FF>) {
$goodnumber++;
($newnumber) = split(/|/, $_, 2);
if ($newnumber != $goodnumber) {
## We have just skipped a number - the perfect place
## to add a new user!
## Decrease goodnumber temporarily
$goodnumber--;
## Save this spot, so we can go back to it quickly
## We need to subtract the line we just read in,
## because the new entry must go *before* it
my $position = tell(FF) - length $_;
## Save the current line, since we have already read it:
$slurp = $_;
## Now we slurp the rest of the file into memory:
## Setting $/ allows us to read the whole thing at once
## by setting the input record separator ($/) to
## nothing. See perlvar for more.
{ local $/; $slurp .= <FF>; }
## Now we rewind the file back to where we marked it:
seek(FF,$position,0);
## This bails us out of the while loop
last;
}
}
## Increment (needed in case no "holes" found before the end of the
+file)
$goodnumber++;
## Some systems need this to switch from read to write:
seek(FF,0,1);
## Add the new entry:
print FF "$goodnumber|$title|$desc\n";
## Add all the entries after that:
print FF $slurp;
## We should not need to truncate, as the size is always
## increasing when adding a user, but just for fun:
truncate(FF, tell(FF));
## Close and unlock
close(FF);
print "Added user $goodnumber to $flatfile.\n";
exit;
} ## end of AddUser
sub DeleteUser() {
## Delete a user from the flat file
## What number user do we want?
my $baduser = $title;
## Sanity check:
$baduser =~ /^\d+$/ or die "User to delete must be a number!\n";
## Open the file in read/write mode:
open(FF, "+< $flatfile") or die "Could not open $flatfile: $!\n";
## Now we lock it so nobody else messes with it until we're done
flock(FF, LOCK_EX) or die "Could not flock $flatfile: $!\n";
## Now, we read through until we find the "bad" number
my $newnumber;
my $slurp="";
while(<FF>) {
($newnumber) = split(/|/, $_, 2);
if ($newnumber == $baduser) {
## Save the spot right before this user, so we can
## go back to it quickly later:
my $position = tell(FF) - length $_;
## Now we slurp the rest of the file into memory:
## Setting $/ allows us to read the whole thing at once
## by setting the input record separator ($/) to
## nothing. See perlvar for more.
{ local $/; $slurp = <FF>; }
## Now we rewind the file back to where we marked it:
seek(FF,$position,0);
## This bails us out of the while loop
last;
}
}
## Some systems need this to switch from read to write:
seek(FF,0,1);
## Add all the entries after the bad one:
print FF $slurp;
## We do need to truncate, as the file size has shrunk:
truncate(FF, tell(FF));
## Close and unlock
close(FF);
if ($slurp) {
print "Deleted user $baduser from $flatfile.\n";
}
else {
print "User $baduser not found in $flatfile.\n";
}
exit;
} ## end of DeleteUser