#!/usr/bin/perl # ------------------------------------------------------------------ # rfc-what-i-mean V1.2 # # Find the most recent/dependent RFCs from a list # # J. Touch # touch@strayalpha.com # https://www.strayalpha.com/ # Copyright (c) 2006-2021 # # Revision date: Mar. 25, 2021 # ------------------------------------------------------------------ # # Copyright (c) 2021 J. Touch # All rights reserved. # # Permission to use, copy, modify, and distribute this software and # its documentation in source and binary forms for non-commercial # purposes and without fee is hereby granted, provided that the above # copyright notice appear in all copies and that both the copyright # notice and this permission notice appear in supporting # documentation, and that any documentation, advertising materials, # and other materials related to such distribution and use acknowledge # that the software was developed by the author. The name of the # author may not be used to endorse or promote products derived from # this software without specific prior written permission. # # THE AUTHOR MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS # SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS PROVIDED "AS IS" AND # WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT # LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE. # ------------------------------------------------------------------ # # usage: # rfc-what-i-mean rfc-index.txt yourfile.txt [> outputfile.txt] # # rfc-index.txt = RFC index file from www.rfc-editor.org # # yourfile.txt = file with RFCs to check, in format: # (RFC|rfc)(\s|\-)*(\d+) # # output = RFCs you really mean in two sets: # # first set: one line each, your RFC and its # appropriate successor(s), including updates: # a single line "RFCS TO AUGMENT OR REPLACE:" # rfc2255 ---> rfc4516 # rfc2798 ---> rfc3698, rfc4517 # rfc3698 +--> rfc3698, rfc4517 # # NOTE: # ---> indicates "replace with" (i.e., obsolated by) # +--> indicates "also add:" (i.e., updated by) # 2+-> indicates "further add:" (i.e.,updated by updated by) # # second set: one line each, of the sorted, # unique set of RFCs you want: # a single line of "RFCS YOU MEAN:" # rfc3698 # rfc4516 # rfc4517 # # output may include warning errors, which appear as: # ERROR-WARN: # # Version history: # Nov. 2, 2006 1.0 initial release # Aug. 2, 2020 1.1 clarified updated/obsoleted # supports RFC numbers of any length # Mar. 25, 2021 1.2 transitive to find A updates B updates C # multilevel debug modes # ------------------------------------------------------------------ ############################################################ # GLOBAL variables ############################################################ my $debug = 0; # set >0 to print debugging statements # 1 = basic # 2 = also getstructure # 3 = also readrfcs my %rfc; # rfc-index entry of the index my %obsoletedby; # things that obsolete the index my %updatedby; # things that update the index my $numlen; # the length of RFC numbers my %seen; # things we've already seen my $round = 1; # transitive search count ############################################################ # debug print ############################################################ sub dprint($$) { my $name = shift; my $s = shift; if (($name =~ /getstructure/) && ($debug < 2)) { return; } if (($name =~ /readrfcs/) && ($debug < 3)) { return; } print "$name: >$s<\n" if $debug; } ############################################################ # inputs the rfc-index.txt file ############################################################ sub readrfcs($) { my $fh = shift; my $line, $whole, $inrfc=0, *INFILE; my $maxrfcnum = 0; while ($line = <$fh>) { $line =~ s/\r?\n?$//; # reset if new RFC entry if ($line =~ /^(\d+)/) { if ($whole =~ /^(\d+)/) { # prevents junk $rfc{"RFC$1"} = $whole; dprint("readrfcs","$whole"); } $whole = ""; } $whole .= $line; } # grab the last one you got if ($whole =~ /^(\d+)/) { # prevents junk my $hold = $1; $rfc{$1} = $whole; if ($maxrfcnum < $hold) { $maxrfcnum = $hold; } dprint("readrfcs",$whole); } # figure out how long the largest RFC num is $numlen = length(sprintf("%d", $maxrfcnum)); return %rfc; } ############################################################ # grab a list of RFC nums off of an obsoletes/updates list # used by getstructure ############################################################ sub getlist($) { my $s = shift; my @result = (); while ($s =~ s/(IEN|STD|NIC|RTR|RFC)\s*(\d+)//) { push(@result, $1 . $2); } warn "ERROR-WARN: getlist missed $s" if ($s !~ /^(\,|\s)+$/); return @result; } ############################################################ # creates datastructure t0 relate current RFCs to right ones ############################################################ sub getstructure() { # populate %obsoleteby, %updatedby my $k, @olist; foreach $k (sort(keys(%rfc))) { if ($rfc{$k} =~ /\(Obsoleted\s+by([^\)]+)\)/) { @olist = getlist($1); dprint("getstructure-obsoletedby","$k --> " . join(' ',@olist)); # RFC k is obsoleted by entries in olist $obsoletedby{$k} = join(' ',@olist); } if ($rfc{$k} =~ /\(Updated\s+by([^\)]+)\)/) { @olist = getlist($1); dprint("getstructure-updatedby","$k --> " . join(' ',@olist)); # RFC k is updated by entries in olist $updatedby{$k} = join(' ',@olist); } } } ############################################################ # given a desired RFCs, find the list you really want ############################################################ sub findwhatimean ($) { my $in = shift; my @outlist = (); my @inlist = (); my @finallist = (); my $changed = 0; my $item; my $updated = 0; dprint("findwhatimean","starting"); push(@inlist,$in); do { dprint("findwhatimean","in repeat with inlist = " . join(" ",@inlist)); @outlist = (); foreach $item (@inlist) { dprint("findwhatimean","in foreach"); dprint("findwhatimean", "looking at $item"); $changed = 0; # check whether to replace item if (defined($obsoletedby{$item})) { push(@outlist, split(/ /,$obsoletedby{$item})); dprint("findwhatimean obsoletedby","$item --> $obsoletedby{$item}"); $changed = 1; } else { # keep this one dprint("findwhatimean","$item OK"); if ($item eq $in) { # don't add it now so it won't show up in the list yet, but add at the end as a citation $updated = 1; } else { push(@finallist, $item); } # check whether to add to item if (defined($updatedby{$item})) { dprint("findwhatimean updatedby","$item +-> $updatedby{$item}"); push(@outlist, split(/ /,$updatedby{$item})); $changed = 1; } } } @inlist = @outlist; } until ($changed == 0); if (!defined($seen{$in})) { # once for each time an RFC is found $seen{$in} = 1; if (@finallist) { # print the RFCS TO REPLACE: if ($round < 2) { print(($updated) ? "Augment $in " : "Replace $in ", ($updated) ? "+--> " : "---> ", join(", ",@finallist), "\n"); } else { print(" also $in ", "$round+-> ", join(", ",@finallist), "\n"); } } } if ($updated) { # add the current one to the list of citations push(@finallist, $in); } return @finallist; } ############################################################ # sort -u ############################################################ sub findset(@) { my @inlist = @_; my %inset; my $item; foreach $item (@inlist) { $inset{$item} = 1; } return sort(keys(%inset)); } ############################################################ ############################################################ # main ############################################################ ############################################################ { local *INFILE, *YOURFILE; my @list; print "RFC-WHAT-I-MEAN v1.1\n\n"; die "usage: $0 rfc-index.txt yourfile.txt\n" if (@ARGV != 2); open(INFILE,$ARGV[0]) || die "rfc-index.txt file: $ARGV[0] could not be opened"; readrfcs(*INFILE); getstructure(); # get the input file list open(YOURFILE,$ARGV[1]) || die "your file: $ARGV[1] could not be opened"; dprint("main","reading $ARGV[1]"); { my $line; while ($line = ) { while ($line =~ s/rfc(\s|\-)*(\d+)//i) { # make RFCs in n-digit format, as per rfc-index.txt my $sprintstring = sprintf("RFC%%0%dd",$numlen); dprint("main RFC list",sprintf($sprintstring, $2)); push(@rfclist, sprintf($sprintstring, $2)); } } } print "RFCS You Mean\n", " Augment (updated by: +->)\n", " Replace (obsoleted by: -->)\n", " also (transitively updated by: #+->)\n\n"; while (1) { my $elt; my @wholelist = (); foreach $elt (sort(@rfclist)) { push(@wholelist, findwhatimean($elt)); } @list = findset(@wholelist); last if (join(' ',sort(@list)) eq join(' ',sort(@rfclist))); @rfclist = @list; $round++; } print "\n\nRFCs You Mean (sorted list for references):\n\n"; print join("\n",@list), "\n"; }