From cd225683d4414006c9422ff23076d88638af1794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Ankarstr=C3=B6m?= Date: Wed, 11 Nov 2020 15:13:38 +0100 Subject: add backslash escaping, auto-remove more punctuation --- rf | 71 +++++++++++++++++++++++++++++++++++++++++++++++++------------------- rf.1 | 28 +++++++++++++++++++++------ 2 files changed, 73 insertions(+), 26 deletions(-) diff --git a/rf b/rf index 754bc70..535affb 100755 --- a/rf +++ b/rf @@ -16,11 +16,25 @@ my @lines; my $i = 0; my @refs; +# Load entire file + while (<>) { # Formats - if (/^\.Ff +(.*)/) { $format_full = $1; next; } - if (/^\.Fx +(.*)/) { $format_extra = $1; next; } - if (/^\.Fl +(.*)/) { $format_list = $1; next; } + if (/^\.F([fxl])(\\?) +(.*)/) { + my ($type, $join, $line) = ($1, $2, $3); + chomp $line; + $line = "\n$line" if not $join; + while (not eof()) { + last if not $line =~ s/\\$//; + $line .= "\n" . <>; + chomp $line; + } + $line .= "\n"; + $format_full = $line if $type eq 'f'; + $format_list = $line if $type eq 'l'; + $format_extra = $line if $type eq 'x'; + next; + } # Reference definitions if (/^\.R([a-z]) +(.*)/) { @@ -46,28 +60,33 @@ while (<>) { push @lines, $_; } +# Print processed file + $i = -1; +my $last = ''; for (@lines) { - $i++; # Inline reference if (/^\.R([fx]) +(.*)/) { my ($suffix, $prefix, @points); - my ($fld, $def) = ($1, $2); - if ($def =~ s/ ([.,:;\])]) ?([\[(])?$//) { + my ($fld, $def) = ($1, $2, $3); + + # find potential prefix/suffix and split into words + if ($def =~ s/ ([.,?!:;\])]) ?([\[(])?$//) { ($suffix, $prefix) = ($1, $2); } - my $winner = 0; my @words = split /\s/, $def; - # replace '' with preceding word + # replace '' with last word on preceding line for (@words) { if ($_ eq "''") { - $_ = $lines[$i-1]; + $_ = $last; chomp; $_ =~ s/^.*\s(\S+)\s*$/$1/; } } + # find matching reference list entry + my $winner = 0; for (my $i = 0; $i < scalar @refs; $i++) { $points[$i] = 0 if not defined $points[$i]; $points[$i] += 100 if likeness($refs[$i]{a}, @words); @@ -81,44 +100,54 @@ for (@lines) { print STDERR "Error: Reference '$def' could not be resolved.\n"; exit 1; } + if ($points[$winner] < 150) { print STDERR "Warning: Guessing that reference '$def' refers to " . fmt($format_full, $winner) . " (match = $points[$winner]).\n"; } + # Print formatted reference + my $fmt; if ($fld eq 'f') { - no warnings; - print $prefix . fmt($format_full, $winner) . "$suffix\n"; + $fmt = fmt($format_full, $winner); } else { - no warnings; - print $prefix . fmt($format_extra, $winner) . "$suffix\n"; + $fmt = fmt($format_extra, $winner); } + $prefix = '' if not $prefix; + $suffix = '' if not $suffix; + chomp $last; + print $last; + $last = $prefix . substr($fmt, 0, index($fmt, "\n")) . "$suffix"; + $last .= substr($fmt, index($fmt, "\n")); next; } # Reference definition if (/^\.R! (\d+)/) { - print fmt($format_list, $1, 1) . "\n"; + print $last; + $last = fmt($format_list, $1, 1) . "\n"; next; } # Non-rf line - print "$_"; + print $last; + $last = $_; } +# Format a given reference after $fmt sub fmt { my ($fmt, $i, $full) = @_; my %ref = %{$refs[$i]}; - for my $fld (split //, 'acdnpqtwy') { - if ($ref{$fld}) { + for my $fld (split //, 'Aacdnpqtwy') { + if ($ref{lc $fld}) { no warnings; - my $val = $ref{$fld}; + my $val = $ref{lc $fld}; $val = fmta($val) if $fld eq 'a' and not $full; $fmt =~ s/\{([^{}%]*)%\Q$fld\E([^{}]*)}/$1$val$2/g; $fmt =~ s/%\Q$fld\E/$val/g; } else { no warnings; - $fmt =~ s/[.(]?\{([^{}%]*)%\Q$fld\E([^{}]*)}[.,:;)]?//g; - $fmt =~ s/[.(]?%\Q$fld\E[.,:;)]?//g; + $fmt =~ s/[.([]?\{([^{}%]*)%\Q$fld\E([^{}]*)}[.,?!:;\])]?//g; + $fmt =~ s/[.([]?%\Q$fld\E[.,?!:;\])]?//g; } } $fmt =~ s/ +/ /g; @@ -129,6 +158,7 @@ sub fmt { return $fmt; } +# Remove forenames from a given string of authors sub fmta { my ($a) = @_; my $r; @@ -144,6 +174,7 @@ sub fmta { return $r; } +# Calculate a basic likeness value a string and a list of strings sub likeness { my ($string, @strings) = @_; my $r = 0; diff --git a/rf.1 b/rf.1 index d85121b..fbf429f 100644 --- a/rf.1 +++ b/rf.1 @@ -189,16 +189,32 @@ For example: \&.Ff (%a{, %y}). \\" Here, ', ' is removed if there is no year. .Ed .Pp -Note that all fields are inserted literally, except -.Em %a , -from which the forenames of the authors are removed. +You can include a newline in your format by ending the line with a backslash and continuing on the next line. +If you add a backslash immediately following the macro name, the interpolated format will be joined to the preceding line. +By using backslashes, you can implement footnote-based references. +For example, using the ms macro package: +.Bd -literal -offset indent +\&.Fx\\ \\**\\ +\&.FS\\ +%A, {\\fI%t\\fR}, {\\*Q%q\\*U}, %y.\\ +\&.FE +.Ed .Pp -Note also that you can use troff requests and other macros in your format by starting with a dot. This will expand to a valid request/macro in the output of +As you can see above, you can use troff requests and other macros in your format. +This will expand to a valid request/macro in the output of .Nm . .Pp -Finally, you usually don't need to worry about excessive spaces and stray punctuation marks, as +Note that all fields are inserted literally, with one exception: +when +.Em %a +is used in an inline reference format, the forenames of the authors are automatically removed. +To include the forenames, use +.Em %A +instead. +.Pp +Finally, you usually don't need to worry about excessive spaces and stray punctuation marks. .Nm -removes many of these these automatically. +removes common punctuation immediately preceding and following an unsuccessful interpolation. . .Sh EXAMPLE Below is an example of an ms-based troff document using -- cgit v1.2.3