"Fossies" - the Fresh Open Source Software Archive

Member "rfcdiff-1.48/rfcdiff.base" (27 Mar 2016, 35194 Bytes) of package /linux/privat/rfcdiff-1.48.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Bash source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 #!/bin/bash
    2 #
    3 # Synopsis:
    4 #   Show changes between 2 internet-drafts using changebars or html
    5 #   side-by-side diff.
    6 #   
    7 # Usage:
    8 #   rfcdiff [options] file1 file2
    9 #   
   10 #   rfcdiff takes two RFCs or Internet-Drafts in text form as input, and
   11 #   produces output which indicates the differences found in one of various
   12 #   forms, controlled by the options listed below. In all cases, page
   13 #   headers and page footers are stripped before looking for changes.
   14 #   
   15 #   --html      Produce side-by-side .html diff (default)
   16 #   
   17 #   --chbars    Produce changebar marked .txt output
   18 #   
   19 #   --diff      Produce a regular diff output
   20 #   
   21 #   --wdiff     Produce paged wdiff output
   22 #   
   23 #   --hwdiff    Produce html-wrapped coloured wdiff output
   24 #   
   25 #   --oldcolour COLOURNAME  Colour for new file in hwdiff (default is "green")
   26 #   --oldcolor COLORNAME    Color for old file in hwdiff (default is "red")
   27 #
   28 #   --newcolour COLOURNAME  Colour for new file in hwdiff (default is "green")
   29 #   --newcolor COLORNAME    Color for new file in hwdiff (default is "green")
   30 #
   31 #   --larger        Make difference text in hwdiff slightly larger
   32 #
   33 #   --browse    Show html output in browser
   34 #   
   35 #   --keep      Don't delete temporary workfiles
   36 #   
   37 #   --version   Show version
   38 #   
   39 #   --help      Show this help
   40 #   
   41 #   --info "Synopsis|Usage|Copyright|Description|Log"
   42 #           Show various info
   43 #   
   44 #   --width N   Set a maximum width of N characters for the
   45 #           display of each half of the old/new html diff
   46 #   
   47 #   --linenum   Show linenumbers for each line, not only at the
   48 #           start of each change section
   49 #   
   50 #   --body      Strip document preamble (title, boilerplate and
   51 #           table of contents) and postamble (Intellectual
   52 #           Property Statement, Disclaimer etc)
   53 #   
   54 #   --nostrip   Don't strip headers and footers (or body)
   55 #   
   56 #   --ab-diff   Before/After diff, suitable for rfc-editor
   57 #   --abdiff
   58 #   
   59 #   --stdout    Send output to stdout instead to a file
   60 #   
   61 #
   62 # Copyright:
   63 #   -----------------------------------------------------------------
   64 #   
   65 #   Copyright 2002 Henrik Levkowetz
   66 #   
   67 #   This program is free software; you can redistribute it and/or modify
   68 #   it under the terms of the GNU General Public License as published by
   69 #   the Free Software Foundation; either version 2 of the License, or
   70 #   (at your option) any later version.
   71 #   
   72 #   This program is distributed in the hope that it will be useful,
   73 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
   74 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   75 #   GNU General Public License for more details.
   76 #   
   77 #   You should have received a copy of the GNU General Public License
   78 #   along with this program; if not, write to the Free Software
   79 #   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   80 #
   81 #   -----------------------------------------------------------------
   82 #
   83 # Description:
   84 #
   85 #   The purpose of this program is to compare two versions of an
   86 #   internet-draft, and as output produce a diff in one of several
   87 #   formats:
   88 #   
   89 #       - side-by-side html diff
   90 #       - paged wdiff output in a text terminal
   91 #       - a text file with changebars in the left margin
   92 #       - a simple unified diff output
   93 #   
   94 #   In all cases, internet-draft headers and footers are stripped before
   95 #   generating the diff, to produce a cleaner diff.
   96 #   
   97 #   It is called as
   98 #   
   99 #       rfcdiff first-file second-file
  100 #   
  101 #   The latest version is available from
  102 #       http://tools.ietf.org/tools/rfcdiff/
  103 #
  104 
  105 export version="1.45"
  106 export progdate=""
  107 export prelines="10"
  108 export basename=$(basename $0)
  109 export workdir="/tmp/$basename-$$"
  110 export pagecache1="$workdir/pagecache1"
  111 export pagecache2="$workdir/pagecache2"
  112 
  113 # ----------------------------------------------------------------------
  114 # Utility to find an executable
  115 # ----------------------------------------------------------------------
  116 lookfor() {
  117     for b in "$@"; do
  118     found=$(which "$b" 2>/dev/null)
  119     if [ -n "$found" ]; then
  120         if [ -x "$found" ]; then
  121         echo "$found"
  122         return
  123         fi
  124     fi
  125     done
  126 }
  127 
  128 AWK=$(lookfor gawk nawk awk)
  129 
  130 # ----------------------------------------------------------------------
  131 # Strip headers footers and formfeeds from infile to stdout
  132 # ----------------------------------------------------------------------
  133 stripbom() {
  134   $AWK '
  135 NR==1               { sub(/^\xef\xbb\xbf/,""); }
  136                 { print ; }
  137 ' "$1"
  138 }
  139 
  140 
  141 # ----------------------------------------------------------------------
  142 # Strip headers footers and formfeeds from infile to stdout
  143 # ----------------------------------------------------------------------
  144 strip() {
  145   $AWK '
  146                 { gsub(/\r/, ""); }
  147                 { gsub(/[ \t]+$/, ""); }
  148                 { pagelength++; }
  149 /\[?[Pp]age [0-9ivx]+\]?[ \t\f]*$/  {
  150                     match($0, /[Pp]age [0-9ivx]+/);
  151                     num = substr($0, RSTART+5, RLENGTH-5);
  152                     print num, outline > ENVIRON["pagecache" ENVIRON["which"]]
  153                     pagelength = 0;
  154                 }
  155 /\f/                { newpage=1;
  156                   pagelength=1;
  157                 }
  158 /\f$/               {
  159                     # a form feed followed by a \n does not contribute to the
  160                     # line count.  (But a \f followed by something else does.)
  161                     pagelength--;
  162                 }
  163 /\f/                { next; }
  164 /\[?[Pp]age [0-9ivx]+\]?[ \t\f]*$/      { preindent = indent; next; }
  165 
  166 /^ *Internet.Draft.+[12][0-9][0-9][0-9] *$/ && (FNR > 15)   { newpage=1; next; }
  167 /^ *INTERNET.DRAFT.+[12][0-9][0-9][0-9] *$/ && (FNR > 15)   { newpage=1; next; }
  168 /^ *Draft.+(  +)[12][0-9][0-9][0-9] *$/     && (FNR > 15)   { newpage=1; next; }
  169 /^RFC[ -]?[0-9]+.*(  +).* [12][0-9][0-9][0-9]$/ && (FNR > 15)   { newpage=1; next; }
  170 /^draft-[-a-z0-9_.]+.*[0-9][0-9][0-9][0-9]$/ && (FNR > 15)  { newpage=1; next; }
  171 /(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; }
  172 newpage && $0 ~ /^ *draft-[-a-z0-9_.]+ *$/ { newpage=1; next; }
  173 
  174 /^[ \t]+\[/         { sentence=1; }
  175 /[^ \t]/            {
  176                    indent = match($0, /[^ ]/);
  177                    if (indent < preindent) {
  178                       sentence = 1;
  179                    }
  180                    if (newpage) {
  181                       if (sentence) {
  182                      outline++; print "";
  183                       }
  184                    } else {
  185                       if (haveblank) {
  186                       outline++; print "";
  187                       }
  188                    }
  189                    haveblank=0;
  190                    sentence=0;
  191                    newpage=0;
  192 
  193                    line = $0;
  194                    sub(/^ *\t/, "        ", line);
  195                    thiscolumn = match(line, /[^ ]/);
  196                 }
  197 /[.:][ \t]*$/           { sentence=1; }
  198 /\(http:\/\/trustee\.ietf\.org\/license-info\)\./ { sentence=0; }
  199 /^[ \t]*$/          { haveblank=1; next; }
  200                 { outline++; print; }
  201 ' "$1"
  202 }
  203 
  204 
  205 # ----------------------------------------------------------------------
  206 # Strip preamble (title, boilerplate and table of contents) and
  207 # postamble (Intellectual Property Statement, Disclaimer etc)
  208 # ----------------------------------------------------------------------
  209 bodystrip() {
  210     $AWK '
  211     /^[ \t]*Acknowledgment/     { inbody = 0; }
  212     /^(Full )*Copyright Statement$/ { inbody = 0; }
  213     /^[ \t]*Disclaimer of Validid/  { inbody = 0; }
  214     /^[ \t]*Intellectual Property/  { inbody = 0; }
  215     /^Abstract$/            { inbody = 0; }
  216     /^Table of Contents$/       { inbody = 0; }
  217     /^1.[ \t]*Introduction$/    { inbody = 1; }
  218 
  219     inbody          { print; }
  220     ' "$1"
  221 }
  222 
  223 
  224 # ----------------------------------------------------------------------
  225 # From two words, find common prefix and differing part, join descriptively
  226 # ----------------------------------------------------------------------
  227 worddiff() {
  228    $AWK '
  229 BEGIN   {
  230         w1 = ARGV[1]
  231         w2 = ARGV[2]
  232         format = ARGV[3]
  233 
  234         do {
  235             if (substr(w1,1,1) == substr(w2,1,1)) {
  236                 w1 = substr(w1,2)
  237                 w2 = substr(w2,2)
  238             } else {
  239                 break;
  240             }
  241             prefixlen++;
  242         } while (length(w1) && length(w2))
  243 
  244         prefix = substr(ARGV[1],1,prefixlen);
  245 
  246         do {
  247             l1 = length(w1);
  248             l2 = length(w2);
  249             if (substr(w1,l1,1) == substr(w2,l2,1)) {
  250                 w1 = substr(w1,1,l1-1)
  251                 w2 = substr(w2,1,l2-1)
  252             } else {
  253                 break;
  254             }
  255         } while (l1 && l2)
  256 
  257         suffix = substr(ARGV[1], prefixlen+length(w1))
  258 
  259         printf format, prefix, w1, w2, suffix;
  260     }
  261 ' "$1" "$2" "$3"
  262 }
  263 
  264 # ----------------------------------------------------------------------
  265 # Generate a html page with side-by-side diff from a unified diff
  266 # ----------------------------------------------------------------------
  267 htmldiff() {
  268    $AWK '
  269 BEGIN   {
  270            FS = "[ \t,]";
  271 
  272        # Read pagecache1
  273        maxpage[1] = 1
  274        pageend[1,0] = 2;
  275        while ( getline < ENVIRON["pagecache1"] > 0) {
  276           pageend[1,$1] = $2;
  277           if ($1+0 > maxpage[1]) maxpage[1] = $1+0;
  278        }
  279 
  280        # Read pagecache2
  281        maxpage[2] = 1
  282        pageend[2,0] = 2;
  283        while ( getline < ENVIRON["pagecache2"] > 0) {
  284           pageend[2,$1] = $2;
  285           if ($1+0 > maxpage[2]) maxpage[2] = $1+0;
  286        }
  287 
  288        wdiff = ENVIRON["wdiffbin"]
  289        base1 = ENVIRON["base1"]
  290        base2 = ENVIRON["base2"]
  291        optwidth = ENVIRON["optwidth"]
  292        optnums =  ENVIRON["optnums"]
  293        optlinks = ENVIRON["optlinks"]
  294        cmdline = ENVIRON["cmdline"]
  295        gsub("--", "- -", cmdline)
  296        ENVIRON["cmdline"] = cmdline
  297        header(base1, base2)
  298 
  299        difflines1 = 0
  300        difflines2 = 0
  301     }
  302 
  303 function header(file1, file2) {
  304    url1 = file1;
  305    url2 = file2;
  306    if (optlinks) {
  307       if (file1 ~ /^draft-/) { url1 = sprintf("<a href=\"https://tools.ietf.org/html/%s\" style=\"color:#008\">%s</a>", file1, file1); }
  308       if (file1 ~ /^draft-/) { prev = sprintf("<a href=\"/rfcdiff?url2=%s\" style=\"color:#008; text-decoration:none;\">&lt;</a>", file1); }
  309       if (file2 ~ /^draft-/) { url2 = sprintf("<a href=\"https://tools.ietf.org/html/%s\" style=\"color:#008\">%s</a>", file2, file2); }
  310       if (file2 ~ /^draft-/) { nxt  = sprintf("<a href=\"/rfcdiff?url1=%s\" style=\"color:#008; text-decoration:none;\">&gt;</a>", file2) }
  311    }   
  312    printf "" \
  313 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"> \n" \
  314 "<!-- Generated by rfcdiff %s: rfcdiff %s --> \n" \
  315 "<!-- <!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional\" > -->\n" \
  316 "<!-- System: %s --> \n" \
  317 "<!-- Using awk: %s: %s --> \n" \
  318 "<!-- Using diff: %s: %s --> \n" \
  319 "<!-- Using wdiff: %s: %s --> \n" \
  320 "<html xmlns=\"http://www.w3.org/1999/xhtml\"> \n" \
  321 "<head> \n" \
  322 "  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" /> \n" \
  323 "  <meta http-equiv=\"Content-Style-Type\" content=\"text/css\" /> \n" \
  324 "  <title>Diff: %s - %s</title> \n" \
  325 "  <style type=\"text/css\"> \n" \
  326 "    body    { margin: 0.4ex; margin-right: auto; } \n" \
  327 "    tr      { } \n" \
  328 "    td      { white-space: pre; font-family: monospace; vertical-align: top; font-size: 0.86em;} \n" \
  329 "    th      { font-size: 0.86em; } \n" \
  330 "    .small  { font-size: 0.6em; font-style: italic; font-family: Verdana, Helvetica, sans-serif; } \n" \
  331 "    .left   { background-color: #EEE; } \n" \
  332 "    .right  { background-color: #FFF; } \n" \
  333 "    .diff   { background-color: #CCF; } \n" \
  334 "    .lblock { background-color: #BFB; } \n" \
  335 "    .rblock { background-color: #FF8; } \n" \
  336 "    .insert { background-color: #8FF; } \n" \
  337 "    .delete { background-color: #ACF; } \n" \
  338 "    .void   { background-color: #FFB; } \n" \
  339 "    .cont   { background-color: #EEE; } \n" \
  340 "    .linebr { background-color: #AAA; } \n" \
  341 "    .lineno { color: red; background-color: #FFF; font-size: 0.7em; text-align: right; padding: 0 2px; } \n" \
  342 "    .elipsis{ background-color: #AAA; } \n" \
  343 "    .left .cont { background-color: #DDD; } \n" \
  344 "    .right .cont { background-color: #EEE; } \n" \
  345 "    .lblock .cont { background-color: #9D9; } \n" \
  346 "    .rblock .cont { background-color: #DD6; } \n" \
  347 "    .insert .cont { background-color: #0DD; } \n" \
  348 "    .delete .cont { background-color: #8AD; } \n" \
  349 "    .stats, .stats td, .stats th { background-color: #EEE; padding: 2px 0; } \n" \
  350 "    span.hide { display: none; color: #aaa;}" \
  351 "    a:hover span { display: inline; }" \
  352 "    tr.change { background-color: gray; } \n" \
  353 "    tr.change a { text-decoration: none; color: black } \n" \
  354 "  </style> \n" \
  355 "  %s \n" \
  356 "</head> \n" \
  357 "<body > \n" \
  358 "  <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\"> \n" \
  359 "  <tr id=\"part-1\" bgcolor=\"orange\"><th></th><th>%s&nbsp;%s&nbsp;</th><th> </th><th>&nbsp;%s&nbsp;%s</th><th></th></tr> \n" \
  360 "", ENVIRON["version"], ENVIRON["cmdline"], ENVIRON["uname"], ENVIRON["awkbin"], ENVIRON["awkver"], ENVIRON["diffbin"], ENVIRON["diffver"], ENVIRON["wdiffbin"], ENVIRON["wdiffver"], file1, file2, ENVIRON["jsinc"], prev, url1, url2, nxt;
  361 }
  362 
  363 function worddiff(w1, w2) {
  364    prefixlen = 0;
  365    word1 = w1;
  366    do {
  367       if (substr(w1,1,1) == substr(w2,1,1)) {
  368      w1 = substr(w1,2);
  369      w2 = substr(w2,2);
  370       } else {
  371      break;
  372       }
  373       prefixlen++;
  374    } while (length(w1) && length(w2));
  375 
  376    prefix = substr(word1,1,prefixlen);
  377 
  378    do {
  379       l1 = length(w1);
  380       l2 = length(w2);
  381       if (substr(w1,l1,1) == substr(w2,l2,1)) {
  382      w1 = substr(w1,1,l1-1);
  383      w2 = substr(w2,1,l2-1);
  384       } else {
  385      break;
  386       }
  387    } while (l1 && l2);
  388 
  389    suffix = substr(word1, prefixlen+length(w1)+1);
  390 
  391    wordpart[0] = prefix;
  392    wordpart[1] = w1;
  393    wordpart[2] = w2;
  394    wordpart[3] = suffix;
  395 }
  396 
  397 function numdisplay(which, line) {
  398     if (optnums && (line != prevline[which])) {
  399     prevline[which] = line;
  400     return line-1;
  401     }
  402     return "";
  403 }
  404 
  405 function fixesc(line) {
  406 #    Making this a no-op for now -- the change in line-breaking
  407 #    "<br><span...>" => "\n" should make this less necessary.
  408 #    line = gensub(/&(<[^>]*>)/, "\\1\\&", "g", line);
  409 
  410 #   We still have to handle cases where we have a broken up "&lt;" / "&gt;"
  411     gsub(/&l<\/span>t;/, "\\&lt;</span>", line);
  412     gsub(/&g<\/span>t;/, "\\&gt;</span>", line);
  413 
  414     gsub(/&<span class="delete">amp;/, "<span class=\"delete\">\\&", line)
  415     gsub(/&<span class="insert">amp;/, "<span class=\"insert\">\\&", line)
  416 
  417     gsub(/&<span class="delete">lt;/, "<span class=\"delete\">\\&lt;", line);
  418     gsub(/&<span class="delete">gt;/, "<span class=\"delete\">\\&gt;", line);
  419     gsub(/&<span class="insert">lt;/, "<span class=\"insert\">\\&lt;", line);
  420     gsub(/&<span class="insert">gt;/, "<span class=\"insert\">\\&gt;", line);
  421 
  422     gsub(/&<span class="delete">l<\/span>t;/, "<span class=\"delete\">\\&lt;</span>", line);
  423     gsub(/&<span class="delete">g<\/span>t;/, "<span class=\"delete\">\\&gt;</span>", line);
  424     gsub(/&<span class="insert">l<\/span>t;/, "<span class=\"insert\">\\&lt;</span>", line);
  425     gsub(/&<span class="insert">g<\/span>t;/, "<span class=\"insert\">\\&gt;</span>", line);
  426 
  427 
  428     return line;
  429 }
  430 
  431 function chunkdiff(chunk) {
  432    if (difflines1 == 0 && difflines2 == 0) return;
  433 
  434    chunkfile1= sprintf("1/chunk%04d", chunk);
  435    chunkfile2= sprintf("2/chunk%04d", chunk);
  436    printf "" > chunkfile1;
  437    printf "" > chunkfile2;
  438    for (l = 0; l < difflines1; l++) { print stack1[l] >> chunkfile1; }
  439    for (l = 0; l < difflines2; l++) { print stack2[l] >> chunkfile2; }
  440    close(chunkfile1);
  441    close(chunkfile2);
  442 
  443    cmd1 = sprintf("%s -n -2 -w \"<span class=\\\"delete\\\">\"  -x \"</span>\" %s %s", wdiff, chunkfile1, chunkfile2);
  444    cmd2 = sprintf("%s -n -1 -y \"<span class=\\\"insert\\\">\"  -z \"</span>\" %s %s", wdiff, chunkfile1, chunkfile2);
  445 
  446    l=0; while (cmd1 | getline > 0) { stack1[l] = fixesc($0); l++; }
  447    difflines1 = l;
  448    l=0; while (cmd2 | getline > 0) { stack2[l] = fixesc($0); l++; }
  449    difflines2 = l;
  450 
  451    close(cmd1);
  452    close(cmd2);
  453 }
  454 
  455 function flush() {
  456    if (difflines1 || difflines2) {
  457       difftag++;
  458       multiline = (difflines1 > 1) || (difflines2 > 1);
  459       if (multiline && (wdiff != "")) chunkdiff(difftag);
  460 
  461       printf "      <tr id=\"diff%04d\"><td></td></tr>\n", difftag;
  462       for (l = 0; l < difflines1 || l < difflines2; l++) {
  463      if (l in stack1) {
  464         line1 = stack1[l];
  465         delete stack1[l];
  466         linenum1++
  467         if (line1 == "")
  468            if (optwidth > 0) {
  469            line1 = substr("                                                                                                                                                                ",0,optwidth);
  470            } else {
  471            line1 = "                                                                         ";
  472            }
  473      } else {
  474         line1 = "";
  475      }
  476      if (l in stack2) {
  477         line2 = stack2[l];
  478         delete stack2[l];
  479         linenum2++;
  480         if (line2 == "")
  481            if (optwidth > 0) {
  482            line2 = substr("                                                                                                                                                                ",0,optwidth);
  483            } else {
  484            line2 = "                                                                         ";
  485            }
  486      } else {
  487         line2 = "";
  488      }
  489 
  490      if (!multiline || (wdiff == "")) {
  491         worddiff(line1, line2);
  492         line1 = fixesc(sprintf("%s<span class=\"delete\">%s</span>%s", wordpart[0], wordpart[1], wordpart[3]));
  493         line2 = fixesc(sprintf("%s<span class=\"insert\">%s</span>%s", wordpart[0], wordpart[2], wordpart[3]));
  494         # Clean up; remove empty spans
  495         sub(/<span class="delete"><\/span>/,"", line1);
  496         sub(/<span class="insert"><\/span>/,"", line2);
  497      }
  498      left  = sprintf("<td class=\"lineno\">%s</td><td class=\"lblock\">%s</td>", numdisplay(1, linenum1), line1);
  499      right = sprintf("<td class=\"rblock\">%s</td><td class=\"lineno\">%s</td>", line2, numdisplay(2, linenum2));
  500      printf "      <tr>%s<td> </td>%s</tr>\n", left, right;
  501       }
  502    }
  503 }
  504 
  505 function getpage(which, line) {
  506     line = line + ENVIRON["prelines"];
  507     page = "?";
  508     for (p=1; p <= maxpage[which]; p++) {
  509     if (pageend[which,p] == 0) continue;
  510     if (line <= pageend[which,p]) {
  511         page = p;
  512         break;
  513     }
  514     }
  515     return page;
  516 }
  517 
  518 function getpageline(which, line, page) {
  519     if (page == "?") {
  520     return line + ENVIRON["prelines"];
  521     } else {
  522     if (pageend[which,page-1]+0 != 0) {
  523         return line + ENVIRON["prelines"] - pageend[which,page-1] + 3; # numbers of header lines stripped
  524     } else {
  525         return "?";
  526     }
  527     }
  528 }
  529 
  530 function htmlesc(line) {
  531     gsub("&", "\\&amp;", line);
  532     gsub("<", "\\&lt;", line);
  533     gsub(">", "\\&gt;", line);
  534     return line;
  535 }
  536 
  537 function expandtabs(line) {
  538     spaces = "        ";
  539     while (pos = index(line, "\t")) {
  540     sub("\t", substr(spaces, 0, (8-pos%8)), line);
  541     }
  542     return line;
  543 }
  544 
  545 function maybebreakline(line,    width) {
  546     width = optwidth;
  547     new = "";
  548     if (width > 0) {
  549     line = expandtabs(line);
  550     while (length(line) > width) {
  551         new = new htmlesc(substr(line, 1, width)) "\n";
  552         line = substr(line, width+1);
  553     }
  554     }
  555     line = new htmlesc(line) ;
  556     return line;
  557 }
  558 
  559 /^@@/   {
  560        linenum1 = 0 - $2;
  561        linenum2 = 0 + $4;
  562        diffnum ++;
  563        if (linenum1 > 1) {
  564           printf "      <tr><td class=\"lineno\"></td><td class=\"left\"></td><td> </td><td class=\"right\"></td><td class=\"lineno\"></td></tr>\n";
  565           page1 = getpage(1,linenum1);
  566           page2 = getpage(2,linenum2);
  567 
  568           difflabel = sprintf("part-%s", diffnum);
  569           if (page1 == "?") {
  570          posinfo1 = sprintf("<small>skipping to change at</small><a href=\"#%s\"><em> line %s<span class=\"hide\"> &para;</span></em></a>", difflabel, getpageline(1, linenum1, page1));
  571           } else {
  572          posinfo1 = sprintf("<small>skipping to change at</small><a href=\"#%s\"><em> page %s, line %s<span class=\"hide\"> &para;</span></em></a>", difflabel, page1, getpageline(1, linenum1, page1));
  573           }
  574 
  575           if (page2 == "?") {
  576          posinfo2 = sprintf("<small>skipping to change at</small><a href=\"#%s\"><em> line %s<span class=\"hide\"> &para;</span></em></a>", difflabel, getpageline(2, linenum2, page2));
  577           } else {
  578          posinfo2 = sprintf("<small>skipping to change at</small><a href=\"#%s\"><em> page %s, line %s<span class=\"hide\"> &para;</span></em></a>", difflabel, page2, getpageline(2, linenum2, page2));
  579           }
  580 
  581           printf "      <tr id=\"%s\" class=\"change\" ><td></td><th>%s</th><th> </th><th>%s</th><td></td></tr>\n", difflabel, posinfo1, posinfo2;
  582        }
  583     }
  584 
  585 /^---/  {  next; }
  586 /^[+][+][+]/    {  next; }
  587 /^[ ]/  {
  588        line = substr($0, 2);
  589        line = maybebreakline(line);
  590 
  591        flush();
  592        linenum1++;
  593        linenum2++;
  594        printf "      <tr><td class=\"lineno\">%s</td><td class=\"left\">%s</td><td> </td>", numdisplay(1, linenum1), line;
  595        printf "<td class=\"right\">%s</td><td class=\"lineno\">%s</td></tr>\n", line, numdisplay(2, linenum2);
  596        diffcount1 += difflines1
  597        difflines1 = 0
  598        diffcount2 += difflines2
  599        difflines2 = 0
  600     }
  601 /^-/    {
  602        line = substr($0, 2);
  603        line = maybebreakline(line);
  604 
  605        stack1[difflines1] = line;
  606        difflines1++;
  607     }
  608 /^[+]/  {
  609        line = substr($0, 2);
  610        line = maybebreakline(line);
  611 
  612        stack2[difflines2] = line;
  613        difflines2++;
  614     }
  615 
  616 END {
  617        flush();
  618        printf("\n" \
  619 "     <tr><td></td><td class=\"left\"></td><td> </td><td class=\"right\"></td><td></td></tr>\n" \
  620 "     <tr id=\"end\" bgcolor=\"gray\"><th colspan=\"5\" align=\"center\">&nbsp;%s. %s change blocks.&nbsp;</th></tr>\n" \
  621 "     <tr class=\"stats\"><td></td><th><i>%s lines changed or deleted</i></th><th><i> </i></th><th><i>%s lines changed or added</i></th><td></td></tr>\n" \
  622 "     <tr><td colspan=\"5\" align=\"center\" class=\"small\"><br/>This html diff was produced by rfcdiff %s. The latest version is available from <a href=\"http://www.tools.ietf.org/tools/rfcdiff/\" >http://tools.ietf.org/tools/rfcdiff/</a> </td></tr>\n" \
  623 "   </table>\n" \
  624 "   </body>\n" \
  625 "   </html>\n", diffnum?"End of changes":"No changes", difftag, diffcount1, diffcount2, ENVIRON["version"]);
  626     }
  627 ' "$1"
  628 }
  629 
  630 # ----------------------------------------------------------------------
  631 # Generate before/after text output from a context diff
  632 # ----------------------------------------------------------------------
  633 abdiff() {
  634 $AWK '
  635 BEGIN   {
  636        # Read pagecache1
  637        maxpage[1] = 1
  638        pageend[1,0] = 2;
  639        while ( getline < ENVIRON["pagecache1"] > 0) {
  640           pageend[1,$1] = $2;
  641           if ($1+0 > maxpage[1]) maxpage[1] = $1+0;
  642        }
  643 
  644        # Read pagecache2
  645        maxpage[2] = 1
  646        pageend[2,0] = 2;
  647        while ( getline < ENVIRON["pagecache2"] > 0) {
  648           pageend[2,$1] = $2;
  649           if ($1+0 > maxpage[2]) maxpage[2] = $1+0;
  650        }
  651 
  652        base1 = ENVIRON["base1"]
  653        base2 = ENVIRON["base2"]
  654 
  655        section = "INTRODUCTION";
  656        para = 0;
  657 
  658     }
  659 /^\+\+/ {
  660        next;
  661     }
  662 /^\-\-/ {
  663        next;
  664     }
  665 /^ Appendix ./  {
  666        section = $1 " " $2;
  667        para = 0;
  668     }
  669 /^  ? ? ?[0-9]+(\.[0-9]+)*\.? / {
  670        section = "Section " $1;
  671        para = 0;
  672     }
  673 /^ ?$/  {
  674        if (inpara) {
  675           printf "\n%s, paragraph %s:\n", section, para;
  676           print "OLD:\n"
  677           print oldpara
  678           print "NEW:\n"
  679           print newpara
  680        }
  681        oldpara = "";
  682        newpara = "";
  683        para ++;
  684        inpara = 0
  685     }
  686 /^ ./   {
  687        oldpara = oldpara $0 "\n"
  688        newpara = newpara $0 "\n"
  689     }
  690 /^\-/   {
  691        sub(/^./, " ");
  692        oldpara = oldpara $0 "\n"
  693        inpara++;
  694     }
  695 /^\+/   {
  696        sub(/^./, " ");
  697        newpara = newpara $0 "\n"
  698        inpara++;
  699     }
  700 END {
  701        if (inpara) {
  702           printf "\n%s, paragraph %s:\n", section, para;
  703           print "OLD:\n"
  704           print oldpara
  705           print "NEW:\n"
  706           print newpara
  707        }    
  708     }
  709 '
  710 }
  711 
  712 
  713 # ----------------------------------------------------------------------
  714 # Utility to extract keyword info
  715 # ----------------------------------------------------------------------
  716 extract() {
  717     $AWK -v keyword="$1" '
  718     BEGIN {
  719         # print "Keyword", keyword;
  720     }
  721     /^# [A-Z]/ {
  722         # print "New key", $2;
  723         if ($2 == keyword ":" ) { output=1; } else { output=0; }
  724         # print "Output", output;
  725     }
  726     /^#\t/  {
  727         # print "Content", output, $0;
  728         if ( output ) {
  729         sub(/^#/,"");
  730         print;
  731         }
  732     }
  733     {
  734         next;
  735     }
  736 
  737     ' "$2"
  738 }
  739 # ----------------------------------------------------------------------
  740 # Utility to start a browser
  741 # ----------------------------------------------------------------------
  742 
  743 browse() {
  744     if   [ "$(uname)" = "Darwin" ]; then
  745     open "$@"
  746     else
  747     browser=$(lookfor firefox phoenix MozillaFirebird mozilla opera Netscape netscape dillo)
  748 
  749     if [ -z "$browser" ]; then
  750         echo "Couldn't find any browser, can't display $*."
  751         exit 1
  752     fi
  753 
  754     # make sure file name is absolute
  755     if [ "${1#/}" = "$1" ]; then
  756         # not absolute path, add pwd
  757         arg="file://$PWD/$1"
  758     else
  759         arg="file://$1"
  760     fi
  761 
  762 
  763     # see if a browser is running, act accordingly
  764     $browser -remote "ping()" >/dev/null 2>&1
  765     if [ $? -eq 0 ]; then
  766         # use running instance
  767         $browser -raise -remote "openurl($arg, new-tab)"
  768     else
  769         # error exit: no running instance
  770         echo "Starting web browser."
  771 
  772         $browser "$arg" >/dev/null 2>&1 &
  773     fi
  774     fi
  775 }
  776 
  777 
  778 # ----------------------------------------------------------------------
  779 # Utility for error exit
  780 # ----------------------------------------------------------------------
  781 die() {
  782    echo $*;
  783    exit 1;
  784 }
  785 
  786 # ----------------------------------------------------------------------
  787 # Process options
  788 # ----------------------------------------------------------------------
  789 
  790 # Default values
  791 opthtml=1; optdiff=0; optchbars=0; optwdiff=0; optshow=0; optnowdiff=0;
  792 optkeep=0; optinfo=0; optwidth=0;  optnums=0;  optbody=0; optabdiff=0;
  793 optstrip=1; opthwdiff=0; optlinks=0;
  794 optoldcolour="red"; optnewcolour="green"; optlarger=""
  795 optstdout=0;
  796 
  797 while [ $# -gt 0 ]; do
  798    case "$1" in
  799       --html)   opthtml=1; optdiff=0; optchbars=0; optwdiff=0; opthwdiff=0; optabdiff=0;;
  800       --diff)   opthtml=0; optdiff=1; optchbars=0; optwdiff=0; opthwdiff=0; optabdiff=0;;
  801       --chbars) opthtml=0; optdiff=0; optchbars=1; optwdiff=0; opthwdiff=0; optabdiff=0;;
  802       --wdiff)  opthtml=0; optdiff=0; optchbars=0; optwdiff=1; opthwdiff=0; optabdiff=0;;
  803       --hwdiff) opthtml=0; optdiff=0; optchbars=0; optwdiff=0; opthwdiff=1; optabdiff=0;;
  804       --changes)opthtml=0; optdiff=0; optchbars=0; optwdiff=0; opthwdiff=0; optabdiff=1;;
  805       --abdiff) opthtml=0; optdiff=0; optchbars=0; optwdiff=0; opthwdiff=0; optabdiff=1;;
  806       --ab-diff)opthtml=0; optdiff=0; optchbars=0; optwdiff=0; opthwdiff=0; optabdiff=1;;
  807       --rfc-editor-diff)opthtml=0; optdiff=0; optchbars=0; optwdiff=0; opthwdiff=0; optabdiff=1;;
  808       --version)echo -e "$basename\t$version\t$progdate"; exit 0;;
  809       --browse) optshow=1;;
  810       --nowdiff)optnowdiff=1;;
  811       --keep)   optkeep=1;;
  812       --info)   optinfo=1; keyword=$2; shift;;
  813       --help)   optinfo=1; keyword="Usage";;
  814       --width)  optwidth=$2; shift;;
  815       --oldcolor)     optoldcolour=$2; shift;;
  816       --oldcolour)    optoldcolour=$2; shift;;
  817       --newcolor)     optnewcolour=$2; shift;;
  818       --newcolour)    optnewcolour=$2; shift;;
  819       --larger)       optlarger='size="+1"';;
  820       --linenum)optnums=1;;
  821       --body)   optbody=1;;
  822       --nostrip)optstrip=0; optbody=0;;
  823       --stdout) optstdout=1;;
  824       --links)  optlinks=1;;
  825       --no-space-changes) optnospacechange=1;;
  826       --ignore-whitespace) optignorewhite=1;;
  827       --wdiff-args) optwdiffargs=$2; shift;;
  828       --)   shift; break;;
  829 
  830       -v) echo "$basename $version"; exit 0;;
  831       -*) echo "Unrecognized option: $1";
  832       exit 1;;
  833       *)  break;;
  834    esac
  835    shift
  836 done
  837 
  838 export optwidth
  839 export optnums
  840 export optlinks
  841 
  842 # ----------------------------------------------------------------------
  843 # Determine output file name. Maybe output usage and exit.
  844 # ----------------------------------------------------------------------
  845 #set -x
  846 
  847 if [ $optinfo -gt 0 ]; then
  848    extract $keyword $0
  849    exit
  850 fi
  851 if [ $# -ge 2 ]; then
  852    if [ "$1" = "$2" ]; then
  853       echo "The files are the same file"
  854       exit
  855    fi
  856    export base1=$(basename "$1")
  857    export base2=$(basename "$2")
  858    outbase=$(worddiff "$base2" "$base1" "%s%s-from-%s")
  859 else
  860    extract Usage $0
  861    exit 1
  862 fi
  863 
  864 
  865 # ----------------------------------------------------------------------
  866 # create working directory.
  867 # ----------------------------------------------------------------------
  868 mkdir $workdir || die "$0: Error: Failed to create temporary directory '$workdir'."
  869 mkdir $workdir/1 || die "$0: Error: Failed to create temporary directory '$workdir/1'."
  870 mkdir $workdir/2 || die "$0: Error: Failed to create temporary directory '$workdir/2'."
  871 
  872 # ----------------------------------------------------------------------
  873 # If any of the files is an http or ftp URL we download it, else copy it
  874 # ----------------------------------------------------------------------
  875 
  876 wgetbin=$(lookfor wget)
  877 dowgetarg1=0
  878 dowgetarg2=0
  879 
  880 if [ -n "$wgetbin" ]; then
  881    if [ "${1#http://}" != "$1" ]; then dowgetarg1=1; fi
  882    if [ "${1#ftp://}" != "$1" ]; then dowgetarg1=1; fi
  883 
  884    if [ "${2#http://}" != "$2" ]; then dowgetarg2=1; fi
  885    if [ "${2#ftp://}" != "$2" ]; then dowgetarg2=1; fi
  886 fi
  887 
  888 
  889 if [ $dowgetarg1 -gt 0 ]; then
  890    $wgetbin -nv "$1" -O $workdir/1/"$base1"
  891 else
  892    cp "$1" $workdir/1/"$base1"
  893 fi
  894 
  895 if [ $dowgetarg2 -gt 0 ]; then
  896    $wgetbin -nv "$2" -O $workdir/2/"$base2"
  897 else
  898    cp "$2" $workdir/2/"$base2"
  899 fi
  900 
  901 # ----------------------------------------------------------------------
  902 # Remove UTF-8 BOMs
  903 # ----------------------------------------------------------------------
  904 
  905 stripbom $workdir/1/"$base1" > $workdir/1/"$base1".nobom
  906 mv -f $workdir/1/"$base1".nobom $workdir/1/"$base1"
  907 stripbom $workdir/2/"$base2" > $workdir/2/"$base2".nobom
  908 mv -f $workdir/2/"$base2".nobom $workdir/2/"$base2"
  909 
  910 # ----------------------------------------------------------------------
  911 # Maybe strip headers/footers from both files
  912 # ----------------------------------------------------------------------
  913 
  914 if [ $optstrip -gt 0 ]; then
  915    export which=1
  916    strip $workdir/1/"$base1" > $workdir/1/"$base1".stripped
  917    mv -f $workdir/1/"$base1".stripped $workdir/1/"$base1"
  918    export which=2
  919    strip $workdir/2/"$base2" > $workdir/2/"$base2".stripped
  920    mv -f $workdir/2/"$base2".stripped $workdir/2/"$base2"
  921 fi
  922 
  923 # ----------------------------------------------------------------------
  924 # Maybe do html quoting
  925 # ----------------------------------------------------------------------
  926 
  927 if [ $opthwdiff -gt 0 ]; then
  928    sed -e 's/&/&amp;/g' -e 's/</\&lt;/g' -e 's/>/\&gt;/g' $workdir/1/"$base1" > $workdir/1/"$base1".quoted
  929    mv -f $workdir/1/"$base1".quoted $workdir/1/"$base1"
  930    sed -e 's/&/&amp;/g' -e 's/</\&lt;/g' -e 's/>/\&gt;/g' $workdir/2/"$base2" > $workdir/2/"$base2".quoted
  931    mv -f $workdir/2/"$base2".quoted $workdir/2/"$base2"
  932 fi
  933 
  934 # ----------------------------------------------------------------------
  935 # Maybe strip preamble (title, boilerplate and table of contents) and
  936 # postamble (Intellectual Property Statement, Disclaimer etc)
  937 # ----------------------------------------------------------------------
  938 if [ $optbody -gt 0 ]; then
  939    bodystrip $workdir/1/"$base1" > $workdir/1/"$base1".stripped
  940    mv $workdir/1/"$base1".stripped $workdir/1/"$base1"
  941    bodystrip $workdir/2/"$base2" > $workdir/2/"$base2".stripped
  942    mv $workdir/2/"$base2".stripped $workdir/2/"$base2"
  943 fi
  944 
  945 # ----------------------------------------------------------------------
  946 # Get output file name
  947 # ----------------------------------------------------------------------
  948 if [ "$3" ]; then
  949   outfile="$3"
  950 else
  951     if [ $opthtml -gt 0 ]; then
  952       outfile=./"$outbase".diff.html
  953     fi
  954     if [ $optchbars -gt 0 ]; then
  955       outfile=./"$outbase".chbar
  956     fi
  957     if [ $optdiff -gt 0 ]; then
  958       outfile=./"$outbase".diff
  959     fi
  960     if [ $optabdiff -gt 0 ]; then
  961       outfile=./"$outbase".changes
  962     fi
  963     if [ $opthwdiff -gt 0 ]; then
  964       outfile=./"$outbase".wdiff.html
  965     fi
  966 fi
  967 if [ "$outfile" ]; then
  968    tempout=./$(basename "$outfile")
  969 fi
  970 
  971 # ----------------------------------------------------------------------
  972 # Check if we can use wdiff for block diffs
  973 # ----------------------------------------------------------------------
  974 if [ $optnowdiff -eq 0 ]; then
  975    wdiffbin=$(lookfor wdiff)
  976    if [ -n "$wdiffbin" ]; then
  977       wdiffver=$($wdiffbin --version 2>/dev/null | egrep "(wdiff|GNU).+[0-9]\.[0-9]")
  978       if [ -z "$wdiffver" ]; then
  979         wdiffbin="";
  980     echo -en "\n  Found wdiff, but it reported no recognisable version."
  981       fi
  982    else
  983       echo -en "\n  Couldn't find wdiff."
  984    fi
  985    if [ -z "$wdiffbin" ]; then echo " Falling back to builtin diff colouring..."; fi
  986    export wdiffbin
  987    export wdiffver
  988    #echo "Found wdiff at $wdiffbin"
  989 fi
  990 
  991 # ----------------------------------------------------------------------
  992 # Get some misc. info
  993 # ----------------------------------------------------------------------
  994 uname=$(uname -a)
  995 export uname
  996 awkbin=$AWK
  997 export awkbin
  998 awkver=$( { $AWK --version 2>/dev/null || $AWK -V 2>/dev/null; } | head -n 1)
  999 export awkver
 1000 diffbin=$(lookfor diff)
 1001 export diffbin
 1002 diffver=$(diff --version | head -n 1)
 1003 export diffver
 1004 
 1005 # ----------------------------------------------------------------------
 1006 # Set up the JS code to page through chunks.
 1007 # ----------------------------------------------------------------------
 1008 # If there's a rfcdiff.js script in the current directory, include that
 1009 # in the output html, otherwise use the inline script expansion below.
 1010 if [ -e rfcdiff.js ]; then
 1011     jsinc="
 1012     <script>
 1013     $(< rfcdiff.js )
 1014     </script>
 1015     "
 1016 else
 1017     jsinc=$(cat <<'ENDOFSCRIPT'
 1018 <script src="rfcdiff.js"></script>
 1019 ENDOFSCRIPT
 1020 )
 1021 fi
 1022 export jsinc
 1023 
 1024 
 1025 # ----------------------------------------------------------------------
 1026 # Check that we don't have a broken awk
 1027 # ----------------------------------------------------------------------
 1028 if [ $opthtml -gt 0 -a "${uname%% *}" == "Darwin" -a "$awkver" == "awk version 20070501" ]; then
 1029     echo -e  "\n  Oops.  Awk version 20070501 on OS X doesn't work with rfcdiff's html mode.\n  To make rfcdiff work, you could install Gnu Awk (gawk), for instance using\n  MacPorts, http://www.macports.org/."
 1030     exit 1
 1031 fi
 1032 
 1033 # ----------------------------------------------------------------------
 1034 # Do diff
 1035 # ----------------------------------------------------------------------
 1036 
 1037 origdir=$PWD
 1038 cd $workdir
 1039 if cmp 1/"$base1" 2/"$base2" >/dev/null; then
 1040    echo ""
 1041    echo "The files are identical."
 1042 fi
 1043 
 1044 if [ $opthtml -gt 0 ]; then
 1045    diff -Bd ${optnospacechange:+-b} ${optignorewhite:+-w} -U $prelines 1/"$base1" 2/"$base2" | tee $workdir/diff | htmldiff > "$tempout"
 1046 fi
 1047 if [ $optchbars -gt 0 ]; then
 1048    diff -Bwd -U 10000 1/"$base1" 2/"$base2" | tee $workdir/diff | grep -v "^-" | tail -n +3 | sed 's/^+/|/' > "$tempout"
 1049 fi
 1050 if [ $optdiff -gt 0 ]; then
 1051    diff -Bwd -U $prelines 1/"$base1" 2/"$base2" | tee $workdir/diff > "$tempout"
 1052 fi
 1053 if [ $optabdiff -gt 0 ]; then
 1054    diff -wd -U 1000 1/"$base1" 2/"$base2" | tee $workdir/diff | abdiff
 1055 fi
 1056 if [ $optwdiff -gt 0 ]; then
 1057    wdiff -a $optwdiffargs 1/"$base1" 2/"$base2"
 1058 fi
 1059 if [ $opthwdiff -gt 0 ]; then
 1060     echo "<html><head><title>wdiff "$base1" "$base2"</title></head><body>"  >  "$tempout"
 1061     echo "<pre>"                                >> "$tempout"
 1062     wdiff -w "<strike><font color='$optoldcolour' $optlarger>" -x "</font></strike>"    \
 1063           -y "<strong><font color='$optnewcolour' $optlarger>" -z "</font></strong>"    \
 1064       1/"$base1" 2/"$base2"                         >> "$tempout"
 1065     echo "</pre>"                               >> "$tempout"
 1066     echo "</body></html>"                           >> "$tempout"
 1067 fi
 1068 
 1069 if [ $optstdout -gt 0 ]; then
 1070   cat "$tempout"
 1071   rm  "$tempout"
 1072 else
 1073   cd "$origdir"; if [ -f $workdir/"$tempout" ]; then mv $workdir/"$tempout" "$outfile"; fi
 1074 fi
 1075 
 1076 if [ $optshow -gt 0 ]; then
 1077    browse "$outfile"
 1078 fi
 1079 
 1080 if [ $optkeep -eq 0 ]; then
 1081    if [ -f $pagecache1 ]; then rm $pagecache1; fi
 1082    if [ -f $pagecache2 ]; then rm $pagecache2; fi
 1083    rm -fr $workdir/1
 1084    rm -fr $workdir/2
 1085    if [ -f $workdir/diff ]; then
 1086       rm $workdir/diff
 1087    fi
 1088    rmdir $workdir
 1089 else
 1090    cd /tmp
 1091    tar czf $basename-$$.tgz $basename-$$
 1092    echo "
 1093    Temporary workfiles have been left in $workdir/, and packed up in $workdir.tgz"
 1094 fi
 1095