home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / emacs-19.28-src.tgz / tar.out / fsf / emacs / lib-src / rcs2log < prev    next >
Text File  |  1996-09-28  |  12KB  |  463 lines

  1. #!/bin/sh
  2.  
  3. # RCS to ChangeLog generator
  4.  
  5. # Generate a change log prefix from RCS files and the ChangeLog (if any).
  6. # Output the new prefix to standard output.
  7. # You can edit this prefix by hand, and then prepend it to ChangeLog.
  8.  
  9. # Ignore log entries that start with `#'.
  10. # Clump together log entries that start with `{topic} ',
  11. # where `topic' contains neither white space nor `}'.
  12.  
  13. # Author: Paul Eggert <eggert@twinsun.com>
  14.  
  15. # $Id: rcs2log,v 1.18 1994/08/15 22:44:10 eggert Exp $
  16.  
  17. # Copyright 1992, 1993 Free Software Foundation, Inc.
  18.  
  19. # This program is free software; you can redistribute it and/or modify
  20. # it under the terms of the GNU General Public License as published by
  21. # the Free Software Foundation; either version 2, or (at your option)
  22. # any later version.
  23. # This program is distributed in the hope that it will be useful,
  24. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  25. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  26. # GNU General Public License for more details.
  27. # You should have received a copy of the GNU General Public License
  28. # along with this program; see the file COPYING.  If not, write to
  29. # the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  30.  
  31. nl='
  32. '
  33.  
  34. # Parse options.
  35.  
  36. # defaults
  37. : ${TMPDIR=/tmp}
  38. hostname= # name of local host (if empty, will deduce it later)
  39. indent=8 # indent of log line
  40. initialize_fullname= # awk assignments to set up fullname array
  41. initialize_mailaddr= # awk assignments to set up mailaddr array
  42. length=79 # suggested max width of log line
  43. logins= # login names for people we know fullnames and mailaddresses of
  44. loginsout= # temporary file holding sorted logins
  45. rlog_options= # options to pass to rlog
  46. tabwidth=8 # width of horizontal tab
  47.  
  48. while :
  49. do
  50.     case $1 in
  51.     -i)    indent=${2?};;
  52.     -h)    hostname=${2?};;
  53.     -l)    length=${2?};;
  54.     -n)    logins=$logins$nl${2?}
  55.         loginsout=$TMPDIR/rcs2log$$l
  56.         case $2${3?}${4?} in
  57.         *\"* | *\\* | *"$nl"*)
  58.             echo >&2 "$0: -n '$2' '$3' '$4': special characters not allowed"
  59.             exit 1
  60.         esac
  61.         initialize_fullname="$initialize_fullname
  62.             fullname[\"$2\"] = \"$3\""
  63.         initialize_mailaddr="$initialize_mailaddr
  64.             mailaddr[\"$2\"] = \"$4\""
  65.         shift; shift;;
  66.     -r)    rlog_options=$rlog_options$nl${2?};;
  67.     -t)    tabwidth=${2?};;
  68.     -*)    echo >&2 "$0: usage: $0 [options] [file ...]
  69. Options:
  70.     [-h hostname] [-i indent] [-l length] [-n login fullname mailaddr]...
  71.     [-r rlog_option]... [-t tabwidth]"
  72.         exit 1;;
  73.     *)    break
  74.     esac
  75.     shift; shift
  76. done
  77.  
  78. month_data='
  79.     m[0]="Jan"; m[1]="Feb"; m[2]="Mar"
  80.     m[3]="Apr"; m[4]="May"; m[5]="Jun"
  81.     m[6]="Jul"; m[7]="Aug"; m[8]="Sep"
  82.     m[9]="Oct"; m[10]="Nov"; m[11]="Dec"
  83.  
  84.     # days in non-leap year thus far, indexed by month (0-12)
  85.     mo[0]=0; mo[1]=31; mo[2]=59; mo[3]=90
  86.     mo[4]=120; mo[5]=151; mo[6]=181; mo[7]=212
  87.     mo[8]=243; mo[9]=273; mo[10]=304; mo[11]=334
  88.     mo[12]=365
  89. '
  90.  
  91.  
  92. # Log into $rlogout the revisions checked in since the first ChangeLog entry.
  93.  
  94. date=1970
  95. if test -s ChangeLog
  96. then
  97.     # Add 1 to seconds to avoid duplicating most recent log.
  98.     e='
  99.         /^... ... [ 0-9][0-9] [ 0-9][0-9]:[0-9][0-9]:[0-9][0-9] [0-9]+ /{
  100.             '"$month_data"'
  101.             year = $5
  102.             for (i=0; i<=11; i++) if (m[i] == $2) break
  103.             dd = $3
  104.             hh = substr($0,12,2)
  105.             mm = substr($0,15,2)
  106.             ss = substr($0,18,2)
  107.             ss++
  108.             if (ss == 60) {
  109.                 ss = 0
  110.                 mm++
  111.                 if (mm == 60) {
  112.                     mm = 0
  113.                     hh++
  114.                     if (hh == 24) {
  115.                         hh = 0
  116.                         dd++
  117.                         monthdays = mo[i+1] - mo[i]
  118.                         if (i == 1 && year%4 == 0 && (year%100 != 0 || year%400 == 0)) monthdays++
  119.                         if (dd == monthdays + 1) {
  120.                             dd = 1
  121.                             i++
  122.                             if (i == 12) {
  123.                                 i = 0
  124.                                 year++
  125.                             }
  126.                         }
  127.                     }
  128.                 }
  129.             }
  130.             printf "%d/%02d/%02d %02d:%02d:%02d\n", year, i+1, dd, hh, mm, ss
  131.             exit
  132.         }
  133.     '
  134.     d=`awk "$e" <ChangeLog` || exit
  135.     case $d in
  136.     ?*) date=$d
  137.     esac
  138. fi
  139. datearg="-d>$date"
  140.  
  141. repository=
  142. rlog=rlog
  143. case $CVSROOT in
  144. ?*)
  145.     if test -d "$CVSROOT" && test -f CVS/Repository
  146.     then
  147.         r=`cat <CVS/Repository` || exit
  148.         if test -d "$CVSROOT/$r"
  149.         then
  150.             repository=$CVSROOT/$r
  151.             rlog='cvs log'
  152.         fi
  153.     fi
  154. esac
  155.  
  156. # With no arguments, examine all files under the RCS directory.
  157. case $# in
  158. 0)
  159.     case $repository in
  160.     '')
  161.         files=
  162.         for file in RCS/.* RCS/* .*,v *,v
  163.         do
  164.             case $file in
  165.             RCS/. | RCS/..) continue;;
  166.             RCS/.\* | RCS/\* | .\*,v | \*,v) test -f "$file" || continue
  167.             esac
  168.             files=$files$nl$file
  169.         done
  170.         case $files in
  171.         '') exit 0
  172.         esac
  173.         oldIFS=$IFS
  174.         IFS=$nl
  175.         set $files
  176.         IFS=$oldIFS
  177.     esac
  178. esac
  179.  
  180. rlogout=$TMPDIR/rcs2log$$r
  181. trap exit 1 2 13 15
  182. trap "rm -f $loginsout $rlogout; exit 1" 0
  183.  
  184. $rlog "$datearg" $rlog_options ${1+"$@"} >$rlogout || exit
  185.  
  186.  
  187. # Get the full name of each author the logs mention, and set initialize_fullname
  188. # to awk code that initializes the `fullname' awk associative array.
  189. # Warning: foreign authors (i.e. not known in the passwd file) are mishandled;
  190. # you have to fix the resulting output by hand.
  191.  
  192. case $loginsout in
  193. ?*) sort -u -o $loginsout <<EOF || exit
  194. $logins
  195. EOF
  196. esac
  197. authors=`
  198.     sed -n 's|^date: *[0-9]*[-/][0-9][0-9][-/][0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9][-+0-9]*; *author: *\([^; ]*\).*|\1|p' <$rlogout |
  199.     case $loginsout in
  200.     '') sort -u;;
  201.     ?*) sort -u | comm -23 - $loginsout
  202.     esac
  203. `
  204. case $authors in
  205. ?*)
  206.     initialize_author=
  207.     for author in $authors
  208.     do
  209.         initialize_author="$initialize_author
  210.             author[\"$author\"] = 1
  211.         "
  212.     done
  213.     awkscript='
  214.         BEGIN {
  215.             alphabet = "abcdefghijklmnopqrstuvwxyz"
  216.             ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  217.             '"$initialize_author"'
  218.         }
  219.         {
  220.             if (author[$1]) {
  221.                 fullname = $5
  222.                 if (fullname ~ /[0-9]+-[^(]*\([0-9]+\)$/) {
  223.                     # Remove the junk from fullnames like "0000-Admin(0000)".
  224.                     fullname = substr(fullname, index(fullname, "-") + 1)
  225.                     fullname = substr(fullname, 1, index(fullname, "(") - 1)
  226.                 }
  227.                 if (fullname ~ /,[^ ]/) {
  228.                     # Some sites put comma-separated junk after the fullname.
  229.                     # Remove it, but leave "Bill Gates, Jr" alone.
  230.                     fullname = substr(fullname, 1, index(fullname, ",") - 1)
  231.                 }
  232.                 abbr = index(fullname, "&")
  233.                 if (abbr) {
  234.                     a = substr($1, 1, 1)
  235.                     A = a
  236.                     i = index(alphabet, a)
  237.                     if (i) A = substr(ALPHABET, i, 1)
  238.                     fullname = substr(fullname, 1, abbr-1) A substr($1, 2) substr(fullname, abbr+1)
  239.                 }
  240.                 printf "fullname[\"%s\"] = \"%s\"\n", $1, fullname
  241.                 author[$1] = 0
  242.             }
  243.         }
  244.     '
  245.  
  246.     initialize_fullname=`
  247.         (cat /etc/passwd; ypmatch $authors passwd) 2>/dev/null |
  248.         awk -F: "$awkscript"
  249.     `$initialize_fullname
  250. esac
  251.  
  252.  
  253. # Function to print a single log line.
  254. # We don't use awk functions, to stay compatible with old awk versions.
  255. # `Log' is the log message (with \n replaced by \r).
  256. # `files' contains the affected files.
  257. printlogline='{
  258.  
  259.     # Following the GNU coding standards, rewrite
  260.     #    * file: (function): comment
  261.     # to
  262.     #    * file (function): comment
  263.     if (Log ~ /^\([^)]*\): /) {
  264.         i = index(Log, ")")
  265.         files = files " " substr(Log, 1, i)
  266.         Log = substr(Log, i+3)
  267.     }
  268.  
  269.     # If "label: comment" is too long, break the line after the ":".
  270.     sep = " "
  271.     if ('"$length"' <= '"$indent"' + 1 + length(files) + index(Log, CR)) sep = "\n" indent_string
  272.  
  273.     # Print the label.
  274.     printf "%s*%s:", indent_string, files
  275.  
  276.     # Print each line of the log, transliterating \r to \n.
  277.     while ((i = index(Log, CR)) != 0) {
  278.         logline = substr(Log, 1, i-1)
  279.         if (logline ~ /[^     ]/) {
  280.             printf "%s%s\n", sep, logline
  281.         } else {
  282.             print ""
  283.         }
  284.         sep = indent_string
  285.         Log = substr(Log, i+1)
  286.     }
  287. }'
  288.  
  289. case $hostname in
  290. '')
  291.     hostname=`(
  292.         hostname || uname -n || uuname -l || cat /etc/whoami
  293.     ) 2>/dev/null` || {
  294.         echo >&2 "$0: cannot deduce hostname"
  295.         exit 1
  296.     }
  297. esac
  298.  
  299.  
  300. # Process the rlog output, generating ChangeLog style entries.
  301.  
  302. # First, reformat the rlog output so that each line contains one log entry.
  303. # Transliterate \n to \r so that multiline entries fit on a single line.
  304. # Discard irrelevant rlog output.
  305. awk <$rlogout '
  306.     /^Working file:/ { filename = $3 }
  307.     /^date: /, /^(-----------*|===========*)$/ {
  308.         if ($0 ~ /^branches: /) { next }
  309.         if ($0 ~ /^date: [0-9][- +\/0-9:]*;/) {
  310.             date = $2
  311.             if (date ~ /-/) {
  312.                 # An ISO format date.  Replace all "-"s with "/"s.
  313.                 newdate = ""
  314.                 while ((i = index(date, "-")) != 0) {
  315.                     newdate = newdate substr(date, 1, i-1) "/"
  316.                     date = substr(date, i+1)
  317.                 }
  318.                 date = newdate date
  319.             }
  320.             # Ignore any time zone; ChangeLog has no room for it.
  321.             time = substr($3, 1, 8)
  322.             author = substr($5, 1, length($5)-1)
  323.             printf "%s %s %s %s %c", filename, date, time, author, 13
  324.             next
  325.         }
  326.         if ($0 ~ /^(-----------*|===========*)$/) { print ""; next }
  327.         printf "%s%c", $0, 13
  328.     }
  329. ' |
  330.  
  331. # Now each line is of the form
  332. # FILENAME YYYY/MM/DD HH:MM:SS AUTHOR \rLOG
  333. #    where \r stands for a carriage return,
  334. #    and each line of the log is terminated by \r instead of \n.
  335. # Sort the log entries, first by date+time (in reverse order),
  336. # then by author, then by log entry, and finally by file name (just in case).
  337. sort +1 -3r +3 +0 |
  338.  
  339. # Finally, reformat the sorted log entries.
  340. awk '
  341.     BEGIN {
  342.         # Some awks do not understand "\r" or "\013", so we have to
  343.         # put a carriage return directly in the file.
  344.         CR="" # <-- There is a single CR between the " chars here.
  345.  
  346.         # Initialize the fullname and mailaddr associative arrays.
  347.         '"$initialize_fullname"'
  348.         '"$initialize_mailaddr"'
  349.  
  350.         # Initialize indent string.
  351.         indent_string = ""
  352.         i = '"$indent"'
  353.         if (0 < '"$tabwidth"')
  354.             for (;  '"$tabwidth"' <= i;  i -= '"$tabwidth"')
  355.                 indent_string = indent_string "\t"
  356.         while (1 <= i--)
  357.             indent_string = indent_string " "
  358.  
  359.         # Set up date conversion tables.
  360.         # RCS uses a nice, clean, sortable format,
  361.         # but ChangeLog wants the traditional, ugly ctime format.
  362.  
  363.         # January 1, 0 AD (Gregorian) was Saturday = 6
  364.         EPOCH_WEEKDAY = 6
  365.         # Of course, there was no 0 AD, but the algorithm works anyway.
  366.  
  367.         w[0]="Sun"; w[1]="Mon"; w[2]="Tue"; w[3]="Wed"
  368.         w[4]="Thu"; w[5]="Fri"; w[6]="Sat"
  369.  
  370.         '"$month_data"'
  371.     }
  372.  
  373.     {
  374.         newlog = substr($0, 1 + index($0, CR))
  375.  
  376.         # Ignore log entries prefixed by "#".
  377.         if (newlog ~ /^#/) { next }
  378.  
  379.         if (Log != newlog || date != $2 || author != $4) {
  380.  
  381.             # The previous log and this log differ.
  382.  
  383.             # Print the old log.
  384.             if (date != "") '"$printlogline"'
  385.  
  386.             # Logs that begin with "{clumpname} " should be grouped together,
  387.             # and the clumpname should be removed.
  388.             # Extract the new clumpname from the log header,
  389.             # and use it to decide whether to output a blank line.
  390.             newclumpname = ""
  391.             sep = "\n"
  392.             if (date == "") sep = ""
  393.             if (newlog ~ /^\{[^     }]*}[     ]/) {
  394.                 i = index(newlog, "}")
  395.                 newclumpname = substr(newlog, 1, i)
  396.                 while (substr(newlog, i+1) ~ /^[     ]/) i++
  397.                 newlog = substr(newlog, i+1)
  398.                 if (clumpname == newclumpname) sep = ""
  399.             }
  400.             printf sep
  401.             clumpname = newclumpname
  402.  
  403.             # Get ready for the next log.
  404.             Log = newlog
  405.             if (files != "")
  406.                 for (i in filesknown)
  407.                     filesknown[i] = 0
  408.             files = ""
  409.         }
  410.         if (date != $2  ||  author != $4) {
  411.             # The previous date+author and this date+author differ.
  412.             # Print the new one.
  413.             date = $2
  414.             author = $4
  415.  
  416.             # Convert nice RCS date like "1992/01/03 00:03:44"
  417.             # into ugly ctime date like "Fri Jan  3 00:03:44 1992".
  418.             # Calculate day of week from Gregorian calendar.
  419.             i = index($2, "/")
  420.             year = substr($2, 1, i-1) + 0
  421.             monthday = substr($2, i+1)
  422.             i = index(monthday, "/")
  423.             month = substr(monthday, 1, i-1) + 0
  424.             day = substr(monthday, i+1) + 0
  425.             leap = 0
  426.             if (2 < month && year%4 == 0 && (year%100 != 0 || year%400 == 0)) leap = 1
  427.             days_since_Sunday_before_epoch = EPOCH_WEEKDAY + year * 365 + int((year + 3) / 4) - int((year + 99) / 100) + int((year + 399) / 400) + mo[month-1] + leap + day - 1
  428.  
  429.             # Print "date  fullname  (email address)".
  430.             # Get fullname and email address from associative arrays;
  431.             # default to author and author@hostname if not in arrays.
  432.             if (fullname[author])
  433.                 auth = fullname[author]
  434.             else
  435.                 auth = author
  436.             printf "%s %s %2d %s %d  %s  ", w[days_since_Sunday_before_epoch%7], m[month-1], day, $3, year, auth
  437.             if (mailaddr[author])
  438.                 printf "<%s>\n\n", mailaddr[author]
  439.             else
  440.                 printf "<%s@%s>\n\n", author, "'"$hostname"'"
  441.         }
  442.         if (! filesknown[$1]) {
  443.             filesknown[$1] = 1
  444.             if (files == "") files = " " $1
  445.             else files = files ", " $1
  446.         }
  447.     }
  448.     END {
  449.         # Print the last log.
  450.         if (date != "") {
  451.             '"$printlogline"'
  452.             printf "\n"
  453.         }
  454.     }
  455. ' &&
  456.  
  457.  
  458. # Exit successfully.
  459.  
  460. exec rm -f $loginsout $rlogout
  461.