#!/bin/bash
#
#    'idnits' looks for violations of Section 2.1 and 2.2 of the
#    requirements listed on http://www.ietf.org/ID-Checklist.html

# Copyright:
#  -----------------------------------------------------------------
#
#  Copyright The IETF Trust 2022, All Rights Reserved
#  Copyright 2002-2010 Henrik Levkowetz
#
#  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.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#  -----------------------------------------------------------------

version="2.17.1";
progdate="";
export LC_ALL=C
program=${0##*/}
progdir=${0%/*} ; [ "$progdir" ] || progdir=.
system=$(uname)
today=$(date +"%Y-%m-%d")
[ "$HOME" ] || HOME=/tmp
statusdir=$HOME/.idnits
statusfile=$statusdir/rfc-status
wordlist=$statusdir/rfc-wordlist
obslist=$statusdir/rfcs-obsoleted
updlist=$statusdir/rfcs-updated
exclist=$statusdir/downref-exceptions

datatracker=https://datatracker.ietf.org

# ----------------------------------------------------------------------
# Utilities
# ----------------------------------------------------------------------

# ----------------------------------------------------------------------
# Error exit
function die() { echo "$program: Error: $*" >&2; exit 1; }

# ----------------------------------------------------------------------
# Message
function msg() {
    [ "$optsilent" ] || echo "$*" >&2
    logger -p user.info -t idnits "$*"
}

# ----------------------------------------------------------------------
# Note
function note() { [ "$optverbose" ] && echo "$*" >&2; }

# ----------------------------------------------------------------------
# Note
function debug() { [ "$optdebug" ] && echo "$*" >&2; }

# ----------------------------------------------------------------------
# Find an executable

lookfor() {
    default="$1"; shift
    for b in "$@"; do
	found=$(which "$b" 2>/dev/null)
	if [ -n "$found" ]; then
	    if [ -x "$found" -o -x "$found.exe" ]; then
		echo "$found"
		return
	    fi
	fi
    done
    echo "$default"
}


# ----------------------------------------------------------------------
# Set up which program to use

FDATE='date +%Y-%m-%d -r'
[ "$system" = "Darwin" ] && FDATE='stat -f %Sm -t %Y-%m-%d'

SED="sed -r"
[ "$system" = "Darwin" ] && SED="sed -E"

AWK=$(lookfor gawk $AWK gawk nawk awk)

WGET=$(lookfor wget $WGET wget curl lynx)
[ ${WGET##*/} = wget ] && [ "$system" = "Darwin" ] && WARG="--timeout=2 -q -O - "
[ ${WGET##*/} = wget ] && [ "$system" = "Darwin" ] || WARG="--timeout=2 -q -O - --no-check-certificate"
[ ${WGET##*/} = curl ] && WARG="--connect-timeout 2 -s -k"
[ ${WGET##*/} = lynx ] && WARG="-connect_timeout=2 -dump"

SPELL=$(lookfor "" aspell)

GRAMMAR=$(lookfor "" languagetool)

# ----------------------------------------------------------------------
# Convert DOS (^M^J) and MAC (^M) line ending to Unix (^J)
# ----------------------------------------------------------------------
fixnl() {
  $AWK '
BEGIN	{ RS = "\r"; }
	{ sub(/^\n/, ""); print; }
  ' "$1"
}

# ----------------------------------------------------------------------
# Strip headers and footers, end-of-line whitespace, BOM, and \r (CR)
# ----------------------------------------------------------------------
hfstrip() {
  $AWK '
BEGIN				{
				  expiration = 0;
				  longestpage = 1;
				  textcolumn=8; # initialise minimum indentation we found
				}
NR==1				{ sub(/^\xef\xbb\xbf/,""); }
				{ gsub(/\r/, ""); }
				{ gsub(/[ \t]+$/, ""); }
				{ pagelength++; }

tolower($0) ~ / *expire on.*20[0-9][0-9]/	{ if (FNR <= 58 ) expiration = 1; }
tolower($0) ~ / *expires.*20[0-9][0-9]/		{ if (FNR <= 58 ) expiration = 1; }
tolower($0) ~ / *expiration.*20[0-9][0-9]/	{ if (FNR <= 58 ) expiration = 1; }
/FORMFEED[ \t]*\[Page/				{ missing_nroff_postprocessing ++; }
/\[?[Pp]age [0-9ivx]+\]?[ \t\f]*$/	{
				    match($0, /[Pp]age [0-9ivx]+/);
				    num = substr($0, RSTART+5, RLENGTH-5);
				    if (num+0 > maxpage) maxpage = num+0;
				    pagecount++;
				    countedpage=1;
				    if (pagelength > 58) longpagecount++;
				    if (maxlength < pagelength) {
					maxlength = pagelength;
					longestpage = num;
				    }
				    if (!firstpagelength) firstpagelength = outline;
				    pagelength = 0;
				}
/\f/				{ newpage=1;
				  ffcount++;
				  if (pagelength > 58) longpagecount++;
				  if (! countedpage) {
				      pagecount++;
				      countedpage=0;
				  }
				  if (maxlength < pagelength) {
				      maxlength = pagelength;
				      longestpage = pagecount;
				  }
				  pagelength=1;
				}
/\f$/				{
				    # a form feed followed by a \n does not contribute to the
				    # line count.  (But a \f followed by something else does.)
				    pagelength--;
				}
/\f/				{ next; }
/\[?[Pp]age [0-9ivx]+\]?[ \t\f]*$/		{ preindent = indent; next; }

/^ *Internet.Draft.+[12][0-9][0-9][0-9] *$/ && (FNR > 15)	{ newpage=1; next; }
/^ *INTERNET.DRAFT.+[12][0-9][0-9][0-9] *$/ && (FNR > 15)	{ newpage=1; next; }
/^ *INTERNET.DRAFT        / && (FNR > 15)			{ newpage=1; next; }
/^ *Draft.+(  +)[12][0-9][0-9][0-9] *$/	    && (FNR > 15)	{ newpage=1; next; }
/^RFC[ -]?[0-9]+.*(  +)[12][0-9][0-9][0-9]$/ && (FNR > 15)	{ newpage=1; next; }
/^draft-[-a-z0-9_.]+.*[0-9][0-9][0-9][0-9]$/ && (FNR > 15)	{ newpage=1; next; }
/(Jan|Feb|Mar|March|Apr|April|May|Jun|June|Jul|July|Aug|Sep|Oct|Nov|Dec) (19[89][0-9]|20[0-9][0-9]) *$/ && pagelength < 3  { newpage=1; next; }
newpage && $0 ~ /^ *draft-[-a-z0-9_.]+ *$/ { newpage=1; next; }

/^[ \t]+\[/			{ sentence=1; }
/[^ \t]/			{
				   indent = match($0, /[^ ]/);
				   if (indent < preindent) {
				      sentence = 1;
				   }
				   if (newpage) {
				      if (sentence) {
					 outline++; print "";
				      }
				   } else {
				      if (haveblank) {
					  outline++; print "";
				      }
				   }
				   haveblank=0;
				   sentence=0;
				   newpage=0;

				   line = $0;
				   sub(/^ *\t/, "        ", line);
				   thiscolumn = match(line, /[^ ]/);
				   if (thiscolumn && thiscolumn < textcolumn) textcolumn = thiscolumn;
				}
/[.:][ \t]*$/			{ sentence=1; }
/\(http:\/\/trustee\.ietf\.org\/license-info\)\./ { sentence=0; }
/in effect on the date of publication of this document\. *$/ { sentence=0; }

/^[ \t]*$/			{ haveblank=1; next; }
				{ outline++; print; }
END				{
				  if (pagecount == 0) pagecount = 1;
				  if (longpagecount == 0 && pagelength > 58) longpagecount++;

				  if (firstpagelength > 58) firstpagelenght = 58;
				  if (firstpagelength == 0) firstpagelength = 58;
				  print  "";
				  printf "-+- Pagecount: %d -+-\n", (pagecount > maxpage ? pagecount : maxpage);
				  printf "-+- Firstpagelength: %d -+-\n", firstpagelength;
				  printf "-+- Maxpagelength: %d -+-\n", (pagelength > maxlength ? pagelength : maxlength);
				  printf "-+- Longpagecount: %d -+-\n", longpagecount;
				  printf "-+- Longestpage: %d -+-\n", longestpage;
				  printf "-+- Formfeedcount: %d -+-\n", ffcount;
				  printf "-+- Expiration: %s -+-\n", expiration;
				  printf "-+- NoNroffPostproc: %s -+-\n", missing_nroff_postprocessing;
				}
' $1
}

tmpfile() {
    prefix=$(basename $0)
    for tmpdir in $TMPDIR $TMP $TEMP /tmp .; do
	if [ -d $tmpdir -a -w $tmpdir ]; then
	    tmpfn=$tmpdir/$prefix-$$.tmp
	    if [ -f $tmpfn ]; then rm $tmpfn; fi
	    echo $tmpfn
	    exit
	fi
    done
    if [ -z $tmpfn ]; then
	msg "Can't find any writable directory for temporary files; this won't work..."
    fi
}

checknits() {

    program=$(tmpfile)

    cat << 'EOF' > $program

BEGIN {
    option_verbose = 0;
    option_warn = 1;
    option_nonascii = 0;

    split(ENVIRON["CHECKNITS"], argv);

    columns = ENVIRON["COLUMNS"];
    if (! columns ) columns = 78;
    if (columns > 80) columns = 80;
    if (columns < 16) columns = 16;
    if (columns < 73) bpcols = columns; else bpcols = 73;

    indentation = 8;
    errmark  = "**";
    flawmark  = "~~"
    warnmark = "==";
    infomark = "--";
    notemark = "  ";

    for (i in argv) {
	# Deprecated
	if (argv[i] == "--rfc3667") {
	    printf "\nOption %s does nothing any more\n", argv[i];
	    argv[i] = "";
	}
	if (argv[i] == "--no3667") {
	    printf "\nOption %s does nothing any more\n", argv[i];
	    argv[i] = "";
	}
	if (argv[i] == "--rfc2026") {
	    printf "\nOption %s does nothing any more\n", argv[i];
	    argv[i] = "";
	}
	if (argv[i] == "--nowarn") {
	    option_warn = 0;
	    argv[i] = "";
	}
	if (argv[i] == "--filename") {
	    option_filename = argv[i+1];
	    argv[i] = "";
	    argv[i+1] = "";
	}
	if (argv[i] == "--debug") {
	    option_debug = 1;
	    argv[i] = "";
	}
	if (argv[i] == "--nitcount") {
	    option_nitcount = 1;
	    argv[i] = "";
	}
	if (argv[i] == "--pass1") {
	    option_pass1 = 1;
	    argv[i] = "";
	}
	if (argv[i] == "--verbose") {
	    option_verbose++;
	    argv[i] = "";
	}
	if (argv[i] == "--list-matches") {
	    option_list_matches = 1;
	    argv[i] = "";
	}
	if (argv[i] == "--year") {
	    option_year = argv[i+1];
	    argv[i] = "";
	    argv[i+1] = "";
	}
	if (argv[i] == "--checklistwarn") {
	    option_checklistwarn++;
	    argv[i] = "";
	}
	if (argv[i] == "--submitcheck") {
	    option_submitcheck++;
	    argv[i] = "";
	}
	if (argv[i] == "--status") {
	    option_status = argv[i+1];
	    argv[i] = "";
	    argv[i+1] = "";
	}
	if (argv[i] == "--nonascii") {
	    option_nonascii = 1;
	    argv[i] = "";
	}
	if (argv[i] == "--ascii") {
	    option_nonascii = 0;
	    argv[i] = "";
	}
	if (argv[i] == "--") {
	    argv[i] = "";
	}
	if (argv[i] ~ "^-.+") {
	    printf "\nUnknown option: %s\n\n", argv[i];
	    usage();
	    exit 1;
	}
	if (argv[i] ~ "^--.+") {
	    printf "\nUnknown option: %s\n\n", argv[i];
	    usage();
	    exit 1;
	}
    }
    option_pass2 = ! option_pass1;

    split("SOH STX ETX EOT ENQ ACK BEL BS TAB LF VT FF CR SO SI DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US", controlchar)
    for (i in controlchar) {
	controlchar[sprintf("%c",i+0)] = controlchar[i];
	delete controlchar[i];
    }

    split("January February March April May June July August September October November December", monthnames);
    for (i in monthnames) {
       mn = monthnames[i];
       month[mn] = i;
       month[substr(mn, 1, 3)] = i;
    }
    doc_date = 0;

    # Extract a list of hyphenated words from the document, and build an array of valid fragments
    if ( length(option_filename) ) {
	cmd = sprintf("cat %s | sed 's/[^A-Za-z0-9_-]/\\n/g' | sort | uniq | egrep -- '-' | egrep -v -- '-$' | egrep '^[A-Za-z0-9]'", option_filename )
	while ( cmd | getline > 0 ) {
	    word = $0
	    frag = ""
	    for (;;) {
		if ( length(word) == 0 ) break

		pos = index(word, "-")
		if ( pos == 0 ) break

	        frag = frag substr(word, 1, pos )
	        hyphenfrags[frag]
		has_hyphenlist = 1
		word = substr(word, pos+1)
	    }
	}
    }

    # get current year
    if ( option_year > 1990 ) {
	year = option_year;
    } else if ( "date +'%Y'" | getline year <= 0 ) {
	year = "[0-9]+"
    }

    # establish the format we expect references to have, for later use
    has_refs = 0;
    one_ref_format =	"(([0-9A-Z-]|I-?D.)[0-9A-Za-z-]*( [0-9A-Z-]+)?|(IEEE|ieee)[A-Za-z0-9.-]+|(ITU ?|ITU-T ?|G\\.)[A-Za-z0-9.-]+)";
    reference_format =	"\\["  one_ref_format  "(, ?"  one_ref_format  ")*\\]";
    abnf_rule_format =  "^ +([A-Za-z0-9-]+ *=|;).*";
    imap_rule_format =  "^ *S: (\\*|[A-Za-z0-9]+) (OK|NO|BAD|PREAUTH|BYE) \\[[-A-Za-z]+\\]";
    code_start_format = "(<CODE BEGINS>)";
    code_end_format = "(<CODE ENDS>)";
    inline_code_format = "(/\\*|\\*/|^ *#)"

    # read obsoleted RFC information from file
    if (obslist) {
	while ( getline < obslist > 0 ) {
	    oldrfc = $1; $1 = "";
	    obsoleted[oldrfc] = $0;
	}
    } else {
	    obsoleted[""] = "";
    }

    # read the rfc status information from file
    if (statusfile) {
	while ( getline < statusfile > 0 ) {
	    rfcstatus = rfcstatus $0;
	}
    } else {
	rfcstatus = "";
    }

    # read exception RFC information from file
    if (exclist) {
	while ( getline < exclist > 0 ) {
	    exception[$1] = $1;
	}
    } else {
	    exception[""] = "";
    }

    status2code["bcp"] = "B";
    status2code["best current"] = "B";
    status2code["best current practice"] = "B";
    status2code["experimental"] = "E";
    status2code["experimental track"] = "E";
    status2code["informational"] = "I";
    status2code["ps"] = "P";
    status2code["proposed standard"] = "P";
    status2code["proposed standards"] = "P";
    status2code["standards track"] = "P";
    status2code["standard track"] = "P";
    status2code["ds"] = "D";
    status2code["draft standard"] = "D";
    status2code["standard"] = "S";
    status2code["full standard"] = "S";
    status2code["historic"] = "H";
    status2code["not issued"] = "N";

    code2status["O"] = "Obsolete";
    code2status["U"] = "Unknown state";
    code2status["E"] = "Experimental";
    code2status["I"] = "Informational";
    code2status["B"] = "Best Current Practice";
    code2status["P"] = "Proposed Standard";
    code2status["D"] = "Draft Standard";
    code2status["S"] = "Full Standard";
    code2status["H"] = "Historic";
    code2status["N"] = "Not Issued";

    warncodes["H"] = "N";
    warncodes["E"] = "N O";
    warncodes["I"] = "N O";
    warncodes["B"] = "N E I O U H";
    warncodes["P"] = "N E I O U H";
    warncodes["D"] = "N E I O U H P";
    warncodes["S"] = "N E I P U H P D";

    split("B P D S", ietfstreamcodes)

    has[""];		# make sure this exists as a global variable here
    seen_ref[""];
    miss_ref[""];

    # ------------------------------------------------------------------
    #   3978 and 3979 section texts
    #

    bp["any_text"] =  ".*"

    bp["rfc3667_3_claim"] = "This document is an Internet-Draft and is subject to all provisions " \
		      "of (S|s)ection 3 of( | \\])RFC( |.?)(3667|3667\\])\\.$";

    bp["rfc3667_5_1"] = "By submitting this Internet-Draft, (I|we) certify that any applicable " \
                      "patent or other IPR claims of which (I am|we are) aware have been disclosed,( or " \
                      "will be disclosed,)? and any of which (I|we) become aware will be disclosed, " \
		      "in accordance with RFC 3668.$";

    bp["rfc3978_5_1"] = "By submitting this Internet-Draft, each author represents that any " \
		      "applicable patent or other IPR claims of which he or she is aware " \
		      "have been or will be disclosed, and any of which he or she becomes " \
		      "aware will be disclosed, in accordance with Section 6 of BCP 79.$";

    bp["rfc3978_5_1_a"] = "By submitting this Internet-Draft, (each|the) author represents that any " \
		      "applicable patent or other IPR claims of which (he or she|he|she) is aware " \
		      "have been or will be disclosed, and any of which (he or she|he|she) becomes? " \
		      "aware will be disclosed, in accordance with( (S|s)ection 6 of)? (BCP 79|RFC 3979|RFC 3668).$";

    bp["rfc3978_5_2a"] = "This document may not be modified, and derivative works of it may " \
                      "not be created, except to publish it as an RFC and to translate it " \
                      "into languages other than English\\.$";

    bp["rfc3978_5_2ax"] = "This document may not be modified, and derivative works of it may " \
                      "not be created, except to publish it as an RFC and to translate it " \
                      "into languages other than English other than to extract section " \
                      "[0-9.]+ as-is for separate use\\.$";

    bp["rfc3978_5_2b"] = "This document may not be modified, and derivative works of it may " \
                      "not be created\\.$";

    bp["rfc3978_5_2bx"] = "This document may not be modified, and derivative works of it may " \
                      "not be created other than to extract section [0-9.]+ as-is for separate use.$";

    bp["rfc3978_5_3"] = "This document may only be posted in an Internet-Draft.$";

    bp["rfc3978_5_4_p1"] = "Copyright \\(C\\) The Internet Society \\(?[0-9][0-9][0-9][0-9]\\)?.$"

    bp["rfc3978_5_4_p1_u4748"] = "Copyright \\(C\\) The IETF Trust \\(?[0-9][0-9][0-9][0-9]\\)?.$"

    bp["rfc3978_5_4_p1_e"] = ".+Copyright \\(C\\) The Internet Society \\(?[0-9][0-9][0-9][0-9]\\)?\\..+"

    bp["rfc3978_5_4_p1_u4748_e"] = ".+Copyright \\(C\\) The IETF Trust \\(?[0-9][0-9][0-9][0-9]\\)?\\..+"

    bp["rfc3978_5_4_p1_now"] = sprintf("Copyright \\(C\\) The Internet Society \\(?%s\\)?", year);

    bp["rfc3978_5_4_p1_u4748_now"] = sprintf("Copyright \\(C\\) The IETF Trust \\(?%s\\)?", year);

    bp["trust-16-oct-2008_6_b_p2_now"] = sprintf("Copyright \\(c\\) %s IETF Trust and " \
		      "the persons identified as the document authors", year);

    bp["trust-12-feb-2009_6_b_p2_now"] = sprintf("Copyright \\(c\\) %s IETF Trust and " \
		      "the persons identified as the document authors", year);

    bp["trust-12-sep-2009_6_b_p2_now"] = sprintf("Copyright \\(c\\) %s IETF Trust and " \
		      "the persons identified as the document authors.", year);

    bp["trust-28-dec-2009_6_b_i_p2_now"] = sprintf("Copyright \\(c\\) %s IETF Trust and " \
		      "the persons identified as the document authors.", year);

# ----------------------------------------------------------------------------------------------

    bp["rfc3978_5_4_p2"] = "This document is subject to the rights, licenses and restrictions contained in BCP " \
                      "78, and except as set forth therein, the authors retain all their rights.$";

    bp["rfc3978_5_5"] = "This document and the information contained herein are provided " \
                      "on an \"AS IS\" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE " \
                      "REPRESENTS OR IS SPONSORED BY \\(IF ANY\\), THE INTERNET SOCIETY AND " \
                      "THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, " \
                      "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT " \
                      "THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR " \
                      "ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A " \
                      "PARTICULAR PURPOSE.$";

    bp["rfc3978_5_5_u4748"] = "This document and the information contained herein are provided " \
                      "on an \"AS IS\" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE " \
                      "REPRESENTS OR IS SPONSORED BY \\(IF ANY\\), THE INTERNET SOCIETY, THE IETF TRUST AND " \
                      "THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, " \
                      "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT " \
                      "THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR " \
                      "ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A " \
                      "PARTICULAR PURPOSE.$";

    bp["rfc3979_5_p1"] = "The IETF takes no position regarding the validity or scope of any " \
                      "Intellectual Property Rights or other rights that might be claimed " \
                      "to pertain to the implementation or use of the technology " \
                      "described in this document or the extent to which any license " \
                      "under such rights might or might not be available; nor does it " \
                      "represent that it has made any independent effort to identify any " \
                      "such rights.  Information on the procedures with respect to rights " \
                      "in RFC documents can be found in BCP 78 and BCP 79.$";

    bp["rfc3979_5_p2"] = "Copies of IPR disclosures made to the IETF Secretariat and any " \
                      "assurances of licenses to be made available, or the result of an " \
                      "attempt made to obtain a general license or permission for the use " \
                      "of such proprietary rights by implementers or users of this " \
                      "specification can be obtained from the IETF on-line IPR repository " \
                      "at https?://www.ietf.org/ipr.$";

    bp["rfc3979_5_p3"] = "The IETF invites any interested party to bring to its attention " \
                      "any copyrights, patents or patent applications, or other " \
                      "proprietary rights that may cover technology that may be required " \
                      "to implement this standard.  Please address the information to the " \
                      "IETF at ietf-ipr@ietf.org.$";

# ----------------------------------------------------------------------------------------------

    bp["trust-16-oct-2008_6_a"] = "This Internet-Draft is submitted to IETF in full " \
		      "conformance with the provisions of BCP 78 and BCP 79\\.$";

    bp["trust-16-oct-2008_6_b_p2"] = "Copyright \\(c\\) [0-9][0-9][0-9][0-9] IETF Trust and " \
		      "the persons identified as the document authors\\.  All rights reserved\\.$";
    
    bp["trust-16-oct-2008_6_b_p3"] = "This document is subject to BCP 78 and the " \
		      "IETF Trust's Legal Provisions Relating to IETF Documents " \
		      "\\(http://trustee.ietf.org/license-inf(o|o/)\\) " \
		      "in effect on the date of publication of this document.  " \
		      "Please review these documents " \
		      "carefully, as they describe your rights and restrictions with respect to " \
		      "this document\\.$";

    bp["trust-16-oct-2008_6_c_i"] = "This document may not be modified, and derivative " \
		      "works of it may not be created, except to format it for publication " \
		      "as an RFC and to translate it into languages other than English\\.$";

# ----------------------------------------------------------------------------------------------

    bp["trust-12-feb-2009_6_a"] = bp["trust-16-oct-2008_6_a"]
    bp["trust-12-feb-2009_6_b_p2"] = bp["trust-16-oct-2008_6_b_p2"]
    
    bp["trust-12-feb-2009_6_b_p3"] = \
		      "This document is subject to BCP 78 and the IETF Trust's Legal " \
		      "Provisions Relating to IETF Documents in effect on the date of " \
		      "publication of this document \\(http://trustee.ietf.org/license-inf(o|o/)\\)\\.  " \
		      "Please review these documents carefully, as they describe your " \
		      "rights and restrictions with respect to this document\\.$"

    bp["trust-12-feb-2009_6_c_i"] = "This document may not be modified, and derivative " \
		      "works of it may not be created, except to format it for publication " \
		      "as an RFC or to translate it into languages other than English\\.$";

    bp["trust-12-feb-2009_6_c_ii"] = "This document may not be modified, and " \
		      "derivative works of it may not be created, and it may not be published " \
		      "except as an Internet-Draft\\.$"

    bp["trust-12-feb-2009_6_c_iii"] = \
		      "This document may contain material from IETF Documents or IETF " \
		      "Contributions published or made publicly available before November " \
		      "10, 2008\\.  The person\\(s\\) controlling the copyright in some of this " \
		      "material may not have granted the IETF Trust the right to allow " \
		      "modifications of such material outside the IETF Standards Process\\. " \
		      "Without obtaining an adequate license from the person\\(s\\) " \
		      "controlling the copyright in such materials, this document may not " \
		      "be modified outside the IETF Standards Process, and derivative " \
		      "works of it may not be created outside the IETF Standards Process, " \
		      "except to format it for publication as an RFC or to translate it " \
		      "into languages other than English\\.$"

# ----------------------------------------------------------------------------------------------

    bp["trust-12-sep-2009_6_a"]		= bp["trust-12-feb-2009_6_a"]
    bp["trust-12-sep-2009_6_b_p2"]	= bp["trust-12-feb-2009_6_b_p2"]
    bp["trust-12-sep-2009_6_c_i"]	= bp["trust-12-feb-2009_6_c_i"]
    bp["trust-12-sep-2009_6_c_ii"]	= bp["trust-12-feb-2009_6_c_ii"]
    bp["trust-12-sep-2009_6_c_iii"]	= bp["trust-12-feb-2009_6_c_iii"]
    
    bp["trust-12-sep-2009_6_b_p3"] = \
		      "This document is subject to BCP 78 and the IETF Trust's Legal " \
		      "Provisions Relating to IETF Documents \\(https?://trustee.ietf.org/license-inf(o|o/)\\) " \
		      "in effect on the date of publication of this document\\.  " \
		      "Please review these documents carefully, as they describe your " \
		      "rights and restrictions with respect to this document\\.  " \
		      "Code Components extracted from this document must include " \
		      "(Simplified|Revised) BSD License text as described in Section 4.e of the " \
		      "Trust Legal Provisions and are provided without warranty as described " \
		      "in the BSD License\\.$"


# ----------------------------------------------------------------------------------------------

    bp["trust-28-dec-2009_6_b_i_p2"]	= bp["trust-12-sep-2009_6_b_p2"]
    bp["trust-28-dec-2009_6_c_i"]	= bp["trust-12-sep-2009_6_c_i"]
    bp["trust-28-dec-2009_6_c_ii"]	= bp["trust-12-sep-2009_6_c_ii"]
    bp["trust-28-dec-2009_6_c_iii"]	= bp["trust-12-sep-2009_6_c_iii"]

    bp["trust-28-dec-2009_6_a"] = "This Internet-Draft is submitted in full " \
		      "conformance with the provisions of BCP 78 and BCP 79\\.$";

    # This applies to the IETF stream
    bp["trust-28-dec-2009_6_b_i_p3"] = \
		      "This document is subject to BCP 78 and the IETF Trust's Legal Provisions " \
		      "Relating to IETF Documents \\(https?://trustee\\.ietf\\.org/license-inf(o|o/)\\) " \
		      "in effect on the date of publication of this document\\.  " \
		      "Please review these documents carefully, as they describe your " \
		      "rights and restrictions with respect to this document\\.  " \
		      "Code Components extracted from this document must include " \
		      "(Simplified|Revised) BSD License text as described in Section 4.e of the " \
		      "Trust Legal Provisions and are provided without warranty as described " \
		      "in the (Simplified|Revised) BSD License\\.$"

    # This applies to non-IETF streams
    bp["trust-28-dec-2009_6_b_ii_p3"] = \
		      "This document is subject to BCP 78 and the IETF Trust's Legal Provisions " \
		      "Relating to IETF Documents \\(https?://trustee\\.ietf\\.org/license-inf(o|o/)\\) " \
		      "in effect on the date of publication of this document\\.  " \
		      "Please review these documents carefully, as they describe your " \
		      "rights and restrictions with respect to this document\\.$"

# ----------------------------------------------------------------------------------------------

    bp["rfc2026_10_4A"] = "The IETF takes no position regarding the validity or scope of " \
                      "any intellectual property or other rights that might be claimed " \
                      "to pertain to the implementation or use of the technology " \
                      "described in this document or the extent to which any license " \
                      "under such rights might or might not be available; neither does " \
                      "it represent that it has made any effort to identify any such " \
                      "rights.  Information on the IETF\047s procedures with respect to " \
                      "rights in standards-track and standards-related documentation " \
                      "can be found in BCP-11.  Copies of claims of rights made " \
                      "available for publication and any assurances of licenses to " \
                      "be made available, or the result of an attempt made " \
                      "to obtain a general license or permission for the use of such " \
                      "proprietary rights by implement[oe]rs or users of this " \
                      "specification can be obtained from the IETF Secretariat.$";

    bp["rfc2026_10_4B"] = "The IETF invites any interested party to bring to its " \
                      "attention any copyrights, patents or patent applications, or " \
                      "other proprietary rights which may cover technology that may be " \
                      "required to practice this standard.  Please address the " \
                      "information to the IETF Executive Director.$";

    bp["rfc2026_10_4C_p1"] = "Copyright \\([Cc]\\) The Internet Society \\(?[0-9][0-9][0-9][0-9]\\)?.  All Rights " \
                      "Reserved.$";

    bp["rfc2026_10_4C_p2"] = "This document and translations of it may be copied and " \
                      "furnished to others, and derivative works that comment on or " \
                      "otherwise explain it or assist in its implementation may be " \
                      "prepared, copied, published and distributed, in whole or in " \
                      "part, without restriction of any kind, provided that the above " \
                      "copyright notice and this paragraph are included on all such " \
                      "copies and derivative works.  However, this document itself may " \
                      "not be modified in any way, such as by removing the copyright " \
                      "notice or references to the Internet Society or other Internet " \
                      "organizations, except as needed for the purpose of developing " \
                      "Internet standards in which case the procedures for copyrights " \
                      "defined in the Internet Standards process must be followed, or " \
                      "as required to translate it into languages other than English.$";

    bp["rfc2026_10_4C_p3"] = "The limited permissions granted above are perpetual and will " \
                      "not be revoked by the Internet Society or its successors or " \
                      "assign(ee)?s.$";

    bp["rfc2026_10_4C_p4"] = "This document and the information contained herein is provided " \
                      "on an \"AS IS\" basis and THE INTERNET SOCIETY AND THE INTERNET " \
                      "ENGINEERING TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR " \
                      "IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE " \
                      "OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY " \
                      "IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A " \
                      "PARTICULAR PURPOSE.$";

    bp["rfc2026_10_4D"] = "The IETF has been notified of intellectual property rights " \
                      "claimed in regard to some or all of the specification contained " \
                      "in this document.  For more information consult the online list " \
                      "of claimed rights.$";

    bp["rfc2026_claim"] = "This document is an Internet-Draft and is in full conformance " \
			  "with all provisions of Section 10 of( | \\])RFC( |.?)(2026|2026\\])\\.";

    bp["rfc2026_lax_claim"] = "This document is an Internet-Draft and is in full conformance " \
			  "with all provisions of Section 10 of( | \\])RFC( |.?)(2026|2026\\])( ?\\[[0-9]+\\].*)?.";

    bp["rfc2026b_claim"] = "This document is an Internet-Draft and is subject to all provisions " \
			  "of Section 10 of( | \\])RFC( |.?)(2026|2026\\])\\.";

    bp["rfc2026b_lax_claim"] = "This document is an Internet-Draft and is subject to all provisions " \
			  "of Section 10 of( | \\])RFC( |.?)(2026|2026\\])( ?\\[[0-9]+\\].*)?.";

    bp["rfc3978_3_claim"] = "This document is an Internet-Draft and is subject to all provisions " \
		      "of (S|s)ection 3 of( | \\])RFC( |.?)(3978|3978\\])\\.$";

    bp["rfc2119_p2"]	= "The key words \"MUST\", \"MUST NOT\", \"REQUIRED\", \"SHALL\", " \
			"\"SHALL NOT\", \"SHOULD\", \"SHOULD NOT\", \"RECOMMENDED\",( | \"NOT RECOMMENDED\", )\"MAY\", " \
			"and \"OPTIONAL\" in this document are to be interpreted as described in" \
			"( | \\[| BCP 14, | BCP 14, \\[)RFC( |.?)2119(\\.|.*\\.|\\].*\\.)$";

    bp["rfc2119_p2a"]	= "The key words \"MUST\", \"MUST NOT\", \"REQUIRED\", \"SHALL\", " \
			"\"SHALL NOT\", \"SHOULD\", \"SHOULD NOT\", \"RECOMMENDED\",( | \"NOT RECOMMENDED\", )\"MAY\", " \
			"and \"OPTIONAL\" in this document are to be interpreted as described in " \
			"(['\"]?Key words for use in RFCs to Indicate Requirement Levels['\"]? )?" \
			"\\[[0-9A-Z-][0-9A-Za-z-]* ?[0-9A-Z-]*\\].*\\.$";

    # This is from RFC 8174 which updates 2119:
    bp["rfc8174_p11"]	= "The key words \"MUST\", \"MUST NOT\", \"REQUIRED\", \"SHALL\", \"SHALL " \
			"NOT\", \"SHOULD\", \"SHOULD NOT\", \"RECOMMENDED\", \"NOT RECOMMENDED\", " \
			"\"MAY\", and \"OPTIONAL\" in this document are to be interpreted as " \
			"described in BCP 14 \\[RFC2119\\] ?\\[RFC8174\\] when, and only when, they " \
			"appear in all capitals, as shown here.";

    bp["1id_guidelines_p1"] = "Internet-Drafts are working documents of the Internet Engineering " \
			"Task Force \\(IETF\\), its areas, and its working groups.  Note that other " \
			"groups may also distribute working documents as Internet-Drafts.$";

    bp["1id_guidelines_p1a"] = "Internet-Drafts are working documents of the Internet Engineering " \
			"Task Force \\(IETF\\).  Note that other groups may also distribute " \
			"working documents as Internet-Drafts.  The list of current "\
			"Internet-Drafts is at https?://datatracker.ietf.org/drafts/current/? ?\\.$";

    bp["1id_guidelines_p2"] = "Internet-Drafts are draft documents valid for a maximum of six months " \
			"and may be updated, replaced, or obsoleted by other documents at any " \
			"time.  It is inappropriate to use Internet-Drafts as reference " \
			"material or to cite them other than as \"?work in progress(\\.\"|\"?\\.)$";

    bp["1id_guidelines_p2a"] = "Internet-Drafts are draft documents valid for a maximum of six months " \
			"and may be updated, replaced, or obsoleted by other documents at any " \
			"time.  It is inappropriate to use Internet-Drafts as reference " \
			"material or to cite them other than as \"?work in progress(\\.\"|\"?\\.)$";

    bp["1id_guidelines_p3"] = "The list of current Internet-Drafts can be accessed at " \
			"https?://www.ietf.org/1id-abstracts.html$";

    bp["1id_guidelines_p3a"] = "The list of current Internet-Drafts can be accessed at:? " \
			"https?:// *www.ietf.org/ *(ietf/)?1id-abstracts.(txt|html) ?\\.?$";

    bp["1id_guidelines_p4"] = "The list of Internet-Draft Shadow Directories can be accessed at " \
			"https?://www.ietf.org/shadow.html$";

    bp["1id_guidelines_p4a"] = "The list of Internet-Draft Shadow Directories can be accessed at:? " \
			"https?:// *www.ietf.org/ *shadow.html ?\\.?$";


    bad["MUST not"]	= "Using lowercase 'not' together with uppercase 'MUST', "\
			  "'SHALL', 'SHOULD', or 'RECOMMENDED' is not an " \
			  "accepted usage according to RFC 2119.  Please use uppercase 'NOT' " \
			  "together with RFC 2119 keywords (if that is what you mean)."

    bad["SHALL not"] = bad["MUST not"]
    bad["SHOULD not"] = bad["MUST not"]
    bad["not RECOMMENDED"] = bad["MUST not"]

    bad["MAY NOT"]	= "The expression 'MAY NOT', while looking like RFC 2119 requirements " \
			  "text, is not defined in RFC 2119, and should not be used.  Consider " \
			  "using 'MUST NOT' instead (if that is what you mean)."

}


# ----------------------------------------------------------------------
#   usage()
#

function usage() {
    print "" \
"Usage: idnits [options] filename\n" \
"\n" \
"    Options:\n" \
"	--version	Print the version and exit\n" \
"	--help		Print this text and exit\n" \
"	--nowarn	Don't issue warnings, only ID-Checklist violations\n" \
"	--verbose	Show more information about offending lines\n" \
"	--nitcount	Show a count of nits\n" \
"	--debug		Debug output, especially of boilerplate matching\n" \
"	--year NNNN	Expect the given year in the boilerplate\n" \
"	--checklistwarn	Only warn (no errors) for ID-Checklist violations\n" \
"	--nonascii	Disable warnings for non-ascii characters\n" \
"	--submitcheck	Only output errors and warnings related to 1id-guidelines\n" \
"	--status doctype Assume the given intended document type\n" \
""
}


# ----------------------------------------------------------------------
#   strip()
#
function strip(str) {
    sub(/^[ \t\f\r]+/, "", str);
    sub(/[ \t\f\r]+$/, "", str);
    return str;
}

# ----------------------------------------------------------------------
#   list()
function list(str, lst) {
    split(str, lst)
    for (i in lst) {
	lst[lst[i]] = i
	delete lst[i];
    }
}

# ----------------------------------------------------------------------
#   findall()
#
function findall(str, re, array,	i) {
    array[0] = str
    while (match(str, re) > 0) {
	i += 1
	array[i] = substr(str, RSTART, RLENGTH)
        str = substr(str, RSTART+RLENGTH)
    }
    delete array[0]
}

# ----------------------------------------------------------------------
#   get_para()
#
function get_para() {
    para = strip($0);
    while ((getline > 0) && (text = strip($0)) != "") {
	check_line()
	if (para ~ /-$/) {
	    para = para text;
	} else if (para ~ /[A-Za-z0-9]\/$/ && text ~ /^[a-z0-9_-]+([)\/]|\.[a-z])/) {
	    para = para text;
	} else if (para ~ /[A-Za-z0-9]$/ && text ~ /^\/[a-z0-9_-]+([)\/]|\.[a-z])/) {
	    para = para text;
	} else if (para ~ /[0-9A-Fa-f]:$/ && text ~ /^[0-9A-Fa-f]+:/) {
	    para = para text;
	} else {
	    para = para " " text;
	}
    }
    return para;
}

# ----------------------------------------------------------------------
#   match_para()
#
function match_para(para, name1, name2, name3, name4) {
    name[1] = name1; name[2] = name2; name[3] = name3; name[4] = name4;
    for (i=1; i <= 4; i++) {
	pat[i] = bp[name[i]];
    }
    orig = para;
    gsub(/  +/, " ", para);
    pattern = "";
    for (i=1; i <= 4; i++) {
	if (!pat[i]) {
	    almost_boilerplate[name[1]] = orig;
	    if (option_debug && option_pass2) {
		print "** End of patterns **"
	    }
	    return 0;
	}

	gsub(/.  /,".  ?", pat[i]);
	pattern = pattern pat[i];

	if (option_debug && option_pass2) {
	    print "\n----",name[i],"----"
	    print "line", FNR, "\n"
	    print ""
	    print "'"para"'"
	    print ""
	    print "'"pattern"'"
	    print ""
	    if (para ~ pattern) {	
		print "** Matches **"
	    } else {
		print "** No Match **"
	    }
	}
	if (para ~ pattern) {
	    for (j=1; j<= i; j++) has[name[j]] = FNR-1;
	    if (option_debug && option_pass2) {
		print "** Exit **"
	    }	
	    return 1;
	}
	sub(/ *\$?$/," +", pattern);
	if (para ~ pattern) {
	    if (option_debug && option_pass2) {
		print "** Match with trailing text **"
	    }
	    for (j=1; j<= i; j++) has[name[j]] = FNR-1;
	    if (!pat[i+1]) {
		if (option_debug && option_pass2) {
		    print "** Exit: end of patterns **"
		}
		return 1;
	    }
	} else {
	    if (option_debug && option_pass2) {
		print "** Exit: No prefix match **"
	    }
	    for (k=1; k < i; k++) {
		pref = pat[k];
		sub(/ *\$?$/," +", pref);
		sub(pref, "", orig);
	    }
	    stripped_orig = strip(orig)
	    if (index(substr(bp[name[i]], 1, 48), substr(stripped_orig, 1, 32))) {
		almost_boilerplate[name[i]] = orig;
	    }
	    return 0;
	}
    }

    almost_boilerplate[name[1]] = orig;
    return 0
}

# ----------------------------------------------------------------------
#   rindex(str, find)
#
function rindex(str, find,	start, pos, incr, i) {
    start = 0;
    pos = 0;
    incr = length(find);

    while (1) {
	i = index(str, find);
	if ( i == 0 ) return pos;
	pos = start + i;
	str = substr(str, i+incr);
	start = start+i+incr-1;
    }
}

# ----------------------------------------------------------------------
#   fold(str)
#
#   This could have been done by piping through fold, too, but we'd have
#   to postprocess to add indentation anyway, and this should be a lot
#   quicker for short texts.
#
function fold(str, indent, cols,        width, pos, lpos, npos) {
    pos = 1;

    #print "str:        ", str;
    #print "indent:     ", indent;
    #print "cols:       ", cols;

    insert = "\n" indent;
    width = cols;
    while (pos+width-1 < length(str)) {
	frag = substr(str, pos, width);
	lpos = rindex(frag, " ");
	npos = index(frag, "\n");
	if (npos && npos < lpos) lpos = npos;
	if (!lpos) {
	    frag = substr(str, pos+width);
	    lpos = index(frag, " ");
	    if (lpos) lpos += width;
	}
	if (lpos) {
	    #print "** before break :", "..." substr(str, pos+lpos-9, 8)
	    #print "** after  break :", substr(str, pos+lpos, 8) "..."

	    str = substr(str, 1, pos+lpos-2) insert substr(str, pos+lpos);
	    pos = pos + lpos + length(insert);
	} else {
	    return str;
	}
	width = cols - length(indent);
    }
    return str;
}

# ----------------------------------------------------------------------
#   markiff(p1, p2)
#
function markdiff(p1, p2,	prefixlen, para2, suffix, leading, point, temp) {
   prefixlen = 0;
   para2 = p2;
   gsub(/\.  +/, ". ", p1);
   gsub(/\.  +/, ". ", p2);
   do {
      if (substr(p1,1,1) == substr(p2,1,1)) {
	 p1 = substr(p1,2);
	 p2 = substr(p2,2);
      } else {
	 break;
      }
      prefixlen++;
   } while (length(p1) && length(p2));

   prefix = substr(para2,1,prefixlen);
   suffix = substr(para2,prefixlen+1);
   leading = length(prefix) - rindex(prefix, "\n");

   temp = substr(prefix, length(prefix) - leading)
   spacefix = gsub(/\.  +/, ". ", temp);

   point = index(suffix, "\n");
   marker = substr("........................................................................", 1, leading+spacefix) "^\n";
   return prefix substr(suffix, 1, point) marker substr(suffix, point+1)
}

# ----------------------------------------------------------------------
#   expandname(name)
#
function expandname(name) {
    sub(/_u/, ", updated by RFC " , name);
    sub(/_p/, ", paragraph " , name);
    sub(/_e/, " embedded", name);
    if (name ~ /^rfc/ || name ~ /^trust-/) {
	sub("_", ", Section ", name);
	split("x ix viii vii vi v iv iii ii i", roman)
	for (i in roman) {
	    r = roman[i];
	    sub("_" r, "(" r ")", name);
	}
	gsub("_", ".", name);
	sub("rfc", "RFC ", name)
	sub("trust-", "IETF Trust Legal Provisions of ", name)
    }
    return name
}

# ----------------------------------------------------------------------
#   showalmost(name)
#
function showalmost(name, orig) {
    p2 = almost_boilerplate[name];
    p2 = fold(p2, "        ",  bpcols);
    p2 = sprintf ("        %s\n\n", p2)
    if (orig) p2 = markdiff(orig, p2);
    printf(p2)
}
# ----------------------------------------------------------------------
#   showsection(name)
#
function showsection(name, suppress, initial, final) {
	p = bp[name];
	"date +'%Y'" | getline year
	# Get rid of regexp stuff in the boilerplate text we show the user:
	# - mark metacharacters with mark character "!"
	gsub(/[()|?+*^$]/, "!&", p);
	# - get rid of mark preceeded by escape char...
	gsub(/\\!/, "", p);
	# - get rid of alternates, keeping the first only
	gsub(/!\|[^)]*!\)/, "", p);
	# - and we can get rid of metacharacters not preceeded by escape char
	gsub(/![\(\)?+*^$]/, "", p);
        # and also of escape chars themselves
	gsub(/\\/, "", p);
	# - also say "year" instead of "[0-9]"
	##gsub(/\[0-9\]/, "<year>", p);
	gsub(/\[0-9\]\[0-9\]\[0-9\]\[0-9\]/, year, p);

	if (suppress < 2) note( initial expandname(name) final ":");
	p1 = fold(p, "        ", bpcols);
	p1 = sprintf("        %s", p1);
	printf(p1)

	if (name in almost_boilerplate && suppress == 0) {
	    note( "\n\n     ... text found in draft:");
	    showalmost(name, p1)
	    print ""
	} else {
	    print "\n";
	}
}

# ----------------------------------------------------------------------
#   checkmultibp(name)
#
function checkmultibp(name) {
    if ( has[name] && almost_boilerplate[name]) {
	warn(sprintf("In addition to %s boilerplate, a section with a similar start was also found:\n", expandname(name)))
	showalmost(name)
    }
}

# ----------------------------------------------------------------------
#   sectionerr(name, str)
#
function sectionerr(name, str, wflag) {
    if (! name in has) {
	print "?? internal error, expected a table match on %s", name;
	exit;
    }
    if (str ~ /^the /) {
	str = "The document seems to lack " str;
    } else if (str ~ /^[aeiouyAEIOUYRH]/) {
	str = "The document seems to lack an " str;
    } else {
	str = "The document seems to lack a " str;
    }
    if (name in almost_boilerplate) {
	str = str " -- however, there\047s a paragraph with a matching beginning. Boilerplate error?";
    } else {
	if ( str !~ /(\.|\.\))$/ ) str = str ". ";
    }
    if (wflag == 3) {
        comment(str);
    } else if (wflag == 2) {
        warn(str);
    } else if (wflag == 1) {
	flaw(str);
    } else {
	err(str);
    }
    if ((option_verbose || option_submitcheck) && option_pass2) {
	showsection(name)
    }
}

function sectioncomment(name, str) {
    sectionerr(name, str, 3);
}
function sectionwarn(name, str) {
    sectionerr(name, str, 2);
}
function sectionflaw(name, str) {
    sectionerr(name, str, 1);
}

# ----------------------------------------------------------------------
#   oldandnewerr(old, new)
#
function oldandnewerr(old, new) {
    if (! new in has) {
	print "?? internal error, expected a table match on %s", new;
	exit;
    }
    newname = expandname(new)
    oldname = expandname(old)
    str = sprintf("Found boilerplate matching %s (on line %s), which is fine, but *also* found old %s text on line %s.", newname, has[new], oldname, has[old]);
    err(str);
    if (option_verbose && option_pass2) {
	showsection(old, 1)
	showsection(new, 1)
    }
}


# ----------------------------------------------------------------------
#   newandolderr(old, new)
#
function newandolderr(old, new) {
    newname = expandname(new)
    oldname = expandname(old)
    str = sprintf("Found new %s boilerplate, which is fine, but *also* found old boilerplate from %s on line %s.", newname, oldname, has[old]);
    err(str);
    if (option_verbose && option_pass2) {
	showsection(old, 1)
    }
}


# ----------------------------------------------------------------------
#   olderr(old, new)
#
function olderr(old) {
    oldname = expandname(old)
    str = sprintf("Found old boilerplate from %s on line %s.", oldname, has[old]);
    comment(str);
    if (option_verbose && option_pass2) {
	showsection(old, 1, "The obsolete ", " text")
    }
}


# ----------------------------------------------------------------------
# update_references(line, pos)
#

function update_references(this, pos) {
    if (!match(this, /\[Page/)) {
	nnnote( sprintf("update_references( %s)\n", this))
	if ( match(this, "(^|[\\]A-Za-z0-9 \t.()])" reference_format "\\)?([) \t,;:.?[]|$)") ) {

	    line[pos] = this
	    if (in_refs && this ~ "^ *([0-9.]+ *)?" reference_format) {
		# Reference definitions
		match(this, reference_format)
		reftag = substr(this, RSTART+1, RLENGTH-2)
		numrefs = split(reftag, reftags, /, +/)
		for (i=1; i<= numrefs; i++) {
		    reftag = reftags[i]
		    ref_def[reftag] = pos
		    if ( ! (reftag in ref_def_seen) ) {
			ref_def_seen[reftag]; ref_def_list[++ref_def_count] = reftag
		    }

		    if ( reftag ~ /^[0-9]+$/ ) {
			has_numeric_refs = 1
		    } else {
			has_symbolic_refs = 1	
		    }

		    if (option_pass2) nnnote(sprintf("	ref_def[%s] at %s: %s\n", reftag, pos, this));
		    ref_text[reftag] = this;
		    if (in_normref) {
			ref_norm[reftag];
		    }
	        }
	    } else {
		# Reference usage
		while (	match(this, reference_format )) {
		    reftag = substr(this, RSTART+1, RLENGTH-2)
		    numrefs = split(reftag, reftags, /, +/)
		    for (i=1; i<= numrefs; i++) {
			tags = substr(this, RSTART+1, RLENGTH-2)
			split(tags, reftags, /(,|, )/)
			for (i in reftags) {
			    reftag = reftags[i]
			    ref_use[reftag] = pos
			    if ( ! (reftag in ref_use_seen) ) {
				ref_use_seen[reftag]; ref_use_list[++ref_use_count] = reftag;
			    }
			    if (option_verbose > 2 && option_pass2) printf "	ref_use[%s] = %s\n", reftag, pos
			}
			this = substr(this, RSTART+RLENGTH)
		    }
		}
	    }
	} else if (option_verbose && option_pass1) {
	    match(this, /\[[0-9A-Z-]+\]/)
	    printf("%s(%d): Unexpected reference format: '...%s...'\n", FILENAME, pos, substr(this,RSTART-5, RLENGTH+10));
	    if (option_verbose > 1) {
		printf("  --> %s\n", this);
		printf("      %*s\n", RSTART, "^");
	    }
	}
    }
}

# ----------------------------------------------------------------------
#   announce()
#

function announce(str) {
###    print ""
    print ""
    print fold("  " str, "  ", columns);
    print "  " substr("----------------------------------------------------------------------------", 1, columns-2);
    print ""
}


# ----------------------------------------------------------------------
#   note()
#

function note(str) {
    if (option_pass1) {
        printf("%s(%d): %s.\n", FILENAME, FNR, str);
    } else {
        print fold("  " notemark " " str, "     ", columns);
    }
}

function nnote(str) {
    if (option_verbose) {
	if (option_pass1) {
	    printf("%s(%d): %s.\n", FILENAME, FNR, str);
	}
    }
}

function nnnote(str) {
    if (option_verbose >= 2) {
	if (option_pass1) {
	    printf("%s(%d): %s.\n", FILENAME, FNR, str);
	} else {
	    print fold("  " notemark " " str, "     ", columns);
	}
    }
}

# ----------------------------------------------------------------------
#   comment()
#

function comment(str, indented) {
    if (option_warn) {
        print fold("  " infomark " " str, "     ", columns);
	if (indented) {
	    print fold("     (A line matching the expected section header was found, but with an unexpected indentation:", "    ", columns);
	    print fold("     '" indented "' )", "    ", columns);
	}
	print "";
	comments++;
    }
}


# ----------------------------------------------------------------------
#   warn()
#

function warn(str, indented) {
    if (option_warn) {
        print fold("  " warnmark " " str, "     ", columns);
	if (indented) {
	    print fold("     (A line matching the expected section header was found, but with an unexpected indentation:", "    ", columns);
	    print fold("     '" indented "' )", "    ", columns);
	}
	print "";
	warnings++;
    }
}


# ----------------------------------------------------------------------
#   flaw()
#

function flaw(str, indented) {
    print fold("  " flawmark " " str, "     ", columns);
    print "";
    flaws++;
}


# ----------------------------------------------------------------------
#   err()
#

function err(str, indented) {
    print fold("  " errmark " " str, "     ", columns);
    if (indented) {
	print fold("     (A line matching the expected section header was found, but with an unexpected indentation:", "    ", columns);
	print fold("     '" indented "' )", "    ", columns);
    }
    print "";
    errors++;
}


# ----------------------------------------------------------------------
#   errc()
#
#   Errors which are IESG checklist errors, but not submission errors

function errc(str, indented) {
    if (!option_submitcheck) {
	if (option_checklistwarn) {
	    warn(str, indented);
	} else {
	    err(str, indented);
	}
    }
}


# ----------------------------------------------------------------------
#   errw()
#
#   Errors which are only warnings in submission mode


function errw(str, indented) {
    if (option_submitcheck) {
	warn(str, indented);
    } else {
	err(str, indented);
    }
}


# ----------------------------------------------------------------------
#   warnc()
#	
#   Warnings which are IESG checlist warnings, but not submission warnings

function warnc(str, indented) {
    if (!option_submitcheck) {
	warn(str, indented);
    }
}


# ----------------------------------------------------------------------
#   errg()
#

function errg(str, indented) {
    if (option_guidelinewarn) {
	warn(str, indented);
    } else {
	err(str, indented);
    }
}


# ----------------------------------------------------------------------
#   get_state(draft)
#

function get_state(draft, state, quiet,		count) {
    for (key in state) delete state[key];
    statefile = statusdir "/" draft ".state"
## Not sure that this fallback handling is necessary.  Keeping it commented out for now.
#    if ( ( getline < statefile ) < 0 ) {
#	close(statefile)
#	note("... Trying to download missing state file for " draft " ...")
#	command = "idnits --download '" draft " state' http://tools.ietf.org/draft/" draft "/state " statusdir "/" draft ".state 'Doc-tag: " draft "'"
#	while (	(command | getline) > 0) print $0;
#    }
    while ( getline < statefile > 0 ) {
	if ($1 ~ /^Doc-/) {
	   count++;
	   key = substr($1, 5, length($1)-5);
	   $1 = "";
	   value = strip($0)
	   sub(/;[^;]*$/, "", value)
	   state[key] = value
	}
    }
    close(statefile)
    datefile  = statefile ".date"
    if ( getline < datefile > 0 && $0 ) {
	state["downloaded"] = $0
    } else {
	state["downloaded"] = "unsuccessful"
    }
    if (!count && !quiet) comment("No information found for " draft " - is the name correct?");
    return count
}


# ----------------------------------------------------------------------
# check_downref(tag)
#

function referr(msg, num, tag, comment,		info, numstr) {
    numstr = sprintf("%d", num);
    if (numstr in obsoleted) {
	info = obsoleted[numstr];
	gsub(" ", ", RFC ", info);
	info = " (Obsoleted by " substr(info, 3) ")";
    }

    if (tag ~ numstr) {
	errc(sprintf("%s: RFC %4d%s%s", msg, num, info, comment));
    } else {
	errc(sprintf("%s: RFC %4d (ref. '%s')%s%s", msg, num, tag, info, comment));
    } 
}

function refcomment(msg, num, tag,	info, numstr) {
    numstr = sprintf("%d", num);
    if (numstr in obsoleted) {
	info = obsoleted[numstr];
	gsub(" ", ", RFC ", info);
	info = " (Obsoleted by " substr(info, 3) ")";
    }

    if (tag ~ numstr) {
	comment(sprintf("%s: RFC %4d%s", msg, num, info));	
    } else {
	comment(sprintf("%s: RFC %4d (ref. '%s')%s", msg, num, tag, info));
    } 
}

function get_rfcnum(ref,	num) {
    for (;;) {
	if (match(ref, /(RFC|rfc)[0-9][0-9]+/)){
	    num = substr(ref, RSTART+3, RLENGTH-3) + 0;
	} else if (match(ref, /(RFC|rfc)[ -][0-9][0-9]+/)) {
	    num = substr(ref, RSTART+4, RLENGTH-4) + 0;	
	} else if (match(ref, /(Request [Ff]or Comments) [0-9][0-9]+/)) {
	    num = substr(ref, RSTART+21, RLENGTH-21) + 0;	
	} else if (match(ref, /(Request [Ff]or Comments)( \([^\)]*\))? [0-9][0-9]+/)) {
	    num = substr(ref, RSTART+RLENGTH-4, 4) + 0;	
	} else {
	    if (! num) note( "Unexpected reference format, failed extracting the RFC number: " ref )
	    break;
	}
	if (RSTART+RLENGTH <= 1) break
	ref = substr(ref, RSTART+RLENGTH)
    }
    return num
}

function get_draftname(ref,	draftname) {
#    print "** ref:", ref
    if (match(ref, /draft-[a-zA-Z0-9_-]+-[0-9][0-9][^a-zA-Z0-9_-]/)) {
	draftname = substr(ref, RSTART, RLENGTH-4);
    } else if (match(ref, /draft-[a-zA-Z0-9_-]+-[0-9][0-9]$/)) {
	draftname = substr(ref, RSTART, RLENGTH-3);
    } else if (match(ref, /draft-[a-zA-Z0-9_-]+/)) {
	draftname = substr(ref, RSTART, RLENGTH);
    } else if (match(ref, /I-D\.[a-zA-Z0-9_-]+/)) {
	draftname = "draft-" substr(ref, RSTART+4, RLENGTH-4);
    }
#    print "** doc:", draftname
    return draftname;
}

function get_draftrev(ref,	draftrev) {
    if (match(ref, /draft-[a-zA-Z0-9_-]+-[0-9][0-9]$/)) {
	draftrev = substr(ref, RSTART+RLENGTH-2, 2);
    } else if (match(ref, /draft-[a-zA-Z0-9_-]+-[0-9][0-9][^a-zA-Z0-9_-]/)) {
	draftrev = substr(ref, RSTART+RLENGTH-3, 2);
    } 
    return draftrev;
}

function get_draftcode(draftname, state,	code) {
    if (draftname) {
	if ("deststatus" in state) {
	    status = tolower(state["deststatus"]);
	    if (status != "none") {
		code = status2code[status];
	    }
	}
    }
    return code;
}

function check_downref(tag, warnlist,		rfcnum, draftname, code, status, doc) {

    if (tag in ref_text) ref = ref_text[tag]; else ref = tag;
    gsub(/  +/, " ", ref);

    if (option_verbose > 2) {
	note("Reference text: " ref)
    }

    if (ref ~ /[, ([](draft-|I-D\.)/) {
	draftname = get_draftname(ref);
	draftrev  = get_draftrev(ref);
	doc = draftname;
	get_state(draftname, state);
	if (state["downloaded"] != today) {
	    download_state = " (However, the state information for " draftname " is not up-to-date.  The last update was " state["downloaded"] ")"
	}
	
	## Refactoring needed.  We probably need a check_reference() rather than check_downref(),
	## with the published and current-version checks below being done as part of the general
	## reference checking, rather than inside the downref check.
	if ("rfcnum" in state) {
	    warnc(sprintf("Outdated reference: %s has been published as RFC %s", draftname, state["rfcnum"]));
	} else if (draftrev && ("rev" in state) && (draftrev != state["rev"])) {
	    if (draftrev+0 < state["rev"]+0) {
		warnc(sprintf("Outdated reference: A later version (-%s) exists of %s-%s", state["rev"], draftname, draftrev))
	    } else {
		comment(sprintf("Unexpected draft version: The latest known version of  %s is -%s, but you're referring to -%s.%s", draftname, state["rev"], draftrev, download_state))
	    }
	}
    } else if (ref ~ /(RFC|rfc|Request [Ff]or Comments)( \([^\)]*\)|-)? ?[0-9][0-9]+/) {	
	rfcnum = get_rfcnum(ref);
	doc = "RFC" rfcnum;
    } 
    if (doc && doc in seen_ref && !(seen_ref[doc] in miss_ref) ) {
	comment(sprintf("Duplicate reference: %s, mentioned in '%s', was also mentioned in '%s'.", doc, tag, seen_ref[doc]));
    }
    seen_ref[doc] = tag;

    if (rfcnum) code = substr(rfcstatus, rfcnum, 1);
    if (draftname) code = get_draftcode(draftname, state);
    if (rfcnum && code == "O") {
	if (tag in ref_norm) {
	    referr("Obsolete normative reference", rfcnum, tag);
	} else if (!(tag in ref_def)) {
	    referr("Obsolete undefined reference", rfcnum, tag);
	} else {
	    refcomment("Obsolete informational reference (is this intentional?)", rfcnum, tag);
	}
    } else {
	if (tag in ref_norm) {
	    if (rfcnum) {
		if (code in warnlist) {
		    if ( rfcnum in exception ) {
			referr(sprintf("Downref: Normative reference to an %s RFC", code2status[code]), rfcnum, tag,	\
			       "\n(Excemption has been granted for downref to this document in the past, see https://trac.tools.ietf.org/group/iesg/trac/wiki/DownrefRegistry)");
		    } else {
			referr(sprintf("Downref: Normative reference to an %s RFC", code2status[code]), rfcnum, tag);
		    }
		}
	    } else if ("U" in warnlist) {
		if (ref ~ /[, ([](draft-|I-D\.)/) {
		    if (code) {
			if (code in warnlist) {
			    errc(sprintf("Downref: Normative reference to an %s draft: %s (ref. '%s')", code2status[code], draftname, tag));
			}
		    } else {
			if (state["deststatus"] == "None") {
			    statmsg = " (No intended status found in state file of " draftname ")";
			} else {
			    statmsg = "";
			}
			comment(sprintf("Possible downref: Normative reference to a draft: ref. '%s' %s", tag, statmsg));
		    }
		} else {
		    comment(sprintf("Possible downref: Non-RFC (?) normative reference: ref. '%s'", tag));
		}
	    }
	} else if (!(tag in ref_def)) {
	    # Comment out this -- the warning for a missing reference is enough for a first pass.
# 	    if (rfcnum) {
# 		if (code in warnlist) {
# 		    refcomment(sprintf("Possible downref: Undefined reference to an %s RFC", code2status[code]), rfcnum, tag);
# 		}
# 	    } else if ("U" in warnlist) {
# 		if (ref ~ /[, ([](draft-|I-D\.)/) {
# 		    if (code) {
# 			if (code in warnlist) {
# 			    warnc(sprintf("Possible downref: Undefined reference to an %s draft: %s (ref. '%s')", code2status[code], draftname, tag));
# 			}
# 		    } else {
# 			warnc(sprintf("Possible downref: Undefined reference to a draft: ref. '%s'", tag));
# 			if (state["deststatus"] == "None") {
# 			    note("(No intended status found in state file of " draftname ")");
# 			}
# 		    }
# 	        } else {
# 		    comment(sprintf("Possible downref: Undefined Non-RFC (?) reference : ref. '%s'", tag));
# 		}
# 	    }
	} 	
    }
}

# ----------------------------------------------------------------------
function jdn(date	, part, year, month, day, a, y, m, d) {
    split(date, part, "-")
    year  = part[1]
    month = part[2]; sub(/^0/, "", month)
    day   = part[3]; sub(/^0/, "", day)

    a = int((14-month)/12)
    y = year + 4800 - a
    m = month + 12*a - 3

    d = day + int((153*m+2)/5) +365*y + int(y/4) - int(y/100) + int(y/400) - 32045
    return d
}

# ----------------------------------------------------------------------
function get_creation_date(doc,		state, created, i) {
    get_state(doc, state);
    split("creationdate created submitted", keys);
    for (i in keys) {
	key = keys[i]
	if (key in state) {
	    created = state[key];
	    if (created == "None") created = "";
	    break;
	}
    }
    return created;
}
# ----------------------------------------------------------------------
#   report()
#

function report(filename) {
    if (skip_file) return;
    if (option_pass1) return;
    if (option_status) {
	if ( ! (tolower(option_status) in status2code) ) {
	    note(sprintf("Unexpected value in --status switch ('%s'), ignoring it. (Use one of 'informational', 'experimental', 'bcp', 'ps', 'ds', 'standard'.)", option_status));
	} else {
	    intended_status = tolower(option_status);
	}
    }

    if (got_input) {
        is_rfc = (filename ~ /[Rr][Ff][Cc][0-9]+\.txt$/);

	draftname = full_docname
	sub(/\.[a-z]+$/, "", draftname)
	sub(/-[0-9][0-9]$/, "", draftname)
	get_state(draftname, state, quiet=1)
	destcode = get_draftcode(draftname, state)

	if (option_submitcheck) {
	    printf("\n  Showing Errors (" errmark "), Flaws (" flawmark "), Warnings (" warnmark "), and Comments (" infomark ").\n")
	    print("  Errors MUST be fixed before draft submission.  Flaws SHOULD be fixed before draft submission.");
	}

	announce("Checking boilerplate required by RFC 5378 and the IETF Trust (see https://trustee.ietf.org/license-info):")
	errcount = errors; flawcount = flaws; warncount = warnings; commentcount = comments;

	# 12 Sep 2009 values below are valid during the post-28-dec-2009 transition
	if (has["trust-28-dec-2009_6_a"] ||
	    has["trust-12-sep-2009_6_a"] ||
	    has["trust-28-dec-2009_6_b_i_p2"] ||
	    has["trust-28-dec-2009_6_b_i_p3"] ||
	    has["trust-28-dec-2009_6_b_ii_p3"] ||
	    has["trust-12-sep-2009_6_b_p3"]) {

	    # We have some text from the current Trust Provisions boilerplate.  Check that we don't have older
	    # boilerplate, too.
	        for ( bpname in bp ) {
		    if ( bpname ~ /^rfc2026/ ||
			 bpname ~ /^rfc3667/ ||
			 bpname ~ /^rfc3978/ ||
			 bpname ~ /^rfc3979/ ) {
			if ( has[bpname] && !(bpname ~ "_now")) newandolderr(bpname, "IETF Trust Provisions of 12 Sep 2009")
		    }
		}

	    if ( !is_rfc ) {
		if ( ! (has["trust-28-dec-2009_6_a"] || has["trust-12-sep-2009_6_a"])  ) sectionerr("trust-28-dec-2009_6_a",
				"Notice of Compliance with BCP 78 and BCP 79 according to " \
				"IETF Trust Provisions of 28 Dec 2009, Section 6.a " \
				"or Provisions of 12 Sep 2009, Section 6.a");
	    }
	    if ( ! has["trust-28-dec-2009_6_b_i_p2"] ) sectionerr("trust-28-dec-2009_6_b_i_p2",
				"IETF Trust Provisions of 28 Dec 2009, Section 6.b Copyright Notice");
	    if (full_docname ~ /^draft-ietf/ || destcode in ietf_stream_codes ) {
		if ( ! (has["trust-28-dec-2009_6_b_i_p3"] || has["trust-12-sep-2009_6_b_p3"]) ) {
		    sectionerr("trust-28-dec-2009_6_b_i_p3",
				 "License Notice according IETF Trust Provisions of 28 Dec 2009, Section 6.b.i or Provisions of 12 Sep 2009 Section 6.b");
		    if ( has["trust-28-dec-2009_6_b_ii_p3"] ) comment("It seems you're using the 'non-IETF stream' Licence Notice instead");
		}
	    } else if (full_docname ~ /^draft-(iab|irtf|rfced|rfc-editor)-/ ) {
		if ( ! (has["trust-28-dec-2009_6_b_ii_p3"] || has["trust-12-sep-2009_6_b_p3"]) ) {
		    sectionerr("trust-28-dec-2009_6_b_ii_p3",
				"License Notice according IETF Trust Provisions of 28 Dec 2009, Section 6.b.ii or Provisions of 12 Sep 2009 Section 6.b");
		    if ( has["trust-28-dec-2009_6_b_i_p3"] ) comment("It seems you're using the 'IETF stream' Licence Notice instead");
		}
	    } else {
		if ( ! (has["trust-28-dec-2009_6_b_i_p3"] || has["trust-28-dec-2009_6_b_ii_p3"] || has["trust-12-sep-2009_6_b_p3"]) ) {
		    sectionerr("trust-28-dec-2009_6_b_ii_p3",
				"License Notice according IETF Trust Provisions of 28 Dec 2009, Section 6.b.ii or Provisions of 12 Sep 2009 Section 6.b");
		}
	    }
	    # The following is just an informational note because we've emitted an error earlier:
	    if (has["trust-12-feb-2009_6_b_p3"]) note("(You're using the IETF Trust Provisions' Section 6.b License Notice from " \
						      "12 Feb 2009 rather than one of the newer Notices.  " \
						      "See https://trustee.ietf.org/license-info/.)\n");
	    if (has["trust-12-sep-2009_6_b_p3"]) err("You're using the IETF Trust Provisions' Section 6.b License Notice from " \
						      "12 Sep 2009 rather than the newer Notice from 28 Dec 2009.  " \
						      "(See https://trustee.ietf.org/license-info/)");
	    if (full_docname ~ /^draft-ietf/ || destcode in ietf_stream_codes ) {
		if ( has["trust-28-dec-2009_6_c_i"])
		    { warn("The document has an IETF Trust Provisions of 28 Dec 2009, Section 6.c(i) Publication Limitation clause."); }
		if ( has["trust-28-dec-2009_6_c_ii"])
		    { err("The document has an IETF Trust Provisions, 28 Dec 2009, Section 6.c(ii) Publication Limitation clause."); }
	    } else {
		if ( has["trust-28-dec-2009_6_c_i"])
		    { comment("The document has an IETF Trust Provisions (28 Dec 2009) Section 6.c(i) Publication Limitation clause."); }
		if ( has["trust-28-dec-2009_6_c_ii"])
		    { comment("The document has an IETF Trust Provisions (28 Dec 2009) Section 6.c(ii) Publication " \
			"Limitation clause.  If this document is intended for submission to the IESG for " \
			"publication, this constitutes an error."); }
	    }
	} else if ( (has["rfc3667_5_1"]  ||
	      has["rfc3978_5_1_a"] ||
	      has["rfc3978_5_1"] ||
	      has["rfc3978_5_2b"] ||
	      has["rfc3978_5_3"]  ||
	      has["rfc3978_5_5"]  ||
	      has["rfc3978_5_5_u4748"]  ||
	      has["rfc3979_5_p1"]  ||
	      has["rfc3979_5_p2"]  ||
	      has["rfc3979_5_p3"] ) ) {

	    err("It looks like you\047re using RFC 3978 boilerplate.  You should update this to the " \
		 "boilerplate described in the IETF Trust License Policy document (see " \
		 "https://trustee.ietf.org/license-info), which is required now.")
	    {
		if (has["rfc3667_5_1"]) olderr("rfc3667_5_1");
		if (has["rfc3978_5_1_a"]) olderr("rfc3978_5_1_a");
		if (has["rfc3978_5_1"]) olderr("rfc3978_5_1");
		if (has["rfc3978_5_2b"]) olderr("rfc3978_5_2b");
		if (has["rfc3978_5_3"]) olderr("rfc3978_5_3");
		if (has["rfc3978_5_5"]) olderr("rfc3978_5_5");
		if (has["rfc3978_5_5_u4748"]) olderr("rfc3978_5_5_u4748");
		if (has["rfc3979_5_p1"]) olderr("rfc3979_5_p1");
		if (has["rfc3979_5_p2"]) olderr("rfc3979_5_p2");
		if (has["rfc3979_5_p3"]) olderr("rfc3979_5_p3");
	    }
	    # Ok, so we have some 3978/3979 boilerplate - make sure we don't *also* have some
	    # lingering old boilerplate...
	    {
	       if ( has["rfc3978_5_1"] )  if ( has["rfc3667_5_1"] )	  oldandnewerr("rfc3667_5_1", "rfc3978_5_1");

	       if ( has["rfc3978_5_4_p1"] ) if ( has["rfc2026_10_4C_p1"] ) oldandnewerr("rfc2026_10_4C_p1", "rfc3978_5_4_p1");
	       if ( has["rfc3978_5_4_p1_u4748"] ) if ( has["rfc2026_10_4C_p1"] ) oldandnewerr("rfc2026_10_4C_p1", "rfc3978_5_4_p1_u4748");
	       if ( has["rfc3978_5_4_p1_u4748"] ) if ( has["rfc3978_5_4_p1"] ) oldandnewerr("rfc3978_5_4_p1", "rfc3978_5_4_p1_u4748");
	       if ( has["rfc3978_5_4_p1_u4748_e"] ) if ( has["rfc3978_5_4_p1"] ) oldandnewerr("rfc3978_5_4_p1", "rfc3978_5_4_p1_u4748_e");
	       if ( has["rfc3978_5_4_p1_u4748"] ) if ( has["rfc3978_5_4_p1_e"] ) oldandnewerr("rfc3978_5_4_p1_e", "rfc3978_5_4_p1_u4748");

	       if ( has["rfc3978_5_5"] )  if ( has["rfc2026_10_4C_p4"] ) oldandnewerr("rfc2026_10_4C_p4", "rfc3978_5_5");
	       if ( has["rfc3978_5_5_u4748"] )  if ( has["rfc2026_10_4C_p4"] ) oldandnewerr("rfc2026_10_4C_p4", "rfc3978_5_5_u4748");
	       if ( has["rfc3978_5_5_u4748"] )  if ( has["rfc3978_5_5"] ) oldandnewerr("rfc3978_5_5", "rfc3978_5_5_u4748");

	       if ( has["rfc3979_5_p1"] )  if ( has["rfc2026_10_4A"] )   oldandnewerr("rfc2026_10_4A", "rfc3979_5_p1");
	       if ( has["rfc3979_5_p3"] )  if ( has["rfc2026_10_4B"] )   oldandnewerr("rfc2026_10_4B", "rfc3979_5_p3");
	    }

	    if ( has["rfc2026_lax_claim"] ||
	       has["rfc2026b_lax_claim"] )
				    {
					err("The document claims conformance with section 10 of RFC 2026, but uses " \
					    "some RFC 3978/3979 boilerplate.  As RFC 3978/3979 replaces section 10 of " \
					    "RFC 2026, you should not claim conformance with it if you have changed " \
					    "to using RFC 3978/3979 boilerplate.");
				    }

	    if (!(has["rfc3978_5_1"]) && !is_rfc )
				    {
					sectionerr("rfc3978_5_1", "RFC 3978 Section 5.1 IPR Disclosure Acknowledgement");
				    }
	    if (full_docname ~ /^draft-ietf/ || destcode in ietf_stream_codes ) {
		if ( has["rfc3978_5_2b"] ) { err("The document has an RFC 3978 Section 5.2(b) Derivative Works Limitation clause."); }
		if ( has["rfc3978_5_3"])  { err("The document has an RFC 3978 Section 5.3 Publication Limitation clause."); }
	    } else {
		
		if ( has["rfc3978_5_2b"] ) { comment("The document has an RFC 3978 Section 5.2(b) Derivative Works Limitation clause.  " \
						    "If this document is intended for submission to the IESG for publication, this " \
						    "constitutes an error."); }
		if ( has["rfc3978_5_3"])  { comment("The document has an RFC 3978 Section 5.3 Publication Limitation clause.  " \
						    "If this document is intended for submission to the IESG for publication, this " \
						    "constitutes an error."); }
	    }
	    if (! has["rfc3978_5_4_p1_u4748"])  {
					  if (has["rfc3978_5_4_p1"]) {
					      err("This document has an original RFC 3978 Section 5.4 Copyright Line, " \
						  "instead of the newer IETF Trust Copyright according " \
						  "to RFC 4748.");
					  } else {
					      sectionerr("rfc3978_5_4_p1_u4748", "RFC 3978 Section 5.4 (updated by RFC 4748) Copyright Line");
					  }
				    }
	    if (! has["rfc3978_5_4_p2"]) {
					sectionerr("rfc3978_5_4_p2", "RFC 3978 Section 5.4 Reference to BCP 78");
				      }
	    if (! has["rfc3978_5_5_u4748"])  {
					  if (has["rfc3978_5_5"]) {
					      err("This document has an original RFC 3978 Section 5.5 Disclaimer, " \
						  "instead of the newer disclaimer which includes the IETF Trust according " \
						  "to RFC 4748.");
					  } else {
					      sectionerr("rfc3978_5_5_u4748", "RFC 3978 Section 5.5 (updated by RFC 4748) Disclaimer"); 
					  }
				      }
	    if (! has["rfc3979_5_p1"]) {
					sectionerr("rfc3979_5_p1", "RFC 3979 Section 5, para. 1 IPR Disclosure Acknowledgement");
					if (has["rfc2026_10_4A"]) {
						note("( - It does however have an RFC 2026 Section 10.4(A) Disclaimer.)");
					}
				      }
	    if (! has["rfc3979_5_p2"]) {
					sectionerr("rfc3979_5_p2", "RFC 3979 Section 5, para. 2 IPR Disclosure Acknowledgement");
				      }
	    if (! has["rfc3979_5_p3"]) {
					sectionerr("rfc3979_5_p3", "RFC 3979 Section 5, para. 3 IPR Disclosure Invitation");
					if (has["rfc2026_10_4B"]) {
						note("( - It does however have an RFC 2026 Section 10.4(B) IPR Disclosure Invitation.)");
					}
				    }
	    if ( has["rfc3667_3_claim"] || (has["rfc3667_5_1"] || has["rfc3978_5_1_a"]) && !has["rfc3978_5_1"])	{
					err("The document uses RFC 3667 boilerplate or RFC 3978-like " \
					     "boilerplate instead of verbatim RFC 3978 boilerplate.  After 6 May 2005, " \
					     "submission of drafts without verbatim RFC 3978 boilerplate is not " \
					     "accepted.");

					note("The following non-3978 patterns matched text found in the document.  That text should be removed or replaced:\n")
					if (has["rfc3667_3_claim"]) showsection("rfc3667_3_claim", 2)
					if (has["rfc3667_5_1"]) showsection("rfc3667_5_1", 2)
					if (has["rfc3978_5_1_a"] && !has["rfc3978_5_1"]) showsection("rfc3978_5_1_a", 2)

				    }
	    if ((has["rfc3978_5_4_p1"] && has["rfc3978_5_4_p1_e"] ) ||
		(has["rfc3978_5_4_p1_u4748"] && has["rfc3978_5_4_p1_u4748_e"]))  {
					warn("In addition to a regular copyright notice, the document also has a " \
					      "copyright notice embedded in the text.");
				    }
	} else if (( has["rfc2026_lax_claim"] ||
	      has["rfc2026b_lax_claim"] ||
	      has["rfc2026_10_4C_p2"] ||
	      has["rfc2026_10_4C_p3"] ||
	      has["rfc2026_10_4C_p4"] ) ) {
					if (option_verbose) {
					    print "";
					    if (has["rfc2026_lax_claim"] || has["rfc2026b_lax_claim"])
						note("  [Claims RFC 2026 conformance...]")
					    if (has["rfc2026_10_4C_p1"])
						note("  [Has RFC 2026 Sec. 10.4 para. 1...]")
					    if (has["rfc2026_10_4C_p2"])
						note("  [Has RFC 2026 Sec. 10.4 para. 2...]")
					    if (has["rfc2026_10_4C_p3"])
						note("  [Has RFC 2026 Sec. 10.4 para. 3...]")
					    if (has["rfc2026_10_4C_p4"])
						note("  [Has RFC 2026 Sec. 10.4 para. 4...]")
					    print "";
					}
					err("Looks like you\047re using RFC 2026 boilerplate.  This must be updated to follow RFC 3978/3979, as updated by RFC 4748.");
	} else {
	    if (some_copyright) {
		copyright_msg = "  Found some kind of copyright notice around line " some_copyright " but it does not match any copyright boilerplate known by this tool."
	    } else {
		copyright_msg = ""
	    }
	    err("Cannot find the required boilerplate sections (Copyright, IPR, etc.) in this document." copyright_msg);

	    note("Expected boilerplate is as follows today (" today ") according to https://trustee.ietf.org/license-info :\n")
	    showsection("trust-28-dec-2009_6_a")
	    showsection("trust-28-dec-2009_6_b_i_p2")
	    showsection("trust-28-dec-2009_6_b_i_p3")
	}

	if (errcount == errors && flawcount == flaws && warncount == warnings && commentcount == comments) note("No issues found here.");

	if ( !is_rfc ) {
	    announce("Checking nits according to https://www.ietf.org/id-info/1id-guidelines.txt:");
	    errcount = errors; flawcount = flaws; warncount = warnings; commentcount = comments;

	    if (! has_id_indication) {
		errg("Missing document type: Expected \"INTERNET-DRAFT\" in the upper left hand corner of the first page");
	    }

	    if (! has_expiration) {
		errg("Missing expiration date.  The document expiration date should appear on the first and last page.")
	    }

	    if (!(has["1id_guidelines_p1"] ||
	          has["1id_guidelines_p1a"])) { sectionerr("1id_guidelines_p1", "1id_guidelines paragraph about Internet-Drafts being working documents"); }
	    if (!(has["1id_guidelines_p2"] ||
		  has["1id_guidelines_p2a"])) { sectionerr("1id_guidelines_p2", "1id_guidelines paragraph about 6 months document validity"); }
	    if (!(has["1id_guidelines_p3"] ||
		  has["1id_guidelines_p3a"] ||
		  has["1id_guidelines_p1a"])) { sectionerr("1id_guidelines_p3", "1id_guidelines paragraph about the list of current Internet-Drafts"); }
	    if (!(has["1id_guidelines_p4"] ||
		  has["1id_guidelines_p4a"] ||
		  has["1id_guidelines_p1a"])) { sectionerr("1id_guidelines_p4", "1id_guidelines paragraph about the list of Shadow Directories"); }
	    if (has["1id_guidelines_p1a"] &&
                has["1id_guidelines_p3"])     { newandolderr("1id_guidelines_p3", "1id_guidelines_p1a"); }
	    if (has["1id_guidelines_p1a"] &&
                has["1id_guidelines_p3a"])     { newandolderr("1id_guidelines_p3a", "1id_guidelines_p1a"); }
	    if (has["1id_guidelines_p1a"] &&
                has["1id_guidelines_p4"])     { newandolderr("1id_guidelines_p4", "1id_guidelines_p1a"); }
	    if (has["1id_guidelines_p1a"] &&
                has["1id_guidelines_p4a"])     { newandolderr("1id_guidelines_p4a", "1id_guidelines_p1a"); }

	    if (has_docname) {
		file_docname = option_filename

		# Get rid of path and extension
		sub(".*/", "", file_docname)
		sub(/\.(txt|pdf|xml|doc|nr)$/, "", file_docname)

		sub(".*/", "", full_docname)
		sub(/\.(txt|pdf|xml|doc)$/, "", full_docname)

		if (good_docname != full_docname || full_docname ~ /[^a-z0-9-]/) {
		    errg(sprintf("Bad filename characters: the document name given in the document, '%s', contains other characters than digits, lowercase letters and dash.", full_docname))
		} else if (file_docname ~ /[^a-z0-9-]/) {
		    errg(sprintf("Bad filename characters: the file name of the draft, '%s', contains other characters than digits, lowercase letters and dash.", file_docname))
		}

		if ( ! match(full_docname, /.*-[0-9][0-9]$/) ) {
		    errg(sprintf("Missing revision: the document name given in the document, '%s', does not give the document revision number", full_docname))
		}

		doc_has_empty_source = 0;
		if ( match(full_docname, /^draft--/) ) {
		    doc_has_empty_source = 1;
		    errg(sprintf("Empty name element: the document name given in the document, '%s', lacks the source element (it starts with 'draft--'). See https://www.ietf.org/id-info/1id-guidelines.txt, section 7, on how to construct a good document name.", full_docname))
		} 

		if ( match(full_docname, /--[0-9][0-9]$/) ) {
		    errg(sprintf("Negative revision number: the document name given in the document, '%s', seems to have a negative revision number (it ends with '--%s'). See https://www.ietf.org/id-info/1id-guidelines.txt, section 7, on how to construct a good document name.", full_docname, substr(full_docname, length(full_docname)-1)))
		}

		if ( ! match(full_docname, /^draft-[^-]+-.+-[0-9][0-9](\.[a-z]+)?$/) ) {
		    if (! doc_has_empty_source) {
			flaw(sprintf("Missing draftname component: the document name given in the document, '%s', does not seem to contain all the document name components required ('draft' prefix, document source, document name, and revision) -- see https://www.ietf.org/id-info/guidelines#naming for more information.", full_docname));
		    }
		}

		if (length(file_docname) > length(full_docname)) long_docname = file_docname; else long_docname = full_docname;
		sub(/-[0-9][0-9]$/, "", long_docname);
		if (length(long_docname) > 50) {
		    errg(sprintf("Too long document name: The document name (without revision number), '%s', is %d characters long, but may be at most 50 characters", long_docname, length(long_docname)))
		}

		if (file_docname && file_docname != full_docname) {
		    warn(sprintf("Mismatching filename: the document gives the document name as '%s', but the file name used is '%s'", full_docname, file_docname))
		}

	    } else {
		errg("Expected the document's filename to be given on the first page, but didn't find any");
	    }

	    if (has["rfc3978_5_1"] > firstpagelength && !is_rfc) {
		errg("An " expandname("rfc3978_5_1") " paragraph was found, but not on the first page, as required." );
	    }

	    # According to the nits page we should be comparing to 15 here, but
	    # that may be without the boilerplate - and practice seems to permit
	    # also 16 pages including boilerplate, so...
	    if (!has_toc && ((pagecount > 16) || (FNR > 50*16))) {
		errw("The document is more than 15 pages and seems to lack a Table of Contents.");
	    }

	    if (nonascii && !option_nonascii) {
		warn(sprintf("There %s %d instance%s of lines with non-ascii characters in the document.", \
				  (nonascii==1?"is":"are"), nonascii, (nonascii==1?"":"s") ));
	    }

	    if (length(intended_status) == 0) {
		warn("No 'Intended status' indicated for this document; assuming Proposed Standard");
		intended_status = "proposed standard";
	    }

	    if ( maxpagelength > 58 ) {
		warnc(sprintf("The page length should not exceed 58 lines per page, " \
			     "but there was %d longer page%s, the longest (page %d) being %d lines", longpagecount, (longpagecount==1?"":"s"), longestpage, maxpagelength));
	    }
	    if ( pagecount > formfeedcount+1 ) {
		warnc(sprintf("It seems as if not all pages are separated by form feeds - " \
			     "found %d form feeds but %d page%s", formfeedcount, pagecount, (pagecount==1?"":"s")));
	    }
	    if ( missing_nroff_postprocessing ) {
		comment(sprintf("Found %d instances of the string 'FORMFEED[Page...' -- is this a case of missing nroff postprocessing?", missing_nroff_postprocessing))
	    }

	    if (errcount == errors && flawcount == flaws && warncount == warnings && commentcount == comments) note("No issues found here.");
	}

	if (option_submitcheck) {
	    announce("Running in submission checking mode -- *not* checking nits according to https://www.ietf.org/id-info/checklist .")
	} else {
	    announce("Checking nits according to https://www.ietf.org/id-info/checklist :")
	    errcount = errors; flawcount = flaws; warncount = warnings; commentcount = comments;

	    if (!has_abstract)	{ err("The document seems to lack an Abstract section.", indented_abstract); }
	    if (!has_intro)	{ errc("The document seems to lack an Introduction section.", indented_intro); }
	    if (!has_security)	{ errc("The document seems to lack a Security Considerations section.", indented_security); }
	    if (is_rfc) {
		if (!has_iana)	{ comment("The document seems to lack an IANA Considerations section.  ", indented_iana); }
	    } else {
		if (!has_iana)	{ errc("The document seems to lack an IANA Considerations section.  "	\
				   "(See Section 2.2 of https://www.ietf.org/id-info/checklist for how "	\
				   "to handle the case when there are no actions for IANA.)", indented_iana); }
	    }
	    if (!has_addr)	{ errc("The document seems to lack an Authors\047 Addresses Section."); }
	    if (has_refs && ! has_splitrefs)	{
				    errc("The document seems to lack separate sections for Informative/Normative References.  " \
					"All references will be assumed normative when checking for downward references.");
	    }

	    if (option_list_matches) {
		print fold("  Boilerplate matches:", "  ", columns)
		for ( n in has ) {
		    if (n) note(sprintf("Found", n, "on line", has[n]))
		}
		print "";
	    }

	    if (spacing > 50) {
		errc(sprintf("There %s %d instance%s of weird spacing in the document.  " \
				  "Is it really formatted ragged-right, rather than justified?", \
				  (spacing==1?"is":"are"), spacing, (spacing==1?"":"s") ));
	    }

	    if (longlines) {
		msg = sprintf("There %s %d instance%s of too long lines in the document, " \
				 "the longest one being %d character%s in excess of 72.", \
				  (longlines==1?"is":"are"), longlines, (longlines==1?"":"s"), \
				  excesslength, ((excesslength > 1) ? "s" : ""));
		if (!option_submitcheck) {
		    errc(msg);
		} else {
		    warn(msg);
		}
	    }

	    if (controlchars) {
		errc(sprintf("There %s %d instance%s of lines with control characters in the document.", \
				  (controlchars==1?"is":"are"), controlchars, (controlchars==1?"":"s") ));
	    }

	    if (abstract_references) {
		list(abstract_references, ref_list)
		ref_str = ""
		for (i in ref_list) {
		    if (ref_str) ref_str = ref_str ", ";
		    ref_str = ref_str i;
		}
		errc("The abstract seems to contain references (" ref_str "), which it shouldn't.  Please replace those with straight textual mentions of the documents in question.")
	    }

	    if (badfqdns) {
		warnc(sprintf("There %s %d instance%s of lines with " \
				  "non-RFC2606-compliant FQDNs in the document.", \
				  (controlchars==1?"is":"are"), badfqdns, (badfqdns==1?"":"s") ));
	    }

	    if (badipv4addr) {
		warnc(sprintf("There %s %d instance%s of lines with " \
				  "non-RFC6890-compliant IPv4 addresses in the document.  " \
				  "If these are example addresses, they should be changed.", \
				  (controlchars==1?"is":"are"), badipv4addr, (badipv4addr==1?"":"s") ));
	    }
	    if (mcastipv4addr) {
		warnc(sprintf("There %s %d instance%s of lines with " \
				  "multicast IPv4 addresses in the document.  " \
				  "If these are generic example addresses, they should be changed "	\
				  "to use the 233.252.0.x range defined in RFC 5771", \
				  (controlchars==1?"is":"are"), mcastipv4addr, (mcastipv4addr==1?"":"s") ));
	    }
	    if (privipv4addr) {
		warnc(sprintf("There %s %d instance%s of lines with " \
				  "private range IPv4 addresses in the document.  " \
				  "If these are generic example addresses, they should be changed "	\
				  "to use any of the ranges defined in RFC 6890 (or successor): " \
				  "192.0.2.x, 198.51.100.x or 203.0.113.x.", \
				  (controlchars==1?"is":"are"), privipv4addr, (privipv4addr==1?"":"s") ));
	    }

	    if (badipv6addr) {
		warnc(sprintf("There %s %d instance%s of lines with " \
				  "non-RFC3849-compliant IPv6 addresses in the document.  " \
				  "If these are example addresses, they should be changed.", \
				  (controlchars==1?"is":"are"), badipv6addr, (badipv6addr==1?"":"s") ));
	    }
	    if (ulipv6addr) {
		warnc(sprintf("There %s %d instance%s of lines with " \
				  "Unique Local Unicast IPv6 addresses (RFC 4193) in the document.  " \
				  "If these are generic example addresses, they should be changed", \
				  "to use the 2001:DB8::/32 range defined in RFC 4291.", \
				  (controlchars==1?"is":"are"), ulipv6addr, (ulipv6addr==1?"":"s") ));
	    }
	    if (llipv6addr) {
		warnc(sprintf("There %s %d instance%s of lines with " \
				  "Link Local Unicast IPv6 addresses (RFC 4291) in the document.  " \
				  "If these are generic example addresses, they should be changed", \
				  "to use the 2001:DB8::/32 range defined in RFC 4291.", \
				  (controlchars==1?"is":"are"), llipv6addr, (llipv6addr==1?"":"s") ));
	    }
	    if (ipv4_example_addr && !ipv6_example_addr) {
		comment("The document has examples using IPv4 documentation addresses " \
				  "according to RFC6890, but does not use any IPv6 documentation " \
				  "addresses.  Maybe there should be IPv6 examples, too?");
	    }

	    if ( keywords && !has["rfc2119_p2"] && !has["rfc2119_p2a"] !has["rfc8174_p11"]) {
		# Check whether there is a reference to RFC 2119 at all
		for (reftag in ref_text) {
		    if (ref_text[reftag] ~ /RFC[- ]?2119/) found_2119_ref = ref_def[reftag];
		}
		if ( ! found_2119_ref ) {
		    sectionerr("rfc8174_p11" , "both a reference to RFC 2119 and the recommended RFC 2119 boilerplate, even if it appears to use RFC 2119 keywords", option_checklistwarn)
		    if ( option_verbose <= 1) {
			for (i=1; i<=5; i++) {
			    if (i in rfc2119line) {
				fnr = rfc2119line[i]
				printf("     RFC 2119 keyword, line %d: '...%s...'\n", fnr, line[fnr]);
			    }
			}
			if (keywords > 5)
			    note(sprintf("(%d more instance%s...)",keywords-5, (keywords-5==1?"":"s")));
		    }
		    print("");
		}
	    }

	    if (obsoletes_junk) {
		warnc("The 'Obsoletes: ' line in the draft header should list only the _numbers_ of the RFCs " \
			"which will be obsoleted by this document (if approved); it should not include the word " \
			"'RFC' in the list.");
	    }

	    if (updates_junk) {
		warnc("The 'Updates: ' line in the draft header should list only the _numbers_ of the RFCs " \
			"which will be updated by this document (if approved); it should not include the word " \
			"'RFC' in the list.");
	    }

	    list(abstract_mentions, abstract_rfcs)

	    list(obsoletes_docs, obsoletes_list)
	    list(abstract_obsoletes, abs_obs_list)
	    for (doc in obsoletes_list) {
		if (! (doc in abs_obs_list)) {
		    if (doc in abstract_rfcs) {
			comment("The draft header indicates that this document obsoletes " doc ", but the abstract doesn't seem to directly say this.  It does mention " doc " though, so this could be OK.")
		    } else {
			comment("The draft header indicates that this document obsoletes " doc ", but the abstract doesn't seem to mention this, which it should.")
		    }
		}
	    }
	    for (doc in abs_obs_list) {
		if (! (doc in obsoletes_list)) {
		    comment("The abstract seems to indicate that this document obsoletes " doc ", but the header doesn't have an 'Obsoletes:' line to match this.")
		}
	    }

	    list(updates_docs, updates_list)
	    list(abstract_updates, abs_upd_list)
	    for (doc in updates_list) {
		if (! (doc in abs_upd_list)) {
		    if (doc in abstract_rfcs) {
			comment("The draft header indicates that this document updates " doc ", but the abstract doesn't seem to directly say this.  It does mention " doc " though, so this could be OK.")
		    } else {
			comment("The draft header indicates that this document updates " doc ", but the abstract doesn't seem to mention this, which it should.")
		    }
		}
	    }
	    for (doc in abs_upd_list) {
		if (! (doc in updates_list)) {
		    comment("The abstract seems to indicate that this document updates " doc ", but the header doesn't have an 'Updates:' line to match this.")
		}
	    }


	    if (errcount == errors && flawcount == flaws && warncount == warnings && commentcount == comments) note("No issues found here.");


	    # --------------------------------------------------------------
	    # Warnings

	    {
		announce("Miscellaneous warnings:")
		errcount = errors; flawcount = flaws; warncount = warnings; commentcount = comments;
		warnshere = warnings;

		if (has_pk_mark) { comment("The first octets (the first characters of the first line) of this " \
					   "draft are 'PK', which can make Internet Explorer erroneously think " \
					   "that it is a zip file.  It is recommended that you change this, for " \
					   "instance by inserting a blank line before the line starting with 'PK'.") }

		if (has_bm_mark) { comment("The first octets (the first characters of the first line) of this " \
					   "draft are 'BM', which can make the draft submission tool erroneously think " \
					   "that it is an image .bmp file.  It is recommended that you change this, for " \
					   "instance by inserting a blank line before the line starting with 'BM'.") }

		checkmultibp("rfc3978_5_1");
		checkmultibp("rfc3978_5_2b");
		checkmultibp("rfc3978_5_3");
		checkmultibp("rfc3978_5_5");
		checkmultibp("rfc3978_5_5_u4748");
		checkmultibp("rfc3979_5_p1");
		checkmultibp("rfc3979_5_p2");
		checkmultibp("rfc3979_5_p3");

		if ( ( has["rfc2026_lax_claim"] ||
		       has["rfc2026b_lax_claim"] ) &&

		     ( has["rfc2026_10_4C_p2"] ||
		       has["rfc2026_10_4C_p3"] ||
		       has["rfc2026_10_4C_p4"] ) ) {
			if (option_rfc2026) {

		    if (!has["rfc2026_10_4A"]  )  {	warn("The document seems to lack an RFC 2026 Section 10.4(A) Disclaimer."); }
		    if (!has["rfc2026_10_4B"]  )  {   warn("The document seems to lack an RFC 2026 Section 10.4(B) IPR Disclosure Invitation."); }
		    if ( has["rfc2026_10_4D"])    {   warn("The document has an RFC 2026 Section 10.4(D) IPR Notice."); }

			}
		}
		if (full_docname ~ /^draft-ietf/ || destcode in ietf_stream_codes ) {
		    if ( has["rfc3978_5_2a"])	    {	warn("The document has an RFC 3978 Section 5.2(a) Derivative Works Limitation clause."); }
		} else {
		    if ( has["rfc3978_5_2a"])	    {	note("The document has an RFC 3978 Section 5.2(a) Derivative Works Limitation clause."); }
		}
		if ( has["rfc3978_5_4_p1"] &&
		    !has["rfc3978_5_4_p1_now"])   {	warn("The copyright year in the RFC 3978 Section 5.4 Copyright Line does not match the current year"); }
		if ( has["rfc3978_5_4_p1_u4748"] &&
		    !has["rfc3978_5_4_p1_u4748_now"])   {	warn("The copyright year in the IETF Trust Copyright Line does not match the current year"); }
		if ( has["trust-28-dec-2009_6_b_i_p2"] &&
		    !has["trust-28-dec-2009_6_b_i_p2_now"])   {	warn("The copyright year in the IETF Trust and authors Copyright Line does not match the current year"); }
		if ( has["rfc3978_5_4_p1_old"] &&
		     has["rfc3978_5_4_p1_now"])   {	warn(sprintf("The copyright year in the RFC 3978 Section 5.4 Copyright around line %s does not match the year around line %s",
								   has["rfc3978_5_4_p1_old"], has["rfc3978_5_4_p1_now"]));
						  }
		if ( has["rfc3978_5_4_p1_old"] &&
		     has["rfc3978_5_4_p1_u4748_now"])   {	warn(sprintf("The copyright year in the IETF Trust Copyright around line %s does not match the year around line %s",
								   has["rfc3978_5_4_p1_old"], has["rfc3978_5_4_p1_u4748_now"]));
						  }

    #	  if (has["rfc3978_5_4_p1"] > firstpagelength) {
    #		  warn(  "An " expandname("rfc3978_5_4_p1") " paragraph was found, but not on the first page, as suggested." );
    #	  }
    #
    #	  if (has["rfc3978_5_4_p1_u4748"] > firstpagelength) {
    #		  warn(  "An " expandname("rfc3978_5_4_p1_u4748") " paragraph was found, but not on the first page, as suggested." );
    #	  }
    #

		if (hyphens && !is_rfc) {
		    warnc(sprintf("There %s %d instance%s of lines with hyphenated line breaks in the document.", \
				 (hyphens==1?"is":"are"), hyphens, (hyphens==1?"":"s")));
		}

		if ( misspelled_toc )	{ warnc("The \"Table of Contents\" section title seems to be misspelled."); }

		if ( misspelled_addr ) { warnc("The \"Author\047s Address\" (or \"Authors\047 Addresses\") section title is misspelled."); }

		if (!option_verbose && spacing) {
		    for (i=1; i<=5; i++) {
			if (i in sp_line)
			    warnc(sprintf("Line %d has weird spacing: \047...%s...\047", linenum[i], extract[i]));
		    }
		    if (spacing > 5)
			warnc(sprintf("(%d more instance%s...)",spacing-5, (spacing-5==1?"":"s")));
		}

		if ( keywords && !has["rfc2119_p2"] && !has["rfc2119_p2a"] && !has["rfc8174_p11"] && found_2119_ref ) {
		    sectionwarn("rfc8174_p11", "the recommended RFC 2119 boilerplate, even if it appears to use RFC 2119 keywords")
		    note("(The document does seem to have the reference to RFC 2119 which the ID-Checklist requires).")
		}

		if ( keywords && !has["rfc2119_p2"] && has["rfc2119_p2a"] && !has["rfc8174_p11"]) {
		    # Require that the reference used in "rfc2119_p2a" actually mentions RFC 2119...
		    if ( ref_text[reftag2119] !~ /RFC[- ]?2119/) {
			errg(sprintf("The document contains RFC2119-like boilerplate, but doesn't seem to mention RFC 2119.  The boilerplate contains a reference [%s], but that reference does not seem to mention RFC 2119 either.", reftag2119))
		    }
		}

		if ( has_not_recommended && ( has["rfc2119_p2"] ||  has["rfc2119_p2a"] || has["rfc8174_p11"]) && !rfc2119_p2_not_recommended ) {
		    warnc("The document seems to use 'NOT RECOMMENDED' as an RFC 2119 keyword, but does not include the phrase in its RFC 2119 key words list.");
		}

		if ( !keywords ) {
		    if (has["rfc2119_p2"] || has["rfc2119_p2a"] || has["rfc8174_p11"]) {
			warnc("The document doesn't use any RFC 2119 keywords, yet seems to have RFC 2119 boilerplate text.")
		    } else if (almost_boilerplate["rfc2119_p2"] || almost_boilerplate["rfc2119_p2a"] || almost_boilerplate["rfc8174_p11"]) {
			warnc("The document doesn't use any RFC 2119 keywords, yet has text resembling RFC 2119 boilerplate text.")
		    }
		}

		for (combo in rfc2119undefined) {
		    comment("The exact meaning of the all-uppercase expression '" combo \
		    "' is not defined in RFC 2119.  If it is intended as a requirements " \
		    "expression, it should be rewritten using one of the combinations " \
		    "defined in RFC 2119; otherwise it should not be all-uppercase.")
		}

		for (bad_line in found_bad_pat) {
		    pattern = found_bad_pat[bad_line]
		    para = found_bad_para[bad_line]
		    warn(bad[pattern] "\n\n     Found '" strip(pattern) "' in this paragraph:\n\n    " para)
		}

		if ( ! (intended_status in status2code) ) {
		    warn(sprintf("Unrecognized Status in '%s', assuming Proposed Standard", intended_status_text));
		    note("(Expected one of 'Standards Track', 'Full Standard', 'Draft Standard', 'Proposed Standard', 'Best Current Practice', 'Informational', 'Experimental', 'Informational', 'Historic'.)\n")
		    intended_status = "proposed standard";
		}


		{
		    # The 'RFC 5378 fix' announced around 5 Feb 2009 means that drafts first
		    # submitted before 10 Nov 2008 with a high probability should have an additional
		    # boilerplate paragraph excempting it from full 5378 compliance.  On the other
		    # hand, a draft first submitted on or after 10 Nov 2008 should probably not have
		    # such a paragraph.
		    if (has_docname) docname = full_docname; else docname = file_docname;
		    sub(/-[0-9][0-9](\.txt)?$/, "", docname)
		    get_state(docname, docstate, 1)
		    if ("creationdate" in docstate) created = docstate["creationdate"]
		    if ("created" in docstate) created = docstate["created"]

		    for (i in obsoletes) {
			if (i !~ /draft-/) continue;
			refcreated = get_creation_date("rfc" i);
			if (refcreated && jdn(refcreated) < jdn(created) && jdn(refcreated) < jdn("2008-11-10")) {
			    created = refcreated;
			    refcreated = "";
			    note(sprintf("(Using the creation date from RFC%i, obsoleted by this document, for RFC5378 checks: %s)\n", i, created))
			}
		    }
		    for (i in updates) {
			if (i ~ /draft-/) continue;
			refcreated = get_creation_date("rfc" i);
			if (refcreated && jdn(refcreated) < jdn(created) && jdn(refcreated) < jdn("2008-11-10")) {
			    created = refcreated;
			    refcreated = "";
			    note(sprintf("(Using the creation date from RFC%i, updated by this document, for RFC5378 checks: %s)\n", i, created))
			}
		    }
#			    for (key in docstate) {
#				printf("docstate[%s]: %s\n", key, docstate[key]);
#			    }


		    if (created ~ /[12][0-9][0-9][0-9]-[01][0-9]-[0123][0-9]/) {\
			if (jdn(created) < jdn("2008-11-10")) {
			    if (! has["trust-28-dec-2009_6_c_iii"]) {
				sectioncomment("trust-28-dec-2009_6_c_iii",
					"disclaimer for pre-RFC5378 work, but may have content which was first "	\
					"submitted before 10 "		\
					"November 2008.  If you have contacted all the original authors and they "	\
					"are all willing to grant the BCP78 rights to the IETF Trust, then this "	\
					"is fine, and you can ignore this comment.  If not, you may need to add "	\
					"the pre-RFC5378 disclaimer.  (See the Legal Provisions document at "		\
					"https://trustee.ietf.org/license-info for more information.)")
			    } else {
				comment("The document seems to contain a disclaimer for pre-RFC5378 work, and "		\
					"may have content which was first submitted before 10 November 2008.  "		\
					"The disclaimer is necessary when "\
					"there are original authors that you have been unable to contact, or "		\
					"if some do not wish to grant the BCP78 rights to the IETF Trust.  If "		\
					"you are able to get all authors (current and original) to grant those "	\
					"rights, you can and should remove the disclaimer; otherwise, the "		\
					"disclaimer is needed and you can ignore this comment. "			\
					"(See the Legal Provisions document at https://trustee.ietf.org/license-info "	\
					"for more information.)")
			    }
			} else {	# document created on or after 2008-11-10
			    if (has["trust-28-dec-2009_6_c_iii"]) {
				warn("The document seems to contain a disclaimer for pre-RFC5378 work, but was "		\
					"first submitted on or after 10 November 2008.  The disclaimer is usually "		\
					"necessary only for documents that revise or obsolete older RFCs, and "		\
					"that take significant amounts of text from those RFCs.  If you can "		\
					"contact all authors of the source material and they are willing to "		\
					"grant the BCP78 rights to the IETF Trust, you can and should remove "		\
					"the disclaimer.  Otherwise, the disclaimer is needed and you can "		\
					"ignore this comment.  (See the Legal Provisions document at "			\
					"https://trustee.ietf.org/license-info for more information.)")
			    } else {
				;	# No comment for documents created after 2008-11-10 without a disclaimer
			    }
			}
		    } else {
			warn("Couldn't figure out when the document was first submitted -- there may comments "		\
			     "or warnings related to the use of a disclaimer for pre-RFC5378 work that could "		\
			     "not be issued because of this.  Please check the Legal Provisions document at "		\
			     "https://trustee.ietf.org/license-info to determine if you need the pre-RFC5378 "		\
			     "disclaimer.");

		    }
		}

		{
		    if (doc_date) {
			days_off = doc_date - jdn(today)
			if (days_off > date_skew)
			    comment(sprintf("The document date (%s) is %s days in the future.  Is this intentional?", date_lit, days_off));
			if (days_off < -date_skew)
			    comment(sprintf("The document date (%s) is %s days in the past.  Is this intentional?", date_lit, -days_off));
		    } else {
			comment("Couldn't find a document date in the document -- date freshness check skipped.")
		    }
		}


		if (code_comment_found) {
		    comment("Found something which looks like a code comment -- if you have code sections in the document, please surround them with '<CODE BEGINS>' and '<CODE ENDS>' lines.");
		}


		if (errcount == errors && flawcount == flaws && warncount == warnings && commentcount == comments) note("No issues found here.");
	    }
	    {
		announce("Checking references for intended status: " (code2status[status2code[intended_status]] ? code2status[status2code[intended_status]] : "None"))
		errcount = errors; flawcount = flaws; warncount = warnings; commentcount = comments;
		warnshere = warnings;

		intended_code = status2code[intended_status];
		split(warncodes[intended_code], warnlist);
		for (i in warnlist) warnlist[warnlist[i]];

		std_codes["B"];std_codes["P"];std_codes["D"];std_codes["S"];
		if (intended_code in std_codes) {
		    note("(See RFCs 3967 and 4897 for information about using normative references to lower-maturity documents in RFCs)\n")
		}

		for ( i = 1; i <= ref_use_count; i++ ) {
		    tag = ref_use_list[i]
		    if (! (tag in ref_def)) {
			is_numeric = tag ~ /^[0-9]+$/
			if ((is_numeric && has_numeric_refs) || (!is_numeric && has_symbolic_refs)) {
			    warnc(sprintf("Missing Reference: '%s' is mentioned on line %s, but not defined", tag, ref_use[tag])	\
				    (option_verbose ? sprintf("\n'%s...'", strip(substr(line[ref_use[tag]],1,68))) : ""));
			    miss_ref[tag];
			    check_downref(tag, warnlist);
			} else if (has_numeric_refs || has_symbolic_refs) {
			    comment(sprintf("Looks like a reference, but probably isn't: '%s' on line %s", tag, ref_use[tag])	\
				    (option_verbose ? sprintf("\n'%s...'", strip(substr(line[ref_use[tag]],1,68))) : ""));
			} else {
			    comment(sprintf("Missing reference section? '%s' on line %s looks like a reference", tag, ref_use[tag])	\
				    (option_verbose ? sprintf("\n'%s...'", strip(substr(line[ref_use[tag]],1,68))) : ""));
			}

		    }
		}

		for ( i = 1; i <= ref_def_count; i++ ) {
		    tag = ref_def_list[i];
		    if (! (tag in ref_use)) {
			warnc(sprintf("Unused Reference: '%s' is defined on line %s, but no explicit reference was found in the text", tag, ref_def[tag])	\
			    (option_verbose ? sprintf("\n'%s...'", strip(substr(line[ref_def[tag]],1,68))) : ""));
		    }
		}

		for ( i = 1; i <= ref_def_count; i++ ) {
		    tag = ref_def_list[i];
		    check_downref(tag, warnlist);
		}
		if (errcount == errors && flawcount == flaws && warncount == warnings && commentcount == comments) note("No issues found here.");
	    }
	}

	# --------------------------------------------------------------
	# Summary

	if (errors == 0 && warnings == 0 && comments == 0) {
	    print "";
	    note("No nits found.");
	} else {
	    print "";
	    note(sprintf("Summary: %d error%s (%s), %d flaw%s (%s), %d warning%s (%s), %d comment%s (%s).", errors, (errors==1?"":"s"), errmark, flaws, (flaws==1?"":"s"), flawmark, warnings, (warnings==1?"":"s"), warnmark, comments, (comments==1?"":"s"), infomark));
	    if ( !option_verbose && !option_submitcheck ) {
	        print "";
		note("Run idnits with the --verbose option for more detailed information about the items above.");
	    }
	}
    }
}


function check_line() {
    got_input = 1;

    gsub(/\r/,"");
    sub(/[\n\t ]+$/, "");
    thiscolumn = match($0, /[^ ]/);
    if (thiscolumn && thiscolumn < indentation) indentation = thiscolumn;

    # 1.1.a	Max column 72
    if (length($0) > 72 && ! skip_file) {
	if (match($0, "[^\001-\177]")) {
	   printf("%s(%d): Line appears to be too long, but this could be caused by non-ascii characters in UTF-8 encoding\n", FILENAME, FNR);
	} else { 
	    if (option_verbose && option_pass1) {
		printf("%s(%d): Line is too long: the offending characters are \047%s\047\n", FILENAME, FNR, substr($0,73));
	    }
	    longlines++;
	    if (length($0) - 72 > excesslength) {
		excesslength = length($0) - 72;
	    }
	}
    }

    # 1.1.b	Ragged right
    if (! skip_file  && ! match($0, /^ *Internet.Draft/)  && ! match($0, /^ *INTERNET.DRAFT/) && match($0, /[A-Za-z][a-z]   ? ?[a-z]/) ) {
	if (option_verbose && option_pass1 ) {
	    printf("%s(%d): Line has weird spacing: \047...%s...\047\n", FILENAME, FNR, substr($0, RSTART-5, 14));
	}
	spacing++;
	sp_line[spacing] = $0;
	linenum[spacing] = FNR;
	extract[spacing] = substr($0, RSTART-5, 14)

    }

    # 1.1.c	No hyphenation for line-breaks
    if ( $0 ~ /[A-Za-z0-9_]-$/ && ! skip_file && ! has_refs && has_hyphenlist ) {
	word = $0
	sub(/^.*[^A-Za-z0-9_-]/, "", word)
	if ( ! word in hyphenfrags ) {
	    if (option_verbose && option_pass1) {
		printf("%s(%d): Line seems to end with a hyphenated word.\n", FILENAME, FNR)
		if (option_verbose > 1 && option_pass1) {
		    printf("  --> %s\n", $0);
		}
	    }
	   hyphens++
	}
    }

    # 1.1.e	ASCII-only
    # The test really should go from \200 to \377 - but the awk implementation
    # on *BSD seems to have used \377 internally as a sentinel or something...
    if (match($0, "[^\001-\177]") && ! skip_file) {
	if (option_verbose && option_pass1) {
	    printf("%s(%d): Found non-ascii character (%s) in position %d.\n", FILENAME, FNR, substr($0, RSTART,1), RSTART);
	    if (option_verbose > 1 && option_pass1) {
		printf("  --> %s\n", $0);
		printf("      %*s\n", RSTART, "^");
	    }
	}
	nonascii++;
    }
    # 1.1.e	no control characer except CR NL FF
    #  nawk can't handle octal escapes inside character classes, it seems.  Sigh.
    # if (match($0, /[\001-\011\013\016-\037]/) && ! skip_file) ...
    if (match($0, /(\001|\002|\003|\004|\005|\006|\007|\010|\011|\013|\016|\017|\020|\021|\022|\023|\024|\025|\026|\027|\030|\031|\032|\033|\034|\035|\036|\037)/) && ! skip_file) {
	if (option_verbose && option_pass1) {
	    printf("%s(%d): Found control character %s in position %d.\n", FILENAME, FNR, controlchar[substr($0, RSTART, 1)], RSTART);
	    if (option_verbose > 1 && option_pass1) {
	        printf("  --> %s\n", $0);
	        printf("      %*s\n", RSTART, "^");
	    }
	}
	controlchars++;
    }

    # 1.1.f	Do not number the Abstract section
    if ( $0 ~ /^[ \t]*Abstract[ \t]*$/ ) {
	abstract_seen = 1;
    }
    if ( $0 ~ /^[ \t]*[0-9][.0-9 \t]*Abstract[ \t]*$/ && ( ! abstract_seen ) && ! skip_file && option_pass2) {
	errg("The Abstract section seems to be numbered");
	abstract_seen = 1;
    }

    # 1.1.f	Do not number the "Status of Memo" section
    if ( $0 ~ /^[ \t]*Status of (This )?Memo[ \t]*$/ ) {
	has_status_of_memo = 1;
    }
    if ( $0 ~ /^[ \t]*[0-9][.0-9 \t]*Status of (This )?Memo[ \t]*$/ && ( ! has_status_of_memo ) && ! skip_file && option_pass2) {
	errg("The Status of Memo section seems to be numbered");
	has_status_of_memo = 1;
    }

    # RFC 2119 language    
    if (!match($0, "IN NO EVENT SHALL") && !match($0,"SHALL THE COPYRIGHT")			\
	&& match($0, /[ \t](MUST|REQUIRED|SHALL|SHOULD|RECOMMENDED|MAY|OPTIONAL)([ \t\.,]|$)/ )	\
        ) {

	keywords++;
	rfc2119line[keywords] = FNR;
	rstart = RSTART-16;
	rlength = 38;
	if (rstart+rlength > length($0)) rstart = length($0) - rlength +1;
	if (rstart < 1) rstart = 1;
	if ( ! (FNR in line) ) line[FNR] = substr($0, rstart, rlength);
	if (option_verbose > 1 && option_pass1) {
	    printf("%s(%d): RFC 2119 keyword: %s.\n", FILENAME, FNR, $0);
	}

	# Keywords:
	#
	#	MUST		MUST NOT
	#	SHALL		SHALL NOT
	#	SHOULD		SHOULD NOT
	#	RECOMMENDED	NOT RECOMMENDED
	#	REQUIRED
	#	MAY
	#	OPTIONAL

	# Find uppercase words before or after our keywords, and check if the combination is defined
	l = $0
	sub(/\t/, " ", l)
	notkwds["MAY NOT"]; notkwds["NOT REQUIRED"]; notkwds["NOT OPTIONAL"]; 
	for (combo in notkwds) {
	    if (l ~ combo) {
		rfc2119undefined[combo]
		if (option_verbose > 1 && option_pass1) {
		    printf("%s(%d): Undefined keyword combination: '%s'.\n", FILENAME, FNR, combo)
		}
	    }
	}
    }

    # ID-Checklist section 3.6: Example domain names
    if ( has_intro && !has_refs && (match($0, /[ \t]([A-Z0-9_-]+\.)+[A-Z0-9_-]+\.[A-Z]+/) \
				 || match($0, /[ \t]([a-z0-9_-]+\.)+[a-z0-9_-]+\.[a-z]+/) ) ) {
	FQDN = substr($0, RSTART+1, RLENGTH-1)
	FQDN1 = substr($0, RSTART+1, RLENGTH)
	addrstart = RSTART
	fqdn = tolower(FQDN)
	fqdn1= tolower(FQDN1)
	
	if (  fqdn !~ /([a-z0-9_-]+\.)+example(.(com|org|net))?$/	\
	      && fqdn !~ /([a-z0-9_-]+\.)+(urn|uri|in-addr)\.arpa?$/	\
	      && fqdn !~ /www.ietf.org/					\
	      && fqdn !~ /[0-9]+\.[0-9]+\./				\
	      && fqdn !~ /.\..\../ 					\
	      && fqdn1!~ /.*@$/ ) {
	    if (option_verbose && option_pass1) {
		printf("%s(%d): Found possible FQDN '%s' in position %d; this doesn't match " \
			"RFC 2606's suggested \".example\" or \".example.(com|org|net)\".\n", \
			FILENAME, FNR, FQDN, addrstart)
		if (option_verbose > 1 && option_pass1) {
		    printf("  --> %s\n", $0);
		    printf("      %*s\n", addrstart, "^");
		}
	    }
	    badfqdns++;
	}
    }
    # ID-Checklist section 3.6 A: Example numeric IPv4 addresses
    if ( has_intro && !in_refs && match($0, /[ \t][0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)+/)) {
	IPv4addr = substr($0, RSTART+1, RLENGTH-1);

	if (IPv4addr ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/) {
	    addrstart = RSTART+1;
	    split(IPv4addr, part);
	    for (i=1; i <= 4; i++) if (part[i]+0 > bigpart) bigpart = part[i]+0;

	    if (IPv4addr ~ /^192\.0\.2\.[0-9]+$/) {
		ipv4_example_addr++;
	    }


	    if (IPv4addr !~ /^192\.0\.2\.[0-9]+$/					\
		    && IPv4addr !~ /198\.51\.100\.[0-9]+$/				\
		    && IPv4addr !~ /203\.0\.113\.[0-9]+$/				\
		    && IPv4addr !~ /233\.252\.0\.[0-9]+$/				\
		    && IPv4addr !~ /^0\.0\.0\.0$/					\
		    && IPv4addr !~ /^255\.255\.255\.255$/				\
		    && !(IPv4addr in sections)						\
		    && bigpart < 256							\
		    && !match($0, "([Ss]ections?|[Cc]lause)[ \t]" IPv4addr)) {
		if (option_verbose && option_pass1) {
		    printf("%s(%d): Found possible IPv4 address '%s' in position %d; this doesn't match " \
			    "the suggested documentation address ranges specified in RFC 6890 (or successor): " \
			    "blocks 192.0.2.0/24 (TEST-NET-1), 198.51.100.0/24 " \
			    "(TEST-NET-2), and 203.0.113.0/24 (TEST-NET-3); " \
			    "or the 233.252.0.0/24 (MCAST-TEST-NET) example multicast " \
			    "address range specified in RFC 5771.\n", \
			    FILENAME, FNR, IPv4addr, addrstart)
		    if (option_verbose > 1 && option_pass1) {
			printf("  --> %s\n", $0);
			printf("      %*s\n", addrstart, "^");
		    }
		}
		if (IPv4addr ~ /^192\.168\.[0-9]+\.[0-9]+$/				\
		    || IPv4addr ~ /^10\.[0-9]\.[0-9]+\.[0-9]+$/				\
		    || IPv4addr ~ /^172\.(1[6-9]|2[0-9]|3[01])\.[0-9]+\.[0-9]+$/) {
		    privipv4addr++;
		} else if (IPv4addr ~ /^2(2[4-9]|3[0-9])\.[0-9]+\.[0-9]+\.[0-9]+$/	\
		    && IPv4addr !~ /233\.252\.0\.[0-9]+$/) {
		    mcastipv4addr++;
		} else {
		    badipv4addr++;
		} 
	    }
	}
    }

}


function is_ipv6(addr) {
    # We have to match either 8 parts separated by colons or fewer parts with one embedded '::' or fewer parts ending in '::':
    if (   addr ~ /^[0-9A-Fa-f]+:[0-9A-Fa-f]+:[0-9A-Fa-f]+:[0-9A-Fa-f]+:[0-9A-Fa-f]+:[0-9A-Fa-f]+:[0-9A-Fa-f]+:[0-9A-Fa-f]+$/ \
	|| addr ~ /([0-9A-Fa-f]+:)([0-9A-Fa-f]+:)?([0-9A-Fa-f]+:)?([0-9A-Fa-f]+:)?([0-9A-Fa-f]+:)?([0-9A-Fa-f]+:)?([0-9A-Fa-f]+:)?:$/ \
	|| addr ~ /([0-9A-Fa-f]+:)([0-9A-Fa-f]+:)?([0-9A-Fa-f]+:)?([0-9A-Fa-f]+:)?([0-9A-Fa-f]+:)?([0-9A-Fa-f]+:)?(:[0-9A-Fa-f]+)(:[0-9A-Fa-f]+)?(:[0-9A-Fa-f]+)?(:[0-9A-Fa-f]+)?(:[0-9A-Fa-f]+)?(:[0-9A-Fa-f]+)?$/ ) {
	return 1;
    } else {
	return 0;
    }
}

function check_para(para) {
    # Checking various things that need to be checked after assembling a full paragraph;
    # in particular, word combinations can't be reliably checked line-by-line as they
    # may be split across a line break.
    for (pattern in bad) {
	xpat = "[^-A-Za-z0-9_]" pattern "[^-A-Za-z0-9_]"
	if (para ~ xpat) {
	    found_bad_pat[FNR] = pattern
	    found_bad_para[FNR] = para
	}
    }

    if (match(para, /NOT RECOMMENDED/)) { has_not_recommended++; }

    # ID-Checklist section 3.6 B: Example numeric IPv6 addresses
    if ( has_intro && !in_refs && match(para, /[ \t\[][0-9A-Fa-f]+:[0-9A-Fa-f]+(:[0-9A-Fa-f]*)+(EUI64)?/)) {
	if (para ~ /.+EUI64/) {
	    IPv6addr = substr(para, RSTART+1, RLENGTH-6) "0"
	} else {
	    IPv6addr = substr(para, RSTART+1, RLENGTH-1)
	}
	addrstart = RSTART+1

	if (IPv6addr ~ /^2001:0?[Dd][Bb]8(:[0-9A-Fa-f]*)+$/) {
	    if (is_ipv6(IPv6addr)) {
		ipv6_example_addr++;
	    }
	} else {
	    if (is_ipv6(IPv6addr)) {
		if (option_verbose && option_pass1) {
		    printf("%s(%d): Found possible IPv6 address '%s' in position %d in the paragraph; this doesn't match " \
			    "RFC 3849's suggested 2001:DB8::/32 address range or RFC 4193's Unique Local Address range FC00::/7.\n", \
			    FILENAME, FNR, IPv6addr, addrstart)
		    if (option_verbose > 1 && option_pass1) {
			printf("  --> %s\n", para);
			printf("      %*s\n", addrstart, "^");
		    }
		}
		if (IPv6addr ~ /^[Ff][CcDd][0-9A-Fa-f]?[0-9A-Fa-f]?(:[0-9A-Fa-f]*)+$/) {
		    ulpv6addr++;
		} else if (IPv6addr ~ /^[Ff][Ee][89ABab][0-9A-Fa-f](:[0-9A-Fa-f]*)+$/) {
		    lldipv6addr++;
		} else {
		    badipv6addr++;	
		}

		# TODO:  Permit IPv6 addresses with embedded IPv4 example addresses.  The prefixes which
		# may have such are (from David Thaler 2010-08-20 19:45 +0000):
		#
		# The list of relevant IPv4-embedded IPv6 address formats I'm currently aware of is:
		# 
		# 6to4 (RFC 3056 section 2): 
		# 2002:<4 bytes of IPv4 address>::/48
		# 
		# Teredo (RFC 4380 section 4):
		# 2001:0:<4 bytes of IPv4 address>::/64
		# 
		# IPv6/IPv4 Translation Address (https://tools.ietf.org/html/draft-ietf-behave-address-format-10#section-2.2 table 2):
		# 64:ff9b::<4 bytes of IPv4 address>
		# 
		# IPv4-Compatible IPv6 Address (RFC 4291 section 2.5.5.1):
		# ::<4 bytes of IPv4 address>
		# 
		# IPv4-Mapped IPv6 Address (RFC 4291 section 2.5.5.2):
		# ::FFFF:<4 bytes of IPv4 address>
		# 
		# IPv4-Translatable (aka IPv4-Translated) IPv6 Address (RFC 2765 section 2.1):
		# ::FFFF:0:<4 bytes of IPv4 address>
		# 
		# And two other relevant cases of *multicast* address spaces with well-known prefixes
		# and embedded IPv4 or IPv6 *unicast* address prefixes:
		# 
		# Unicast-Prefix-Based IPv6 Multicast Address (RFC 3306 section 4):
		# FF3x:<prefix len>:<IPv6 prefix>::
		# So since 2001:DB8::/32 is a doc prefix, then
		# FF3x:20:2001:DB8::/64 would be a multicast range for documentation.
		# And by changing the "20" hex to anything larger is also ok of course.
		# 
		# Embedded RP (RFC 3956) section 3 modifies the above to also allow FF7x... in addition to FF3x...
		# 
		# Unicast-Prefix-Based IPv4 Multicast Address (https://tools.ietf.org/html/draft-ietf-mboned-ipv4-uni-based-mcast-06 section 3, in RFC editors queue):
		# <TBD>.<IPv4 prefix>
		# Where it's still waiting for IANA to assign the TBD byte out of the multicast range (so stay tuned).
		# So the multicast addresss 
		# TBD.192.0.2
		# TBD.198.51.100
		# TBD.203.0.113
		# would automatically be for documentation purposes once TBD is assigned.
	    }
	}

	# TODO: Warn for violations of RFC 5952:
	# 
	# RFC 5952                IPv6 Text Representation             August 2010
	# 
	# 4. A Recommendation for IPv6 Text Representation
	# 
	#    A recommendation for a canonical text representation format of IPv6
	#    addresses is presented in this section.  The recommendation in this
	#    document is one that complies fully with [RFC4291], is implemented by
	#    various operating systems, and is human friendly.  The recommendation
	#    in this section SHOULD be followed by systems when generating an
	#    address to be represented as text, but all implementations MUST
	#    accept and be able to handle any legitimate [RFC4291] format.  It is
	#    advised that humans also follow these recommendations when spelling
	#    an address.
	# 
	# 4.1. Handling Leading Zeros in a 16-Bit Field
	# 
	#    Leading zeros MUST be suppressed.  For example, 2001:0db8::0001 is
	#    not acceptable and must be represented as 2001:db8::1.  A single 16-
	#    bit 0000 field MUST be represented as 0.
	# 
	# 4.2. "::" Usage
	# 
	# 4.2.1. Shorten as Much as Possible
	# 
	#    The use of the symbol "::" MUST be used to its maximum capability.
	#    For example, 2001:db8:0:0:0:0:2:1 must be shortened to 2001:db8::2:1.
	#    Likewise, 2001:db8::0:1 is not acceptable, because the symbol "::"
	#    could have been used to produce a shorter representation 2001:db8::1.
	# 
	# 4.2.2. Handling One 16-Bit 0 Field
	# 
	#    The symbol "::" MUST NOT be used to shorten just one 16-bit 0 field.
	#    For example, the representation 2001:db8:0:1:1:1:1:1 is correct, but
	#    2001:db8::1:1:1:1:1 is not correct.
	# 
	# 4.2.3. Choice in Placement of "::"
	# 
	#    When there is an alternative choice in the placement of a "::", the
	#    longest run of consecutive 16-bit 0 fields MUST be shortened (i.e.,
	#    the sequence with three consecutive zero fields is shortened in 2001:
	#    0:0:1:0:0:0:1).  When the length of the consecutive 16-bit 0 fields
	#    are equal (i.e., 2001:db8:0:0:1:0:0:1), the first sequence of zero
	#    bits MUST be shortened.  For example, 2001:db8::1:0:0:1 is correct
	#    representation.
	# 
	# 4.3. Lowercase
	# 
	#    The characters "a", "b", "c", "d", "e", and "f" in an IPv6 address
	#    MUST be represented in lowercase.
    }

    if (in_abstract && para !~ /^[ \t]*Abstract[ \t]*$/ ) {
	lcpara = tolower(para)

	if (match(lcpara, /obsoletes ((\[?rfcs? ?)?[0-9]+\]?(, | and )?)+/))  {
	    findall(lcpara, "obsoletes ((\\[?rfcs? ?)?[0-9]+\\]?(, )?)+", lst)
	    for (i in lst) {
		txt = lst[i]
		findall(txt, "(rfc ?)?[0-9]+", rfcs)
		for (j in rfcs) {
		    r = rfcs[j]
		    if (! (r ~ /rfc/)) r = "rfc" r
		    sub(/ /, "", r); sub(/rfc/, "RFC", r)
		    abstract_obsoletes = abstract_obsoletes " "  r
		}
	    }
	}

	# Accept 'replaces' as a synonym of 'obsoletes' in the abstract text
	if (match(lcpara, /replaces ((\[?rfcs? ?)?[0-9]+\]?(, | and )?)+/))  {
	    findall(lcpara, "replaces ((\\[?rfcs? ?)?[0-9]+\\]?(, )?)+", lst)
	    for (i in lst) {
		txt = lst[i]
		findall(txt, "(rfc ?)?[0-9]+", rfcs)
		for (j in rfcs) {
		    r = rfcs[j]
		    if (! (r ~ /rfc/)) r = "rfc" r
		    sub(/ /, "", r); sub(/rfc/, "RFC", r)
		    abstract_obsoletes = abstract_obsoletes " "  r
		}
	    }
	}

	if (match(lcpara, /updates ((\[?rfcs? ?)?[0-9]+\]?(, | and )?)+/))  {
	    findall(lcpara, "updates ((\\[?rfcs? ?)?[0-9]+\\]?(, | and )?)+", lst)
	    for (i in lst) {
		txt = lst[i]
		findall(txt, "(rfc ?)?[0-9]+", rfcs)
		for (j in rfcs) {
		    r = rfcs[j]
		    if (! (r ~ /rfc/)) r = "rfc" r
		    sub(/ /, "", r); sub(/rfc/, "RFC", r)
		    abstract_updates = abstract_updates " " r
		}
	    }
	}

	if (match(lcpara, /rfc ?[0-9]+/))  {
	    findall(lcpara, "rfc ?[0-9]+", rfcs)		
	    for (j in rfcs) {
		r = rfcs[j]
		sub(/ /, "", r); sub(/rfc/, "RFC", r)
		abstract_mentions = abstract_mentions " " r
	    }
	}

	if (match(para, reference_format))  {
	    findall(para, reference_format, refs)
	    for (j in refs) {
		r = refs[j]
		sub(/ /, "", r); sub(/rfc/, "RFC", r)
		if ( tolower(r) !~ /\[page ?[0-9]+\]/) {
		    abstract_references = abstract_references " " r;
		}
	    }
	}
    }
}


# ----------------------------------------------------------------------
#  Pattern / actions:
#

/^This Internet-Draft(, draft-.*)? has been deleted./ {
    if (option_pass2) printf "  Skipping this file; it looks like a tombstone file to me.\n";
    skip_file = 1;
}

/^This +document has been replaced by/ {
    if (option_pass2) printf "  Skipping this file; it looks like a tombstone file to me.\n";
    skip_file = 1;
}

/^This Internet-Draft(, draft-.*)? was published as/ {
    if (option_pass2) printf "  Skipping this file; it looks like a tombstone file to me.\n";
    skip_file = 1;
}

/^A new Request for Comments is now available/ {
    if (option_pass2) printf "  Skipping this file; it looks like a tombstone file to me.\n";
    skip_file = 1;
}

/^RFC [0-9]+ was never issued./ {
    if (option_pass2) printf "  Skipping this file; it looks like a tombstone file to me.\n";
    skip_file = 1;
}

/.*/				{ check_line() }

# Pagecount
/-\+- Pagecount: [0-9]+ -\+-/	  { pagecount = $3+0; next  }
/-\+- Firstpagelength: [0-9]+ -\+-/ { firstpagelength = $3+0; next  }
/-\+- Maxpagelength: [0-9]+ -\+-/ { maxpagelength = $3+0; next  }
/-\+- Longestpage: [0-9]+ -\+-/	  { longestpage = $3+0; next  }
/-\+- Longpagecount: [0-9]+ -\+-/ { longpagecount = $3+0; next  }
/-\+- Formfeedcount: [0-9]+ -\+-/ { formfeedcount = $3+0; next  }
/-\+- Expiration: [0-9]+ -\+-/    { has_expiration = $3+0; next  }
/-\+- NoNroffPostproc: [0-9]+ -\+-/ { missing_nroff_postprocessing = $3+0; next  }


# If the draft begins with 'PK' it some software will think it's a zip file.  Mention this.
/^PK/ && FNR==1			  { has_pk_mark = 1; }
/^BM/ && FNR==1			  { has_bm_mark = 1; }

# Check for required sections
# Nawk doesn't understand IGNORECASE = 0, so we have to explicitly convert.
# Also normalise the indentation
/\. ?\. ?\. ?\. ?\. *[0-9]+$/					{   if (!has_intro) { has_toc = 1; in_toc = 1; }; next; }
								{
								    normalized = tolower($0);
								    if (indentation) normalized = substr(normalized, indentation);
								    #if (option_verbose > 2 && option_pass1) printf "  normalized: '%s'\n", normalized;
								}
/::= +BEGIN *$/						{   in_mib = 1 }
/^ +END *$/						{   in_mib = 0 }

normalized ~ /^internet[ -]draft/				{   if (FNR < 15) has_id_indication = 1; else has_late_id_indication = 1; }

normalized ~ /^expires:?.*20[0-9][0-9]/				{   has_expiration = FNR; }

normalized ~ /[12][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]$/	{
								    match($0, /[12][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]$/)
								    datestr = substr($0, RSTART, RLENGTH)
								    if ((FNR < 30) && ($0 !~ "(Expires|Expiration [Dd]ate): *" datestr)) {
									split(datestr, part, /-/)

									m = part[2]
									d = part[3]
									y = part[1]
									isodate = sprintf("%s-%s-%s", y, m, d)
									new_date = jdn(isodate)
									if (new_date > doc_date) {
									    doc_date = new_date
									    date_lit = datestr
									    iso_date = isodate
									    date_skew = 3
									}
								    }
								}
normalized ~ /[adfjmnos][a-z]+ [0-9]+, [12][0-9][0-9][0-9]$/	{
								    match($0, /[ADFJMNOS][a-z]+ [0-9]+, [12][0-9][0-9][0-9]$/)
								    datestr = substr($0, RSTART, RLENGTH)
								    if ((FNR < 30) && ($0 !~ "(Expires|Expiration [Dd]ate): *" datestr)) {
									split(datestr, part, /[ ,]+/)

									m = month[part[1]]
									d = part[2]
									y = part[3]
									isodate = sprintf("%s-%s-%s", y, m, d)
									new_date = jdn(isodate)
									if (new_date > doc_date) {
									    doc_date = new_date
									    date_lit = datestr
									    iso_date = isodate
									    date_skew = 3
									}
								    }
								}
normalized ~ /([0-9]+ )?[adfjmnos][a-z]+ [12][0-9][0-9][0-9]$/		{
                                                                    is_docdate = 0
                                                                    if (match($0, /[0-9]+ [ADFJMNOS][a-z]+ [12][0-9][0-9][0-9]$/)) {
                                                                        datestr = substr($0, RSTART, RLENGTH)
                                                                        if ((FNR < 30) && ($0 !~ "(Expires|Expiration [Dd]ate): *" datestr)) {
                                                                            is_docdate = 1
                                                                            split(datestr, part, /[ ,]+/)

                                                                            m = month[part[2]]
                                                                            d = part[1]
                                                                            y = part[3]
									    date_skew = 3
                                                                        }
                                                                    } else {
                                                                        match($0, /[ADFJMNOS][a-z]+ [12][0-9][0-9][0-9]$/)
                                                                        datestr = substr($0, RSTART, RLENGTH)
                                                                        if ((FNR < 30) && ($0 !~ "(Expires|Expiration [Dd]ate): *" datestr)) {
                                                                            is_docdate = 1
                                                                            split(datestr, part, /[ ,]+/)

                                                                            m = month[part[1]]
                                                                            d = 15
                                                                            y = part[2]
                                                                            date_skew = 16
                                                                        }
                                                                    }
                                                                    if (is_docdate) {
                                                                        isodate = sprintf("%s-%s-%s", y, m, d)
                                                                        new_date = jdn(isodate)
                                                                        if (new_date > doc_date) {
                                                                            doc_date = new_date
                                                                            date_lit = datestr
                                                                            iso_date = isodate
                                                                        }
                                                                    }
								}
normalized ~ /^([0123.]+ +)?table of contents:?( *\(.+\))?$/	{   has_toc = 1; in_toc = 1; }

normalized ~ /^[ \t]*contents$/					{   if (!has_intro) { has_toc = 1; in_toc = 1; } }

normalized ~ /^([0123.]+ )? ? ? ? ? ?introduction[ \t]*.*/	{   has_intro = 1; }
normalized ~ /^ +([0123.]+ )? ? ? ? ? ?introduction[ \t]*.*/	{   indented_intro = $0; }

normalized ~ /^([0123.]+ )? ? ? ? ? ?overview[ \t]*.*/		{   has_intro = 1; }
normalized ~ /^ +([0123.]+ )? ? ? ? ? ?overview[ \t]*.*/	{   indented_intro = $0; }

normalized ~ /^([0123.]+ )? ? ? ? ? ?rationale[ \t]*.*/		{   has_intro = 1; }
normalized ~ /^ +([0123.]+ )? ? ? ? ? ?rationale[ \t]*.*/	{   indented_intro = $0; }

normalized ~ /^([0123.]+ )? ? ? ? ? ?scope[ \t]*.*/		{   has_intro = 1; }
normalized ~ /^ +([0123.]+ )? ? ? ? ? ?scope[ \t]*.*/		{   indented_intro = $0; }

normalized ~ /^([0123.]+ )? ? ? ? ? ?(historical )?background[ \t]*.*/{   has_intro = 1; }
normalized ~ /^ +([0123.]+ )? ? ? ? ? ?(historical )?background[ \t]*.*/{   indented_intro = $0; }

normalized ~ /^([0-9.]+ )? ? ? ? ? ?security considerations?[ \t]*.*/	{   has_security = 1; }
normalized ~ /^ +([0-9.]+ )? ? ? ? ? ?security considerations?[ \t]*.*/	{   indented_security = $0; }

normalized ~ /^([0-9.]+ )? ? ? ? ? ?security$/			{   has_security = 1; }
normalized ~ /^ +([0-9.]+ )? ? ? ? ? ?security$/			{   indented_security = $0; }

normalized ~ /^(appendix )?([0-9a-z.]+ )? ? ? ? ? ?iana considerations?[ \t]*.*/	{   has_iana = 1; }
normalized ~ /^ +([0-9a-z.]+ )? ? ? ? ? ?iana considerations?[ \t]*.*/{   indented_iana = $0; }

								# New section.  If we were in a reference section, we've now left it.
normalized ~ /^[0-9.]+ +/					{   in_refs = 0; in_abnf = 0; in_imap = 0; in_abstract = 0; in_code=0; sections[$1] = substr($0, length($1)); }
normalized ~ /^status of (this )?memo/				{   in_refs = 0; in_abnf = 0; in_imap = 0; in_abstract = 0; in_code=0; }
normalized ~ /^conventions used /				{   in_refs = 0; in_abnf = 0; in_imap = 0; in_abstract = 0; in_code=0; }
normalized ~ /^requirements language/				{   in_refs = 0; in_abnf = 0; in_imap = 0; in_abstract = 0; in_code=0; }
normalized ~ /^table of contents/				{   in_refs = 0; in_abnf = 0; in_imap = 0; in_abstract = 0; in_code=0; }

normalized ~ /^([0123.]+ )? *abstract$/				{   has_abstract = 1; in_abstract = 1; }
normalized ~ /^ +([0123.]+ )? *abstract$/			{   indented_abstract = $0; in_abstract = 1; }

normalized ~ /^appendix/					{   if (has_refs) has_appendix = 1; in_refs = 0; nnnote("Appendix start: " $0); }
normalized ~ /^ +appendix/					{   if (has_refs) indented_appendix = $0; }

normalized ~ /(^| )abnf([ .]|$)/				{   abnf_mentioned = 1; }
normalized ~ abnf_rule_format					{   in_abnf++;   } 
$0         ~ imap_rule_format					{   in_imap++;   } # The imap rule format has some parts specifically in uppercase -- can't use 'normalized'
normalized ~ /^ *$/						{   in_abnf = 0; in_imap = 0; } 
$0         ~ code_start_format					{   in_code = 1; nnote("Code start at " FNR ": " $0); }
$0         ~ code_end_format					{   in_code = 0; nnote("Code end at " FNR ": " $0); }
$0	   ~ inline_code_format					{   if (! in_code) { code_comment_found = 1; nnote("Possible code comment in line: " $0); }}


normalized ~ /^([0-9]+[.]?0? )? ? ? ? ? ?references$/			{   if (has_intro || has_security || has_iana) { has_refs = 1; in_refs = 1; in_normref = 1; }}
normalized ~ /^ +([0-9]+[.]?0? )? ? ? ? ? ?references$/			{   if (has_intro || has_security || has_iana) { indented_refs = $0; }}
normalized ~ /^ *([0-9.]+ )? ? ? ? ? ?normative references?$/		{   if (has_intro || has_security || has_iana) { has_splitrefs = 1; has_refs = 1; in_refs = 1; in_normref = 1; }}
normalized ~ /^ *([0-9.]+ )? ? ? ? ? ?informative references?$/		{   if (has_intro || has_security || has_iana) { has_splitrefs = 1; has_refs = 1; in_refs = 1; in_normref = 0; }}
normalized ~ /^ *([0-9.]+ )? ? ? ? ? ?informational references?$/	{   if (has_intro || has_security || has_iana) { has_splitrefs = 1; has_refs = 1; in_refs = 1; in_normref = 0; }}
normalized ~ /^ *([0-9.]+ )? ? ? ? ? ?non-normative references?$/	{   if (has_intro || has_security || has_iana) { has_splitrefs = 1; has_refs = 1; in_refs = 1; in_normref = 0; }}
normalized ~ /^ *([0-9.]+ )? ? ? ? ? ?references[^a-z]+normative\)?$/	{   if (has_intro || has_security || has_iana) { has_splitrefs = 1; in_refs = 1; in_normref = 1; }}
normalized ~ /^ *([0-9.]+ )? ? ? ? ? ?references[^a-z]+informative\)?$/	{   if (has_intro || has_security || has_iana) { has_splitrefs = 1; in_refs = 1; in_normref = 0; }}
normalized ~ /^ *([0-9.]+ )? ? ? ? ? ?normative$/			{   if (has_refs) { has_splitrefs = 1; in_refs = 1; in_normref = 1; }}
normalized ~ /^ *([0-9.]+ )? ? ? ? ? ?informative$/			{   if (has_refs) { has_splitrefs = 1; in_refs = 1; in_normref = 0; }}

normalized ~ /^[0-9a-z.]* *((author|editor)(\047s|s\047) +(and +)?)+address(es)?/{   has_addr = 1; }
normalized ~ /^[0-9a-z.]* *author information$/			{   has_addr = 1; }
normalized ~ /^[0-9a-z.]* *(author|editor)(\047s|s\047) contact information$/	{   has_addr = 1; }
normalized ~ /^[0-9A-Z.]* *contact information$/		{   has_addr = 1; }
normalized ~ /^[0-9A-Z.]* *(author|editor)s?:?$/		{   has_addr = 1; }


# RFC 3978 / 2026 compliance

/^[ \t0-9.]*This document may only be posted in an Internet-Draft.$/ {   has["rfc3978_5_3"] = FNR; }

								{ gsub(/ +/, " ", normalized); }
normalized ~ /by submitting this internet-draft/                {
								    para = get_para()

								    almost = almost_boilerplate["rfc3978_5_1"]

								    # Order is significant here -- try the longer text before subsets of it
                                                                    if ( match_para(para, "rfc3978_5_1", "rfc3978_5_2ax")	\
								      || match_para(para, "rfc3978_5_1", "rfc3978_5_2a")	\
								      || match_para(para, "rfc3978_5_1", "rfc3978_5_2bx")	\
								      || match_para(para, "rfc3978_5_1", "rfc3978_5_2b")	\
								      || match_para(para, "rfc3978_5_1")			\
                                                                      || match_para(para, "rfc3978_5_1_a", "rfc3978_5_2ax")	\
	                                                              || match_para(para, "rfc3978_5_1_a", "rfc3978_5_2a")	\
                                                                      || match_para(para, "rfc3978_5_1_a", "rfc3978_5_2bx")	\
                                                                      || match_para(para, "rfc3978_5_1_a", "rfc3978_5_2b")	\
                                                                      || match_para(para, "rfc3978_5_1_a")			\
								      || match_para(para, "rfc3667_5_1", "rfc3978_5_2ax")	\
								      || match_para(para, "rfc3667_5_1", "rfc3978_5_2a")	\
                                                                      || match_para(para, "rfc3667_5_1", "rfc3978_5_2bx")	\
                                                                      || match_para(para, "rfc3667_5_1", "rfc3978_5_2b")	\
                                                                      || match_para(para, "rfc3667_5_1")			\
								      ) {
								        almost_boilerplate["rfc3978_5_1"] = almost;
								    }


                                                                }
normalized ~ /this document may not be modified/                {
								    para = get_para()
								    match_para(para, "trust-28-dec-2009_6_c_i")		\
								    || match_para(para, "trust-28-dec-2009_6_c_ii")	\
								    || match_para(para, "rfc3978_5_2a")			\
								    || match_para(para, "rfc3978_5_2ax")		\
								    || match_para(para, "rfc3978_5_2b")			\
								    || match_para(para, "rfc3978_5_2bx");
                                                                }
normalized ~ /copyright ?\(c\) ?the /				{
								    if (! in_mib) {
									some_copyright = FNR
									para = get_para()
									has_some_year = match_para(para, "rfc3978_5_4_p1", "rfc3978_5_4_p2", "rfc3978_5_5");
									has_some_year = match_para(para, "rfc3978_5_4_p1_u4748", "rfc3978_5_4_p2", "rfc3978_5_5_u4748") || has_some_year;

									has_this_year = match_para(para, "rfc3978_5_4_p1_now");
									has_this_year = match_para(para, "rfc3978_5_4_p1_u4748_now") || has_this_year;

									if ( ! has_some_year ) {
									    match_para(para, "rfc3978_5_4_p1_e");
									    match_para(para, "rfc3978_5_4_p1_u4748_e");
									}

									match_para(para, "rfc2026_10_4C_p1", "rfc2026_10_4C_p2", "rfc2026_10_4C_p3", "rfc2026_10_4C_p4");
									if (has_some_year && !has_this_year) has["rfc3978_5_4_p1_old"] = FNR;
								    }
                                                                }
normalized ~ /this document is subject to the rights/		{
								    para = get_para()
								    match_para(para, "rfc3978_5_4_p2");
								}
normalized ~ /this document and the information/		{
								    para = get_para()
                                                                    match_para(para, "rfc3978_5_5");
                                                                    match_para(para, "rfc3978_5_5_u4748");
                                                                    match_para(para, "rfc2026_10_4C_p4");
                                                                }
normalized ~ /the ietf takes no position regarding/		{
								    para = get_para()
                                                                    match_para(para, "rfc3979_5_p1", "rfc3979_5_p2", "rfc3979_5_p3");
                                                                    match_para(para, "rfc2026_10_4A");
                                                                }
normalized ~ /copies of ipr disclosures made to the/		{
								    para = get_para()
								    match_para(para, "rfc3979_5_p2", "rfc3979_5_p3");
								}
normalized ~ /the ietf invites any interested party/		{
								    para = get_para()
                                                                    match_para(para, "rfc3979_5_p3");
                                                                    match_para(para, "rfc2026_10_4B");
                                                                }
normalized ~ /this document and translations of it/		{
								    para = get_para()
                                                                    match_para(para, "rfc2026_10_4C_p2", "rfc2026_10_4C_p3", "rfc2026_10_4C_p4");
                                                                }
normalized ~ /the limited permissions granted above/		{
								    para = get_para()
                                                                    match_para(para, "rfc2026_10_4C_p3", "rfc2026_10_4C_p4");
                                                                }
normalized ~ /the ietf has been notified of intellectual/	{
								    para = get_para()
                                                                    match_para(para, "rfc2026_10_4D");
                                                                }

normalized ~ /this document is an internet-draft and is/	{
								    para = get_para()
								    match_para(para, "rfc2026_lax_claim");
								    match_para(para, "rfc2026b_lax_claim");

								    ref_in_heading_nit	= ( has["rfc2026_lax_claim"] ||
											    has["rfc2026_lax_claim"] ) &&
											! match_para(para, "rfc2026_claim") &&
											! match_para(para, "rfc2026b_claim");

								    match_para(para, "rfc3667_3_claim", "rfc3667_5_1" );
								    match_para(para, "rfc3667_3_claim", "rfc3978_5_1_a" );
								    match_para(para, "rfc3978_3_claim", "rfc3667_5_1" );
								    match_para(para, "rfc3978_3_claim", "rfc3978_5_1");
								    match_para(para, "rfc3978_3_claim", "rfc3978_5_1_a");

								    if (match( para, reference_format )) { 
									update_references(para, FNR);
								    }
								}


# IETF Trust Provisions boilerplate
normalized ~ /this internet-draft is submitted to ietf in/	{
								    para = get_para()
								    match_para(para, "trust-12-sep-2009_6_a", "trust-12-sep-2009_6_c_i")
								    match_para(para, "trust-12-sep-2009_6_a", "trust-12-sep-2009_6_c_ii")
								    match_para(para, "trust-12-sep-2009_6_a", "trust-12-sep-2009_6_c_iii")

								    match_para(para, "trust-12-sep-2009_6_a", "trust-28-dec-2009_6_c_i")
								    match_para(para, "trust-12-sep-2009_6_a", "trust-28-dec-2009_6_c_ii")
								    match_para(para, "trust-12-sep-2009_6_a", "trust-28-dec-2009_6_c_iii")
								}
normalized ~ /this internet-draft is submitted in full/	{
								    para = get_para()
								    match_para(para, "trust-28-dec-2009_6_a", "1id_guidelines_p1")		\
								    || match_para(para, "trust-28-dec-2009_6_a", "1id_guidelines_p1a")		\
								    || match_para(para, "trust-28-dec-2009_6_a", "trust-28-dec-2009_6_c_i")	\
								    || match_para(para, "trust-28-dec-2009_6_a", "trust-28-dec-2009_6_c_ii")	\
								    || match_para(para, "trust-28-dec-2009_6_a", "trust-28-dec-2009_6_c_iii")	\
								}
normalized ~ /copyright \(c\) [0-9]+ ietf trust and/		{
								    if (! in_mib) {
									some_copyright = FNR
									para = get_para()
									has_some_year = match_para(para, "trust-28-dec-2009_6_b_i_p2", "trust-28-dec-2009_6_b_i_p3");
									has_some_year = match_para(para, "trust-28-dec-2009_6_b_i_p2", "trust-28-dec-2009_6_b_ii_p3");
									has_this_year = match_para(para, "trust-28-dec-2009_6_b_i_p2_now");
								    }
								}
normalized ~ /this document is subject to bcp 78/		{
								    para = get_para()
								    match_para(para, "trust-12-feb-2009_6_b_p3", "trust-12-feb-2009_6_c_i")
								    match_para(para, "trust-12-feb-2009_6_b_p3", "trust-12-feb-2009_6_c_ii")
								    match_para(para, "trust-12-feb-2009_6_b_p3", "trust-12-feb-2009_6_c_iii")

								    match_para(para, "trust-12-sep-2009_6_b_p3", "trust-12-sep-2009_6_c_i")
								    match_para(para, "trust-12-sep-2009_6_b_p3", "trust-12-sep-2009_6_c_ii")
								    match_para(para, "trust-12-sep-2009_6_b_p3", "trust-12-sep-2009_6_c_iii")

								    match_para(para, "trust-12-sep-2009_6_b_p3", "trust-28-dec-2009_6_c_i")
								    match_para(para, "trust-12-sep-2009_6_b_p3", "trust-28-dec-2009_6_c_ii")
								    match_para(para, "trust-12-sep-2009_6_b_p3", "trust-28-dec-2009_6_c_iii")

								    match_para(para, "trust-28-dec-2009_6_b_i_p3", "trust-28-dec-2009_6_c_i")
								    match_para(para, "trust-28-dec-2009_6_b_i_p3", "trust-28-dec-2009_6_c_ii")
								    match_para(para, "trust-28-dec-2009_6_b_i_p3", "trust-28-dec-2009_6_c_iii")

								    match_para(para, "trust-28-dec-2009_6_b_ii_p3", "trust-28-dec-2009_6_c_i")
								    match_para(para, "trust-28-dec-2009_6_b_ii_p3", "trust-28-dec-2009_6_c_ii")
								    match_para(para, "trust-28-dec-2009_6_b_ii_p3", "trust-28-dec-2009_6_c_iii")
								}
normalized ~ /this document may contain material from ietf/	{
								    para = get_para()
								    match_para(para, "trust-12-sep-2009_6_c_iii")
								    match_para(para, "trust-28-dec-2009_6_c_iii")
								}


# RFC 2119 compliance
normalized ~ /(the key ?words|in this document, several words are used)/	{
								    para = get_para()
								    if (match_para(para, "rfc2119_p2") || match_para(para, "rfc2119_p2a") || match_para(para, "rfc8174_p11") ) {
									if (match(para, /NOT RECOMMENDED/)) rfc2119_p2_not_recommended++;
								    }

								    if (match( para, reference_format )) { 
									reftag = substr(para, RSTART+1, RLENGTH-2);
									reftag2119 = reftag
									update_references(para, FNR);
								    }
								}

#/^[ \t0-9.]*Table Of [Cc]ontents?:?$/			{   has_toc = 1; misspelled_toc = 1}
/^[ \t0-9.]*(Author|Editor)([^\047]s|s[^\047]) [Aa]ddress(es)?$/	{   has_addr = 1; misspelled_addr = 1; }
/^[ \t0-9.]*(Authors?|Editors?) +[Aa]ddress(es)?$/	{   has_addr = 1; misspelled_addr = 1; }

# 1id_guidelines.txt compliance
normalized ~ /internet-drafts are working documents of/		{
								    para = get_para()
                                                                    match_para(para, "1id_guidelines_p1", "1id_guidelines_p2", "1id_guidelines_p3a", "1id_guidelines_p4a") ||
                                                                    match_para(para, "1id_guidelines_p1", "1id_guidelines_p2a", "1id_guidelines_p3a", "1id_guidelines_p4a") ||
                                                                    match_para(para, "1id_guidelines_p1a", "1id_guidelines_p2") ||
                                                                    match_para(para, "1id_guidelines_p1a", "1id_guidelines_p2a");
                                                                }
normalized ~ /internet-drafts are draft documents valid for/	{
								    para = get_para()
                                                                    match_para(para, "1id_guidelines_p2", "1id_guidelines_p3a", "1id_guidelines_p4a") ||
								    match_para(para, "1id_guidelines_p2a");
                                                                }
normalized ~ /the list of current internet-drafts can be/	{
								    para = get_para()
                                                                    match_para(para, "1id_guidelines_p3", "1id_guidelines_p4a") ||
                                                                    match_para(para, "1id_guidelines_p3a", "1id_guidelines_p4a");
                                                                }
normalized ~ /the list of internet-draft shadow directories/	{
								    para = get_para()
                                                                    match_para(para, "1id_guidelines_p4") ||
                                                                    match_para(para, "1id_guidelines_p4a");
                                                                }

normalized ~ /^obsoletes: /					{
								    if (FNR < 10) {
									c = split($0, obsoletelist, /[, ]+/);
									docs = ""
									for (i=2; i<c; i++) {
									    header_obsoletes = 1;
									    word = obsoletelist[i];
									    if (word ~ /^[0-9]+/) docs = docs "RFC" word " ";
									    if (word ~ /^draft-/) docs = docs word " ";
									    if (word ~ /^[0-9]+$/) {
									        obsoletes[word] = FNR;
									    } else if (word ~ /[Rr][Ff][Cc]/) {
										obsoletes_junk = FNR;
									    } else {
										break;
									    } 

									}
									if (docs) { gsub(/ $/, "", docs); }
									obsoletes_docs = docs
								    }
								}
normalized ~ /^updates: /					{
								    if (FNR < 10) {
									c = split($0, updatelist, /[, ]+/);
									docs = ""
									for (i=2; i<c; i++) {
									    header_updates = 1;
									    word = updatelist[i];
									    if (word ~ /^[0-9]+/) docs = docs "RFC" word " ";
									    if (word ~ /^draft-/) docs = docs word " ";
									    if (word ~ /^[0-9]+$/) {
									        updates[word] = FNR;
									    } else if (word ~ /[Rr][Ff][Cc]/) {
										updates_junk = FNR;
									    } else {
										break;
									    } 
									    
									}
									if (docs) { gsub(/ $/, "", docs); }
									updates_docs = docs
								    }
								}

/draft-/							{
								    if ( !has_intro && !has_abstract && !has_refs && !has_appendix && !has_docname) {
									if ( $0 ~ /Updates: *draft-/ ) {
									    updates[$2];
									} else if ( $0 ~ /Obsoletes: *draft-/ ) {
									    obsoletes[$2];
									} else {
									    match($0, /draft-[a-z0-9-]*/)
									    good_docname = substr($0, RSTART, RLENGTH)
									    match($0, /draft-[^ \t>]*/)
									    full_docname = substr($0, RSTART, RLENGTH)
									    has_docname = 1
									}
								    }
								}

# References and downref checking

normalized ~ /(intended status|category):/			{
								    if ( !has_intro && !has_abstract && !has_refs && !has_appendix && !has_status_of_memo) {
									sta = tolower($0);
									sub(/^.*(intended status|category): */, "", sta);
									sub(/  .*$/, "", sta);
									if ( length(intended_status) == 0) {
									    intended_status = sta;
									    intended_status_text = $0;
									    sub(/   .*$/, "", intended_status_text)
									}
								    }
								}

$0 ~ reference_format						{
								    if (in_refs) {
									refpos = FNR;
									reftext = strip($0);
									while ((getline > 0) && (text = strip($0)) != "") {
									    check_line()
									    if ($0 ~ reference_format) {
										# start of a new reference without intervening blank line
									        update_references(reftext, refpos);
										refpos = FNR;
										reftext = text;
									    } else {
										if (reftext ~ /-$/) {
										    reftext = reftext text;
										} else {
										    reftext = reftext " " text;
										}
									    }
									}
								        update_references(reftext, refpos);
								    } else {
									if ((!abnf_mentioned || !in_abnf ) && !in_imap && !in_code ) update_references($0, FNR);
									if ((abnf_mentioned && in_abnf ) && ($0 ~ (";.*" reference_format))) update_references($0, FNR);
								    } 
								}
/\[[0-9A-Z-]+- *$/						{
								    incomplete_ref_line = strip($0)
								    incomplete_ref_pos  = FNR
								}
/^ *[0-9A-Z-]+\]/						{
								    if (incomplete_ref_pos+1 == FNR) {
									update_references(incomplete_ref_line strip($0), incomplete_ref_pos)
								    }
								}

{
    text = strip($0);
    if ( text == "") {
	if (para) check_para(para);
	para = "";
    } else {
	if (para ~ /-$/) {
	    para = para text;
	} else {
	    para = para " " text;
	}
    }
}



END {

    report(option_filename);
    if (errors || longlines || hyphens || spacing || nonascii || controlchars )  print "";
    if (errors < 256) {
	exit errors;
    } else {
       exit 255;
    }
}
EOF

    $AWK -v statusdir="$statusdir" -v statusfile="$statusfile" -v obslist="$obslist" -v exclist="$exclist" -v today="$today" -f $program $*
    errors=$?
    if [ ! "$optkeep" = "1" ]; then
	rm -f $program
    fi
    return $errors
}

helpmsg() {
	  echo "
Usage: idnits [options] filename

    Options:
	--version	Print the version and exit
	--help		Print this text and exit
	--nowarn	Don\047t issue warnings, only ID-nits violations
	--verbose	Show more information about offending lines
	--nitcount	Show a count of nits
	--debug		Debug output, especially of boilerplate matching
	--year NNNN	Expect the given year in the boilerplate (instead
			of the current year)
	--checklistwarn	Only warn (no errors) for ID-Checklist volations
	--submitcheck	Only output errors and warnings related to 1id-guidelines
	--status doctype Assume the given intended document type

'idnits' looks for violations of Section 2.1 and 2.2 of the
requirements listed on https://www.ietf.org/id-info/checklist

idnits works on Linux, OS-X, Windows under Cygwin, on *BSD and may work on
Solaris.  Testing on *BSD and Solaris has been minimal, though.  To install,
simply download the script, place it in your path and make it executable.
idnits uses awk and sh internally.

Many, but not all ID-nits are checked; here's the list:

1.1 Formatting

yes  * Not beyond the 72nd column of a line
       This is especially important for diagrams and code, which the RFC Editor
       may not be able to trivially reformat to fall within the margins.
yes  * Must be ragged right
yes  * No hyphenation for line-breaks
     * No footnotes
yes  * ASCII-only, no control characters (other than CR, NL & FF)
yes  * Do not number the \"Status of Memo\" or Abstract sections
yes@ * Do not add a numbered reference in the ID boilerplate to RFC 3978 or 3979
       (makes it harder for the RFC editor to process the document when they
       strip off the ID boilerplate)
     * Reasonably well formatted for readibility and clarity.
     * Use network byte order in diagrams
       (see draft-rfc-editor-rfc2223bis-07.txt section 3.4)

1.2 Required sections - all IDs

yes@ * Internet Draft boilerplate
       Must contain boilerplate that permits publication as an RFC
       (see RFC 3978, Section 5.2.)
     * List of authors/editors
       There should not be > 5 authors/editors
       (see https://www.rfc-editor.org/policy.html)
yes  * Abstract
yes  * Table of Contents, required if document is more than 15 pages
yes  * Introduction
yes  * Security Considerations
yes  * IANA Considerations
yes  * References
       Must be split into normative and informative sections
       (see https://www.rfc-editor.org/policy.html)
yes  * Author's Address
yes  * IPR notices, IPR Notice, verbatim from RFC 3979, Section 5.
yes  * Copyright Notice and Disclaimer,
       verbatim from RFC 3978, Sections 5.4 and 5.5.


------------------------------------------------------------------------
 @: Interpreted in light of the new requirements introduced by RFC 3667
    (replaced by 3978) and RFC 3668 (replaced by 3979:

yes * IPR Disclosure Acknowledgement per RFC 3978 Section 5.1
yes * No Derivative Works Limitation clause per RFC 3978 Section 5.2(b)
yes * No Publication Limitation clause per RFC 3978 Section 5.3
yes * Copyright Notice per RFC 3978 Section 5.4
yes * Disclaimer per RFC 3978 Section 5.5
yes * Disclaimer of IPR validity per RFC 3979 Section 5

"
}


download() (
    name="$1"; url=$2; file=$3; test="$4"

    cd $statusdir
    if [ "$file" ]; then
	filedate=$($FDATE $file.date 2>/dev/null) 
	tempfile=$(tmpfile)
	debug "Status file $file is dated $filedate."

	if [ "$filedate" = "$today" ]; then
	    true
	else
	    note "" 
	    if [ -f "$file" ]; then
		note " - The $name file is not from today."
		note "   Attempting to download a newer one..."
	    else
		note " - No $name file. Attempting to download it..."
	    fi
	    if $WGET $WARG $url > $tempfile; then
		if grep -qsE "$test" $tempfile; then
		    note " - Success fetching $name file."
		    tr -d "\r" < $tempfile > $file.date
		    if ! cmp -s $file.date $file; then
			mv $file.date $file
			touch -r $tempfile $file
		    else
			rm $file.date
		    fi
		    echo "$today" > $file.date; 
		else
		    if [ -z "$optquietdownload" ]; then
		        [ "$optverbose" ] || echo "   Attempted to download $name..."	>&2
		        echo "   The downloaded file seems to be corrupt, proceeding with outdated information." >&2
		    fi
		    touch $file.date
		fi
	    else
		if [ -z "$optquietdownload" ]; then
		    [ "$optverbose" ] || echo "   Attempted to download $name..."	>&2
	            echo "   Failure fetching the file, proceeding without it."	>&2
		fi
		touch $file.date
	    fi
	fi
    fi
)


while [ $# -gt 0 ]; do
    case "$1" in
	--checklistwarn)
	    options="$options $1"
	    ;;
	--debug)
	    optdebug="$optdebug $1"
	    options="$options $1"
	    ;;
	--download)
	    shift
	    download "$@"
	    exit
	    ;;
	--quiet-download*)
	    optquietdownload=1
	    ;;
	--help)
	    helpmsg
	    exit
	    ;;
	--keep)
	    optkeep=1
            ;;
	--showtext)
	    optshowtext=1
	    ;;
	--silent)
	    optsilent=1
	    ;;
	--status)
	    options="$options $1 $2"
	    shift
	    ;;
	--spellcheck)
	    optspellcheck=1
	    ;;
	--grammarcheck)
	    optgrammarcheck=1
	    ;;
	--submitcheck)
	    options="$options $1"
	    ;;
	--verbose)
	    optverbose="$optverbose $1"
	    options="$options $1"
	    ;;
	--version)
            echo "idnits	$version"
	    exit
	    ;;
	--width)
	    optwidth=$2
	    shift
	    ;;
	--year)
	    options="$options $1 $2"
	    shift
	    ;;
	--) shift;
	    files="$files $*"
	    break
	    ;;
	-*) options="$options $1"
	    ;;
	*)  files="$files $1"
	    ;;
    esac
    shift
done

echo "idnits $version $progdate"

if [ "$optwidth" ]; then
   COLUMNS=$optwidth
elif [ -z $COLUMNS ]; then
   SIZE=$(stty size 2>/dev/null)
   if [ "$SIZE" ]; then
       set $SIZE
       LINES=$1
       COLUMNS=$2
   fi
fi

[ -d $statusdir ] || mkdir $statusdir || die "Could not create directory to hold auxiliary files: '$statusdir'"

#download "RFC Status" http://tools.ietf.org/tools/idnits/idnits-rfc-status $statusfile "^UUOUUUUUUOOUUUUOUUUUUUUUUUUUUUUUUUIUUUUUUUUUUUUUUUUUUUUUUUUUOUUU\$"
#download "RFC Status" http://tools.ietf.org/tools/idnits/idnits-rfc-status $statusfile "^UUOUUUUUUOOUUNUOUUUUUUUUUNUUUUUUUUIUUUUUUUUUUUUUUUUUUUUUUUUUOUUU\$"
note ""
download "RFC Status" $datatracker/doc/idnits2-rfc-status $statusfile "^UUOUUUUUUOOUUNUOUUUSUUUUUNUUUUUUUUIUUUUUUUUUUUUUUUUUUUUUUUUUOUUU\$"
#download "RFC Words" https://tools.ietf.org/tools/idspell/ietf-words.wl $wordlist "^personal_ws-1.1 en "
download "RFCs Obsoleted" $datatracker/doc/idnits2-rfcs-obsoleted $obslist "3 10"
#download "Downref Exceptions" https://tools.ietf.org/rfc/meta/rfcs-obsoleted.txt $exclist "952"

export CHECKNITS COLUMNS

if [ -z "$files" ]; then
    CHECKNITS=$options
    echo "Reading input from pipe.  Skipping some checks." > /dev/stdout
    fixnl | hfstrip | checknits
else
    for file in $files; do
	echo ""
	echo "$file:"

	if [ ! -f "$file" ]; then
	    die "No such file: $file"
	fi

	if [ -n "$optverbose" ]; then
	    # run a first pass without stripping, to get correct line
	    # numbers in the verbose reports
	    (CHECKNITS="$optverbose --pass1" checknits $file)
	fi


	# The next 
	draftrefs=$(cat $file | tr -c "A-Za-z0-9+-" "[\n*]" | tr -s "\n" |	\
	awk '
	   /draft-$/ { printf $0 ; next; }
	   /draft-[a-z0-9+-]+-$/ { printf $0 ; next; }
				 { print; }
	' | egrep "^draft-[a-z0-9+-]*" | sort | uniq )
	for ref in $draftrefs; do
	    ref=$(echo $ref | tr -d "\r")
	    name=${ref%-[0-9][0-9]}
	    if [ ! "$name" = "draft" ]; then
	        download "$name state" $datatracker/doc/$name/idnits2-state/ $statusdir/$name.state "Doc-tag: $name"
	    fi
	done

	rfcrefs=$(cat $file | tr -c "0-9" "[\n*]" | tr -s "\n" |	\
	awk '
	   /^[0-9][0-9][0-9][0-9]?$/ { print $0 ; next; }
	' | sort | uniq )
	for ref in $rfcrefs; do
	    ref=$(echo $ref | tr -d "\r")
	    name=rfc$ref
	    num=$ref
	    if [ ! "$name" = "rfc" ]; then
	        download "$name state" $datatracker/doc/$name/idnits2-state/ $statusdir/$name.state "Doc-rfcnum: $num"
	    fi
	done

	CHECKNITS="$options --filename $file"
	stripped=/tmp/idnits-$$-stripped-`basename $file`
	if [ "$optkeep" = "1" -o "$optshowtext" = "1" -o "$optgrammarcheck" ]; then
	    fixnl $file | hfstrip | tee $stripped | checknits
	else
	    fixnl $file | hfstrip | checknits
	fi

 	if [ "$wordlist" -a "$SPELL" -a "$optspellcheck" ]; then
 	    echo ""
 	    echo "  Spelling check:"
	    echo "  ----------------------------------------------------------------------------"
 	    words=$($SPELL -p "$wordlist" -a < $file | $AWK '/^& [A-Za-z-]+ / { print $2; }' | sort | uniq )
 	    wordcount=$(echo $(echo "$words" | wc -l))
 	    if [ "$wordcount" -gt 0 ]; then
 		echo -n "    Found $wordcount potential spelling mistakes"
 		if [ "$wordcount" -gt 4 ]; then
 		    echo    " - maybe you should check the document"
 		    echo -n "    using idspell (https://tools.ietf.org/tools/idspell/)"
 		fi
 		echo ":"
 		echo ""
 		echo "$words" | column -c 72 | sed "s/^/	/" # Note: literal tab in sed regex
 	    else
 		echo "    No potential spelling mistakes found."
 	    fi
 	 fi
 	 
	if [ "$GRAMMAR" -a "$optgrammarcheck" ]; then
	    echo ""
            echo "  Grammar check:"
            echo "  ----------------------------------------------------------------------------"
            # disable some languagetool checks, because they cause lots of spurious warnings on I-Ds
            skiprules=EN_QUOTES,COMMA_PARENTHESIS_WHITESPACE,WHITESPACE_RULE
            cat $stripped | grep -v '^-+-.*-+-$' |\
            	sed -e 's/^[ ]\{1,\}//g; s/[ ]\{2,\}/ /g; s/^o /\* /' | \
            	$GRAMMAR -v -d $skiprules 2> /dev/null |\
            	grep -Ev '^(Suggestion: |Working on STDIN|Time: )' |\
            	sed -E -e 's/^[0-9]+\.\) Line.*//' -e 's/Message: //' -e 's/(.*)/    \1/'
 	fi 

	if [ "$optshowtext" = "1" ]; then
	    echo "--------------------------------------------------------------------------------"
	    echo ""
	    cat $stripped | sed -r -e 's/^-\+- .* -\+-$//' -e '/./=' | sed '/./N; s/\n/	/' # Note: literal tab in sed regex
	fi

	if [ -e $stripped -a ! "$optkeep" = "1" ]; then rm -f $stripped; fi
    done
fi
