#!/bin/sh
#

#########################################################################
#                                                                       #
#  Copyright 1994, University of Cambridge Computer Laboratory  	#
#                                                                       #
#  All Rights Reserved.						        #
#                                                                       #
#########################################################################
#
# ID :    $Id: middl2manual 1.1 Thu, 18 Feb 1999 14:20:06 +0000 dr10009 $
# Author: dme
#
# middl2manual iffiles...
#
# middl2manual writes to the standard output LaTeX source suitable
# for inclusion in a higher-level manual for the interfaces given
# by the "iffiles" arguments.
# 
# It assigns interfaces to chapters and to sections within chapters by
# scanning them for "\chapter{}" and "\section{}" directives.  These should
# normally be in the first comment block, which is ignored by middl2tex.
#
# If an interface is given no explicit section, the default section name
# is that of the interface.
#
# If an interface is given no explicit chapter, the default chapter name
# is "Miscellaneous Interfaces".  It should be rare that this is what you
# actually want - give your interface an explicit \chapter{}.
#
# Chapters and sections are then output in an order determined by
# topologically sorting the NEEDS/EXTENDS dependencies between interfaces.
# Interfaces which share a section with other interfaces each get their
# own subsection.  The (sub)sections "\input{}" the interface .tex files
# generated by middl2tex, in an environment which defines "\intfsection"
# appropriately.

(
  ####################### First echo the main script:

  cat << \EOF_PYTHON

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

import sys
import string
import regex
import regsub
import time

intfs_by_name = {}
chaps_by_name = {}
secns         = 0

chaps_by_rank = []

def FindInterface (name, creation_ch) :
  if intfs_by_name.has_key (name) :
    return intfs_by_name [name]
  else :
    new_if = Intf().init (name, creation_ch)
    intfs_by_name [name] = new_if
    return new_if

def FindChapter (name) :
  if chaps_by_name.has_key (name) :
    return chaps_by_name [name]
  else :
    new_chap = Chap().init (name)
    chaps_by_name [name] = new_chap
    return new_chap

def SortByRank (l) :
  n         = len (l)
  res       = range (n)
  tmp       = range (n)
  tmp_range = range (n)
  
  for i in tmp_range:
    tmp[i] = (l[i].rank, l[i])
  tmp.sort ()
  
  for i in tmp_range :
    res[i] = tmp[i][1]

  return res


class Chap :

  def init (ch, title) :
    ch.title        = title
    ch.secs_by_name = {}
    ch.depends      = []
    ch.secs         = []
    ch.rank         = 0
    return ch

  def find_or_add_section (ch, name) :
    res = ch.find_section (name)
    if res :
      return res
    else :
      return ch.add_section (Sec().init (name, ch))

  def find_section (ch, name) :
    if ch.secs_by_name.has_key (name) :
      return ch.secs_by_name [name]
    else :
      return None

  def add_section (ch, sec) :
    ch.secs_by_name [sec.title] = sec
    ch.secs.append (sec)
    return sec

  def remove_section (ch, sec) :
    ch.secs.remove (sec)
    del ch.secs_by_name [sec.title]

  def depends_on (ch, ch1) :
    if ch != ch1 and not ch1 in ch.depends :
      ch.depends.append (ch1)

  def visit (ch, next_rank) :
    ch.rank = -1
    for ch1 in ch.depends :
      if ch1.rank == 0 :
	next_rank = ch1.visit (next_rank)

    ch.secs_by_rank = SortByRank (ch.secs)

    sys.stderr.write ('Chapter ' + ch.title + ' gets rank ' + `next_rank`\
	+ '\n')
    ch.rank = next_rank
    chaps_by_rank [next_rank - 1] = ch
    return next_rank + 1

class Sec :

  def init (sec, title, ch) :
    global secns
    sec.title   = title
    sec.chapter = ch
    sec.intfs   = []
    sec.depends = []
    sec.rank    = 0
    secns = secns + 1
    return sec

  def add_intf (sec, intf) :
    sec.intfs.append (intf)

  def remove_intf (sec, intf) :
    global secns
    sec.intfs.remove (intf)
    if len (sec.intfs) == 0 :
      sec.chapter.remove_section (sec)
      secns = secns - 1
      del sec

  def move_to_chapter (sec, ch) :
    global secns
    sec.chapter.remove_section (sec)
    sec.chapter = ch
    existing_sec = ch.find_section (sec.title)
    if existing_sec :
      existing_sec.intfs = existing_sec.intfs + sec.intfs
      secns = secns - 1
      del sec
    else :
      ch.add_section (sec)

  def depends_on (sec, sec1) :
    if sec != sec1 and not sec1 in sec.depends :
      sec.depends.append (sec1)

  def visit (sec, next_rank) :
    sec.rank = -1
    for sec1 in sec.depends :
      sec.chapter.depends_on (sec1.chapter)
      if sec1.rank == 0 :
	next_rank = sec1.visit (next_rank)

    sec.intfs_by_rank = SortByRank (sec.intfs)

    sys.stderr.write ('Section ' + sec.title + ' gets rank ' + `next_rank`\
	+ '\n')
    sec.rank = next_rank
    return next_rank + 1

class Intf :

  def init (intf, name, creation_ch) :
    intf.name    = name
    intf.chapter = creation_ch
    intf.section = None
    intf.imports = []
    intf.rank    = 0
    intf.move_to_section_named (name)
    return intf

  def imprt (intf, imp) :
    intf.imports.append (imp)

  def move_to_chapter (intf, ch) :
    intf.chapter = ch
    if intf.section :
      intf.section.move_to_chapter (ch)
  
  def move_to_section_named (intf, name) :
    if intf.section :
      intf.section.remove_intf (intf)
    intf.section = intf.chapter.find_or_add_section (name)
    intf.section.add_intf (intf)

  def visit (intf, next_rank) :
    intf.rank = -1
    for imp in intf.imports :
      intf.section.depends_on (imp.section)
      if imp.rank == 0 :
	next_rank = imp.visit (next_rank)

    sys.stderr.write ('Intf ' + intf.name + ' gets rank ' + `next_rank` + '\n')
    intf.rank = next_rank
    return next_rank + 1


misc_chapter = FindChapter ('Miscellaneous Interfaces')

def Chapter (intf_name, title):
  chap = FindChapter (string.strip (title))
  intf = FindInterface (string.strip (intf_name), chap)
  intf.move_to_chapter (chap)

def Section (intf_name, title):
  intf = FindInterface (string.strip (intf_name), misc_chapter)
  intf.move_to_section_named (string.strip (title))

def Imports (intf_name, imp):
  intf = FindInterface (string.strip (intf_name), misc_chapter)
  intf.imprt (FindInterface (string.strip (imp), misc_chapter))

def RankInterfaces () :
  next_rank = 1
  for intf in intfs_by_name.values () :
    if intf.rank == 0 :
      next_rank = intf.visit (next_rank)

def RankSections () :
  next_rank = 1
  for ch in chaps_by_name.values () :
    for sec in ch.secs :
      if sec.rank == 0 :
	next_rank = sec.visit (next_rank)

def RankChapters () :
  next_rank = 1
  for ch in chaps_by_name.values () :
    if ch.rank == 0 :
      next_rank = ch.visit (next_rank)

whitespace_etc_pat = regex.compile ('[^A-Za-z0-9]+')

def Label (title) :
  return regsub.gsub (whitespace_etc_pat, '-', title)

def main():
  sys.stderr.write ('\nmiddl2manual: inputs are:\n\n')

  for ch in chaps_by_name.values () :
    sys.stderr.write ('Chapter "' + ch.title + '":\n')
    for sec in ch.secs :
      sys.stderr.write ('  Section "' + sec.title + '":\n')
      for intf in sec.intfs :
	sys.stderr.write ('    ' + intf.name + ' imports:\n')
	for imp in intf.imports :
	  sys.stderr.write ('      ' + imp.name + '\n')

  sys.stderr.write ('\nranking...\n\n')

  global chaps_by_rank
  chaps_by_rank = range (len (chaps_by_name))
  sys.stderr.write ('len (chaps_by_rank) = ' + `len (chaps_by_rank)` + '\n')

  RankInterfaces ()
  RankSections ()
  RankChapters ()

  sys.stderr.write ('\noutputting...\n\n')

  print '%% Automatically generated'
  print '%% by $Id: middl2manual 1.1 Thu, 18 Feb 1999 14:20:06 +0000 dr10009 $'
  print '%% on', time.ctime (time.time ())
  print
  print '\\def\\intfsection{\\subsection}'
  print

  for ch in chaps_by_rank :
    sys.stderr.write ('Chapter ' + ch.title + ' has rank ' + `ch.rank` + '\n')

    if len (ch.secs_by_rank) == 0 :
      sys.stderr.write ('  (' + ch.title + ' is empty)\n')
      continue

    print '\\chapter{' + ch.title + '}'
    print '\\label{ch-' + Label (ch.title) + '}'
    print

    for sec in ch.secs_by_rank :
      sys.stderr.write ('  Section ' + sec.title + \
	  ' has rank ' + `sec.rank` + '\n')

      print '  \\section{' + sec.title + '}'
      print '  \\label{sec-' + Label (sec.title) + '}'
      print

      shared = (len (sec.intfs_by_rank) > 1)
      if shared :
        print '   {\\def\\intfsection{\\subsubsection}'

      for intf in sec.intfs_by_rank :
	sys.stderr.write ('    Intf ' + intf.name + \
	    ' has rank ' + `intf.rank` + '\n')
	for imp in intf.imports :
	  sys.stderr.write ( '      imports ' + imp.name + '\n')

	if shared :
	  print '    \\subsection{' + intf.name + '}'

	print '    \\input{' + intf.name + '.tex}'
	print '    \\label{intf-' + intf.name + '}'
	print

      if shared :
        print '   }'
      print


#------------------------------------------------------------------
EOF_PYTHON


  ####################### Then echo the function calls 

  for i in $* ; do
    if_name=`basename $i .if`

    cat $i | sed -n -e "
s/.*\\chapter[ \t]*{\(.*\)}.*/Chapter (\'$if_name\', \'\1\')/p
s/.*\\section[ \t]*{\(.*\)}.*/Section (\'$if_name\', \'\1\')/p
s/^[ \t]*NEEDS[ \t]*\([^;]*\);.*/Imports (\'$if_name\', \'\1\')/p
s/^[ \t]*EXTENDS[ \t]*\([^;]*\);.*/Imports (\'$if_name\', \'\1\')/p"

  done

  ####################### Finally, engage!

  echo 'main ()'

) | python -


