#!/usr/bin/perl # For documentation, type: # randsig --man # Or even: # perldoc /path/to/randsig # # Especially note the bit about so1036, it's important. (It will explain why # you might have problems with the double dashes appearing twice in your # signature. It tries to address other issues as well.) # # For copyright information, run this script with the --version option. # ############################################################################# # # RCS Info: # $Id: randsig,v 1.50 2007/09/23 03:11:53 infynity Exp $ # ############################################################################# # # TODO : # - Code overhaul... Still. :( (Mostly the 'running' and 'parse_config' # subroutines. The main loop could use some work, too.) # - Option for a logfile (point STDOUT and STDERR to it, rather than # /dev/null?). # ############################################################################# use 5.004; # Need at least perl 5.004 to run. use strict; use FileHandle; use File::Basename; use Getopt::Long; use POSIX qw(setsid); # #### Function prototypes: #### # sub ljust($@); sub rjust($@); sub center($@); sub getsig; sub parsesig(@); sub parse_config; sub sigsize_check; sub quote; sub randomquote(*); sub cleanup; sub running; sub usage($); sub version; # #### End Function prototypes #### # # #### Global variables: #### # my (%OPTS); my $VERSION = '0.98.1 (Beta)'; my $RCS_VERSION = (split(' ', '$Revision: 1.50 $'))[1]; # Harvest the basename. (Program name without the preceeding path, if any.) my $BaseName = basename($0); # #### End Global variables #### # # #### Start running. #### # chdir; # Go to home directory. parse_config; # Parse the config file, and the command-line options. undef &parse_config; # We're done with this code, get rid of it. if ($OPTS{'ONCE'}) { print getsig; exit; } running; # Is this program already running? undef &running; # We're done with this code, get rid of it. # Do this so there are no surprises for the user (hopefully): die "Signature text file \"$OPTS{REALSIG}\" doesn't exist.\n" unless ( -f $OPTS{'REALSIG'}); die "Signature quotes file \"$OPTS{QUOTES}\" doesn't exist.\n" if ( ! $OPTS{'COMMAND'} && ! -f $OPTS{'QUOTES'}); sigsize_check; # Get the users attention if they have a sig that's too large. undef &sigsize_check; # We're done with this code, get rid of it. fork and exit; # Go to the background. # Become a process group leader so we don't inherit signals: setsid or die "$!\n"; # Keep from 'hanging' ssh and other similar sessions, or keeping tty's from # being closed: (This has an unfortunate side effect: Can't see error # messages any more. (Fixed?)) close STDIN; close STDOUT; close STDERR; open (STDIN, "-"); open (STDOUT, ">-"); open (STDERR, ">&STDOUT"); #srand(time * $$); # Seed the random number generator. # Trap some signals: $SIG{'INT'} = $SIG{'HUP'} = $SIG{'TERM'} = $SIG{'ABRT'} = $SIG{'QUIT'} = $SIG{'ILL'} = $SIG{'BUS'} = \&cleanup; # Some programs get impatient when trying to read the FIFO, thus making it so # the program gets this signal if it's a little too slow getting around to # printing: $SIG{'PIPE'} = 'IGNORE'; # Create a PID file to make it easy to kill, and use as a check for a # previously running program: open (F, "> .${BaseName}-pid") or die "Can't write .${BaseName}-pid: $!\n"; print F "$$\n"; close (F); # Body of the program. This actually opens the FIFO and spits out the sig: while (1) { my ($sigtext, $tmp); unless (-p $OPTS{'FIFO'}) { if (-f $OPTS{'FIFO'}) { system ("cp -f $OPTS{FIFO} $OPTS{SIGSAVE}") && die "Can't copy $OPTS{FIFO} to $OPTS{SIGSAVE}: $!\n"; unlink $OPTS{FIFO} or die "Can't remove $OPTS{FIFO}: $!\n"; } system('mknod', $OPTS{FIFO}, 'p') && die "Can't mknod $OPTS{FIFO}: $!\n"; } $sigtext = getsig; # Dump the sig to the FIFO: open (FIFO, '> ' . $OPTS{'FIFO'}) or die "Can't write $OPTS{FIFO}: $!\n"; FIFO->autoflush(1); print FIFO $sigtext; close (FIFO); sleep 2; # To avoid duplicate signatures. } # #### Functions: #### # sub ljust($@) ############################################################################# # Purpose: # Left-justify lines with spaces. # Parameters: # 1: Length to justify to. # 2-n: The lines to justify. # Return value: # Array context: The lines, one line per array element, left justified. # Scalar context: The lines left justified, with newlines, as a string. ############################################################################# { my $length = shift; my @lines = @_; my ($len, $line); foreach $line (@lines) { $len = $length - length($line); $line .= (' ' x $len); } return wantarray ? @lines : join("\n",@lines); } sub rjust($@) ############################################################################# # Purpose: # Right-justify lines with spaces. # Parameters: # 1: Length to justify to. # 2-n: The lines to justify. # Return value: # Array context: The lines, one line per array element, right justified. # Scalar context: The lines right justified, with newlines, as a string. ############################################################################# { my $length = shift; my @lines = @_; my ($len, $line); foreach $line (@lines) { $len = $length - length($line); $line = (' ' x $len) . $line; } return wantarray ? @lines : join("\n",@lines); } sub center($@) ############################################################################# # Purpose: # Center lines with spaces. # Parameters: # 1: Length to center to. # 2-n: The lines to center. # Return value: # Array context: The lines, one line per array element, centered. # Scalar context: The lines centered, with newlines, as a string. ############################################################################# { my $length = shift; my @lines = @_; my ($len, $len2, $line); foreach $line (@lines) { $len = $length - length($line); $len2 = int($len / 2); $line = (' ' x $len2) . $line . (' ' x $len2) . ((($len % 2) && $len > 0) ? ' ' : ''); } return wantarray ? @lines : join("\n",@lines); } { my ($sigmtime, @signature) = 0; sub getsig ############################################################################# # Purpose: # Grab, prase and return a signature. # Parameters: # 1: The signature to parse. # Return value: # The signature string. ############################################################################# { my ($tmp, $sigtext); # Check to see if the file has changed (modification time) since we last # read it: (Naturally, it'll have changed if we haven't read it yet.) if (($tmp = (stat($OPTS{'REALSIG'}))[9]) != $sigmtime) { # It has changed. Open the file and read the signature: open (SIG, $OPTS{'REALSIG'}) or die "Can't read $OPTS{REALSIG}: $!\n"; @signature = ; close (SIG); $sigmtime = $tmp; } # Parse the signature, adding the quote: $sigtext = parsesig(@signature); # so1036 says that signatures need 'dash dash space' # (Hex values 0x2D 0x2D 0x20) -ALONE- on the # first line of the signature. # - Here we're just making sure we comply. :) $sigtext = "\x2D\x2D\x20\n" . $sigtext if $sigtext !~ m/^\x2D\x2D\x20$/m; return $sigtext; } } sub parsesig(@) ############################################################################# # Purpose: # Parse the signature lines, adding the quote: # Parameters: # The signature to parse as an array, one line per element. # Return value: # The signature as a string with a quote added. ############################################################################# { my (@signature) = @_; my ($putquote, @quote, $quote, $sigtext, $line); @quote = quote; $quote = join("\n",@quote); foreach $line (@signature) { chomp ($line); if ( $line =~ /__ADDQUOTE\s*([\.\-\+]?)(\d*)__/ && ! $putquote ) { if ($2) { SWITCH: { if ($1 eq '+' or $1 eq '') { $quote = join("$'\n$`",ljust($2,@quote)); $line =~ s/__ADDQUOTE\s*[\.\-\+]?\d*__/$quote/; last SWITCH; } if ($1 eq '-') { $quote = join("$'\n$`",rjust($2,@quote)); $line =~ s/__ADDQUOTE\s*[\.\-\+]?\d*__/$quote/; last SWITCH; } if ($1 eq '.') { $quote = join("$'\n$`",center($2,@quote)); $line =~ s/__ADDQUOTE\s*[\.\-\+]?\d*__/$quote/; last SWITCH; } } } else { $line =~ s/__ADDQUOTE\s*[\.\-\+]?\d*__/$quote/; } $putquote = 1; } $sigtext .= $line . "\n"; } # Put the quote at the end if it hasn't been inserted yet: $sigtext .= $quote . "\n" unless ($putquote); $sigtext; # Return the parsed sig. } { my ($quotefh, $quotefilemtime); sub quote ############################################################################# # Purpose: # Grab the quote (the random part of the sig). # Parameters: # None # Return value: # Array context: The quote, one line per array element. # Scalar context: The whole quote, with newlines, as a string. ############################################################################# { my (@quote, $tmp); if (! $OPTS{'COMMAND'}) { # Internal method of getting the quote. if (! defined $quotefh) { # The quote file needs to be opened. $quotefh = FileHandle->new($OPTS{'QUOTES'}, 'r') or die "Can't open $OPTS{QUOTES}: $!\n"; $quotefilemtime = (stat($quotefh))[9]; } elsif (($tmp = (stat($OPTS{'QUOTES'}))[9]) != $quotefilemtime) { # Re-open the quote file, it has been modified. $quotefh->close; $quotefh->open($OPTS{'QUOTES'}, 'r') or die "Can't open $OPTS{QUOTES}: $!\n"; $quotefilemtime = $tmp; } @quote = randomquote($quotefh); # Grab the quote from the file. } else { # Command method of getting the quote. chomp(@quote = `$OPTS{COMMAND}`); # Grab the quote from the command. } return wantarray ? @quote : join("\n",@quote); # Return the quote. } } sub randomquote(*) ############################################################################# # Purpose: # This is what actually gets the quote from the quote file. # Parameters: # 1: The filehandle for the quote file. # Return value: # Array context: The quote, one line per array element. # Scalar context: The whole quote, with newlines, as a string. ############################################################################# { my $fh = shift; my ($reading, @quote, $rand); # Find a random spot in the quote file, and seek to it. $rand = int(rand((stat($fh))[7])); seek ($fh, $rand, 0); scalar(<$fh>); # Discard this line. (Probably only a partial line.) LOOP: while (<$fh>) { chomp; if ($reading) { # We've found a quote, grab it. last LOOP if $_ eq '%'; push(@quote, $_); } elsif ($_ eq '%') { $reading = 1; } } if (! @quote) { # Hmm, must've hit the end of the file without getting the quote. seek ($fh, 0, 0); # Ignore first line in the file if it's a %, otherwise use it. chomp ($_ = <$fh>); push(@quote, $_) if $_ ne '%'; # Make sure we've got the whole quote. LOOP: while (<$fh>) { chomp; last LOOP if $_ eq '%'; push(@quote, $_); } } return wantarray ? @quote : join("\n",@quote); # Return the quote. } sub sigsize_check ############################################################################# # Ok, I'm tired of seeing bloated signatures! See the so1036 stuff in # documentation. ############################################################################# # Purpose: # Check the number of lines in the unparsed signature, complaining if it's # more than seven lines. (A random quote usually ads at least one line.) # Parameters: # None # Return value: # None ############################################################################# { my ($i) = 0; open (F, $OPTS{'REALSIG'}) or die "Can't read $OPTS{REALSIG}: $!\n"; ++$i while (); close (F); if ($i > 7) { print STDERR <<"EOF"; \x07\t\tWARNING: Your signature file is greater than seven lines (The recommended length is four.), not including the random quote. This is a Bad Thing(tm). Please see the introduction in the documentation on so1036. ($BaseName --man) This script is still going to run, you can change the signature while it's running. Just cat your signature out twice to remove the buffered signature, and to check that it looks okay.\x07 EOF # Get their attention! STDERR->autoflush(1); for ($i = 0; $i < 10; ++$i) { select(undef, undef, undef, 0.20); print (STDERR "\x07"); } STDERR->autoflush(0); } } sub cleanup ############################################################################# # Purpose: # This is what the signal traps call, we just clean up an die here. # Parameters: # 1: The name of the signal that was caught. # Return value: # None, it causes the program to exit. ############################################################################# { my $signame = shift; unlink $OPTS{'FIFO'} or die "Can't remove $OPTS{FIFO}: $!\n"; if (-f $OPTS{'SIGSAVE'}) { system ("cp $OPTS{SIGSAVE} $OPTS{FIFO}") && die "Can't copy $OPTS{SIGSAVE} to $OPTS{FIFO}: $!\n"; unlink $OPTS{'SIGSAVE'} or die "Can't remove $OPTS{SIGSAVE}: $!\n"; } else { system ("cp $OPTS{REALSIG} $OPTS{FIFO}") && die "Can't copy $OPTS{REALSIG} to $OPTS{FIFO}: $!\n"; } unlink ".${BaseName}-pid" or die "Can't remove .${BaseName}-pid: $!\n"; #die "Got SIG$signame, exiting.\n"; exit 0; } sub running ############################################################################# # Purpose: # Make it so we can check to see if this program is already running. # Parameters: # None, but the -k/--KILL script flag causes the function to attempt to # kill the script if it's running. # Return value: # None, or causes the program to exit. ############################################################################# { my $pid; if (! -f "$ENV{HOME}/.${BaseName}-pid") { if ($OPTS{'KILL'}) { die "$BaseName isn't running.\nYou may start $BaseName without the --KILL/-k option.\n"; } else { return; } } open (PID, "$ENV{HOME}/.${BaseName}-pid") or die "$ENV{HOME}/.${BaseName}-pid exists, but I can't open it: $!\n"; chomp ($pid = ); close (PID); if ($OPTS{'NFS'}) { if ($OPTS{'KILL'}) { warn "Warning: It is risky to use the --KILL/-k command-line option when this program\nis running over NFS filesystems. (You have the NFS option set.)\n\n"; if (kill(15, $pid)) { sleep 2; # Give the process time to wake up and exit. if (-f "$ENV{HOME}/.${BaseName}-pid") { die "Killed PID $pid, but $ENV{HOME}/.${BaseName}-pid still exists.\nThis probably isn't the machine $BaseName is running on.\n"; } else { print "Killed PID: $pid\nNow you may start $BaseName without the --KILL/-k option.\n"; exit 0; } } else { die "Couldn't kill PID: $pid\n$BaseName may be running on another machine that shares your home\ndirectory.\n"; } } else { die "$BaseName is already running, possibly on another machine that shares\nyour home directory.\n\nIf this is not correct, remove:\n$ENV{HOME}/.${BaseName}-pid\n"; } } open (PS, "ps x |") or die "$ENV{HOME}/.${BaseName}-pid exists, but \"ps x\" can't be run: $!\nYou should check to see if ${BaseName} really is running,\nand if it isn't, remove the file and start $BaseName without arguments.\n"; if (grep {m/^\s*$pid .+$BaseName/} ) { if ($OPTS{'KILL'}) { if (kill(15, $pid)) { print "Killed PID: $pid\nNow you may start $BaseName without the --KILL/-k option.\n"; exit 0; } else { die "Couldn't kill PID: $pid\n"; } } else { die "$BaseName is already running.\nIf this is not correct (It probably is!), remove:\n$ENV{HOME}/.${BaseName}-pid\n" } } close (PS); print STDERR "Warning: $ENV{HOME}/.${BaseName}-pid exists, but ${BaseName} doesn't\nseem to be running. Something probably went wrong somewhere.\n\nStarting anyway, hopefully everything should be okay.\n"; } sub parse_config ############################################################################# # Purpose: # Parse the config file, then command-line flags, propagating %OPTS. # Parameters: # None # Return value: # None ############################################################################# { my $valid_values_paired = "FIFO|REALSIG|SIGSAVE|COMMAND|QUOTES"; my $valid_values_unpaired = "NFS"; my ($parseerrors, $name, $value); (-e ".${BaseName}-config") or die ("No configure file found.\nSee the documentation to get this script running.\nType: $BaseName --man\n\nBye!\n"); open (F, ".${BaseName}-config") or die ("Configuration file exists, but I can't open it.\n"); while () { next if (/^\s*#/ || /^\s*$/); if (($name, $value) = m|^\s*(\w+)\s*=\s*(.+)|) { if ($name =~ m|^($valid_values_paired)$|o) { $OPTS{"$name"} = "$value"; } else { $parseerrors .= "Line $.: $1=$2 is not a valid configuration parameter.\n"; } } elsif (($name) = m|^\s*($valid_values_unpaired)\s*$|) { $OPTS{"$name"} = 1; } else { $parseerrors .= "Line $.: $1 is not a valid configuration parameter.\n"; } } close (F); die "While trying to parse config file \".${BaseName}-config\":\n\n$parseerrors\n\tBye!\n" if $parseerrors; # Gotta love command-line options. Parse 'em here so they override anything # set in the config file: Getopt::Long::config qw(bundling ignore_case); GetOptions( "ONCE|o" => \$OPTS{'ONCE'}, "FIFO|f=s" => \$OPTS{'FIFO'}, "REALSIG|r=s" => \$OPTS{'REALSIG'}, "SIGSAVE|s=s" => \$OPTS{'SIGSAVE'}, "COMMAND|c=s" => \$OPTS{'COMMAND'}, "QUOTES|q=s" => sub{$OPTS{'QUOTES'} = $_[1]; $OPTS{'COMMAND'} = undef;}, "n" => \$OPTS{'NFS'}, # ... (see next line) "NFS!" => \$OPTS{'NFS'}, # ... Don't bundle so we can have --nonfs "VERSION|v" => \&version, "KILL|k" => \$OPTS{'KILL'}, "HELP|h" => sub{usage(0);}, "MAN|H" => sub{exec "perldoc $0";}, ) or usage(1); #foreach (keys %OPTS) #{ # print "\$OPTS{$_} = $OPTS{$_}\n"; #} #exit(0); if (!$OPTS{'FIFO'}) { print STDERR "Config paramater \"FIFO\" not specified, defaulting to \".signature\"\n"; $OPTS{'FIFO'} = ".signature"; } if (!$OPTS{'REALSIG'}) { print STDERR "Config paramater \"REALSIG\" not specified, defaulting to \".signature-text\"\n"; $OPTS{'REALSIG'} = ".signature-text"; } if (!$OPTS{'SIGSAVE'}) { print STDERR "Config paramater \"SIGSAVE\" not specified, defaulting to \".signature-save\"\n"; $OPTS{'SIGSAVE'} = ".signature-save"; } if (!$OPTS{'COMMAND'} && !$OPTS{'QUOTES'}) { print STDERR "Config paramater \"QUOTES\" not specified, and \"COMMAND\" not specified.\n"; print STDERR "Taking default value for \"COMMAND\" of \"fortune -s\"\n"; $OPTS{'COMMAND'} = "fortune -s"; } elsif ($OPTS{'COMMAND'} && $OPTS{'QUOTES'}) { print STDERR "Note that config parameter \"QUOTES\" has no effect when \"COMMAND\" is set.\n"; } } sub usage($) ############################################################################# # Purpose: # Print a usage statement and exit. # Parameters: # 1: Exit value. (Used to decide whether to send to STDOUT or STDERR, # too.) # Return value: # None, causes the program to exit. ############################################################################# { my $rval = shift; my $where; if ($rval) { $where = *STDERR; } else { $where = *STDOUT; } print $where <<"EOF"; Usage: $BaseName [options] Options: -f, --fifo=filename -r, --realsig=filename -s, --sigsave=filename -c, --command=command -q, --quotes=filename -n, --nfs, --nonfs -o, --once -k, --kill Attempt to kill a running $BaseName process. (This isn't safe to use if you're running over an NFS filesystem.) -v, --version Print version information and exit. -h, --help Print this option summary and exit. -H, --man Bring up a man page and exit. EOF exit ($rval); } sub version ############################################################################# # Purpose: # Print the version and copyright info and exit. # Parameters: # None # Return value: # None, causes the program to exit. ############################################################################# { print "$BaseName version $VERSION (RCS version: $RCS_VERSION)\n"; print <<'EOF'; Copyright (C) July 25, 1997 and June 19, 1999 Christian J. Robinson. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. Comments, questions or bug reports can be sent to infynity@onewest.net Make sure to say that your message is regarding the randsig program. Also, I wouldn't mind knowing how you got a copy. EOF exit 0; } # #### End Functions #### # # #### POD Documentation: #### # =head1 NAME randsig - Output random signatures through a named pipe. =head1 SYNOPSIS randsig [ B<-f>, B<--fifo>=I ] [ B<-r>, B<--realsig>=I ] [ B<-s>, B<--sigsave>=I ] [ B<-c>, B<--command>=I ] [ B<-q>, B<--quotes>=I ] [ B<-n>, B<--nfs>, B<--nonfs> ] [ B<-o>, B<--once> ] randsig B<-k>, B<--kill> randsig B<-v>, B<--version> randsig B<-h>, B<--help> randsig B<-H>, B<--man> =head1 DESCRIPTION I attempts to create a FIFO (Birst Bn Birst But, or a named pipe.) to control what is seen by programs that try to read your signature. As its name suggests, it outputs random text plus any other constant text you specify. =head1 OPTION FORMAT Each long option has an equivalent short option. For the long options, case doesn't matter and they may be abbreviated to uniqueness. The options all override the corresponding config file parameters (see L<"CONFIGURATION FILE OPTIONS">). The B<--nonfs> option is a special case. It only has meaning if you have defined NFS in the configuration file and want to turn it off temporarily. Using the B<--quotes> (B<-q>) option resets any previously defined COMMAND option, either in the configuration file, or on the command line. The B<--kill> and B<-k> options attempt to kill a running randsig process. The only other options that have any meaning with this option are the B<--nfs>, B<-n>, and B<--nonfs>. Note that this isn't safe to use if you're running over an NFS filesystem. The B<--once> and B<-o> option will print one random signature on the standard output and exit. This can be used even if there's a randsig running in the background. The B<--version> and B<-v> options print out version and copyright information, then exit. The B<--help> and B<-h> options print out a short option summary and exit. I other options are ignored. The B<--man> and B<-H> options bring up this man page and exit. I other options are ignored. =head1 INTRODUCTION First some stuff about a document called "so1036" (Son Of RFC1036): =head2 Delimiter The document explains that a signature file should always start with a line that contains the three characters dash, dash, space (S<-- >) alone on the line. Some news and email software even use this delimiter to locate the signature. Therefore, this script will put it there at the top if it does not appear I somewhere in the signature. If you get a second line with the two dashes on it when you read in your signature, check to make sure that the signature file that this script uses either has it as S<'-- '> or doesn't have it at all. This delimiter line does not count towards the number of lines in your signature. =head2 Signature Size The document also says that signatures shouldn't exceed four lines, which makes it difficult to run this thing and comply. It's possible though. I suggest that as a general rule, ascii art, lines that are used as seperators (such as a line of stars, dashes, equal signs, etc..), blank lines, and so on should be removed. At one time, my signature contained two lines of constant text. In these two lines, I had my full name, my email address, my ICQ number, my web page URL, and a sentence that details how to obtain my PGP public key. This leaves two lines for random quotes. In my opinion, signatures that exceed eight lines are B! So this script will complain if your signature is more than seven lines. (Remember that a random quote will usually add at least one line to the signature.) If someone needs more information than will fit in that space, email it to them in a seperate, personal message instead of making everybody you write suffer through an overly large signature. Thank you for your cooperation in making the 'net a better place. =head1 SHUTTING DOWN Once the program is running, it creates a file in your home directory with the name F<.programname-pid> (usually F<.randsig-pid>) with the process ID number of the program in it as the only line. This makes it easy to shut the program down by doing something like: kill `cat ~/.randsig-pid` Or better yet, just use the B<-k> option. (See above.) =head1 CONFIGURATION FILE OPTIONS This script looks for a configuration file on startup that tells it how to run, and what files to use at runtime. The file name is F<.programname-config> (usually F<.randsig-config>), and consists of NAME=VALUE pairs. Blank lines and lines that start with # are ignored. Note that this file is parsed before the command-line options, so command-line options override anything set in this file. (See L<"OPTION FORMAT"> above.) The configuration parameters are: =over 4 =item * NFS This parameter is a standalone option. That means it is to be set on a line by itself. Set it if you might be starting this program on multiple machines that share your home directory. (NFS = Network FileSystem) The reason for this is that if a PID file (see L<"FILES">) exists, the script will try to determine if the file was left behind due to being shut down improperly. It does this by scanning the output of "ps x", which is useless in a NFS situation. =item * FIFO This is the filename that is used by your mail software to read in your signature. Default is: F<.signature> =item * REALSIG This is the filename that the script uses to built the random signature, it is parsed for an __ADDQUOTE__ directive. (see L<"Quote File Format">) Default is: F<.signature-text> =item * SIGSAVE Usually the file that is pointed to by the FIFO parameter will already exist. If it does, it is moved to this the filename that is contained in this paramater while the script is running. Default is: F<.signature-save> =item * COMMAND This is the command to run to get "random" data to insert into signature. If this paramater is specified, the "QUOTES" paramater has no meaning, and is ignored. It would be wise to make sure this command can be run. Default is: B if both this and the "QUOTES" parameter is not set. =item * QUOTES This is the file to use to extract a random quote if the "COMMAND" paramater isn't specified. There is no default for this paramater. (see L<"Quote File Format>) =back =head2 Example: FIFO = .signature REALSIG = .signature-text SIGSAVE = .signature-save #COMMAND = fortune -s QUOTES = .signature-quotes Since the COMMAND line starts with a #, it is ignored (also known as a comment), and the QUOTES paramater is used. The default location for all of these files is your home directory. You may specify full pathnames if you wish, however. =head2 Caveat: The configuration paramaters that take filenames as their value should not have the same value. Doing so could have unpredictable and undesirable results. This script does not, and will never check for this occurance. If you do it, the assumption is that you know what you're doing. =head1 QUOTE FILE FORMAT A quote file, if you use the internal mechanism, is in the following format: quote one rest of quote one % quote two % quote three more of quote three rest of quote three % etc... Basically, you just seperate your quotes with a % on a line by itself. If you want one of your quotes to have a line with a % at the beginning by itself, add some white space after it. (Such as a space.) =head1 SIGNATURE FILE FORMAT The signature file will have the quote placed at the end of the file, or it will be inserted in the place of the first __ADDQUOTE__ it sees. Any subsequent lines that contain an __ADDQUOTE__ will be printed as normal. In addition, you can have the quote padded with spaces on the left side, right side, or both (centering). This is accomplished by using one of the three: __ADDQUOTEEsomenumberE__ This adds spaces on the right. __ADDQUOTE+EsomenumberE__ This adds spaces on the right. __ADDQUOTE-EsomenumberE__ This adds spaces on the left. __ADDQUOTE.EsomenumberE__ This centers. All of these options pad each line of the quote until it's EsomenumberE in length. If the line is already longer than EsomenumberE, the line will not be truncated, or even padded. This makes it easy to have quotes that are only a few words inserted in the middle of a line between some other text, a box, or in some ascii art. If you want to use this feature make sure all your quotes are one line, the command you're using will never output more than one line, or your signature won't look odd with multi-line padded quotes. (See the last example below to see how this is currently handled.) Whitespace is allowed between the word ADDQUOTE and the number, it is simply ignored. Eg: __ADDQUOTE .56__ __ADDQUOTE 40__ __ADDQUOTE -50__ This is so you can preserve formatting in your signature if you're embedding a quote in the middle of a boxed line, or something. =head1 EXAMPLES If the script were set to run the command: date "+%I:%M %p %b %d %Y" And the .signature-text file contained: -- Sent at: __ADDQUOTE__ John Doe It would show up as: -- Sent at: 11:20 AM Dec 08 1997 John Doe =head2 Padding Examples If your .signature-text file containined something like: -- Random quote: __ADDQUOTE 20__ John Doe It would show up something like: -- Random quote: (Random quote) John Doe Or if you used __ADDQUOTE-20__ instead: -- Random quote: (Random quote) John Doe Or if you used __ADDQUOTE.20__ instead: -- Random quote: (Random quote) John Doe =head2 Default If your .signature-text and your .randsig-config file was blank, the output might look like: -- Arithmetic is being able to count up to twenty without taking off your shoes. -- Mickey Mouse Lastly, if you had decided to use some kind ascii art or border, but your quotes were multi-line, eg: -- -------------------------------------------------- >_ADDQUOTE .48__< -------------------------------------------------- You'd see something like: -- -------------------------------------------------- > Some < > multi-line < > quote. < -------------------------------------------------- It just prepends and appends the same text that occurred respectively before and after the __ADDQUOTE__ to each line. =head1 FILES Unless specified differently in the config: =over 6 =item F<~/.signature> Your signature file. =item F<~/.signature-text> File that contains the text to use at runtime. =item F<~/.signature-save> File to save your 'original' signature in. =item F<~/.signature-quotes> File to get quotes from, if you're not going command based. =item F<~/.randsig-config> Program config file. =item F<~/.randsig-pid> Program PID file. (Also acts as a lock file.) =back These last two filenames are actually determined by the executable name. The word "randsig" will be replaced by whatever the executable is named. =head1 NOTES =over 4 =item * I use the words "script" and "program" interchangeably. =item * I use the words "option" and "parameter" semi-interchangeably. =item * I use the default filenames for the configuration parameters in the examples. =item * I have spent way too much time on this thing. ;) =back =head1 CAVEATS This program traps most signals and tries to restore your original signature when it exits, but if for some reason this doesn't happen, your signature file will remain a named pipe (FIFO), and anything that tries to read from it might hang. Usually you can just start this program and the reading process will come back. You might have to remove your ~/.randsig-pid file first, though. If that doesn't work, kill the reading process, make sure this program isn't running, remove the FIFO, and then start this program back up. =head2 Caveat Emptor This program comes WITHOUT ANY WARRANTY! Express or implied. In other words, USE THIS AT YOUR OWN RISK! I am not responsible for ANY lost data as a result of running this program. =head1 DIAGNOSTICS I hope the error messages are clear enough. You should have no problems if you actually bothered to read this documentation. If all else fails, read the code, or complain to me. =head1 BUGS Probably, the code is an absolute mess. If you find a bug, want to see something added or changed, have a comment on my documentation, or just want to praise me, feel free to email. The script can die silently once it's gone into daemon mode. There are warn/die statements that would normally print diagnostic information, but the STDERR filehandle points to /dev/null. I may create a way to specify a log file in the future. =head1 AUTHOR Christian J. Robinson =head1 COPYRIGHT Copyright (C) July 25, 1997 and June 19, 1999 Christian J. Robinson. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. Comments, questions or bug reports can be sent to infynity@onewest.net Make sure to say that your message is regarding the randsig program. Also, I wouldn't mind knowing how you got a copy. =head1 SEE ALSO mkfifo(1), mknod(1), perlipc(1), fortune(6), I =cut