Index: doc/tools/pdl2texi/Drive.d
===================================================================
--- doc/tools/pdl2texi/Drive.d	(revision d4bc481ded89c54759a5cd2c51565486d72312c6)
+++ doc/tools/pdl2texi/Drive.d	(revision d4bc481ded89c54759a5cd2c51565486d72312c6)
@@ -0,0 +1,890 @@
+OBJECT:  Drive
+  DESCRIPTION:
+    This object provides an interface to the elevation/azimuth
+    drive system(ECA).  
+  THEORY OF OPERATION:
+    This object encapsulates the turret drive.  The object provides
+    an interface to control the turret drive.  A method is provided
+    to set the azimuth and elevation rate of the drive.  Methods are
+    provided to request the drive to run in stabilized or power mode
+    and set the drive speed to high or low.  A method is also provided
+    to allow for drift adjust.  
+
+    This object also tracks drive system on hours.  It gets the hours
+    from the EEPROM data, allows the hours to be set, updates the
+    hours via the activation and deactivation of the drive, and
+    provides access to the hours.
+
+      + This is the first bullet.
+      + This is the second bullet.
+      + This is the third bullet.
+      + This is the fourth bullet.
+      + This is the fifth bullet.
+
+    The object also provides methods to access azimuth and elevation
+    displacement, and methods to process changes in the hardware
+    discretes drive on, stabilized mode, thermal fault and fault.
+  
+    The object also provides methods to enable and disable
+    both the remote and turret power interlocks which determine if
+    the drive can be driven by the remote or turret handstation
+    respectively.   
+
+ATTRIBUTE DESCRIPTIONS:
+
+
+ATTRIBUTE:  Azimuth_displacement
+  DESCRIPTION:
+    This attribute represents the current azimuth displacement
+    of the turret.
+  TYPE: floating point
+  RANGE: 0-0
+  UNITS: degrees
+  REQUIREMENTS:
+    AFS 8.2.11 Receive Azimuth Displacement and Elevation Displacement
+    AIS 5.2.6 Azimuth Displacement analog
+
+
+ATTRIBUTE:  Azimuth_rate
+  DESCRIPTION:
+    Azimuth rate command for movement of the turret. 
+  TYPE: floating point
+  RANGE: X-Y
+  UNITS: degrees / second
+  DEFAULT: none
+  REQUIREMENTS:
+    AFS 8.2.3 Send Azimuth and Elevation Rate to ECA
+    AIS 5.3.4 Elevation Rate analog
+    AIS 5.3.5 Azimuth Rate analog
+
+
+ATTRIBUTE:  Drift_adjust
+  DESCRIPTION:
+    This attribute represents the current drift adjustment mode
+    of the ECA turret drive.
+  TYPE: enumerated
+  MEMBERS:
+    ADJUST_OFF - drift adjust mode is OFF
+    ADJUST_ON  - drift adjust mode is ON
+  REQUIREMENTS:
+    AFS 8.2.6 Capable of turret drift adjustment
+    AIS 5.3.1 Drift Adjust discrete
+ 
+
+ATTRIBUTE:  Drive_on
+  DESCRIPTION:
+    Current status of the turret drive.  The turret drive may either be
+    switched on or off.
+  TYPE: boolean
+  MEMBERS:
+    FALSE - turret drive is off
+    TRUE  - turret drive is on
+  DEFAULT: FALSE
+  REQUIREMENTS:
+    AFS 8.2.9 Monitor Drive System On
+    AIS 5.2.1 Drive System On discrete
+
+
+ATTRIBUTE:  Drive_system_hours
+  DESCRIPTION: 
+    Total number of hours drive has been activated. 
+  TYPE: integer
+  RANGE: 0 - +INFINITY 
+  UNITS: seconds
+  REQUIREMENTS:
+    AFS 4.11 Respond to CDT elapsed time indicator key
+
+
+ATTRIBUTE:  Elevation_displacement 
+  DESCRIPTION:
+    This attribute represents the current elevation displacement
+    of the turret.
+  TYPE: floating point
+  RANGE: 0-0
+  UNITS: degrees
+  REQUIREMENTS:
+    AFS 8.2.11 Receive Azimuth Displacement and Elevation Displacement
+    AIS 5.2.7 Elevation Displacement analog
+
+
+ATTRIBUTE:  Elevation_rate
+  DESCRIPTION:
+    Elevation rate command for movement of the turret.
+  TYPE: floating point
+  RANGE: X - Y
+  UNITS: degrees / second
+  DEFAULT: none
+  REQUIREMENTS:
+    AFS 8.2.3 Send Azimuth and Elevation Rate to ECA
+    AIS 5.3.4 Elevation Rate analog
+    AIS 5.3.5 Azimuth Rate analog
+ 
+
+ATTRIBUTE:  Fault
+  DESCRIPTION:
+    This attribute denotes whether a turret malfunction has occurred.
+  TYPE: boolean
+  MEMBERS:
+    TRUE  - a turret malfunction has occurred
+    FALSE - no turret malfunction has occurred
+  DEFAULT: FALSE
+  REQUIREMENTS:
+    AFS 8.2.7 Check for ECA failure indication
+    AIS 5.2.4 Turret Drive Malfunction discrete
+
+
+ATTRIBUTE:  Mode
+  DESCRIPTION:
+    Current requested drive mode.
+  TYPE: enumerated
+  MEMBERS:
+    POWER - operate in POWER mode
+    STAB  - operate in STABILIZED mode
+  DEFAULT: POWER
+  REQUIREMENTS:
+    bogus requirement
+
+
+ATTRIBUTE:  Remote_power_interlock_position
+  DESCRIPTION:
+    This is the status of the remote's power interlock.
+  TYPE: enumerated
+  MEMBERS:
+    OPEN   - relay is open
+    CLOSED - relay is closed
+  REQUIREMENTS:  
+    AFS 8.2.12 Control Power Interlock
+ 
+
+ATTRIBUTE:  Speed
+  DESCRIPTION:
+    Current speed mode of the ECA turret drive.
+  TYPE: enumerated
+  MEMBERS:
+    LOW_SPEED  - turret drive does not allow the maximum rate
+    HIGH_SPEED - turret drive allows the maximum rate
+  DEFAULT:  HIGH 
+  REQUIREMENTS:
+    AFS 8.2.5 Control Turret
+    AIS 5.3.3 Turret High Speed Mode discrete
+
+
+ATTRIBUTE:  Stabilized_mode
+  DESCRIPTION:
+    Current drive mode indicating whether the ECA electronics are
+    operating in Stabilized mode or Power mode.
+  TYPE: boolean
+  MEMBERS:
+    FALSE - not operating in STABILIZED mode
+    TRUE  - operating in STABILIZED mode
+  DEFAULT: FALSE
+  REQUIREMENTS:
+    AFS 8.2.8 Monitor Turret Stab Mode Indicate
+    AIS 5.2.3 Turret Stab Mode Indicate discrete
+
+
+ATTRIBUTE:  State
+  DESCRIPTION:
+    Current state of the turret drive.
+  TYPE: enumerated
+  MEMBERS:
+    ACTIVATED   - turret drive is ON and active
+    DEACTIVATED - turret drive is ON but deactivated
+  DEFAULT: DEACTIVATED
+  REQUIREMENTS:
+    AFS 8.2.4 Set Plam Grips Active
+    AIS 5.3.2 Palm Grips Active discrete
+
+
+ATTRIBUTE:  Thermal_fault
+  DESCRIPTION:
+    This attribute denotes whether a turret thermal fault has occurred.
+  TYPE: boolean
+  MEMBERS:
+    TRUE  - a turret thermal fault has occurred
+    FALSE - no turret thermal fault has occurred
+  DEFAULT: FALSE
+  REQUIREMENTS:
+    AFS 8.2.7 Check for ECA failure indication
+    AIS 5.2.2 Turret ECA Thermal Failure discrete
+
+
+ATTRIBUTE:  Turret_power_interlock_position
+  DESCRIPTION:
+    This is the status of the turret's power interlock.
+  TYPE: enumerated
+  MEMBERS:
+    OPEN   - relay is open
+    CLOSED - relay is closed
+  REQUIREMENTS:  
+    AFS 8.2.12 Control Power Interlock
+ 
+
+ASSOCIATION DESCRIPTIONS: none
+
+ABSTRACT TYPE DESCRIPTIONS:
+
+ABSTRACT TYPE:  Drift_adjust_t
+  DESCRIPTION:
+    This abstract type represents the current drift adjustment mode
+    of the ECA turret drive.
+  VISIBILITY: public
+  DERIVATION:  enumerated
+  MEMBERS:  
+    ADJUST_OFF = 0 - drift adjust mode is OFF
+    ADJUST_ON  = 1 - drift adjust mode is ON
+  REQUIREMENTS:
+    AIS 5.3.1 Drift Adjust discrete
+
+ABSTRACT TYPE: Interlock_status
+  DESCRIPTION:
+    An interlock state being OPEN or CLOSED.
+  VISIBILITY: private
+  DERIVATION: enumerated
+  MEMBERS:
+    OPEN   - interlock relay is in the open state
+    CLOSED - interlock relay is in the closed state
+  REQUIREMENTS: 
+    AFS 8.2.12 Control Power Interlock
+
+ABSTRACT TYPE:  Mode
+  DESCRIPTION:
+    This type specifies the drive mode of the ECA turret drive.
+  VISIBILITY: public  
+  DERIVATION: enumerated
+  MEMBERS:
+    POWER - operate in POWER mode
+    STAB  - operate in STABILIZED mode
+
+ABSTRACT TYPE:  Speed
+  DESCRIPTION:
+    This type specifies the Speed mode of the ECA turret drive.
+  VISIBILITY: public
+  DERIVATION:  enumerated
+  MEMBERS:  
+    LOW_SPEED  = 0 - turret drive does not allow the maximum rate
+    HIGH_SPEED = 1 - turret drive allows the maximum rate
+  REQUIREMENTS:
+    AIS 5.3.3 Turret High Speed Mode discrete
+
+ABSTRACT TYPE:  State_t
+  DESCRIPTION:
+    This type specifies the state of the turret drive.
+  VISIBILITY: private
+  DERIVATION:  enumerated
+  MEMBERS:  
+    DEACTIVATED - turret drive is ON but deactivated
+    ACTIVATED   - turret drive is ON and active
+
+DATA ITEM DESCRIPTIONS:
+
+DATA ITEM: Azimuth_displacement_control
+  DESCRIPTION:
+    This data item contains the Azimuth_displacement analog control
+    information.
+  TYPE: handle
+
+
+DATA ITEM: Azimuth_rate_control
+  DESCRIPTION:
+    This data item contains the Azimuth_rate analog control
+    information.
+  TYPE: handle
+
+
+DATA ITEM: Drift_adjust_control
+  DESCRIPTION:
+    This data item contains the Drift_adjust discrete control
+    information.
+  TYPE: handle
+
+
+DATA ITEM: Drive_activate_control
+  DESCRIPTION:
+    This data item contains the activate drive relay control information. 
+  TYPE: handle
+
+
+DATA ITEM: Drive_activated_timestamp
+  DESCRIPTION:
+    This data item stores the timestamp of the last time the drive was
+    activated.  It will be used to compute the activated time when a
+    deactivate is received.
+  TYPE: time_t
+  DEFAULT: none
+  NOTES:
+    see C library for data type information 
+
+
+DATA ITEM: Drive_on
+  DESCRIPTION:
+    The current status of the turret drive representing whether
+    the drive is on or off.
+  TYPE: boolean
+  DEFAULT: FALSE
+
+
+DATA ITEM: Drive_on_control
+  DESCRIPTION:
+    This data item contains the Drive_on discrete flag control
+    information.
+  TYPE: handle
+
+
+DATA ITEM: Drive_system_hours
+  DESCRIPTION:
+    This data item stores the actual time the drive has been active.
+    This value will be output by the Get_time method and will be set
+    by the Set_time method.
+  TYPE: integer 
+  RANGE: 0 - +INFINITY 
+  UNITS: seconds
+  DEFAULT: 0
+
+
+DATA ITEM: Elevation_rate_control
+  DESCRIPTION:
+    This data item contains the Elevation_rate analog control 
+    information.
+  TYPE: handle
+
+
+DATA ITEM: Elevation_displacement_control
+  DESCRIPTION:
+    This data item contains the Elevation_displacement analog control
+    information.
+  TYPE: handle
+
+
+DATA ITEM: Fault_control
+  DESCRIPTION:
+    This data item contains the Fault discrete flag control
+    information.
+  TYPE: handle
+
+
+DATA ITEM: Remote_power_interlock_control
+  DESCRIPTION:
+    This data item contains the Remote_power_interlock relay control
+    information.
+  TYPE: handle
+
+
+DATA ITEM: Stabilized_mode_control
+  DESCRIPTION:
+    This data item contains the Stabilized_mode discrete flag control
+    information.
+  TYPE: handle
+
+
+DATA ITEM: State 
+  DESCRIPTION:
+    The current state of the turret drive representing an activated or
+    deactivated drive.
+  TYPE: State_t
+  DEFAULT: DEACTIVATED
+  
+
+DATA ITEM: Thermal_fault_control
+  DESCRIPTION:
+    This data item contains the Thermal_fault discrete flag control
+    information.
+  TYPE: handle
+
+
+DATA ITEM: Turret_power_interlock_control
+  DESCRIPTION:
+    This data item contains the Turret_power_interlock relay control
+    information.
+  TYPE: handle
+
+
+DATA ITEM: Turret_power_interlock_status
+  DESCRIPTION:
+    The accurate status of the turret power interlock relay.  This value
+    will be used to control the relay during a transition from the remote
+    back to the turret console.
+  TYPE: Interlock_status
+  DEFAULT: none
+
+
+METHOD DESCRIPTIONS:
+
+METHOD:  Activate 
+  DESCRIPTION:
+    This method activates the turret drive system.  A timestamp of the
+    activation is recorded to keep a running count of hours the drive has
+    been active.
+  VISIBILITY:  public
+  INPUTS:  none
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.4 Set Palm Grips Active
+    AIS 5.3.2 Palm Grips Active discrete
+  PDL:
+
+    if Drive_on
+      if State is DEACTIVATED
+        close the Drive_activate_control relay using the Relay object
+        timestamp Drive_activated_timestamp for Drive_system_hours
+        running total
+        set State to ACTIVATED
+      else
+        already in the ACTIVATED state
+
+
+METHOD:  Create
+  DESCRIPTION:
+    This method initializes the Turret Drive object.  All monitors of 
+    turret drive signals are initialized and/or registered.  All turret
+    drive characteristics are set to their initial values.
+  VISIBILITY:  public
+  INPUTS: none
+  OUTPUTS: none
+  PDL:
+
+    set State to DEACTIVATED
+
+    get Drive_system_hours to initialize the elapsed drive time
+      from the Statistics (eeprom database) object - this is
+        stored upon shutdown in eeprom
+
+    create Fault_control using Discrete_flag object with the
+     following attributes 
+      true_sense     - DISCRETE_FLAG_TRUE_IS_1
+      is_buffered    - false
+      is_monitored   - true
+      period         - 10ms
+      change_routine - Change_fault
+    change fault using initial value returned from create Fault_control
+ 
+    create Thermal_fault_control using Discrete_flag object with
+     the following attributes 
+      true_sense     - DISCRETE_FLAG_TRUE_IS_0
+      is_buffered    - false
+      is_monitored   - true
+      period         - 10ms
+      change_routine - Change_thermal_fault
+    change thermal fault using initial value returned from create
+     Thermal_fault_control
+
+    create Stabilized_mode_control using Discrete_flag object with the
+     following attributes  
+      true_sense     - DISCRETE_FLAG_TRUE_IS_1
+      is_buffered    - false
+      is_monitored   - true
+      period         - 10ms 
+      change_routine - Change_stabilized_mode
+    change stabilized mode using initial value returned from create
+     Stabilized_mode_control
+
+    create Drive_on_control using Discrete_flag object with the 
+     following attributes 
+      true_sense     - DISCRETE_FLAG_TRUE_IS_1
+      is_buffered    - false
+      is_monitored   - true
+      period         - 10ms
+      change_routine - Change_drive_on
+    change drive on using initial value returned from create
+     Drive_on_control
+
+    create Azimuth_rate_control using the Linear_dac object
+     with the following attributes:
+      voltage_low   - -10
+      voltage_high  - +10
+      logical_low   - -60
+      logical_high  - +60
+      is_buffered   - false
+      period        - none
+      initial_value - 0
+
+    create Elevation_rate_control using the Linear_dac object with the
+     following attributes:
+      voltage_low   - -10
+      voltage_high  - +10
+      logical_low   - -60
+      logical_high  - +60
+      is_buffered   - false
+      period        - none
+      initial_value - 0
+
+    create Azimuth_displacement_control using the Linear_adc object with
+     the following attributes:
+      voltage low    - 
+      voltage high   - 
+      logical low    - 
+      logical high   - 
+      is_buffered    - false 
+      is_monitored   - false
+      period         - none
+      change_routine - none
+      delta          - none
+
+    create Elevation_displacement_control using the Linear_adc object 
+     with the following attributes:
+      voltage low    - 
+      voltage high   - 
+      logical low    - 
+      logical high   - 
+      is_buffered    - false
+      is_monitored   - false
+      period         - none
+      change_routine - none
+      delta          - none
+
+    create Drive_activate_control using the Relay object with the
+     following attributes:
+      open_sense    - RELAY_OPEN_IS_0
+      is_buffered   - false
+      period        - none
+      initial_state - RELAY_OPEN
+
+    create Turret_power_interlock_control using the Relay object with 
+     the following attributes:
+      open_sense    - RELAY_OPEN_IS_0
+      is_buffered   - false
+      period        - none
+      initial_state - RELAY_OPEN
+    set Turret_power_interlock_status to OPEN
+
+    create Remote_power_interlock_control using the Relay
+     object with the following attributes:
+      open_sense    - RELAY_OPEN_IS_0
+      is_buffered   - false
+      period        - none
+      initial_state - RELAY_OPEN
+
+    create Drift_adjust_control using the Discrete_out object with 
+     following attributes:
+      is_buffered   - false
+      period        - 10ms
+      initial_value - ADJUST_OFF
+
+
+METHOD:  Deactivate
+  DESCRIPTION:
+    This method deactivates the turret drive system.  A timestamp of the
+    activation is recorded to keep a running count of hours the drive
+    has been active.
+  VISIBILITY:  public
+  INPUTS:  none
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.4 Set Plam Grips Active
+    AIS 5.3.2 Palm Grips Active discrete
+  PDL:
+
+    if Drive_on
+      if State is DEACTIVATED 
+        timestamp deactivate for Drive_system_hours running total
+        deactivate drive by opening the Drive_activate_control relay
+        using the Relay object
+        compute running time since last activate
+        add running time to Drive_system_hours
+        set State to DEACTIVATED
+      else
+        already in the DEACTIVATED state
+ 
+
+METHOD:  Disable_remote_power_interlock
+  DESCRIPTION:
+    This method disables the turret power interlock safety preventing
+    the turret from being driven by the remote's handstation. 
+  VISIBILITY:  public
+  INPUTS:  none
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.12 Control Power Interlock
+  PDL:
+
+    open the remote power interlock relay
+    return the turret relay to its designated state defined by the
+      Turret_power_interlock_status
+ 
+
+METHOD:  Disable_turret_power_interlock
+  DESCRIPTION:
+    This method disables the turret power interlock safety preventing
+    the turret from being driven by the turret's handstation. 
+  VISIBILITY:  public
+  INPUTS: none
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.12 Control Power Interlock
+  PDL:
+
+    Deactivate the Drive object
+    open the turret power interlock relay
+    set Turret_power_interlock_status to OPEN
+
+
+METHOD:  Enable_remote_power_interlock
+  DESCRIPTION:
+    This method will enable the remote power interlock to allow
+    the turret to be driven from the remote station.
+  VISIBILITY:  public
+  INPUTS: none
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.12 Control Power Interlock
+  NOTES:
+    At this time the turret's power interlock is opened to avoid
+    having both relays closed at a time.
+  PDL:
+
+    if Drive_on
+      open the turret power interlock relay using the Relay object
+      close the remote power interlock relay using the Relay object
+
+
+METHOD:  Enable_turret_power_interlock
+  DESCRIPTION:
+    This method will enable the turret power interlock to allow
+    the turret to be driven from the turret station.
+  VISIBILITY:  public
+  INPUTS: none
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.12 Control Power Interlock
+  NOTES:
+    At this time the closed state is saved to allow the correct state
+    to be returned to in the event a remote overrides the turret and the 
+    remote gives up its control, we must return to the previous state of
+    the turret power interlock.
+  PDL:
+
+    if Drive_on
+      close the turret power interlock relay using the Relay object
+
+      set the Turret_power_interlock_status to CLOSED to return to
+        when remote gives up control
+
+
+METHOD: Get_AZ_EL_displacement  
+  DESCRIPTION:
+    This method returns the current azimuth displacement and elevation
+    displacement from the ECA.
+  VISIBILITY:  public
+  INPUTS:  none
+  OUTPUTS:
+    current azimuth displacement value
+    current elevation displacement value
+  REQUIREMENTS:
+    AFS 8.2.11 Receive Azimuth Displacement and Elevation Displacement
+    AIS 5.2.6 Azimuth Displacement analog
+    AIS 5.2.7 Elevation Displacement analog
+  NOTES:
+    Currently supported as an on-demand type of signal.  May change to a
+    monitored analog signal.
+  PDL:
+    
+    if Drive_on
+      get and return current azimuth displacement and current elevation
+      displacement from ECA using the Linear_ADC object
+ 
+
+METHOD:  Get_hours
+  DESCRIPTION:
+    This method returns the current value contained in the elapsed
+    turret drive active time.
+  VISIBILITY:  public
+  INPUTS: none
+  OUTPUTS:
+    elapsed turret drive time - the current elasped turret drive time
+  REQUIREMENTS:
+    bogus requirements
+  REFERENCES:
+    AFS 4.11 Respond to CDT elapsed time indicator key
+  NOTES:
+    bogus notes
+  PDL:
+
+    return the current value for Drive_system_hours
+
+
+METHOD:  Set_drift_adjust
+  DESCRIPTION:
+    This method provides the ability to set the drift adjust to the 
+    value represented by the handstation.  Drift adjust is either ON
+    or OFF.
+  VISIBILITY:  public
+  INPUTS:
+    value - new drift adjust value 
+      ADJUST_OFF - drift adjust mode is OFF
+      ADJUST_ON  - drift adjust mode is ON
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.6 Capable of turret drift adjustment
+    AIS 5.3.1 Drift Adjust discrete
+  PDL:
+ 
+    if Drive_on
+      set drift adjust discrete to value using Discrete_out object
+
+METHOD:  Set_hours
+  DESCRIPTION:
+    This method will set the turret drive activated elapsed time value
+    to the specified value.
+  VISIBILITY:  public
+  INPUTS:
+    new_elapsed_time_value - specified turret drive elapsed time value
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 4.11 Respond to CDT elapsed time indicator key
+  PDL:
+ 
+    set the value of Drive_system_hours to the value specified by
+      new_elapsed_time_value
+ 
+
+METHOD: Set_mode
+  DESCRIPTION:
+    This method sets the requested mode of the turret drive.
+  VISIBILITY: public
+  INPUTS:
+    mode - new requested mode
+      POWER - operating in POWER mode
+      STAB  - operating in STABILIZED mode
+  OUTPUTS:  none
+  REQUIREMENTS:
+    bogus requirements
+  PDL:
+    if mode is POWER
+      set drive mode switchlight to PWR_LAMP using the Console object
+    else if mode is STAB
+      set drive mode switchlight to STB_LAMP using the Console object
+
+    
+METHOD:  Set_rate
+  DESCRIPTION:
+    This method provides the ability to send azimuth and elevation rates
+    to the drive.  Two parameters are used as input to designate the
+    corresponding rates.  These parameters must be in degrees per second.
+  VISIBILITY:  public
+  INPUTS:
+    azimuth rate   - new azimuth rate in degress per second
+    elevation rate - new elevation rate in degress per second
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.3 Send Azimuth and Elevation Rate to ECA
+    AIS 5.3.4 Elevation Rate analog
+    AIS 5.3.5 Azimuth Rate analog
+  NOTES:
+    May need to couple these analogs with an interface driver to close
+    the window between writes and conversions.
+  PDL:
+
+    if Drive_on
+      send the new azimuth rate to the Azimuth analog
+      send the new elevation rate to Elevation analog
+
+
+METHOD:  Set_speed
+  DESCRIPTION:
+    This method sets the turret drive speed mode to the specified value
+    either high or low based on user input.
+  VISIBILITY:  public
+  INPUTS:
+    new_speed_value - new value to set the turret speed to
+      LOW_SPEED  - turret drive does not allow the maximum rate
+      HIGH_SPEED - turret drive allows the maximum rate
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.5 Control Turret
+    AIS 5.3.3 Turret High Speed Mode discrete
+  PDL:
+
+    if Drive_on
+      set turret drive high speed mode discrete to the new_speed_value
+       using Discrete_out object
+
+
+METHOD:  Change_drive_on
+  DESCRIPTION:
+    This method will be used to report a transition of the drive system
+    on discrete.
+  VISIBILITY:  private
+  INPUTS:
+    value - new drive system on value 
+      FALSE - indicates drive not on
+      TRUE  - indicates drive on
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.9 Monitor Drive System On
+    AIS 5.2.1 Drive System On discrete
+  REFERENCES:
+    bogus references
+  NOTES:
+    bogus notes
+  PDL:
+
+    set Drive_on to value
+    if Drive_on is FALSE
+      deactivate the turret drive using the Drive object
+
+
+METHOD:  Change_fault
+  DESCRIPTION:
+    This method will be used to indicate that a turret malfunction
+    has been recognized by the turret drive.
+  VISIBILITY:  private
+  INPUTS:
+    value - new value resulting from a fault discrete change
+      FALSE - indicates no fault
+      TRUE  - indicates fault
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.7 Check for ECA failure indication
+    AIS 5.2.4 Turret Drive Malfunction discrete
+  PDL:
+ 
+    if value
+      send turret fault message using the Display object 
+    else 
+      return
+
+
+METHOD:  Change_stabilized_mode
+  DESCRIPTION:
+    This method will determine a transition from STAB mode to POWER mode.
+    It will set the stab mode lamp on the console.
+  VISIBILITY:  private
+  INPUTS:
+    value - new value resulting from a stab mode discrete change
+      FALSE - indicates not stabilized mode
+      TRUE -  indicates stabilized mode
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.8 Monitor Turret Stab Mode Indicate
+    AIS 5.2.3 Turret Stab Mode Indicate discrete
+  PDL:
+
+    if value 
+      set STAB mode light to LAMP_ON using the Console object
+    else value indicates not stabilized mode
+      set STAB mode light to LAMP_OFF using the Console object
+
+
+METHOD:  Change_thermal_fault
+  DESCRIPTION:
+    This method will be used to indicate that a thermal fault has been
+     recognized by the turret drive.
+  VISIBILITY:  private
+  INPUTS:
+    value - new value resulting from a thermal fault discrete change
+      FALSE - indicates no thermal fault
+      TRUE  - indicates thermal fault
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.7 Check for ECA failure indication
+    AIS 5.2.2 Turret ECA Thermal Failure discrete
+  PDL:
+ 
+    if value
+      send thermal fault message using the Display object
+    else 
+      return
+
+TASK DESCRIPTIONS: none
+
+ENDOBJECT: Drive
Index: doc/tools/pdl2texi/Makefile
===================================================================
--- doc/tools/pdl2texi/Makefile	(revision d4bc481ded89c54759a5cd2c51565486d72312c6)
+++ doc/tools/pdl2texi/Makefile	(revision d4bc481ded89c54759a5cd2c51565486d72312c6)
@@ -0,0 +1,66 @@
+#
+#  COPYRIGHT (c) 1996-1997.
+#  On-Line Applications Research Corporation (OAR).
+#  All rights reserved.
+#
+#  $Id$
+#
+
+CC=gcc
+#CFLAGS=-O4 -fomit-frame-pointer
+CFLAGS=-g 
+
+PROJECT=afcc
+TEXINPUTS=/home/gnu/work/binutils-2.6/texinfo:.  
+BASE=Drive
+FILES=afcc.texi $(BASE).txt
+PROG=pdl2texi
+
+all:  $(PROG)
+
+$(BASE).texi: $(BASE).d ./$(PROG)
+	./$(PROG) $(BASE).d
+
+$(PROG): main.o chain.o
+	gcc -static main.o chain.o -o $(PROG)
+
+drive: Drive
+Drive: $(BASE).txt
+	/usr1/home/joel/tmp/makeinfo $(PROJECT).texi
+
+main.o: main.c base.h
+
+chain.o: chain.c 
+
+dvi: $(PROJECT).dvi
+ps: $(PROJECT).ps
+info: Drive
+html: $(PROJECT).texi $(BASE).txt
+	 ../textools/texi2html $(PROJECT).texi
+
+$(BASE).txt: $(BASE).d
+	./$(PROG) -v -p "Turret Subsystem" -u "Turret Subsystem" $(BASE).d
+
+$(PROJECT).ps: $(PROJECT).dvi
+	dvips -o $(PROJECT).ps $(PROJECT).dvi
+
+$(PROJECT).dvi: $(FILES)
+	texi2dvi $(PROJECT).texi
+
+view:
+	test -r $(PROJECT).ps && ghostview $(PROJECT).ps
+
+tests: test test1
+
+test: $(PROG)
+	rm -f $(BASE).texi
+	./$(PROG) -v -p "Turret Subsystem" -u "Turret Subsystem" $(BASE).d
+
+test1:
+	rm -f t1.txt
+	./$(PROG) -v t1.d
+
+clean:
+	rm -f *.o $(PROG) *.txt core *.html $(PROJECT) Drive.texi
+	rm -f *.dvi *.ps *.log *.aux *.cp *.fn *.ky *.pg *.toc *.tp *.vr $(BASE)
+
Index: doc/tools/pdl2texi/afcc.texi
===================================================================
--- doc/tools/pdl2texi/afcc.texi	(revision d4bc481ded89c54759a5cd2c51565486d72312c6)
+++ doc/tools/pdl2texi/afcc.texi	(revision d4bc481ded89c54759a5cd2c51565486d72312c6)
@@ -0,0 +1,94 @@
+\input texinfo   @c -*-texinfo-*-
+@c %**start of header
+@setfilename afcc
+@settitle AFCC Internals Guide
+@paragraphindent 0
+@c %**end of header
+
+@c
+@c  COPYRIGHT (c) 1996-1997.
+@c  On-Line Applications Research Corporation (OAR).
+@c  All rights reserved.
+@c
+
+@c  This prevents a black box from being printed on "overflow" lines.
+@c  The alternative is to rework a sentence to avoid this problem.
+@finalout
+
+@tex
+\global\parindent 0in
+\global\chapheadingskip = 15pt plus 4pt minus 2pt
+\global\secheadingskip = 12pt plus 4pt minus 2pt
+\global\subsecheadingskip = 9pt plus 4pt minus 2pt
+
+@ifclear smallbook
+\global\parskip 6pt plus 1pt
+@end ifclear
+@end tex
+
+@ifinfo
+@format
+START-INFO-DIR-ENTRY
+* AFCC: .             AFCC Objects
+END-INFO-DIR-ENTRY
+@end format
+@end ifinfo
+
+
+@c Joel's Questions
+@c
+@c  1.  Why does paragraphindent only impact makeinfo?
+@c
+
+@setchapternewpage odd
+
+@c
+@c   Master file for the C User's Guide
+@c
+@include Drive.texi
+@ifinfo
+
+@node Top, Turret Subsystem, Gunner Station Subsystem, (dir)
+@top afcc
+
+This is the info version of the object documentation for the AFCC.
+
+The following subsystems are in the AFCC:
+
+@menu
+* Turret Subsystem::
+* Vehicle Subsystem::
+* Gunner Station Subsystem::
+@end menu
+
+@node Turret Subsystem, , Top, Top
+
+The following objects are in the Turret Subsystem:
+
+@menu
+* Drive Object::                Drive Object
+@end menu
+
+@node Vehicle Subsystem, Gunner Station Subsystem, , Top
+
+@menu
+The following objects are in the Turret Subsystem:
+
+* ::
+@end menu
+
+@node Gunner Station Subsystem, Top,  Vehicle Subsystem, Top
+@menu
+
+The following objects are in the Gunner Station Subsystem:
+
+* ::
+@end menu
+
+
+@end ifinfo
+@c 
+@c 
+@c Need to copy the emacs stuff and "trailer stuff" (index, toc) into here
+@c
+@bye
Index: doc/tools/pdl2texi/base.h
===================================================================
--- doc/tools/pdl2texi/base.h	(revision d4bc481ded89c54759a5cd2c51565486d72312c6)
+++ doc/tools/pdl2texi/base.h	(revision d4bc481ded89c54759a5cd2c51565486d72312c6)
@@ -0,0 +1,116 @@
+/*
+ *  COPYRIGHT (c) 1997.
+ *  On-Line Applications Research Corporation (OAR).
+ *  All rights reserved.
+ *
+ *  $Id$
+ */
+
+#ifndef __PDL2AMI_h
+#define __PDL2AMI_h
+
+#include "system.h"
+#include "chain.h"
+
+#ifndef EXTERN 
+#define EXTERN extern
+#endif
+
+/*
+ *  Paragraph size should be kept down because it is allocated for each
+ *  Line_Control.  If this number is large, the memory requirements for
+ *  the program increase significantly.
+ */
+
+#define BUFFER_SIZE    (2 * 1024)
+#define PARAGRAPH_SIZE (2 * 1024)
+
+#define NUMBER_ELEMENTS( _x ) (sizeof(_x) / sizeof _x[0])
+
+void exit_application( 
+  int status
+);
+
+void ProcessFile(
+  char   *inname,
+  char   *outname
+);
+
+void strtolower(
+  char *dest,
+  char *src
+);
+
+void strtoInitialCaps(
+  char *dest,
+  char *src
+);
+
+void StripBlanks( void );
+
+void MergeParagraphs( void );
+
+int CheckForIncomplete( void );
+
+int CheckOutline( void );
+
+int CheckSections( void );
+
+void GenerateLists( void );
+
+void GenerateAList( 
+  char          *section,
+  Chain_Control *the_list
+);
+
+void LookForInternalInconsistencies( void );
+
+int Match_Argument( 
+  char  **array,
+  int     entries,
+  char   *users
+);
+
+void usage( void  );
+
+void ReadFileIntoChain(
+  char *inname
+);
+
+int MergeText( void );
+
+int CheckForBadWhiteSpace();
+
+void RemoveCopyright();
+
+void RemovePagebreaks();
+
+int RemoveExtraBlankLines();
+
+void FormatToTexinfo( void );
+
+void PrintFile(
+   char *out
+);
+
+void DumpList( 
+  Chain_Control  *the_list
+);
+
+void ReleaseFile();
+
+EXTERN boolean          Verbose;                 /* status/debug msgs */
+EXTERN boolean          Statistics;              /* statistics msgs */
+EXTERN boolean          IncompletesAreErrors;
+EXTERN boolean          InsertTBDs;
+EXTERN Chain_Control    Lines;
+
+EXTERN int              NumberOfAttributes;
+EXTERN int              NumberOfAssociations;
+EXTERN int              NumberOfAbstractTypes;
+EXTERN int              NumberOfDataItems;
+EXTERN int              NumberOfMethods;
+EXTERN int              NumberOfTasks;
+
+
+#endif
Index: doc/tools/pdl2texi/drive.d
===================================================================
--- doc/tools/pdl2texi/drive.d	(revision d4bc481ded89c54759a5cd2c51565486d72312c6)
+++ doc/tools/pdl2texi/drive.d	(revision d4bc481ded89c54759a5cd2c51565486d72312c6)
@@ -0,0 +1,874 @@
+OBJECT:  Drive
+  DESCRIPTION:
+    This object provides an interface to the elevation/azimuth
+    drive system(ECA).  
+  THEORY OF OPERATION:
+    This object encapsulates the turret drive.  The object provides
+    an interface to control the turret drive.  A method is provided
+    to set the azimuth and elevation rate of the drive.  Methods are
+    provided to request the drive to run in stabilized or power mode
+    and set the drive speed to high or low.  A method is also provided
+    to allow for drift adjust.  
+
+    This object also tracks drive system on hours.  It gets the hours
+    from the EEPROM data, allows the hours to be set, updates the
+    hours via the activation and deactivation of the drive, and
+    provides access to the hours.
+
+    The object also provides methods to access azimuth and elevation
+    displacement, and methods to process changes in the hardware
+    discretes drive on, stabilized mode, thermal fault and fault.
+  
+    The object also provides methods to enable and disable
+    both the remote and turret power interlocks which determine if
+    the drive can be driven by the remote or turret handstation
+    respectively.   
+
+ATTRIBUTE DESCRIPTIONS:
+
+
+ATTRIBUTE:  Azimuth_displacement
+  DESCRIPTION:
+    This attribute represents the current azimuth displacement
+    of the turret.
+  TYPE: floating point
+  RANGE: ???
+  UNITS: degrees
+  REQUIREMENTS:
+    AFS 8.2.11 Receive Azimuth Displacement and Elevation Displacement
+    AIS 5.2.6 Azimuth Displacement analog
+
+
+ATTRIBUTE:  Azimuth_rate
+  DESCRIPTION:
+    Azimuth rate command for movement of the turret. 
+  TYPE: floating point
+  UNITS: degrees / second
+  DEFAULTS: none
+  REQUIREMENTS:
+    AFS 8.2.3 Send Azimuth and Elevation Rate to ECA
+    AIS 5.3.4 Elevation Rate analog
+    AIS 5.3.5 Azimuth Rate analog
+
+
+ATTRIBUTE:  Drift_adjust
+  DESCRIPTION:
+    This attribute represents the current drift adjustment mode
+    of the ECA turret drive.
+  TYPE: enumerated
+  MEMBERS:
+    ADJUST_OFF - drift adjust mode is OFF
+    ADJUST_ON  - drift adjust mode is ON
+  REQUIREMENTS:
+    AFS 8.2.6 Capable of turret drift adjustment
+    AIS 5.3.1 Drift Adjust discrete
+ 
+
+ATTRIBUTE:  Drive_on
+  DESCRIPTION:
+    Current status of the turret drive.  The turret drive may either be
+    switched on or off.
+  TYPE: boolean
+  MEMBERS:
+    FALSE - turret drive is off
+    TRUE  - turret drive is on
+  DEFAULTS: FALSE
+  REQUIREMENTS:
+    AFS 8.2.9 Monitor Drive System On
+    AIS 5.2.1 Drive System On discrete
+
+
+ATTRIBUTE:  Drive_system_hours
+  DESCRIPTION: 
+    Total number of hours drive has been activated. 
+  TYPE: integer
+  RANGE: 0 - +INFINITY 
+  UNITS: seconds
+  REQUIREMENTS:
+    AFS 4.11 Respond to CDT elapsed time indicator key
+
+
+ATTRIBUTE:  Elevation_displacement 
+  DESCRIPTION:
+    This attribute represents the current elevation displacement
+    of the turret.
+  TYPE: floating point
+  RANGE: ???
+  UNITS: degrees
+  REQUIREMENTS:
+    AFS 8.2.11 Receive Azimuth Displacement and Elevation Displacement
+    AIS 5.2.7 Elevation Displacement analog
+
+
+ATTRIBUTE:  Elevation_rate
+  DESCRIPTION:
+    Elevation rate command for movement of the turret.
+  TYPE: floating point
+  UNITS: degrees / second
+  DEFAULTS: none
+  REQUIREMENTS:
+    AFS 8.2.3 Send Azimuth and Elevation Rate to ECA
+    AIS 5.3.4 Elevation Rate analog
+    AIS 5.3.5 Azimuth Rate analog
+ 
+
+ATTRIBUTE:  Fault
+  DESCRIPTION:
+    This attribute denotes whether a turret malfunction has occurred.
+  TYPE: boolean
+  MEMBERS:
+    TRUE  - a turret malfunction has occurred
+    FALSE - no turret malfunction has occurred
+  DEFAULTS: FALSE
+  REQUIREMENTS:
+    AFS 8.2.7 Check for ECA failure indication
+    AIS 5.2.4 Turret Drive Malfunction discrete
+
+
+ATTRIBUTE:  Mode
+  DESCRIPTION:
+    Current requested drive mode.
+  TYPE: enumerated
+  MEMBERS:
+    POWER - operate in POWER mode
+    STAB  - operate in STABILIZED mode
+  DEFAULTS: POWER
+  REQUIREMENTS:
+
+
+ATTRIBUTE:  Remote_power_interlock_position
+  DESCRIPTION:
+    This is the status of the remote's power interlock.
+  TYPE: enumerated
+  MEMBERS:
+    OPEN   - relay is open
+    CLOSED - relay is closed
+  REQUIREMENTS:  
+    AFS 8.2.12 Control Power Interlock
+ 
+
+ATTRIBUTE:  Speed
+  DESCRIPTION:
+    Current speed mode of the ECA turret drive.
+  TYPE: enumerated
+  MEMBERS:
+    LOW_SPEED  - turret drive does not allow the maximum rate
+    HIGH_SPEED - turret drive allows the maximum rate
+  DEFAULTS:  HIGH 
+  REQUIREMENTS:
+    AFS 8.2.5 Control Turret
+    AIS 5.3.3 Turret High Speed Mode discrete
+
+
+ATTRIBUTE:  Stabilized_mode
+  DESCRIPTION:
+    Current drive mode indicating whether the ECA electronics are
+    operating in Stabilized mode or Power mode.
+  TYPE: boolean
+  MEMBERS:
+    FALSE - not operating in STABILIZED mode
+    TRUE  - operating in STABILIZED mode
+  DEFAULTS: FALSE
+  REQUIREMENTS:
+    AFS 8.2.8 Monitor Turret Stab Mode Indicate
+    AIS 5.2.3 Turret Stab Mode Indicate discrete
+
+
+ATTRIBUTE:  State
+  DESCRIPTION:
+    Current state of the turret drive.
+  TYPE: enumerated
+  MEMBERS:
+    ACTIVATED   - turret drive is ON and active
+    DEACTIVATED - turret drive is ON but deactivated
+  DEFAULTS: DEACTIVATED
+  REQUIREMENTS:
+    AFS 8.2.4 Set Plam Grips Active
+    AIS 5.3.2 Palm Grips Active discrete
+
+
+ATTRIBUTE:  Thermal_fault
+  DESCRIPTION:
+    This attribute denotes whether a turret thermal fault has occurred.
+  TYPE: boolean
+  MEMBERS:
+    TRUE  - a turret thermal fault has occurred
+    FALSE - no turret thermal fault has occurred
+  DEFAULTS: FALSE
+  REQUIREMENTS:
+    AFS 8.2.7 Check for ECA failure indication
+    AIS 5.2.2 Turret ECA Thermal Failure discrete
+
+
+ATTRIBUTE:  Turret_power_interlock_position
+  DESCRIPTION:
+    This is the status of the turret's power interlock.
+  TYPE: enumerated
+  MEMBERS:
+    OPEN   - relay is open
+    CLOSED - relay is closed
+  REQUIREMENTS:  
+    AFS 8.2.12 Control Power Interlock
+ 
+
+ASSOCIATION DESCRIPTIONS: none
+
+ABSTRACT TYPE DESCRIPTIONS:
+
+ABSTRACT TYPE:  Drift_adjust_t
+  DESCRIPTION:
+    This abstract type represents the current drift adjustment mode
+    of the ECA turret drive.
+  VISIBILITY: public
+  DERIVATION:  enumerated
+  MEMBERS:  
+    ADJUST_OFF = 0 - drift adjust mode is OFF
+    ADJUST_ON  = 1 - drift adjust mode is ON
+  REQUIREMENTS:
+    AIS 5.3.1 Drift Adjust discrete
+
+ABSTRACT TYPE: Interlock_status
+  DESCRIPTION:
+    An interlock state being OPEN or CLOSED.
+  VISIBILITY: private
+  DERIVATION: enumerated
+  MEMBERS:
+    OPEN   - interlock relay is in the open state
+    CLOSED - interlock relay is in the closed state
+  REQUIREMENTS: 
+    AFS 8.2.12 Control Power Interlock
+
+ABSTRACT TYPE:  Mode
+  DESCRIPTION:
+    This type specifies the drive mode of the ECA turret drive.
+  VISIBILITY: public  
+  DERIVATION: enumerated
+  MEMBERS:
+    POWER - operate in POWER mode
+    STAB  - operate in STABILIZED mode
+
+ABSTRACT TYPE:  Speed
+  DESCRIPTION:
+    This type specifies the Speed mode of the ECA turret drive.
+  VISIBILITY: public
+  DERIVATION:  enumerated
+  MEMBERS:  
+    LOW_SPEED  = 0 - turret drive does not allow the maximum rate
+    HIGH_SPEED = 1 - turret drive allows the maximum rate
+  REQUIREMENTS:
+    AIS 5.3.3 Turret High Speed Mode discrete
+
+ABSTRACT TYPE:  State_t
+  DESCRIPTION:
+    This type specifies the state of the turret drive.
+  VISIBILITY: private
+  DERIVATION:  enumerated
+  MEMBERS:  
+    DEACTIVATED - turret drive is ON but deactivated
+    ACTIVATED   - turret drive is ON and active
+
+DATA ITEM DESCRIPTIONS:
+
+DATA ITEM: Azimuth_displacement_control
+  DESCRIPTION:
+    This data item contains the Azimuth_displacement analog control
+    information.
+  TYPE: handle
+
+
+DATA ITEM: Azimuth_rate_control
+  DESCRIPTION:
+    This data item contains the Azimuth_rate analog control
+    information.
+  TYPE: handle
+
+
+DATA ITEM: Drift_adjust_control
+  DESCRIPTION:
+    This data item contains the Drift_adjust discrete control
+    information.
+  TYPE: handle
+
+
+DATA ITEM: Drive_activate_control
+  DESCRIPTION:
+    This data item contains the activate drive relay control information. 
+  TYPE: handle
+
+
+DATA ITEM: Drive_activated_timestamp
+  DESCRIPTION:
+    This data item stores the timestamp of the last time the drive was
+    activated.  It will be used to compute the activated time when a
+    deactivate is received.
+  TYPE: time_t
+  DEFAULT: none
+  NOTES:
+    see C library for data type information ??? 
+
+
+DATA ITEM: Drive_on
+  DESCRIPTION:
+    The current status of the turret drive representing whether
+    the drive is on or off.
+  TYPE: boolean
+  DEFAULT: FALSE
+
+
+DATA ITEM: Drive_on_control
+  DESCRIPTION:
+    This data item contains the Drive_on discrete flag control
+    information.
+  TYPE: handle
+
+
+DATA ITEM: Drive_system_hours
+  DESCRIPTION:
+    This data item stores the actual time the drive has been active.
+    This value will be output by the Get_time method and will be set
+    by the Set_time method.
+  TYPE: integer 
+  RANGE: 0 - +INFINITY 
+  UNITS: seconds
+  DEFAULT: 0
+
+
+DATA ITEM: Elevation_rate_control
+  DESCRIPTION:
+    This data item contains the Elevation_rate analog control 
+    information.
+  TYPE: handle
+
+
+DATA ITEM: Elevation_displacement_control
+  DESCRIPTION:
+    This data item contains the Elevation_displacement analog control
+    information.
+  TYPE: handle
+
+
+DATA ITEM: Fault_control
+  DESCRIPTION:
+    This data item contains the Fault discrete flag control
+    information.
+  TYPE: handle
+
+
+DATA ITEM: Remote_power_interlock_control
+  DESCRIPTION:
+    This data item contains the Remote_power_interlock relay control
+    information.
+  TYPE: handle
+
+
+DATA ITEM: Stabilized_mode_control
+  DESCRIPTION:
+    This data item contains the Stabilized_mode discrete flag control
+    information.
+  TYPE: handle
+
+
+DATA ITEM: State 
+  DESCRIPTION:
+    The current state of the turret drive representing an activated or
+    deactivated drive.
+  TYPE: State_t
+  DEFAULT: DEACTIVATED
+  
+
+DATA ITEM: Thermal_fault_control
+  DESCRIPTION:
+    This data item contains the Thermal_fault discrete flag control
+    information.
+  TYPE: handle
+
+
+DATA ITEM: Turret_power_interlock_control
+  DESCRIPTION:
+    This data item contains the Turret_power_interlock relay control
+    information.
+  TYPE: handle
+
+
+DATA ITEM: Turret_power_interlock_status
+  DESCRIPTION:
+    The accurate status of the turret power interlock relay.  This value
+    will be used to control the relay during a transition from the remote
+    back to the turret console.
+  TYPE: Interlock_status
+  DEFAULT: none
+
+
+METHODS DESCRIPTIONS:
+
+METHOD:  Activate 
+  DESCRIPTION:
+    This method activates the turret drive system.  A timestamp of the
+    activation is recorded to keep a running count of hours the drive has
+    been active.
+  VISIBILITY:  public
+  INPUTS:  none
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.4 Set Palm Grips Active
+    AIS 5.3.2 Palm Grips Active discrete
+  PDL:
+
+    if Drive_on
+      if State is DEACTIVATED
+        close the Drive_activate_control relay using the Relay object
+        timestamp Drive_activated_timestamp for Drive_system_hours
+        running total
+        set State to ACTIVATED
+      else
+        already in the ACTIVATED state
+
+
+METHOD:  Create
+  DESCRIPTION:
+    This method initializes the Turret Drive object.  All monitors of 
+    turret drive signals are initialized and/or registered.  All turret
+    drive characteristics are set to their initial values.
+  VISIBILITY:  public
+  INPUTS: none
+  OUTPUTS: none
+  PDL:
+
+    set State to DEACTIVATED
+
+    get Drive_system_hours to initialize the elapsed drive time
+      from the Statistics (eeprom database ???) object - this is
+        stored upon shutdown in eeprom
+
+    create Fault_control using Discrete_flag object with the
+     following attributes 
+      true_sense     - DISCRETE_FLAG_TRUE_IS_1
+      is_buffered    - false
+      is_monitored   - true
+      period         - 10ms
+      change_routine - Change_fault
+    change fault using initial value returned from create Fault_control
+ 
+    create Thermal_fault_control using Discrete_flag object with
+     the following attributes 
+      true_sense     - DISCRETE_FLAG_TRUE_IS_0
+      is_buffered    - false
+      is_monitored   - true
+      period         - 10ms
+      change_routine - Change_thermal_fault
+    change thermal fault using initial value returned from create
+     Thermal_fault_control
+
+    create Stabilized_mode_control using Discrete_flag object with the
+     following attributes  
+      true_sense     - DISCRETE_FLAG_TRUE_IS_1
+      is_buffered    - false
+      is_monitored   - true
+      period         - 10ms 
+      change_routine - Change_stabilized_mode
+    change stabilized mode using initial value returned from create
+     Stabilized_mode_control
+
+    create Drive_on_control using Discrete_flag object with the 
+     following attributes 
+      true_sense     - DISCRETE_FLAG_TRUE_IS_1
+      is_buffered    - false
+      is_monitored   - true
+      period         - 10ms
+      change_routine - Change_drive_on
+    change drive on using initial value returned from create
+     Drive_on_control
+
+    create Azimuth_rate_control using the Linear_dac object
+     with the following attributes:
+ none    voltage_low   - -10
+      voltage_high  - +10
+      logical_low   - -60
+      logical_high  - +60
+      is_buffered   - false
+      period        - none
+      initial_value - 0
+
+    create Elevation_rate_control using the Linear_dac object with the
+     following attributes:
+      voltage_low   - -10
+      voltage_high  - +10
+      logical_low   - -60
+      logical_high  - +60
+      is_buffered   - false
+      period        - none
+      initial_value - 0
+
+    create Azimuth_displacement_control using the Linear_adc object with
+     the following attributes:
+      voltage low    - 
+      voltage high   - 
+      logical low    - 
+      logical high   - 
+      is_buffered    - false 
+      is_monitored   - false
+      period         - none
+      change_routine - none
+      delta          - none
+
+    create Elevation_displacement_control using the Linear_adc object 
+     with the following attributes:
+      voltage low    - 
+      voltage high   - 
+      logical low    - 
+      logical high   - 
+      is_buffered    - false
+      is_monitored   - false
+      period         - none
+      change_routine - none
+      delta          - none
+
+    create Drive_activate_control using the Relay object with the
+     following attributes:
+      open_sense    - RELAY_OPEN_IS_0
+      is_buffered   - false
+      period        - none
+      initial_state - RELAY_OPEN
+
+    create Turret_power_interlock_control using the Relay object with 
+     the following attributes:
+      open_sense    - RELAY_OPEN_IS_0
+      is_buffered   - false
+      period        - none
+      initial_state - RELAY_OPEN
+    set Turret_power_interlock_status to OPEN
+
+    create Remote_power_interlock_control using the Relay
+     object with the following attributes:
+      open_sense    - RELAY_OPEN_IS_0
+      is_buffered   - false
+      period        - none
+      initial_state - RELAY_OPEN
+
+    create Drift_adjust_control using the Discrete_out object with 
+     following attributes:
+      is_buffered   - false
+      period        - 10ms
+      initial_value - ADJUST_OFF
+
+
+METHOD:  Deactivate
+  DESCRIPTION:
+    This method deactivates the turret drive system.  A timestamp of the
+    activation is recorded to keep a running count of hours the drive
+    has been active.
+  VISIBILITY:  public
+  INPUTS:  none
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.4 Set Plam Grips Active
+    AIS 5.3.2 Palm Grips Active discrete
+  PDL:
+
+    if Drive_on
+      if State is DEACTIVATED 
+        timestamp deactivate for Drive_system_hours running total
+        deactivate drive by opening the Drive_activate_control relay
+        using the Relay object
+        compute running time since last activate
+        add running time to Drive_system_hours
+        set State to DEACTIVATED
+      else
+        already in the DEACTIVATED state
+ 
+
+METHOD:  Disable_remote_power_interlock
+  DESCRIPTION:
+    This method disables the turret power interlock safety preventing
+    the turret from being driven by the remote's handstation. 
+  VISIBILITY:  public
+  INPUTS:  none
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.12 Control Power Interlock
+  PDL:
+
+    open the remote power interlock relay
+    return the turret relay to its designated state defined by the
+      Turret_power_interlock_status
+ 
+
+METHOD:  Disable_turret_power_interlock
+  DESCRIPTION:
+    This method disables the turret power interlock safety preventing
+    the turret from being driven by the turret's handstation. 
+  VISIBILITY:  public
+  INPUTS: none
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.12 Control Power Interlock
+  PDL:
+
+    Deactivate the Drive object
+    open the turret power interlock relay
+    set Turret_power_interlock_status to OPEN
+
+
+METHOD:  Enable_remote_power_interlock
+  DESCRIPTION:
+    This method will enable the remote power interlock to allow
+    the turret to be driven from the remote station.
+  VISIBILITY:  public
+  INPUTS: none
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.12 Control Power Interlock
+  NOTES:
+    At this time the turret's power interlock is opened to avoid
+    having both relays closed at a time.
+  PDL:
+
+    if Drive_on
+      open the turret power interlock relay using the Relay object
+      close the remote power interlock relay using the Relay object
+
+
+METHOD:  Enable_turret_power_interlock
+  DESCRIPTION:
+    This method will enable the turret power interlock to allow
+    the turret to be driven from the turret station.
+  VISIBILITY:  public
+  INPUTS: none
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.12 Control Power Interlock
+  NOTES:
+    At this time the closed state is saved to allow the correct state
+    to be returned to in the event a remote overrides the turret and the 
+    remote gives up its control, we must return to the previous state of
+    the turret power interlock.
+  PDL:
+
+    if Drive_on
+      close the turret power interlock relay using the Relay object
+
+      set the Turret_power_interlock_status to CLOSED to return to
+        when remote gives up control
+
+
+METHOD: Get_AZ_EL_displacement  
+  DESCRIPTION:
+    This method returns the current azimuth displacement and elevation
+    displacement from the ECA.
+  VISIBILITY:  public
+  INPUTS:  none
+  OUTPUTS:
+    current azimuth displacement value
+    current elevation displacement value
+  REQUIREMENTS:
+    AFS 8.2.11 Receive Azimuth Displacement and Elevation Displacement
+    AIS 5.2.6 Azimuth Displacement analog
+    AIS 5.2.7 Elevation Displacement analog
+  NOTES:
+    Currently supported as an on-demand type of signal.  May change to a
+    monitored analog signal.
+  PDL:
+    
+    if Drive_on
+      get and return current azimuth displacement and current elevation
+      displacement from ECA using the Linear_ADC object
+ 
+
+METHOD:  Get_hours
+  DESCRIPTION:
+    This method returns the current value contained in the elapsed
+    turret drive active time.
+  VISIBILITY:  public
+  INPUTS: none
+  OUTPUTS:
+    elapsed turret drive time - the current elasped turret drive time
+  REQUIREMENTS:
+  REFERENCES:
+    AFS 4.11 Respond to CDT elapsed time indicator key
+  NOTES:
+  PDL:
+
+    return the current value for Drive_system_hours
+
+
+METHOD:  Set_drift_adjust
+  DESCRIPTION:
+    This method provides the ability to set the drift adjust to the 
+    value represented by the handstation.  Drift adjust is either ON
+    or OFF.
+  VISIBILITY:  public
+  INPUTS:
+    value - new drift adjust value 
+      ADJUST_OFF - drift adjust mode is OFF
+      ADJUST_ON  - drift adjust mode is ON
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.6 Capable of turret drift adjustment
+    AIS 5.3.1 Drift Adjust discrete
+  PDL:
+ 
+    if Drive_on
+      set drift adjust discrete to value using Discrete_out object
+
+METHOD:  Set_hours
+  DESCRIPTION:
+    This method will set the turret drive activated elapsed time value
+    to the specified value.
+  VISIBILITY:  public
+  INPUTS:
+    new_elapsed_time_value - specified turret drive elapsed time value
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 4.11 Respond to CDT elapsed time indicator key
+  PDL:
+ 
+    set the value of Drive_system_hours to the value specified by
+      new_elapsed_time_value
+ 
+
+METHOD: Set_mode
+  DESCRIPTION:
+    This method sets the requested mode of the turret drive.
+  VISIBILITY: public
+  INPUTS:
+    mode - new requested mode
+      POWER - operating in POWER mode
+      STAB  - operating in STABILIZED mode
+  OUTPUTS:  none
+  REQUIREMENTS:
+  PDL:
+    if mode is POWER
+      set drive mode switchlight to PWR_LAMP using the Console object
+    else if mode is STAB
+      set drive mode switchlight to STB_LAMP using the Console object
+
+    
+METHOD:  Set_rate
+  DESCRIPTION:
+    This method provides the ability to send azimuth and elevation rates
+    to the drive.  Two parameters are used as input to designate the
+    corresponding rates.  These parameters must be in degrees per second.
+  VISIBILITY:  public
+  INPUTS:
+    azimuth rate   - new azimuth rate in degress per second
+    elevation rate - new elevation rate in degress per second
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.3 Send Azimuth and Elevation Rate to ECA
+    AIS 5.3.4 Elevation Rate analog
+    AIS 5.3.5 Azimuth Rate analog
+  NOTES:
+    May need to couple these analogs with an interface driver to close
+    the window between writes and conversions.
+  PDL:
+
+    if Drive_on
+      send the new azimuth rate to the Azimuth analog
+      send the new elevation rate to Elevation analog
+
+
+METHOD:  Set_speed
+  DESCRIPTION:
+    This method sets the turret drive speed mode to the specified value
+    either high or low based on user input.
+  VISIBILITY:  public
+  INPUTS:
+    new_speed_value - new value to set the turret speed to
+      LOW_SPEED  - turret drive does not allow the maximum rate
+      HIGH_SPEED - turret drive allows the maximum rate
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.5 Control Turret
+    AIS 5.3.3 Turret High Speed Mode discrete
+  PDL:
+
+    if Drive_on
+      set turret drive high speed mode discrete to the new_speed_value
+       using Discrete_out object
+
+
+METHOD:  Change_drive_on
+  DESCRIPTION:
+    This method will be used to report a transition of the drive system
+    on discrete.
+  VISIBILITY:  private
+  INPUTS:
+    value - new drive system on value 
+      FALSE - indicates drive not on
+      TRUE  - indicates drive on
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.9 Monitor Drive System On
+    AIS 5.2.1 Drive System On discrete
+  REFERENCES:
+  NOTES:
+  PDL:
+
+    set Drive_on to value
+    if Drive_on is FALSE
+      deactivate the turret drive using the Drive object
+
+
+METHOD:  Change_fault
+  DESCRIPTION:
+    This method will be used to indicate that a turret malfunction
+    has been recognized by the turret drive.
+  VISIBILITY:  private
+  INPUTS:
+    value - new value resulting from a fault discrete change
+      FALSE - indicates no fault
+      TRUE  - indicates fault
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.7 Check for ECA failure indication
+    AIS 5.2.4 Turret Drive Malfunction discrete
+  PDL:
+ 
+    if value
+      send turret fault message using the Display object 
+    else 
+      return
+
+
+METHOD:  Change_stabilized_mode
+  DESCRIPTION:
+    This method will determine a transition from STAB mode to POWER mode.
+    It will set the stab mode lamp on the console.
+  VISIBILITY:  private
+  INPUTS:
+    value - new value resulting from a stab mode discrete change
+      FALSE - indicates not stabilized mode
+      TRUE -  indicates stabilized mode
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.8 Monitor Turret Stab Mode Indicate
+    AIS 5.2.3 Turret Stab Mode Indicate discrete
+  PDL:
+
+    if value 
+      set STAB mode light to LAMP_ON using the Console object
+    else value indicates not stabilized mode
+      set STAB mode light to LAMP_OFF using the Console object
+
+
+METHOD:  Change_thermal_fault
+  DESCRIPTION:
+    This method will be used to indicate that a thermal fault has been
+     recognized by the turret drive.
+  VISIBILITY:  private
+  INPUTS:
+    value - new value resulting from a thermal fault discrete change
+      FALSE - indicates no thermal fault
+      TRUE  - indicates thermal fault
+  OUTPUTS: none
+  REQUIREMENTS:
+    AFS 8.2.7 Check for ECA failure indication
+    AIS 5.2.2 Turret ECA Thermal Failure discrete
+  PDL:
+ 
+    if value
+      send thermal fault message using the Display object
+    else 
+      return
+
+END OBJECT: Drive
Index: doc/tools/pdl2texi/main.c
===================================================================
--- doc/tools/pdl2texi/main.c	(revision d4bc481ded89c54759a5cd2c51565486d72312c6)
+++ doc/tools/pdl2texi/main.c	(revision d4bc481ded89c54759a5cd2c51565486d72312c6)
@@ -0,0 +1,3256 @@
+/*
+ *  main.c
+ *  
+ *  This program error checks the OAR PDL and converts it into 
+ *  It works by reading the input file into a linked list of lines
+ *  and then performing sweeps on that list until all formatting is
+ *  complete.
+ *
+ *  FEATURES:
+ *    + rudimentary statistics
+ *    + forgiveness features 
+ * 
+ *  CHECKS PERFORMED:
+ *    + unable to open file
+ *    + unexpected end of file
+ *    + line should have a colon
+ *    + basic text (to right or below) associated with keyword improperly placed
+ *    + an "incomplete marker" is still in place
+ *    + missing keywords within a subsection
+ *    + duplicated keywords withing a subsection
+ *    + subsections in correct order
+ *    + section header indicates no subsections and there are subsections
+ *    + section header indicates subsections and there are no subsections
+ *    + inconsistent spacing in RAW text.  This tends to be 1st line with
+ *      text is indented further than a subsequent line.
+ *    + missing components on line (incomplete)
+ *    + invalid repitition of a subsection
+ *    + type keyword validated for class, type, and spacing between
+ *    + both members and range are present
+ *    + neither members and range are present
+ *    + enumerated types and attributes have members
+ *    + non-enumerated types and attributes have ranges.
+ *    + Object name and end object have the same name
+ *    + booleans in attribute section list both true and false
+ *    + task synchronization keyword is checked.  There must be a valid 
+ *      type of synchronization primitive and a description when expected.
+ *    + sections in correct order
+ *
+ *  INTERNAL ERRORS:
+ *    + attempting to reformat an already formatted line
+ *    + end of processing reached and no formatting assigned to line
+ *
+ *  CHECKS NOT PERFORMED:
+ *
+ *  TODO:
+ *
+ *  IDEAS NOT IMPLEMENTED:
+ *    + smarter reporting of sections not in the right order
+ *    + task which uses multiple synchronization types
+ *    + improved error messages
+ *    + no introductions inserted to sections
+ *    + recognize special "symbols" like infinity, +-, etc.
+ *
+ *  QUESTIONS:
+ *    + "what do I know" is actually a table and should not be reformatted.
+ *
+ *  COPYRIGHT (c) 1997.
+ *  On-Line Applications Research Corporation (OAR).
+ *  All rights reserved.
+ *
+ *  $Id$
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* XXX -- just for testing -- these should be set by options */
+char DocsNextNode[256]     = "";
+char DocsPreviousNode[256] = "";
+char DocsUpNode[256]       = "";
+
+extern int   optind;     /* Why is this not in <stdlib.h>? */
+extern char *optarg;     /* Why is this not in <stdlib.h>? */
+
+#ifndef NAME_MAX
+#define NAME_MAX      14 /* Why is the one in limits.h not showing up? */
+#endif
+#define INIT_DATA
+#define EXTERN
+
+#include "base.h"
+
+FILE           *OutFile = stdout;
+
+/*************************************************************************
+ *************************************************************************
+ *****                 DATA TYPES AND CONSTANT TABLES                *****
+ *************************************************************************
+ *************************************************************************/
+/*
+ *  Usage Information
+ */
+
+char *Usage_Strings[] = {
+  "\n",
+  "usage: cmd [-vti?] [-p previous_node] [-u up_node] files ...\n", 
+  "\n",
+  "EOF"
+};
+
+/*
+ *  The page separator is not really a keyword and will be purged before
+ *  it is seen elsewhere.
+ */
+
+#define PAGE_SEPARATOR                            "#PAGE"
+
+/* 
+ *  Section Delimiter Keywords
+ */
+
+#define MAXIMUM_KEYWORD_LENGTH   32
+
+/*
+ *  Level indicates where in the format the delimiter is allowed to occur.
+ *    1 indicates a major section divider (e.g. "ATTRIBUTE DESCRIPTIONS:").
+ *    2 indicates a subsection (e.g. "ATTRIBUTE:"). 
+ *    3 indicates a heading (e.g. "DESCRIPTION:").
+ */
+
+#define TEXT         0
+#define SECTION      1
+#define SUBSECTION   2
+#define HEADING      3
+  
+typedef enum {
+  UNUSED,                            /* dummy 0 slot */
+  OBJECT,                            /* sections */
+  ATTRIBUTE_DESCRIPTIONS,
+  ASSOCIATION_DESCRIPTIONS,
+  ABSTRACT_TYPE_DESCRIPTIONS,
+  DATA_ITEM_DESCRIPTIONS,
+  METHOD_DESCRIPTIONS,
+  TASK_DESCRIPTIONS,
+  END_OBJECT,
+ 
+  ATTRIBUTE,                         /* subsections */
+  ASSOCIATION,
+  ABSTRACT_TYPE,
+  DATA_ITEM,
+  METHOD,
+  TASK,
+ 
+  DESCRIPTION,                       /* headings */
+  COPYRIGHT,
+  PORTING,
+  THEORY_OF_OPERATION,
+  DERIVATION,
+  DEPENDENCIES,
+  NOTES,
+  TYPE,
+  RANGE,
+  MEMBERS,
+  UNITS,
+  SCALE_FACTOR,
+  DEFAULT,
+  TOLERANCE,
+  REQUIREMENTS,
+  REFERENCES,
+  VISIBILITY,
+  ASSOCIATED_WITH,
+  MULTIPLICITY,
+  INPUTS,
+  OUTPUTS,
+  PDL,
+  SYNCHRONIZATION,
+  TIMING
+}  Keyword_indices_t;
+
+#define KEYWORD_FIRST OBJECT
+#define KEYWORD_LAST  TIMING
+
+/*
+ *  Line Management Structure
+ */
+
+typedef enum {
+  NO_EXTRA_FORMATTING_INFO,
+  RAW_OUTPUT,
+  PARAGRAPH_OUTPUT,
+  BULLET_OUTPUT,
+}  ExtraFormat_info_t;
+
+typedef struct {
+  Chain_Node         Node;
+  Keyword_indices_t  keyword;   /* unused is unknown/undecided */
+  ExtraFormat_info_t format;
+  int                number;
+  char               Contents[ PARAGRAPH_SIZE ];
+} Line_Control;
+
+typedef enum {
+  RT_FORBIDDEN,     /* no text to right allowed */
+  RT_OPTIONAL,      /* text to right optional -- none below */
+  RT_NONE,          /* text to right is "none" or nothing -- none below */
+  RT_REQUIRED,      /* text to right required -- none below */
+  RT_BELOW,         /* text to right forbidden -- text below required */
+  RT_NONE_OR_BELOW, /* text to right is "none" OR there is text below  */
+  RT_MAYBE_BELOW,   /* text to right required -- text below optional */
+  RT_EITHER,        /* text to right OR below */
+  RT_BOTH           /* text to right AND below */
+}  Keywords_text_mode_t;
+
+typedef enum {
+  BL_FORBIDDEN,     /* text below forbidden */
+  BL_FORMATTED,     /* text below is to be formatted as paragraphs */
+  BL_RAW,           /* text below is to be unprocessed by this program */
+}  Keywords_text_below_t;
+
+typedef int (*Keyword_validater_t)( Line_Control * );
+
+typedef struct {
+  char                      Name[ MAXIMUM_KEYWORD_LENGTH ];
+  int                       level;
+  Keywords_text_mode_t      text_mode;
+  Keywords_text_below_t     text_below_mode;
+  Keyword_validater_t       keyword_validation_routine;
+}  Keyword_info_t;
+
+/*
+ *  Keyword Validation Routines
+ */
+
+int Validate_visibility(
+  Line_Control   *line
+);
+
+int Validate_synchronization(
+  Line_Control   *line
+);
+
+Keyword_info_t Keywords[] = {
+  { "unused",
+        0,          0,                0, NULL }, /* so 0 can be invalid */
+  { "OBJECT:",
+        SECTION,    RT_REQUIRED,      BL_FORBIDDEN, NULL },
+  { "ATTRIBUTE DESCRIPTIONS:",
+        SECTION,    RT_NONE,          BL_FORBIDDEN, NULL },
+  { "ASSOCIATION DESCRIPTIONS:",
+        SECTION,    RT_NONE,          BL_FORBIDDEN, NULL },
+  { "ABSTRACT TYPE DESCRIPTIONS:",
+        SECTION,    RT_NONE,          BL_FORBIDDEN, NULL },
+  { "DATA ITEM DESCRIPTIONS:",
+        SECTION,    RT_NONE,          BL_FORBIDDEN, NULL },
+  { "METHOD DESCRIPTIONS:",
+        SECTION,    RT_NONE,          BL_FORBIDDEN, NULL },
+  { "TASK DESCRIPTIONS:",
+        SECTION,    RT_NONE,          BL_FORBIDDEN, NULL },
+  { "ENDOBJECT:",
+        SECTION,    RT_REQUIRED,      BL_FORBIDDEN, NULL },
+
+  { "ATTRIBUTE:",
+        SUBSECTION, RT_REQUIRED,      BL_FORBIDDEN, NULL },
+  { "ASSOCIATION:",
+        SUBSECTION, RT_REQUIRED,      BL_FORBIDDEN, NULL },
+  { "ABSTRACT TYPE:",
+        SUBSECTION, RT_REQUIRED,      BL_FORBIDDEN, NULL },
+  { "DATA ITEM:",
+        SUBSECTION, RT_REQUIRED,      BL_FORBIDDEN, NULL },
+  { "METHOD:",
+        SUBSECTION, RT_REQUIRED,      BL_FORBIDDEN, NULL },
+  { "TASK:",
+        SUBSECTION, RT_REQUIRED,      BL_FORBIDDEN, NULL },
+
+  { "DESCRIPTION:",
+        HEADING,    RT_BELOW,         BL_FORMATTED, NULL },
+  { "COPYRIGHT:",
+        HEADING,    RT_BELOW,         BL_FORMATTED, NULL },
+  { "PORTING:",
+        HEADING,    RT_BELOW,         BL_FORMATTED, NULL },
+  { "THEORY OF OPERATION:",
+        HEADING,    RT_BELOW,         BL_FORMATTED, NULL },
+  { "DERIVATION:",
+        HEADING,    RT_REQUIRED,      BL_FORBIDDEN, NULL },
+  { "DEPENDENCIES:",
+        HEADING,    RT_BELOW,         BL_FORMATTED, NULL },
+  { "NOTES:",
+        HEADING,    RT_BELOW,         BL_FORMATTED, NULL },
+  { "TYPE:",
+        HEADING,    RT_REQUIRED,      BL_FORBIDDEN, NULL },
+  { "RANGE:",
+        HEADING,    RT_REQUIRED,      BL_FORBIDDEN, NULL },
+  { "MEMBERS:",
+        HEADING,    RT_BELOW,         BL_RAW,       NULL },
+  { "UNITS:",
+        HEADING,    RT_REQUIRED,      BL_FORBIDDEN, NULL },
+  { "SCALE FACTOR:",
+        HEADING,    RT_REQUIRED,      BL_FORBIDDEN, NULL },
+  { "DEFAULT:",
+        HEADING,    RT_EITHER,        BL_RAW, NULL },
+  { "TOLERANCE:",
+        HEADING,    RT_REQUIRED,      BL_FORBIDDEN, NULL },
+  { "REQUIREMENTS:",
+        HEADING,    RT_BELOW,         BL_RAW,       NULL },
+  { "REFERENCES:",
+        HEADING,    RT_BELOW,         BL_RAW,       NULL },
+  { "VISIBILITY:",
+        HEADING,    RT_REQUIRED,      BL_FORBIDDEN, Validate_visibility },
+  { "ASSOCIATED WITH:",
+        HEADING,    RT_REQUIRED,      BL_FORBIDDEN, NULL },
+  { "MULTIPLICITY:",
+        HEADING,    RT_REQUIRED,      BL_FORBIDDEN, NULL },
+  { "INPUTS:",
+        HEADING,    RT_NONE_OR_BELOW, BL_RAW,       NULL },
+  { "OUTPUTS:",
+        HEADING,    RT_NONE_OR_BELOW, BL_RAW,       NULL },
+  { "PDL:",
+        HEADING,    RT_BELOW,         BL_RAW,       NULL },
+  { "SYNCHRONIZATION:",
+        HEADING,    RT_MAYBE_BELOW,   BL_RAW,    Validate_synchronization },
+  { "TIMING:",
+        HEADING,    RT_REQUIRED,      BL_FORBIDDEN, NULL }
+};
+
+#define NUMBER_OF_KEYWORDS \
+  ( sizeof( Keywords ) / sizeof( Keyword_info_t ) ) - 2
+
+/*
+ *  Section Descriptions
+ */
+
+#define MAXIMUM_ELEMENTS 16
+
+typedef struct {
+  Keyword_indices_t  keyword;
+  boolean            is_required;
+}  Element_info_t;
+
+typedef struct Section_info_struct_t Section_info_t;
+
+typedef (*Section_validater_t)(
+  Section_info_t *,
+  Line_Control *,  /* start */
+  Line_Control *   /* next_section */
+);
+
+struct Section_info_struct_t {
+  boolean              repeats;
+  Keyword_indices_t    This_section;
+  Keyword_indices_t    Next_section;
+  Section_validater_t  section_validation_routine;
+  Element_info_t       Description[MAXIMUM_ELEMENTS];
+};
+
+int Validate_object(
+  Section_info_t *section,
+  Line_Control   *start,
+  Line_Control   *next_section
+);
+  
+int Validate_attribute(
+  Section_info_t *section,
+  Line_Control   *start,
+  Line_Control   *next_section
+);
+  
+int Validate_abstract_type(
+  Section_info_t *section,
+  Line_Control   *start,
+  Line_Control   *next_section
+);
+  
+
+Section_info_t ObjectSection = {
+  FALSE,                        /* subsections repeat */
+  OBJECT,                       /* this section */
+  ATTRIBUTE_DESCRIPTIONS,       /* next section */
+  Validate_object,              /* validation routine */
+  {
+    { OBJECT,              TRUE },     /*  0 */
+    { DESCRIPTION,         TRUE },     /*  1 */
+    { COPYRIGHT,           FALSE },    /*  2 */
+    { PORTING,             FALSE },    /*  3 */
+    { THEORY_OF_OPERATION, TRUE },     /*  4 */
+    { DERIVATION,          FALSE },    /*  5 */
+    { DEPENDENCIES,        FALSE },    /*  6 */
+    { REQUIREMENTS,        FALSE },    /*  7 */
+    { REFERENCES,          FALSE },    /*  8 */
+    { NOTES,               FALSE },    /*  9 */
+    { UNUSED,              FALSE },    /* 10 */
+    { UNUSED,              FALSE },    /* 11 */
+    { UNUSED,              FALSE },    /* 12 */
+    { UNUSED,              FALSE },    /* 13 */
+    { UNUSED,              FALSE },    /* 14 */
+    { UNUSED,              FALSE },    /* 15 */
+  }
+};
+
+Section_info_t AttributeSection = {
+  TRUE,                         /* subsections repeat */
+  ATTRIBUTE_DESCRIPTIONS,       /* this section */
+  ASSOCIATION_DESCRIPTIONS,     /* next section */
+  Validate_attribute,           /* validation routine */
+  {
+    { ATTRIBUTE,           TRUE },     /*  0 */
+    { DESCRIPTION,         TRUE },     /*  1 */
+    { TYPE,                TRUE },     /*  2 */
+    { MEMBERS,             FALSE },    /*  3 */
+    { RANGE,               FALSE },    /*  4 */
+    { UNITS,               FALSE },    /*  5 */
+    { SCALE_FACTOR,        FALSE },    /*  6 */
+    { DEFAULT,             FALSE },    /*  7 */
+    { TOLERANCE,           FALSE },    /*  8 */
+    { REQUIREMENTS,        FALSE },    /*  9 */
+    { REFERENCES,          FALSE },    /* 10 */
+    { NOTES,               FALSE },    /* 11 */
+    { UNUSED,              FALSE },    /* 12 */
+    { UNUSED,              FALSE },    /* 13 */
+    { UNUSED,              FALSE },    /* 14 */
+    { UNUSED,              FALSE },    /* 15 */
+  }
+};
+
+Section_info_t AssociationSection = {
+  TRUE,                         /* subsections repeat */
+  ASSOCIATION_DESCRIPTIONS,     /* this section */
+  ABSTRACT_TYPE_DESCRIPTIONS,   /* next section */
+  NULL,                         /* validation routine */
+  {
+    { ASSOCIATION,         TRUE },     /*  0 */
+    { DESCRIPTION,         TRUE },     /*  1 */
+    { VISIBILITY,          TRUE },     /*  2 */
+    { ASSOCIATED_WITH,     TRUE },     /*  3 */
+    { MULTIPLICITY,        TRUE },     /*  4 */
+    { REQUIREMENTS,        FALSE },    /*  5 */
+    { REFERENCES,          FALSE },    /*  6 */
+    { NOTES,               FALSE },    /*  7 */
+    { UNUSED,              FALSE },    /*  8 */
+    { UNUSED,              FALSE },    /*  9 */
+    { UNUSED,              FALSE },    /* 10 */
+    { UNUSED,              FALSE },    /* 11 */
+    { UNUSED,              FALSE },    /* 12 */
+    { UNUSED,              FALSE },    /* 13 */
+    { UNUSED,              FALSE },    /* 14 */
+    { UNUSED,              FALSE },    /* 15 */
+  }
+};
+
+Section_info_t AbstractTypeSection = {
+  TRUE,                         /* subsections repeat */
+  ABSTRACT_TYPE_DESCRIPTIONS,   /* this section */
+  DATA_ITEM_DESCRIPTIONS,       /* next section */
+  Validate_abstract_type,       /* validation routine */
+  {
+    { ABSTRACT_TYPE,       TRUE },     /*  0 */
+    { DESCRIPTION,         TRUE },     /*  1 */
+    { VISIBILITY,          TRUE },     /*  2 */
+    { DERIVATION,          TRUE },     /*  3 */
+    { MEMBERS,             FALSE },    /*  4 */
+    { RANGE,               FALSE },    /*  5 */
+    { UNITS,               FALSE },    /*  6 */
+    { SCALE_FACTOR,        FALSE },    /*  7 */
+    { DEFAULT,             FALSE },    /*  8 */
+    { TOLERANCE,           FALSE },    /*  9 */
+    { REQUIREMENTS,        FALSE },    /* 10 */
+    { REFERENCES,          FALSE },    /* 11 */
+    { NOTES,               FALSE },    /* 12 */
+    { UNUSED,              FALSE },    /* 13 */
+    { UNUSED,              FALSE },    /* 14 */
+    { UNUSED,              FALSE },    /* 15 */
+  }
+};
+
+Section_info_t DataItemSection = {
+  TRUE,                         /* subsections repeat */
+  DATA_ITEM_DESCRIPTIONS,       /* this section */
+  METHOD_DESCRIPTIONS,          /* next section */
+  NULL,                         /* validation routine */
+  {
+    { DATA_ITEM,           TRUE },     /*  0 */
+    { DESCRIPTION,         TRUE },     /*  1 */
+    { TYPE,                TRUE },     /*  2 */
+    { UNITS,               FALSE },    /*  3 */
+    { SCALE_FACTOR,        FALSE },    /*  4 */
+    { DEFAULT,             FALSE },    /*  5 */
+    { TOLERANCE,           FALSE },    /*  6 */
+    { NOTES,               FALSE },    /*  7 */
+    { UNUSED,              FALSE },    /*  8 */
+    { UNUSED,              FALSE },    /*  9 */
+    { UNUSED,              FALSE },    /* 10 */
+    { UNUSED,              FALSE },    /* 11 */
+    { UNUSED,              FALSE },    /* 12 */
+    { UNUSED,              FALSE },    /* 13 */
+    { UNUSED,              FALSE },    /* 14 */
+    { UNUSED,              FALSE },    /* 15 */
+  }
+};
+
+Section_info_t MethodSection = {
+  TRUE,                         /* subsections repeat */
+  METHOD_DESCRIPTIONS,          /* this section */
+  TASK_DESCRIPTIONS,            /* next section */
+  NULL,                         /* validation routine */
+  {
+    { METHOD,              TRUE },     /*  0 */
+    { DESCRIPTION,         TRUE },     /*  1 */
+    { VISIBILITY,          TRUE },     /*  2 */
+    { INPUTS,              TRUE },     /*  3 */
+    { OUTPUTS,             TRUE },     /*  4 */
+    { REQUIREMENTS,        FALSE },    /*  5 */
+    { REFERENCES,          FALSE },    /*  6 */
+    { NOTES,               FALSE },    /*  7 */
+    { PDL,                 TRUE },     /*  8 */
+    { UNUSED,              FALSE },    /*  9 */
+    { UNUSED,              FALSE },    /* 10 */
+    { UNUSED,              FALSE },    /* 11 */
+    { UNUSED,              FALSE },    /* 12 */
+    { UNUSED,              FALSE },    /* 13 */
+    { UNUSED,              FALSE },    /* 14 */
+    { UNUSED,              FALSE },    /* 15 */
+  }
+};
+
+Section_info_t TaskSection = {
+  TRUE,                         /* subsections repeat */
+  TASK_DESCRIPTIONS,            /* this section */
+  END_OBJECT,                   /* next section */
+  NULL,                         /* validation routine */
+  {
+    { METHOD,              TRUE },     /*  0 */
+    { DESCRIPTION,         TRUE },     /*  1 */
+    { VISIBILITY,          TRUE },     /*  2 */
+    { INPUTS,              TRUE },     /*  3 */
+    { SYNCHRONIZATION,     TRUE },     /*  4 */
+    { TIMING,              TRUE },     /*  5 */
+    { REQUIREMENTS,        FALSE },    /*  6 */
+    { REFERENCES,          FALSE },    /*  7 */
+    { NOTES,               FALSE },    /*  8 */
+    { PDL,                 TRUE },     /*  9 */
+    { UNUSED,              FALSE },    /* 10 */
+    { UNUSED,              FALSE },    /* 11 */
+    { UNUSED,              FALSE },    /* 12 */
+    { UNUSED,              FALSE },    /* 13 */
+    { UNUSED,              FALSE },    /* 14 */
+    { UNUSED,              FALSE },    /* 15 */
+  }
+};
+
+
+Section_info_t *Sections[] = {
+  &ObjectSection,
+  &AttributeSection,
+  &AssociationSection,
+  &AbstractTypeSection,
+  &DataItemSection,
+  &MethodSection,
+  &TaskSection
+};
+
+/*
+ *  exit_application
+ */
+
+void exit_application( 
+  int status
+)
+{
+  fprintf( stderr, "*** Error encountered ***\n" );
+/*
+  fprintf( stderr, "*** Error encountered on line %d ***\n", CurrentLine );
+*/
+  fclose( OutFile ); 
+  exit( status );
+}
+
+/*************************************************************************
+ *************************************************************************
+ *****                LINE MANIPULATION ROUTINES                     *****
+ *************************************************************************
+ *************************************************************************/
+
+/*
+ * PrintLine
+ */
+
+void PrintLine(
+  Line_Control *line
+)
+{
+  assert( line );
+
+  if ( line->number == -1 )
+    fprintf( stderr, "     " );
+  else
+    fprintf( stderr, "%5d", line->number );
+
+#if 0
+  fprintf( stderr, "%s\n", line->Contents );
+#else
+  /* 
+   *  Include some debugging information
+   */
+  fprintf(
+    stderr,
+    "<%d,%d>:%s\n", 
+    line->keyword,
+    line->format,
+    line->Contents
+  );
+#endif
+}
+
+Chain_Control Line_Pool;
+
+/*
+ *  FillLinePool
+ */
+
+void FillLinePool( void )
+{
+  void *pool;
+
+#define LINE_POOL_FILL_COUNT 100
+
+  pool = malloc( sizeof( Line_Control ) * LINE_POOL_FILL_COUNT );
+  assert( pool );
+
+  _Chain_Initialize(
+    &Line_Pool,
+    pool,
+    LINE_POOL_FILL_COUNT,
+    sizeof( Line_Control )
+  );
+}
+
+/*
+ * AllocateLine
+ */
+
+Line_Control *AllocateLine( void )
+{
+  Line_Control  *new_line;
+
+  new_line = (Line_Control *) _Chain_Get( &Line_Pool );
+  if ( !new_line ) {
+    FillLinePool();
+    new_line = (Line_Control *) _Chain_Get( &Line_Pool );
+    assert( new_line );
+  }
+ 
+/*
+ *  This is commented out because although it is helpful during debug,
+ *  it consumes a significant percentage of the program's execution time.
+ */
+
+  memset( new_line->Contents, '\0', sizeof( new_line->Contents ) );
+  new_line->number = -1;
+
+  new_line->keyword = UNUSED;
+  new_line->format = NO_EXTRA_FORMATTING_INFO;
+
+  new_line->Node.next     = NULL;
+  new_line->Node.previous = NULL;
+
+  return new_line;
+}
+
+/*
+ * FreeLine
+ */
+
+void FreeLine(
+  Line_Control *line
+)
+{
+  fflush( stdout );
+  _Chain_Append( &Line_Pool, &line->Node );
+}
+
+/*
+ * DeleteLine
+ */
+
+Line_Control *DeleteLine(
+  Line_Control *line
+)
+{
+  Line_Control *next;
+  
+  next = (Line_Control *)line->Node.next; 
+  _Chain_Extract( &line->Node );
+  FreeLine( line );
+  return next;
+}
+
+/* 
+ *  PrintSurroundingLines
+ */
+
+void PrintSurroundingLines(
+  Line_Control *line,
+  int           backward,
+  int           forward
+)
+{
+  int           i;
+  int           real_backward;
+  Line_Control *local;
+
+  for ( local=line, real_backward=0, i=1 ; 
+        i<=backward ; 
+        i++, real_backward++ ) {
+    if ( &local->Node == Lines.first )
+      break;
+    local = (Line_Control *) local->Node.previous;
+  }
+
+  for ( i=1 ; i<=real_backward ; i++ ) {
+    PrintLine( local );
+    local = (Line_Control *) local->Node.next;
+  }
+ 
+  PrintLine( local );
+
+  for ( i=1 ; i<=forward ; i++ ) {
+    local = (Line_Control *) local->Node.next;
+    if ( _Chain_Is_last( &local->Node ) )
+      break;
+    PrintLine( local );
+  }
+ 
+}
+
+/*
+ *  SetLineFormat
+ */
+
+void SetLineFormat(
+  Line_Control       *line,
+  ExtraFormat_info_t  format
+)
+{
+  if ( line->format != NO_EXTRA_FORMATTING_INFO ) {
+    fprintf( stderr, "Line %d is already formatted\n", line->number );
+    PrintLine( line );
+    assert( FALSE );
+  }
+
+  line->format = format;
+}
+
+/*
+ *  LineCopyFromRight
+ */
+
+void LineCopyFromRight(
+  Line_Control *line,
+  char         *dest
+)
+{
+  char *p;
+
+  for ( p=line->Contents ; *p != ':' ; p++ ) 
+    ;
+  p++;  /* skip the ':' */
+  for ( ; isspace( *p ) ; p++ )
+    ;
+
+  strcpy( dest, p );
+
+}
+
+/*
+ *  LineCopySectionName
+ */
+
+void LineCopySectionName(
+  Line_Control *line,
+  char         *dest
+)
+{
+  char *p;
+  char *d;
+
+  p = line->Contents;
+  d = dest;
+
+  if ( *p == '@' ) {                /* skip texinfo command */
+    while ( !isspace( *p++ ) )
+      ;
+  }
+
+  for ( ; *p != ':' ; ) 
+    *d++ = *p++;
+
+  *d = '\0';
+}
+
+/*
+ *  FormatParagraph
+ */
+
+Line_Control *FormatParagraph(
+  Line_Control *starting
+)
+{
+  Chain_Node   *insert_after;
+  Line_Control *to_add;
+  Line_Control *new_line;
+  char          Paragraph[ PARAGRAPH_SIZE ];
+  int           starting_line;
+  char         *src;
+  char         *dest;
+  boolean       do_loop;
+  boolean       is_bullet;
+  int           length;
+
+  length = 0;
+
+  starting_line = starting->number;
+  insert_after = starting->Node.previous;
+  to_add = starting;
+
+  dest = Paragraph;
+
+  is_bullet = FALSE;
+
+  for ( ; ; ) {
+    src = to_add->Contents;
+
+    for ( ; *src && isspace( *src ) ; src++ ); /* skip leading spaces */
+
+    if ( *src == '+' ) {
+      if ( is_bullet == TRUE )
+        break;
+      is_bullet = TRUE;
+      for ( src++ ; isspace(*src) ; src++ ) ;
+    }
+
+    do_loop = TRUE;
+    while ( *src && do_loop == TRUE ) {
+                    
+      if ( isspace( *src ) ) {         /* convert multiple spaces to 1 */
+
+        if ( *(dest-1) == '.' ) {      /* two spaces after period */
+          *dest++ = ' ';
+          length++;
+        }
+
+        assert( length < PARAGRAPH_SIZE );
+
+        *dest++ = ' ';
+        length++;
+
+        assert( length < PARAGRAPH_SIZE );
+ 
+        for (  ; isspace( *src ) ; src++ )
+          if ( !*src ) {
+            do_loop = FALSE;
+            break;
+          }
+ 
+        continue;
+ 
+      } else {
+        *dest++ = *src;
+        length++;
+        assert( length < PARAGRAPH_SIZE );
+        src++;
+      }
+    }
+
+    to_add = DeleteLine( to_add );
+
+    if ( _Chain_Is_last( &to_add->Node ) )
+      break;
+
+    if ( !strlen( to_add->Contents ) )
+      break;
+
+    if ( to_add->keyword )
+      break;
+
+    if ( !isspace( *(dest-1) ) ) {
+      if ( *(dest-1) == '.' ) {
+        *dest++ = ' ';
+        length++;
+        assert( length < PARAGRAPH_SIZE );
+      }
+      *dest++ = ' ';
+      length++;
+      assert( length < PARAGRAPH_SIZE );
+    }
+  }
+
+  new_line = AllocateLine();
+  
+  SetLineFormat( new_line, (is_bullet) ? BULLET_OUTPUT : PARAGRAPH_OUTPUT );
+
+  *dest = '\0';
+
+  strcpy( new_line->Contents, Paragraph );
+
+  new_line->number = starting_line;
+ 
+  _Chain_Insert( insert_after, &new_line->Node );
+
+  return new_line;
+}
+
+/*
+ *  FormatMultipleParagraphs
+ */
+
+Line_Control *FormatMultipleParagraphs(
+  Line_Control *starting
+)
+{
+  Line_Control *line = starting;
+
+  if ( _Chain_Is_last( &line->Node ) ) {
+    fprintf( stderr, "end of file reached when expecting text\n" ); 
+    exit_application( 0 );
+  }
+
+  if ( line->keyword ) {
+    line = (Line_Control *) line->Node.next;
+
+    if ( _Chain_Is_last( &line->Node ) ) {
+      fprintf( stderr, "end of file reached when expecting text\n" ); 
+      exit_application( 0 );
+    }
+  }
+    
+  for ( ;; ) {
+    assert( line );
+
+    if ( _Chain_Is_last( &line->Node ) )
+      break;
+
+    if ( line->keyword )
+      break;
+    
+    if ( !strlen( line->Contents ) ) {
+      line = DeleteLine( line );
+      continue;
+    }
+
+    line = FormatParagraph( line );
+    line = (Line_Control *) line->Node.next;
+  }
+}
+
+/*************************************************************************
+ *************************************************************************
+ *****              END OF LINE MANIPULATION ROUTINES                *****
+ *************************************************************************
+ *************************************************************************/
+
+/*
+ *  CountKeywordOccurrences
+ */
+
+int CountKeywordOccurrences(
+  Keyword_indices_t  keyword
+)
+{
+  int           count = 0;
+  Line_Control *line;
+
+  for ( line = (Line_Control *) Lines.first ;
+        !_Chain_Is_last( &line->Node ) ; 
+        line = (Line_Control *) line->Node.next ) {
+    if ( line->keyword == keyword )
+      count += 1;
+  }
+
+  return count;
+}
+
+/*
+ *  main
+ */
+
+int main(
+  int    argc,
+  char **argv
+)
+{
+  int      c;
+  int      index;
+  boolean  single_file_mode;
+
+  Verbose = FALSE;
+  Statistics = FALSE;
+  IncompletesAreErrors = TRUE;
+  InsertTBDs = FALSE;
+
+  while ((c = getopt(argc, argv, "istv?n:p:u:")) != EOF) {
+    switch (c) {
+      case 'i':
+        IncompletesAreErrors = FALSE;
+        break;
+      case 't':
+        InsertTBDs = TRUE;
+        break;
+      case 'v':
+        Verbose = TRUE;
+        break;
+      case 'n':
+         strcpy( DocsNextNode, optarg );
+         break;
+      case 'p':
+         strcpy( DocsPreviousNode, optarg );
+         break;
+      case 'u':
+         strcpy( DocsUpNode, optarg );
+         break;
+      case 's':
+         Statistics = TRUE;
+         break;
+      case '?':
+        usage();
+        return 0;
+    }
+  }
+
+  if ( Verbose ) {
+    fprintf( stderr, "Arguments successfully parsed\n" );
+    fprintf( stderr, "Next Node = (%s)\n", DocsNextNode );
+    fprintf( stderr, "Previous Node = (%s)\n", DocsPreviousNode );
+    fprintf( stderr, "Up Node = (%s)\n", DocsUpNode );
+  }
+
+  FillLinePool();
+
+  for ( index=optind ; index < argc ; index++ ) {
+    ProcessFile( argv[ index ], NULL );
+  }
+    
+  if ( Verbose )
+    fprintf( stderr, "Exitting\n" );
+
+  return 0;
+}
+
+/*
+ *  ProcessFile
+ */
+
+void ProcessFile(
+  char   *inname,
+  char   *outname
+)
+{
+   char            out[ 256 ];
+   int             index;
+   int             out_index;
+
+   /*
+    *  Automatically generate the output file name.
+    */
+
+   if ( outname == NULL ) {
+     out_index = 0;
+     for( index=0 ; inname[index] && inname[index] != '.' ; index++ ) {
+/*
+       if ( inname[ index ] != '_' )
+*/
+         out[ out_index++ ] = inname[ index ];
+     }
+
+     out[ out_index++ ] = '.';
+     out[ out_index++ ] = 't';
+     out[ out_index++ ] = 'e';
+     out[ out_index++ ] = 'x';
+     out[ out_index++ ] = 'i';
+     out[ out_index ] = '\0';
+
+   }
+
+   /*
+    *  Read the file into our internal data structure
+    */
+
+   if ( Verbose )
+     printf( "Processing (%s) -> (%s)\n", inname, out );
+
+   ReadFileIntoChain( inname );
+
+   if ( Verbose )
+     fprintf( stderr, "-------->FILE READ IN\n" );
+
+   /*
+    *  Remove any spaces before the keyword and mark each keyword line as
+    *  such.  Also remove extra white space at the end of lines.
+    */
+
+   StripBlanks();
+
+   if ( Verbose )
+     fprintf( stderr, "-------->BLANKS BEFORE KEYWORDS STRIPPED\n" );
+
+   /*
+    *  Count the number of each type of thing
+    */
+
+   NumberOfAttributes    = CountKeywordOccurrences( ATTRIBUTE );
+   NumberOfAssociations  = CountKeywordOccurrences( ASSOCIATION );
+   NumberOfAbstractTypes = CountKeywordOccurrences( ABSTRACT_TYPE );
+   NumberOfDataItems     = CountKeywordOccurrences( DATA_ITEM );
+   NumberOfMethods       = CountKeywordOccurrences( METHOD );
+   NumberOfTasks         = CountKeywordOccurrences( TASK );
+
+   if ( Verbose || Statistics ) {
+     fprintf( stderr, "NUMBER OF ATTRIBUTES = %d\n", NumberOfAttributes );
+     fprintf( stderr, "NUMBER OF ASSOCIATIONS = %d\n", NumberOfAssociations );
+     fprintf( stderr, "NUMBER OF ABSTRACT TYPES = %d\n", NumberOfAbstractTypes);
+     fprintf( stderr, "NUMBER OF DATA ITEMS = %d\n", NumberOfDataItems );
+     fprintf( stderr, "NUMBER OF METHODS = %d\n", NumberOfMethods );
+     fprintf( stderr, "NUMBER OF TASKS = %d\n", NumberOfTasks );
+   }
+
+   /*
+    *  1.  Merge paragraphs entered as multiple lines into a single node 
+    *      on the chain.
+    *  2.  Clean up "colon lines".
+    */
+
+   if ( MergeText() == -1 )
+     exit_application( 1 );
+
+   if ( Verbose )
+     fprintf( stderr, "-------->PARAGRAPHS MERGED\n" );
+
+   RemovePagebreaks();
+   
+   /*
+    *  Remove extraneous white space
+    */
+
+   if ( Verbose )
+     fprintf( stderr, "-------->PAGEBREAKS REMOVED\n" );
+
+   /*
+    *  At this point, the only unmarked lines should be empty or errors.
+    *  This deletes empty unmarked lines and flags the others as errors.
+    */
+
+   if ( RemoveExtraBlankLines() == -1 )
+     exit_application( 1 );
+ 
+   if ( Verbose )
+     fprintf( stderr, "-------->EXTRA BLANK LINES REMOVED\n" );
+
+   /*
+    *  Now report which lines do not appear to be complete
+    */
+
+   if ( CheckForIncomplete() == -1 )
+     exit_application( 1 );
+
+   if ( Verbose )
+     fprintf( stderr, "-------->CHECKED FOR INCOMPLETE KEYWORDS\n" );
+
+   /*
+    *  Check that all the required sections are present.
+    */
+
+   if ( CheckOutline() == -1 )
+     exit_application( 1 );
+  
+   if ( Verbose )
+     fprintf( stderr, "-------->CHECKED FOR HIGH LEVEL SECTION PROBLEMS\n" );
+
+   /*
+    *  Check for problems in each section.
+    */
+
+   if ( CheckSections() == -1 )
+     exit_application( 1 );
+
+   if ( Verbose )
+     fprintf( stderr, "-------->CHECKED FOR INTERNAL SECTION PROBLEMS\n" );
+
+   /*
+    *  Look for Internal Consistencies
+    */
+
+   if ( Verbose ) 
+     fprintf( stderr, "-------->LOOKING FOR ERRORS\n" );
+
+   LookForInternalInconsistencies();
+
+   /*
+    *  Formatting the file 
+    */
+
+   FormatToTexinfo();
+
+   if ( Verbose ) 
+     fprintf( stderr, "-------->FILE FORMATTED TO TEXINFO\n" );
+
+   /*
+    *  Print the file 
+    */
+
+   PrintFile( out );
+
+   if ( Verbose ) 
+     fprintf( stderr, "-------->FILE PRINTED\n" );
+
+   /*
+    *  Clean Up 
+    */
+
+   ReleaseFile();
+
+   if ( Verbose )
+     fprintf( stderr, "-------->FILE RELEASED\n" );
+}
+
+/*
+ *  LookForInternalInconsistencies
+ *
+ *  This routine looks for lines with no formatting information and
+ *  lines with too much formatting information.
+ */
+
+void LookForInternalInconsistencies( void )
+{
+  Line_Control  *line;
+  int            i;
+  int            errors = 0;
+  char          *src;
+
+  for ( line = (Line_Control *) Lines.first ;
+        !_Chain_Is_last( &line->Node ) ;
+        line = (Line_Control *)line->Node.next
+        ) {
+    if ( !line->keyword && line->format == NO_EXTRA_FORMATTING_INFO ) {
+      fprintf(
+        stderr,
+        "\nLine %d has no keyword or formatting:\n", 
+        line->number
+      );
+      PrintSurroundingLines( line, 3,  3 );
+      errors++;
+    } 
+  }
+ 
+  if ( errors ) 
+    exit_application( 1 );
+}
+
+/*
+ *  usage
+ */
+
+void usage( void )
+{
+  int index;
+
+  for ( index=0 ; strcmp( Usage_Strings[ index ], "EOF" ) ; index++ )
+    fprintf( stderr, Usage_Strings[ index ] );
+}
+
+/*
+ *  ReadFileIntoChain
+ */
+
+void ReadFileIntoChain( 
+  char *inname
+)
+{
+   FILE *InFile;
+   int   line_count;
+   int   max_length;
+   char *line;
+   char  Buffer[ BUFFER_SIZE ];
+   Line_Control *new_line;
+
+   InFile = fopen( inname, "r" );
+
+   if ( !InFile ) {
+     fprintf( stderr, "Unable to open (%s)\n", inname );
+     exit( 1 );
+   }
+   assert( InFile );
+
+   max_length = 0;
+   line_count = 0;
+
+   _Chain_Initialize_empty( &Lines );
+
+   for ( ;; ) {
+      line = fgets( Buffer, BUFFER_SIZE, InFile );
+      if ( !line ) 
+        break;
+
+      Buffer[ strlen( Buffer ) - 1 ] = '\0';
+
+      new_line = AllocateLine();
+ 
+      strcpy( new_line->Contents, Buffer );
+
+      new_line->number = ++line_count;
+ 
+      _Chain_Append( &Lines, &new_line->Node );
+   } 
+
+   fclose( InFile ); 
+}
+
+/*
+ *  StripBlanks
+ */
+
+void StripBlanks( void )
+{
+  Line_Control      *line;
+  Keyword_indices_t  index;
+  int                indentation;
+  int                length;
+  Keyword_info_t    *key;
+ 
+  for ( line = (Line_Control *) Lines.first ;
+        !_Chain_Is_last( &line->Node ) ;
+        line = (Line_Control *) line->Node.next
+        ) {
+
+    /*
+     *  Strip white space from the end of each line 
+     */
+
+    length = strlen( line->Contents );
+
+    while ( isspace( line->Contents[ --length ] ) ) 
+      line->Contents[ length ] = '\0';
+
+    /*
+     *  Strip white space from the front of each keyword line.
+     */
+
+    for ( index=KEYWORD_FIRST ; index <= KEYWORD_LAST ; index++ ) {
+      key = &Keywords[ index ];
+
+      switch ( key->level ) {
+        case SECTION:    indentation = 0; break; 
+        case SUBSECTION: indentation = 0; break; 
+        case HEADING:    indentation = 2; break; 
+        default:         assert( FALSE );
+      }
+
+      if ( !strncmp( &line->Contents[ indentation ], key->Name, 
+                    strlen( key->Name ) ) ) {
+        if ( indentation )
+          strcpy( line->Contents,  &line->Contents[ indentation ] );
+        line->keyword = index;
+        break;
+      }
+    }
+  }
+}
+
+/*
+ *  CrunchRight
+ */
+
+boolean CrunchRight(
+  Line_Control *line
+)
+{
+  char  Buffer[ PARAGRAPH_SIZE ];
+  char *src;
+  char *dest;
+  boolean is_text_to_right = FALSE;
+
+  src  = line->Contents;
+  dest = Buffer;
+
+  while( *src != ':' ) {
+    if ( !*src ) {
+      fprintf(
+        stderr,
+        "Line %d should have had a colon\n",
+        line->number
+      );
+      exit_application( 1 );
+    }
+    *dest++ = *src++;
+  }
+ 
+  *dest++ = *src++;    /* this is the ':' */
+ 
+  if ( *src ) {
+    *dest++ = ' ';
+    while ( *src && isspace( *src ) )
+      src++;
+ 
+    if ( *src )
+      is_text_to_right = TRUE;
+ 
+    while ( *src )
+      *dest++ = *src++;
+  }
+ 
+  *dest = '\0';
+
+  strcpy( line->Contents, Buffer );
+
+  return is_text_to_right;
+}
+
+/*
+ *  CrunchBelow
+ */
+
+void CrunchBelow(
+  Line_Control          *start_line,
+  Keywords_text_below_t  text_below_mode
+)
+{
+  Line_Control      *line;
+  int                i;
+  int                leading_white;
+  int                length;
+ 
+  switch ( text_below_mode ) {
+    case BL_FORBIDDEN:
+      assert( FALSE );
+    case BL_FORMATTED:
+      (void) FormatMultipleParagraphs( start_line );
+      break;
+    case BL_RAW:
+      for ( line = (Line_Control *) start_line->Node.next;
+            !_Chain_Is_last( &line->Node ) ;
+          ) {
+         if ( strlen( line->Contents ) )
+           break;
+         line = DeleteLine( line );
+      }  
+
+      for ( i=0 ; isspace( line->Contents[ i ] ) ; i++ )
+        ;
+      
+      leading_white = i;
+
+      for ( ;
+            !_Chain_Is_last( &line->Node ) ;
+            ) {
+ 
+         assert( line );
+
+         if ( line->keyword )
+           break;
+
+         assert( !_Chain_Is_last( &line->Node ) );
+
+         length = strlen( line->Contents );
+
+         if ( length ) {
+           if ( length < leading_white ) {
+             fprintf(
+               stderr,
+               "\nLine %d %s:\n",
+               line->number,
+               "has inconsistent spacing -- is too short -- see 1st line of text"
+             );
+             PrintSurroundingLines( line, 0,  0 );
+             fprintf( stderr, "\n" );
+
+           } 
+           for ( i=0 ; i < leading_white ; i++ ) {
+             if ( !isspace( line->Contents[ i ] ) ) {
+               fprintf(
+                 stderr,
+                 "\nLine %d %s:\n",
+                 line->number,
+                 "has inconsistent spacing -- see 1st line of text"
+               );
+               PrintSurroundingLines( line, 0,  0 );
+               fprintf( stderr, "\n" );
+               break;
+             }
+           } 
+
+           strcpy( line->Contents, &line->Contents[ leading_white ] );
+
+           SetLineFormat( line, RAW_OUTPUT );
+           line = (Line_Control *) line->Node.next;
+         } else {
+           SetLineFormat( line, RAW_OUTPUT );
+           /* line = DeleteLine( line ); */
+           line = (Line_Control *) line->Node.next;
+         }
+      }
+      break;
+  }
+}
+
+/*
+ *  MergeText
+ */
+ 
+/* XXX expand this and address the error numbers */
+char *Format_Errors[] = {
+  /* unused */             "no formatting error",
+  /* RT_FORBIDDEN     */   "no text allowed to right or below",
+  /* RT_OPTIONAL      */   "text to right optional -- none below",
+  /* RT_NONE          */   "text to right is \"none\" or nothing -- none below",
+  /* RT_REQUIRED      */   "text to right required -- none below",
+  /* RT_BELOW         */   "text to right forbidden -- text below required",
+  /* RT_NONE_OR_BELOW */   "text to right is \"none\" OR there is text below",
+  /* RT_MAYBE_BELOW   */   "text to right required -- text below optional",
+  /* RT_EITHER        */   "text to right OR below",
+  /* RT_BOTH          */   "text to right AND below"
+};
+
+
+int MergeText( void )
+{
+  Line_Control      *line;
+  Line_Control      *next;
+  Line_Control      *new_line;
+  Keyword_info_t    *key;
+  boolean            is_text_to_right;
+  boolean            is_text_below;
+  boolean            add_text_to_right;
+  boolean            add_text_below;
+  int                errors = 0;
+  int                error_code = 0;
+ 
+  for ( line = (Line_Control *) Lines.first ;
+        !_Chain_Is_last( &line->Node ) ;
+        ) {
+ 
+    if ( error_code ) {
+
+      fprintf(
+        stderr,
+        "\nLine %d %s:\n",
+        line->number,
+        Format_Errors[ error_code ]
+      );
+      PrintSurroundingLines( line, 0,  3 );
+      fprintf( stderr, "\n" );
+      error_code = 0;
+      line = (Line_Control *) line->Node.next;
+    }
+
+    key = &Keywords[ line->keyword ];
+
+    if ( !line->keyword ) {
+      line = (Line_Control *) line->Node.next;
+      continue;
+    }
+
+    /*
+     *  Figure out where the text is for this keyword.  It is a pretty
+     *  ugly thing to look ahead for text below because of intermediate
+     *  blank lines which we are not allowed to remove.
+     */
+
+    add_text_to_right = FALSE;
+    add_text_below = FALSE;
+
+    is_text_to_right = CrunchRight( line );
+
+    is_text_below = FALSE;
+
+    for ( next = (Line_Control *) line->Node.next ; 
+          !_Chain_Is_last( &next->Node ) ;
+          next = (Line_Control *) next->Node.next ) {
+
+      if ( next->keyword )
+        break;
+
+      if ( strlen( next->Contents ) ) {
+        is_text_below = TRUE;
+        break;
+      }
+    }
+
+    switch ( key->text_mode ) {
+      case RT_FORBIDDEN: /* no text to right or below allowed */
+        if ( is_text_to_right || is_text_below ) {
+          error_code = 1;
+          errors++;
+          continue;
+        }
+        break;
+
+      case RT_OPTIONAL:  /* text to right optional -- none below */
+        if ( is_text_below ) {
+          error_code = 2;
+          errors++;
+          continue;
+        }
+        break;
+
+      case RT_NONE:      /* text to right is "none" or nothing -- none below */
+        if ( (is_text_to_right && !strstr( line->Contents, "none" )) ||
+             is_text_below ) {
+          error_code = 3;
+          errors++;
+          continue;
+        }
+        break;
+
+      case RT_REQUIRED:  /* text to right required -- none below */
+        if ( is_text_below ) {
+          error_code = 4;
+          errors++;
+          continue;
+        }
+
+        if ( !is_text_to_right ) {
+          if ( !InsertTBDs ) {
+            error_code = 4;
+            errors++;
+            continue;
+          } else
+            add_text_to_right = TRUE;
+        }
+        break;
+
+      case RT_BELOW:     /* text to right forbidden -- text below required */
+        if ( is_text_to_right ) {
+          error_code = 5;
+          errors++;
+          continue;
+        }
+
+        if ( !is_text_below ) {
+          if ( !InsertTBDs ) {
+            error_code = 5;
+            errors++;
+            continue;
+          } else
+            add_text_below = TRUE;
+        }
+        break;
+
+      case RT_NONE_OR_BELOW: /* text to right is "none" OR text below */
+
+        if ( is_text_to_right ) {
+          if ( strstr( line->Contents, "none" ) )
+            break;
+          error_code = 6;
+          errors++;
+          continue;
+        }
+
+        if ( !is_text_below ) {
+          if ( !InsertTBDs ) {
+            error_code = 6;
+            errors++;
+            continue;
+          } else
+            add_text_to_right = TRUE;
+        }
+        break;
+                          /* text to right required -- text below optional */
+      case RT_MAYBE_BELOW:
+        if ( !is_text_to_right ) {
+          error_code = 7;
+          errors++;
+          continue;
+        }
+        break;
+
+      case RT_EITHER:    /* text to right OR below */
+        if ( !(is_text_to_right || is_text_below) ) {
+          if ( !InsertTBDs ) {
+            error_code = 8;
+            errors++;
+            continue;
+          } else
+            add_text_to_right = TRUE;
+        }
+        break;
+
+      case RT_BOTH:      /* text to right AND below */
+        if ( !(is_text_to_right && is_text_below) ) {
+          if ( !InsertTBDs ) {
+            error_code = 9;
+            errors++;
+            continue;
+          } else {
+            add_text_to_right = TRUE;
+            add_text_below = TRUE;
+          }
+        }
+        break;
+    }
+
+    if ( add_text_to_right )
+      strcat( line->Contents, " TBD" );
+
+    if ( add_text_below ) {
+      new_line = AllocateLine();
+      strcpy( new_line->Contents, "TBD" );
+      _Chain_Insert( &line->Node, &new_line->Node );
+      is_text_below = TRUE;
+    }
+
+    if ( is_text_below )
+      CrunchBelow( line, key->text_below_mode );
+
+    line = (Line_Control *) line->Node.next;
+  }
+  return (errors) ? -1 : 0;
+}
+
+/*
+ *  CheckForIncomplete
+ */
+
+char *IncompleteMarkers[] = {
+  "??",
+  "xxx",
+  "XXX",
+  "xyz",
+  "XYZ"
+};
+
+int CheckForIncomplete()
+{
+  Line_Control *line;
+  int           i;
+  int           errors = 0;
+
+  for ( line = (Line_Control *) Lines.first ;
+        !_Chain_Is_last( &line->Node ) ; 
+        line = (Line_Control *) line->Node.next
+        ) {
+    for ( i=0 ; i < NUMBER_ELEMENTS( IncompleteMarkers ) ; i++ ) {
+      if ( !strstr( line->Contents, IncompleteMarkers[ i ] ) ) 
+        continue;
+
+      fprintf(
+        stderr,
+        "\nLine %d %s:\n",
+        line->number,
+        "appears to be incomplete"
+      );
+      PrintSurroundingLines( line, 0,  0 );
+      fprintf( stderr, "\n" );
+      if ( IncompletesAreErrors )
+        errors++;
+      break;
+    }
+  }
+  return ( errors ) ? -1 : 0;
+}
+
+/*
+ *  CheckOutline
+ *
+ *  Insure all section headers are present.
+ *  Check sections which say "none", really have subsections.
+ *  Check sections which have subsections, don't say none.
+ */
+
+int CheckOutline( void )
+{
+  Line_Control      *line;
+  int                count;
+  int                errors = 0;
+  void              *none_present;
+  boolean            KeywordsPresent[ KEYWORD_LAST + 1 ];
+  Keyword_indices_t  keyword;
+
+  for ( keyword=0 ; keyword <= KEYWORD_LAST ; keyword++ )
+    KeywordsPresent[ keyword ] = FALSE;
+
+  for ( line = (Line_Control *) Lines.first ;
+        !_Chain_Is_last( &line->Node ) ; 
+        line = (Line_Control *) line->Node.next
+        ) {
+
+    KeywordsPresent[ line->keyword ] = TRUE;
+
+    switch ( line->keyword ) {
+      case ATTRIBUTE_DESCRIPTIONS:
+        count = NumberOfAttributes;
+        break;
+      case ASSOCIATION_DESCRIPTIONS:
+        count = NumberOfAssociations;
+        break;
+      case ABSTRACT_TYPE_DESCRIPTIONS:
+        count = NumberOfAbstractTypes;
+        break;
+      case DATA_ITEM_DESCRIPTIONS:
+        count = NumberOfDataItems;
+        break;
+      case METHOD_DESCRIPTIONS:
+        count = NumberOfMethods;
+        break;
+      case TASK_DESCRIPTIONS:
+        count = NumberOfTasks;
+        break;
+      default:
+        count = -1;
+        break;
+    }
+    if ( count == -1 )
+      continue;
+
+    none_present = strstr( line->Contents, "none" );
+
+    /* valid cases are (none_present && !count) AND (!none_present && count) */
+    if ( none_present && count ) {
+      fprintf(
+        stderr,
+        "\nLine %d : %s\n",
+        line->number,
+        "section header says \"none\" and there are subsections"
+      );
+      PrintSurroundingLines( line, 0,  0 );
+      fprintf( stderr, "\n" );
+      errors++;
+    }
+    if ( !none_present && !count ) {
+      fprintf(
+        stderr,
+        "\nLine %d : %s\n",
+        line->number,
+        "section header does not say \"none\" and there are no subsections"
+      );
+      PrintSurroundingLines( line, 0,  0 );
+      fprintf( stderr, "\n" );
+      errors++;
+    }
+  }
+
+  for ( keyword=0 ; keyword <= KEYWORD_LAST ; keyword++ ) {
+    if ( Keywords[ keyword ].level != SECTION )
+      continue;
+
+    if ( !KeywordsPresent[ keyword ] ) {
+      fprintf(
+        stderr,
+        "Section (%s) is missing\n",
+        Keywords[ keyword ].Name
+      );
+      errors++;
+    }
+  }
+
+  return (errors) ? -1 : 0;
+}
+
+/*
+ *  ReportMissingKeywords
+ */
+
+int ReportMissingKeywords(
+  Section_info_t  *section,
+  Line_Control    *line,
+  int              elements_present[ MAXIMUM_ELEMENTS ]
+)
+{
+  int       i;
+  int       errors = 0;
+  int       last_found;
+
+#ifdef SECTION_DEBUG
+  PrintLine( line );
+  for ( i=0 ; i < MAXIMUM_ELEMENTS ; i++ )
+    fprintf( stderr, "%d ", elements_present[ i ] );
+  fprintf( stderr, "\n" );
+#endif
+  
+  /*
+   *  Check for missing sections
+   */
+
+  for ( i=0 ; i < MAXIMUM_ELEMENTS ; i++ ) {
+    if ( section->Description[ i ].keyword == UNUSED )
+      break;
+
+#ifdef SECTION_DEBUG
+    fprintf(
+      stderr, 
+      "%d %d %s\n",
+      section->Description[ i ].is_required,
+      elements_present[ i ],
+      Keywords[ section->Description[ i ].keyword ].Name
+    );
+#endif
+
+    if ( section->Description[ i ].is_required && elements_present[i] == -1 ) {
+      fprintf( 
+        stderr,
+        "Section starting at line %d is missing the %s keyword\n",
+        line->number,
+        Keywords[ section->Description[ i ].keyword ].Name
+      );
+      errors++;
+    }
+  }
+
+  last_found = -1;
+
+  for ( i=0 ; i < MAXIMUM_ELEMENTS ; i++ ) {
+    if ( section->Description[ i ].keyword == UNUSED )
+      break;
+
+    if ( elements_present[i] == -1 )
+      continue;
+
+    if ( elements_present[i] > last_found ) {
+      last_found = elements_present[i];
+      continue;
+    }
+
+    fprintf(
+      stderr,
+      "Keywords in the section starting at line %d are in the wrong order\n",
+      line->number
+    );
+    errors++;
+    break;
+
+  }
+
+  return errors;
+}
+
+/*
+ *  ScanForNextSection
+ */
+
+Line_Control *ScanForNextSection(
+  Line_Control   *start_line,
+  Section_info_t *section
+)
+{
+  Line_Control   *line;
+  Element_info_t *Elements;
+
+  line = start_line;
+  Elements = section->Description;
+
+  for ( ;; ) {
+    line = (Line_Control *) line->Node.next;
+
+    if ( line->keyword == END_OBJECT ) 
+      break;
+
+    assert( line );
+    assert( !_Chain_Is_last( &line->Node ) );
+
+    if ( Keywords[ line->keyword ].level == SECTION && 
+         section->Next_section != line->keyword ) {
+
+        fprintf( 
+          stderr,
+          "Sections appear to be out of order at line %d\n",
+          start_line->number
+        );
+        return NULL;
+    }
+
+    if ( section->Next_section == line->keyword )   /* next section */
+      break;
+
+    if ( Elements[ 0 ].keyword == line->keyword )   /* repeating subsection */
+      break;
+  }
+
+  return line;
+}
+
+/*
+ *  CheckSections
+ */
+
+int CheckSections( void )
+{
+  Line_Control   *line;
+  Line_Control   *scan_line;
+  Section_info_t *section;
+  Keyword_info_t *key;
+  Element_info_t *Elements;
+  int             elements_present[ MAXIMUM_ELEMENTS ];
+  boolean         stay_in_subsection;
+  int             i;
+  int             errors;
+  int             subsection;
+  int             element;
+  int             keyword_count;
+  Line_Control   *next_section;
+
+  errors = 0;
+  line = (Line_Control *) Lines.first;
+
+  for ( i=0 ; i < NUMBER_ELEMENTS( Sections ) ; i++ ) {
+    section = Sections[ i ];
+    Elements = section->Description;
+    subsection = 0;
+
+    if ( strstr( line->Contents, "none" ) ) {
+      next_section = ScanForNextSection( line, section );
+      if ( !next_section ) {
+        errors++;
+        goto end_object;
+      }
+      line = next_section;
+      if ( line->keyword == END_OBJECT )
+        goto end_object;
+      continue;
+    }
+
+    while ( Elements[ 0 ].keyword != line->keyword ) {
+      if ( line->keyword == END_OBJECT )
+        goto end_object;
+
+      if( !line || _Chain_Is_last( &line->Node ) ) {
+        fprintf( stderr, "out of sync -- aborting\n" );
+        errors++;
+        goto end_object;
+      }
+      line = (Line_Control *) line->Node.next;
+    }
+
+repeating_subsection:
+
+#ifdef SECTION_DEBUG
+    fprintf( stderr, "Checking Section %d Subsection %d\n", i, subsection );
+#endif
+    assert( line );
+    assert( !_Chain_Is_last( &line->Node ) );
+  
+    /*
+     *  Per Subsection Initialization
+     */
+
+    subsection++;
+
+    keyword_count = 0;
+
+    next_section = ScanForNextSection( line, section );
+    if ( !next_section ) {
+      errors++;
+      goto end_object;
+    }
+
+    for ( element=0 ; element < MAXIMUM_ELEMENTS ; element++ )
+      elements_present[ element ] = -1;
+
+    /*  
+     *  Do a subsection
+     */
+
+    /*  validate each keyword */
+
+    for ( scan_line = line ; ; ) {
+
+      if ( !scan_line->keyword ) {
+        scan_line = (Line_Control *) scan_line->Node.next;
+        continue;
+      }
+
+      if ( scan_line == next_section )
+        break;
+
+      if ( *Keywords[ scan_line->keyword ].keyword_validation_routine ) {
+        if ( (*Keywords[ scan_line->keyword ].keyword_validation_routine )(
+               scan_line
+              ) ) errors++;
+      }
+
+      scan_line = (Line_Control *) scan_line->Node.next;
+    }
+
+    /* scan subsection for keywords */
+
+    for ( scan_line = line ; ; ) {
+
+      if ( !scan_line->keyword ) {
+        scan_line = (Line_Control *) scan_line->Node.next;
+        continue;
+      }
+
+      if ( scan_line == next_section )
+        break;
+
+      for ( element=0 ; element < MAXIMUM_ELEMENTS ; element++ ) {
+        if ( scan_line->keyword == Elements[ element ].keyword ) {
+          if ( elements_present[ element ] != -1 ) {
+            fprintf( 
+              stderr,
+              "Section starting at line %d has the %s keyword more than once\n",
+              line->number,
+              Keywords[ Elements[ i ].keyword ].Name
+            );
+            errors++;
+          }
+          
+#ifdef SECTION_DEBUG
+          fprintf( stderr, "FOUND %s %d %d\n", 
+            Keywords[ Elements[ element ].keyword ].Name, 
+            element, keyword_count ); 
+#endif
+          elements_present[ element ] = keyword_count++;
+          break;
+        }
+        if ( Elements[ element ].keyword == UNUSED )
+          break;
+      }
+
+      scan_line = (Line_Control *) scan_line->Node.next;
+    }
+    errors += ReportMissingKeywords( section, line, elements_present );
+
+    /*
+     *  Validate the section as a whole
+     */
+     
+    if ( section->section_validation_routine ) {
+      if ( (*section->section_validation_routine)(section, line, next_section) )
+        errors++;
+    }
+
+scan_for_next_section:
+    /*
+     *  Scan forward until next subsection or next section
+     */
+
+#ifdef SECTION_DEBUG
+    fprintf( stderr, "End of Subsection\n" );
+#endif
+    line = next_section;
+    if ( line->keyword == END_OBJECT ) {
+      goto end_object;
+    }
+
+    if ( Elements[ 0 ].keyword == line->keyword ) { /* repeating subsection */
+      if ( !section->repeats ) {
+        fprintf(
+          stderr,
+          "Invalid repeating subsection starting at line %d\n",
+          line->number
+        );
+        errors++;
+        goto end_object;
+      }
+      goto repeating_subsection;
+    }
+ 
+  }
+
+end_object:
+  return (errors) ? -1 : 0;
+}
+/*
+ *  RemovePagebreaks
+ */
+
+void RemovePagebreaks()
+{
+  Line_Control *line;
+
+  for ( line = (Line_Control *) Lines.first ;
+        !_Chain_Is_last( &line->Node ) ; 
+        ) {
+    if ( !strcmp( line->Contents, PAGE_SEPARATOR ) ) 
+        line = DeleteLine( line );
+    else 
+      line = (Line_Control *) line->Node.next;
+  }
+}
+
+/*
+ *  strIsAllSpace
+ */
+
+boolean strIsAllSpace(
+  char *s
+)
+{
+  char *p;
+
+  for ( p = s ; *p ; p++ )
+    if ( !isspace( *p ) )
+      return FALSE;
+
+  return TRUE;
+}
+
+/*
+ *  RemoveExtraBlankLines
+ *
+ *  NOTE:  Be careful not remove to remove white space in raw text.
+ */
+
+int RemoveExtraBlankLines()
+{
+  Line_Control *line;
+  Line_Control *previous;
+  int errors;
+
+  errors = 0;
+
+  for ( line = (Line_Control *) Lines.first ;
+        !_Chain_Is_last( &line->Node ) ; 
+        line = (Line_Control *) line->Node.next
+        ) {
+    if ( line->keyword || line->format )
+      continue;
+
+    if ( !strlen( line->Contents ) ) {
+      line = DeleteLine( line );
+      line = (Line_Control *) line->Node.previous;
+      continue;
+    }
+
+    fprintf(
+      stderr,
+      "\nLine %d is not associated with a keyword:\n", 
+      line->number
+    );
+    PrintSurroundingLines( line, 0,  0 );
+    fprintf( stderr, "\n" );
+    errors++;
+  }
+  return ( errors ) ? -1 : 0;
+}
+
+/*
+ *  strCopyToColon
+ */
+
+void strCopyToColon(
+  char *src,
+  char *dest
+)
+{
+  char *s = src;
+  char *d = dest;
+
+  for ( ; *s && *s != ':' ; ) 
+    *d++ = *s++;
+  *d ='\0';
+}
+
+/*
+ *  BuildTexinfoNodes
+ */
+
+void BuildTexinfoNodes( void ) 
+{
+  Line_Control *line;
+  Line_Control *new_line;
+  Line_Control *next_node;
+  char          Buffer[ BUFFER_SIZE ];
+  char          ObjectName[ BUFFER_SIZE ];
+  char          NodeName[ BUFFER_SIZE ];
+  char          NextNode[ BUFFER_SIZE ];
+  char          NextNodeName[ BUFFER_SIZE ];
+  char          PreviousNodeName[ BUFFER_SIZE ];
+  char          UpNodeName[ BUFFER_SIZE ];
+  char          SectionName[ BUFFER_SIZE ];
+  char          MenuBuffer[ BUFFER_SIZE ];
+  Line_Control *menu_insert_point;
+  Line_Control *node_line;
+  boolean       next_found;
+  int           menu_items;
+
+  strcpy( PreviousNodeName, DocsPreviousNode );
+
+  for ( line = (Line_Control *) Lines.first ;
+        !_Chain_Is_last( &line->Node ) ; 
+        line = (Line_Control *) line->Node.next
+      ) {
+    menu_insert_point = (Line_Control *) line->Node.next;
+
+    switch ( Keywords[ line->keyword ].level ) {
+      case TEXT:
+      case HEADING:
+        break;
+      case SECTION:
+        if ( line->keyword == END_OBJECT )
+          goto bottom; 
+
+        if ( line->keyword == OBJECT ) {
+          LineCopyFromRight( line, Buffer );
+          sprintf( ObjectName, "%s Object", Buffer );
+          strcpy( NodeName, ObjectName );
+          strcpy( UpNodeName, DocsUpNode );
+        } else {
+          LineCopySectionName( line, Buffer );
+          sprintf( NodeName, "%s %s", ObjectName, Buffer );
+          strcpy( UpNodeName, ObjectName );
+        }
+        strtoInitialCaps( NULL, NodeName );
+        strtoInitialCaps( NULL, UpNodeName );
+        strcpy( SectionName, NodeName );
+
+        /*
+         *  Go ahead and put it on the chain in the right order (ahead of
+         *  the menu) and we can fill it in later (after the menu is built).
+         */
+
+        new_line = AllocateLine();
+        strcpy( new_line->Contents, "@ifinfo" );
+        _Chain_Insert( line->Node.previous, &new_line->Node );
+
+        node_line = AllocateLine();
+        _Chain_Insert( line->Node.previous, &node_line->Node );
+
+        new_line = AllocateLine();
+        strcpy( new_line->Contents, "@end ifinfo" );
+        _Chain_Insert( line->Node.previous, &new_line->Node );
+
+        menu_items = 0;
+       
+        if ( line->keyword == OBJECT ) {
+          next_node = (Line_Control *) line->Node.next;
+          next_found = FALSE;
+          for ( ; ; ) {
+            if ( next_node->keyword == END_OBJECT )
+              break;
+            if ( Keywords[ next_node->keyword ].level == SECTION ) {
+              LineCopySectionName( next_node, Buffer );
+              strtoInitialCaps( NULL, Buffer );     
+
+              if ( !next_found ) {
+                next_found = TRUE;
+                sprintf( NextNodeName, "%s %s", ObjectName, Buffer );
+              }
+              if ( menu_items == 0 ) {
+                new_line = AllocateLine();
+                strcpy( new_line->Contents, "@ifinfo" );
+                _Chain_Insert( menu_insert_point->Node.previous, &new_line->Node );
+ 
+                new_line = AllocateLine();
+                strcpy( new_line->Contents, "@menu" );
+                _Chain_Insert( menu_insert_point->Node.previous, &new_line->Node );
+              }
+
+              menu_items++;
+
+              new_line = AllocateLine();
+              sprintf( new_line->Contents, "* %s %s::", ObjectName, Buffer );
+              _Chain_Insert( menu_insert_point->Node.previous, &new_line->Node);
+            }
+            next_node = (Line_Control *) next_node->Node.next;
+          }
+        } else {
+          next_node = (Line_Control *) line->Node.next;
+        
+          next_found = FALSE;
+          for ( ; ; ) {
+            if ( Keywords[ next_node->keyword ].level == SECTION ) {
+              if ( !next_found ) {
+                if ( next_node->keyword == END_OBJECT ) {
+                  strcpy( NextNodeName, DocsNextNode );
+                } else {
+                  LineCopySectionName( next_node, Buffer );
+                  sprintf( NextNodeName, "%s %s", ObjectName, Buffer );
+                  strtoInitialCaps( NULL, NextNodeName );
+                }
+                next_found = TRUE;
+              }
+              break;
+            } else if ( Keywords[ next_node->keyword ].level == SUBSECTION ) {
+              LineCopySectionName( next_node, Buffer );
+              strtoInitialCaps( NULL, Buffer );
+              sprintf( MenuBuffer, "%s %s - ", ObjectName, Buffer );
+              LineCopyFromRight( next_node, Buffer );
+              strcat( MenuBuffer, Buffer );
+
+              if ( menu_items == 0 ) {
+                new_line = AllocateLine();
+                strcpy( new_line->Contents, "@ifinfo" );
+                _Chain_Insert( menu_insert_point->Node.previous, &new_line->Node );
+ 
+                new_line = AllocateLine();
+                strcpy( new_line->Contents, "@menu" );
+                _Chain_Insert( menu_insert_point->Node.previous, &new_line->Node );
+              }
+
+              menu_items++;
+
+              new_line = AllocateLine();
+              sprintf( new_line->Contents, "* %s::", MenuBuffer );
+              _Chain_Insert( menu_insert_point->Node.previous, &new_line->Node );
+
+              if ( !next_found ) {
+                next_found = TRUE;
+                strcpy( NextNodeName, MenuBuffer );
+              }
+            }
+            next_node = (Line_Control *) next_node->Node.next;
+          }
+        }
+
+        if ( menu_items ) {
+          new_line = AllocateLine();
+          strcpy( new_line->Contents, "@end menu" );
+          _Chain_Insert( menu_insert_point->Node.previous, &new_line->Node );
+ 
+          new_line = AllocateLine();
+          strcpy( new_line->Contents, "@end ifinfo" );
+          _Chain_Insert( menu_insert_point->Node.previous, &new_line->Node );
+        }
+
+#if 0
+        fprintf(
+          stderr,
+          "@node %s, %s, %s, %s\n",
+          NodeName,
+          NextNodeName,
+          PreviousNodeName,
+          UpNodeName
+        );
+#endif
+        /* node_line was previously inserted */
+        sprintf(
+          node_line->Contents,
+          "@node %s, %s, %s, %s",
+          NodeName,
+          NextNodeName,
+          PreviousNodeName,
+          UpNodeName
+        );
+
+        strcpy( PreviousNodeName, NodeName );
+        break;
+
+      case SUBSECTION:
+        strcpy( UpNodeName, SectionName );
+
+        LineCopySectionName( line, Buffer );
+        strtoInitialCaps( NULL, Buffer );
+        sprintf( NodeName, "%s %s - ", ObjectName, Buffer );
+        LineCopyFromRight( line, Buffer );
+        strcat( NodeName, Buffer );
+
+        new_line = AllocateLine();
+        strcpy( new_line->Contents, "@ifinfo" );
+        _Chain_Insert( line->Node.previous, &new_line->Node );
+
+        next_node = (Line_Control *) line->Node.next;
+        for ( ; ; ) {
+          if ( Keywords[ next_node->keyword ].level == SECTION ) {
+            if ( next_node->keyword == END_OBJECT ) {
+              strcpy( NextNodeName, DocsNextNode );
+            } else {
+              LineCopySectionName( next_node, Buffer );
+              sprintf( NextNodeName, "%s %s", ObjectName, Buffer );
+              strtoInitialCaps( NULL, NextNodeName );
+            }
+            break;
+          } else if ( Keywords[ next_node->keyword ].level == SUBSECTION ) {
+            LineCopySectionName( next_node, Buffer );
+            strtoInitialCaps( NULL, Buffer );
+            sprintf( NextNodeName, "%s %s - ", ObjectName, Buffer );
+            LineCopyFromRight( next_node, Buffer );
+            strcat( NextNodeName, Buffer );
+            break;
+          }
+          next_node = (Line_Control *) next_node->Node.next;
+        }
+
+#if 0
+        fprintf(
+          stderr,
+          "@node %s, %s, %s, %s\n",
+          NodeName,
+          NextNodeName,
+          PreviousNodeName,
+          UpNodeName
+        );
+#endif
+        new_line = AllocateLine();
+        sprintf(
+          new_line->Contents,
+          "@node %s, %s, %s, %s",
+          NodeName,
+          NextNodeName,
+          PreviousNodeName,
+          UpNodeName
+        );
+        _Chain_Insert( line->Node.previous, &new_line->Node );
+
+        new_line = AllocateLine();
+        strcpy( new_line->Contents, "@end ifinfo" );
+        _Chain_Insert( line->Node.previous, &new_line->Node );
+
+        strcpy( PreviousNodeName, NodeName );
+        break;
+    }
+  }
+bottom:
+}
+
+/*
+ *  FormatToTexinfo
+ */
+
+char *Texinfo_Headers[] = {
+  "\\input texinfo   @c -*-texinfo-*-",
+  "@c %**start of header",
+  "@setfilename ",
+  "@settitle ",
+  "@paragraphindent 0",
+  "@c %**end of header",
+  "",
+  "@c",
+  "@c  COPYRIGHT (c) 1996.",
+  "@c  On-Line Applications Research Corporation (OAR).",
+  "@c  All rights reserved.",
+  "@c",
+  "@c  This file is automatically generated.  DO NOT EDIT!!",
+  "@c",
+  "",
+  "@c  This prevents a black box from being printed on overfull lines.",
+  "@c  The alternative is to rework a sentence to avoid this problem.",
+  "@finalout",
+  "",
+  "@tex",
+  "\\global\\parindent 0in",
+  "\\global\\chapheadingskip = 15pt plus 4pt minus 2pt",
+  "\\global\\secheadingskip = 12pt plus 4pt minus 2pt",
+  "\\global\\subsecheadingskip = 9pt plus 4pt minus 2pt",
+  "",
+  "@ifclear smallbook",
+  "\\global\\parskip 6pt plus 1pt",
+  "@end ifclear",
+  "@end tex",
+  "@setchapternewpage odd",
+  "@ifinfo",
+  "@top ",
+  "@node Top, (dir), (dir), (dir)",
+  "@end ifinfo",
+  "@c ",
+  "@c This is the end of the header block",
+  "@c "
+};
+
+void FormatToTexinfo( void )
+{
+  Line_Control *line;
+  Line_Control *new_line;
+  char          Buffer[ PARAGRAPH_SIZE ];
+  int           i;
+  char          ChapterTitle[ PARAGRAPH_SIZE ];
+  char          InfoFile[ PARAGRAPH_SIZE ];
+  char          *p;
+  boolean        new_section; 
+  boolean        in_bullets; 
+
+  for ( line = (Line_Control *) Lines.first ;
+        !_Chain_Is_last( &line->Node ) ; 
+      ) {
+
+    switch ( line->keyword ) {
+      case UNUSED:
+        line = (Line_Control *) line->Node.next;
+        break;
+
+      case OBJECT:
+        LineCopyFromRight( line, ChapterTitle );
+        strcpy( InfoFile, ChapterTitle );
+
+        for ( p=InfoFile ; *p ; *p++ )   /* turn this into a file name */
+          if ( isspace( *p ) )
+            *p = '_';             
+
+        sprintf( Buffer, "@chapter %s", line->Contents );
+        strcpy( line->Contents, Buffer );
+        line = (Line_Control *) line->Node.next;
+        break;
+
+      case ATTRIBUTE_DESCRIPTIONS:
+      case ASSOCIATION_DESCRIPTIONS:
+      case ABSTRACT_TYPE_DESCRIPTIONS:
+      case DATA_ITEM_DESCRIPTIONS:
+      case METHOD_DESCRIPTIONS:
+      case TASK_DESCRIPTIONS:
+        sprintf( Buffer, "@section %s", line->Contents );
+        strcpy( line->Contents, Buffer );
+
+        new_line = AllocateLine();
+        strcpy( new_line->Contents, "@page" );
+        _Chain_Insert( line->Node.previous, &new_line->Node );
+
+        line = (Line_Control *) line->Node.next;
+        new_section = TRUE;
+        break;
+
+      case END_OBJECT:
+        line->Contents[ 0 ] = '\0';
+        goto bottom;
+
+      case ATTRIBUTE:
+      case ASSOCIATION:
+      case ABSTRACT_TYPE:
+      case DATA_ITEM:
+        if ( !new_section ) {
+          new_line = AllocateLine();
+          strcpy( new_line->Contents, "@need 4000" );
+          _Chain_Insert( line->Node.previous, &new_line->Node );
+        }
+        new_section = FALSE;
+        sprintf( Buffer, "@subsection %s", line->Contents );
+        strcpy( line->Contents, Buffer );
+        line = (Line_Control *) line->Node.next;
+         break;
+
+      case METHOD:
+      case TASK:
+        if ( !new_section ) {
+          new_line = AllocateLine();
+          strcpy( new_line->Contents, "@page" );
+          _Chain_Insert( line->Node.previous, &new_line->Node );
+        }
+        new_section = FALSE;
+        sprintf( Buffer, "@subsection %s", line->Contents );
+        strcpy( line->Contents, Buffer );
+        line = (Line_Control *) line->Node.next;
+        break;
+
+      case DESCRIPTION:
+      case COPYRIGHT:
+      case PORTING:
+      case THEORY_OF_OPERATION:
+      case DEPENDENCIES:
+      case NOTES:
+        sprintf( Buffer, "@subheading %s\n", line->Contents );
+        strcpy( line->Contents, Buffer );
+        line = (Line_Control *) line->Node.next;
+
+        /* now take care of the paragraphs which are here */
+
+        in_bullets = FALSE;
+        do {
+          line = (Line_Control *) line->Node.next;
+          if ( line->format == BULLET_OUTPUT ) {
+            if ( !in_bullets ) {
+              in_bullets = TRUE;
+
+              new_line = AllocateLine();
+              _Chain_Insert( line->Node.previous, &new_line->Node );
+
+              new_line = AllocateLine();
+              strcpy( new_line->Contents, "@itemize @bullet" );
+              _Chain_Insert( line->Node.previous, &new_line->Node );
+            }
+            sprintf( Buffer, "@item %s\n", line->Contents );
+            strcpy( line->Contents, Buffer );
+          } else if ( in_bullets ) {
+            in_bullets = FALSE;
+
+            new_line = AllocateLine();
+            strcpy( new_line->Contents, "@end itemize" );
+            _Chain_Insert( line->Node.previous, &new_line->Node );
+
+            new_line = AllocateLine();
+            _Chain_Insert( line->Node.previous, &new_line->Node );
+          } else {
+            new_line = AllocateLine();
+            _Chain_Insert( line->Node.previous, &new_line->Node );
+          }
+        } while ( !line->keyword );
+
+        break;
+
+      case DERIVATION:
+      case TYPE:
+      case RANGE:
+      case UNITS:
+      case SCALE_FACTOR:
+      case TOLERANCE:
+      case VISIBILITY:
+      case ASSOCIATED_WITH:
+      case MULTIPLICITY:
+      case TIMING:
+        sprintf( Buffer, "@subheading %s\n", line->Contents );
+        strcpy( line->Contents, Buffer );
+        line = (Line_Control *) line->Node.next;
+        break;
+
+      case MEMBERS:
+      case DEFAULT:
+      case REQUIREMENTS:
+      case REFERENCES:
+      case INPUTS:
+      case OUTPUTS:
+      case PDL:
+      case SYNCHRONIZATION:
+        sprintf( Buffer, "@subheading %s\n", line->Contents );
+        strcpy( line->Contents, Buffer );
+
+        /* now take care of the raw text which is here */
+
+        new_line = AllocateLine();
+        strcpy( new_line->Contents, "@example" );
+        _Chain_Insert( &line->Node, &new_line->Node );
+
+        do {
+          line = (Line_Control *) line->Node.next;
+          if ( !strlen( line->Contents ) ) {
+            new_line = AllocateLine();
+            strcpy( new_line->Contents, "@end example" );
+            _Chain_Insert( line->Node.previous, &new_line->Node );
+
+            new_line = AllocateLine();
+            strcpy( new_line->Contents, "@example" );
+            _Chain_Insert( &line->Node, &new_line->Node );
+
+          } 
+        } while ( !line->keyword );
+
+        new_line = AllocateLine();
+        strcpy( new_line->Contents, "@end example" );
+        _Chain_Insert( line->Node.previous, &new_line->Node );
+        /* at this point line points to the next keyword */
+        break;
+    }   
+  }
+
+bottom:
+#if 0
+  for ( i=NUMBER_ELEMENTS( Texinfo_Headers ) - 1 ; i >= 0 ; i-- ) {
+    new_line = AllocateLine();
+    strcpy( new_line->Contents, Texinfo_Headers[ i ] );
+    if ( !strcmp( "@setfilename ", new_line->Contents ) )
+      strcat( new_line->Contents, ChapterTitle );
+    else if ( !strcmp( "@settitle ", new_line->Contents ) )
+      strcat( new_line->Contents, InfoFile );
+    else if ( !strcmp( "@top ", new_line->Contents ) )
+      strcat( new_line->Contents, InfoFile );
+    _Chain_Insert( _Chain_Head( &Lines ), &new_line->Node );
+  }
+
+  /*
+   *  Remove the special end of object string.  No one wants to see 
+   *  it in the printed output and the node is already marked "END_OBJECT".
+   */
+
+  ((Line_Control *)Lines.last)->Contents[ 0 ] = '\0';
+
+  new_line = AllocateLine();
+  strcpy( new_line->Contents, "@bye" );
+  _Chain_Append( &Lines, &new_line->Node );
+
+#endif
+  if ( Verbose )
+    fprintf( stderr, "-------->INSERTING TEXINFO MENUS\n" );
+
+  BuildTexinfoNodes();
+}
+
+/*
+ *  PrintFile
+ */
+
+void PrintFile(
+  char *out
+)
+{
+  Line_Control *line;
+
+  OutFile = fopen( out, "w+" );
+
+  if ( !OutFile ) {
+    fprintf( stderr, "Unable to open (%s) for output\n", out );
+    exit_application( 1 );
+  }
+  assert( OutFile );
+
+  for ( line = (Line_Control *) Lines.first ;
+        !_Chain_Is_last( &line->Node ) ; 
+        line = (Line_Control *) line->Node.next ) {
+    fprintf( OutFile, "%s\n", line->Contents );
+/*
+    fprintf(
+      OutFile,
+      "(%d,%d)%s\n",
+      line->keyword,
+      line->format,
+      line->Contents
+    );
+*/
+  }
+}
+
+/*
+ *  DumpList
+ */
+
+void DumpList( 
+  Chain_Control  *the_list
+)
+{
+  Line_Control  *line;
+
+  fprintf( stderr, "---> Dumping list (%p)\n", the_list );
+
+  for ( line = (Line_Control *) the_list->first ;
+      !_Chain_Is_last( &line->Node ) ; 
+      line = (Line_Control *) line->Node.next ) {
+    fprintf( stderr, "%s\n", line->Contents );
+  }
+}
+
+/*
+ * ReleaseFile
+ */
+
+void ReleaseFile()
+{
+   Line_Control *line;
+   Line_Control *next;
+
+   for ( line = (Line_Control *) Lines.first ;
+         !_Chain_Is_last( &line->Node ) ; 
+       ) {
+     next = (Line_Control *) line->Node.next;
+     line = next;
+   }
+}
+
+/*
+ *  strtoInitialCaps
+ */
+
+void strtoInitialCaps(
+  char *dest,
+  char *src
+)
+{
+  char *source = src;
+  char *destination = dest;
+
+  if ( !dest )
+    return;
+  strcpy( dest, src );
+#if 0
+  source = src;
+  destination = (dest) ? dest : src;
+
+  while ( *source ) {
+    while ( isspace( *source ) )
+      *destination++ = *source++;
+
+    if ( !*source )
+      break;
+
+    *destination++ = toupper( *source++ );
+
+    for ( ; *source && !isspace( *source ) ; source++ )
+      *destination++ = tolower( *source );
+     
+    if ( !*source )
+      break;
+  }
+
+  *destination = '\0';
+#endif
+}
+
+/*
+ *  Validate_visibility
+ */
+ 
+char *Valid_visibilities[] = {
+  "public",
+  "private"
+};
+ 
+int Validate_visibility(
+  Line_Control   *line
+)
+{
+  char   *s;
+  char   *d;
+  char    Buffer[ BUFFER_SIZE ];
+  char    Visibility[ BUFFER_SIZE ];
+  int     i;
+  boolean found;
+  int     errors = 0;
+ 
+  LineCopyFromRight( line, Buffer );
+ 
+  memset( Visibility, '\0', sizeof( Visibility ) );
+  s = Buffer;
+ 
+  for ( d=Visibility ; ; s++, d++ ) {
+    *d = *s;
+    if ( !*s || isspace(*s) )
+      break;
+  }
+  *d = '\0';
+ 
+  if ( isspace(*s) ) {
+    fprintf(
+       stderr,
+       "Unexpected white space on line %d -- are there multiple words?\n",
+       line->number
+     );
+     errors++;
+  }
+ 
+  /*
+   *  Check out the type part of this keyword
+   */
+ 
+  for ( found=FALSE, i=0 ; i<NUMBER_ELEMENTS(Valid_visibilities) ; i++ ) {
+    if ( !strcmp( Valid_visibilities[ i ], Visibility ) ) {
+      found = TRUE;
+      break;
+    }
+  }
+ 
+  if ( !found ) {
+    if ( !(InsertTBDs && !strcmp( Visibility, "TBD" )) ) {
+      fprintf(
+        stderr,
+        "Invalid visibility type (%s) on line %d\n",
+        Visibility,
+        line->number
+      );
+      errors++;
+    }
+  }
+ 
+  return (errors) ? -1 : 0;
+}
+
+/*
+ *  Validate_synchronization
+ */
+ 
+char *Valid_synchronization[] = {
+  "delay",
+  "event",
+  "mutex",
+  "semaphore",
+  "message",
+  "signal",
+  "period"
+};
+ 
+boolean Valid_synchronization_text_below[] = {
+  FALSE,   /* delay */
+  TRUE,    /* event */
+  TRUE,    /* mutex */
+  TRUE,    /* semaphore */
+  TRUE,    /* message */
+  TRUE,    /* signal */
+  FALSE    /* period */
+};
+
+int Validate_synchronization(
+  Line_Control   *line
+)
+{
+  char   *s;
+  char   *d;
+  char    Buffer[ BUFFER_SIZE ];
+  char    Synchronization[ BUFFER_SIZE ];
+  int     i;
+  boolean found;
+  int     errors = 0;
+ 
+  LineCopyFromRight( line, Buffer );
+ 
+  memset( Synchronization, '\0', sizeof( Synchronization ) );
+  s = Buffer;
+ 
+  for ( d=Synchronization ; ; s++, d++ ) {
+    *d = *s;
+    if ( !*s || isspace(*s) )
+      break;
+  }
+  *d = '\0';
+ 
+  if ( isspace(*s) ) {
+    fprintf(
+       stderr,
+       "Unexpected white space on line %d -- invalid use of multiple words\n",
+       line->number
+     );
+     errors++;
+  }
+ 
+  /*
+   *  Check out the type part of this keyword
+   */
+ 
+  for ( found=FALSE, i=0 ; i<NUMBER_ELEMENTS(Valid_synchronization) ; i++ ) {
+    if ( !strcmp( Valid_synchronization[ i ], Synchronization ) ) {
+      found = TRUE;
+      break;
+    }
+  }
+ 
+  if ( !found ) {
+    fprintf(
+       stderr,
+       "Invalid synchronization type (%s) on line %d\n",
+       Synchronization,
+       line->number
+     );
+     errors++;
+  }
+
+  if ( line->keyword && !Valid_synchronization_text_below[ i ] ) {
+    fprintf(
+       stderr,
+       "Expected text below synchronization type (%s) on line %d\n",
+       Synchronization,
+       line->number
+     );
+     errors++;
+  }
+ 
+  return (errors) ? -1 : 0;
+}
+
+/*
+ *  Validate_abstract_type
+ *
+ * presence of range or members but not both
+ */
+
+int Validate_abstract_type(
+  Section_info_t *section,
+  Line_Control   *start,
+  Line_Control   *next_section
+)
+{
+  boolean       range_found = FALSE;
+  boolean       members_found = FALSE;
+  boolean       enumerated_found = FALSE;
+  boolean       true_found = FALSE;
+  boolean       false_found = FALSE;
+  Line_Control *line;
+  int           errors = 0;
+
+  for ( line = start; 
+        line != next_section ; 
+        line = (Line_Control *) line->Node.next ) {
+    if ( line->keyword == RANGE ) 
+      range_found = TRUE;
+    else if ( line->keyword == MEMBERS ) {
+      members_found = TRUE;
+    } else if ( line->keyword == TYPE || line->keyword == DERIVATION ) {
+      if ( strstr( line->Contents, "enumerated" ) || 
+           strstr( line->Contents, "structure" ) )
+        enumerated_found = TRUE;
+    }
+  }
+
+  if ( !range_found && !members_found ) {
+    fprintf(
+       stderr,
+       "Neither range nor members keyword present in "
+         "subsection starting at line %d\n",
+       start->number
+     );
+     errors++;
+  }
+
+  if ( !InsertTBDs ) {
+    if ( range_found && members_found ) {
+      fprintf(
+         stderr,
+         "Both range and members keyword present in "
+           "subsection starting at line %d\n",
+         start->number
+       );
+       errors++;
+    }
+  }  
+
+  if ( enumerated_found  && !members_found ) {
+    fprintf(
+       stderr,
+       "Enumerated type without list of members in "
+         "subsection starting at line %d\n",
+       start->number
+     );
+     errors++;
+  }
+
+  if ( !enumerated_found  && !range_found ) {
+    fprintf(
+       stderr,
+       "Type does not have range specified in "
+         "subsection starting at line %d\n",
+       start->number
+     );
+     errors++;
+  }
+
+  return (errors) ? -1 : 0;
+}
+
+/*
+ *  Validate_attribute
+ *
+ * presence of range or members but not both
+ */
+ 
+int Validate_attribute(
+  Section_info_t *section,
+  Line_Control   *start,
+  Line_Control   *next_section
+)
+{
+  boolean       range_found = FALSE;
+  boolean       members_found = FALSE;
+  boolean       enumerated_found = FALSE;
+  boolean       boolean_found = FALSE;
+  boolean       true_found = FALSE;
+  boolean       false_found = FALSE;
+  Line_Control *line;
+  int           errors = 0;
+ 
+  for ( line = start;
+        line != next_section ;
+        line = (Line_Control *) line->Node.next ) {
+    if ( line->keyword == RANGE )
+      range_found = TRUE;
+    else if ( line->keyword == MEMBERS ) {
+      members_found = TRUE;
+      if ( boolean_found == TRUE ) {
+        line = (Line_Control *) line->Node.next;
+        while ( !_Chain_Is_last( &line->Node ) ) {
+          if ( line->keyword )
+            break;
+          if ( strstr( line->Contents, "FALSE" ) )
+            false_found = TRUE;
+          else if ( strstr( line->Contents, "TRUE" ) )
+            true_found = TRUE;
+          line = (Line_Control *) line->Node.next;
+        }
+        line = (Line_Control *) line->Node.previous;
+      }
+    } else if ( line->keyword == TYPE || line->keyword == DERIVATION ) {
+      if ( strstr( line->Contents, "enumerated" ) )
+        enumerated_found = TRUE;
+      else if ( strstr( line->Contents, "boolean" ) ) {
+        enumerated_found = TRUE;
+        boolean_found = TRUE;
+      }
+    }
+  }
+ 
+  if ( !range_found && !members_found ) {
+    fprintf(
+       stderr,
+       "Neither range nor members keyword present in "
+         "subsection starting at line %d\n",
+       start->number
+     );
+     errors++;
+  }
+ 
+  if ( !InsertTBDs ) {
+    if ( range_found && members_found ) {
+      fprintf(
+         stderr,
+         "Both range and members keyword present in "
+           "subsection starting at line %d\n",
+         start->number
+       );
+       errors++;
+    }
+  }
+ 
+  if ( enumerated_found  && !members_found ) {
+    fprintf(
+       stderr,
+       "Enumerated type without list of members in "
+         "subsection starting at line %d\n",
+       start->number
+     );
+     errors++;
+  }
+ 
+  if ( !enumerated_found  && !range_found ) {
+    fprintf(
+       stderr,
+       "Type does not have range specified in "
+         "subsection starting at line %d\n",
+       start->number
+     );
+     errors++;
+  }
+ 
+  if ( boolean_found && !true_found ) {
+    fprintf(
+       stderr,
+       "Boolean without a TRUE case specified in "
+         "subsection starting at line %d\n",
+       start->number
+     );
+     errors++;
+  }
+ 
+  if ( boolean_found && !false_found ) {
+    fprintf(
+       stderr,
+       "Boolean without a FALSE case specified in "
+         "subsection starting at line %d\n",
+       start->number
+     );
+     errors++;
+  }
+ 
+  return (errors) ? -1 : 0;
+}
+
+/*
+ *  Validate_object
+ *
+ * presence of range or members but not both
+ */
+ 
+int Validate_object(
+  Section_info_t *section,
+  Line_Control   *start,
+  Line_Control   *next_section
+)
+{
+  char          ObjectName[ BUFFER_SIZE ];
+  char          EndObjectName[ BUFFER_SIZE ];
+  Line_Control *line;
+  int           errors = 0;
+ 
+  
+  LineCopyFromRight( start, ObjectName );
+
+  for ( line = start;
+        !_Chain_Is_last( &line->Node ) ; 
+        line = (Line_Control *) line->Node.next ) {
+    if ( line->keyword == END_OBJECT ) {
+      LineCopyFromRight( line, EndObjectName );
+      break;
+    }
+  }
+ 
+  if ( strcmp( ObjectName, EndObjectName ) ) {
+    fprintf(
+       stderr,
+       "Object and End Object names do not match\n"
+     );
+     errors++;
+  }
+
+  return (errors) ? -1 : 0;
+}
+
Index: doc/tools/pdl2texi/t1.d
===================================================================
--- doc/tools/pdl2texi/t1.d	(revision d4bc481ded89c54759a5cd2c51565486d72312c6)
+++ doc/tools/pdl2texi/t1.d	(revision d4bc481ded89c54759a5cd2c51565486d72312c6)
@@ -0,0 +1,52 @@
+OBJECT:  Drive
+  DESCRIPTION:
+    This object provides an interface to the elevation/azimuth
+    drive system(ECA).  
+  THEORY OF OPERATION:
+    This object encapsulates the turret drive.  The object provides
+    an interface to control the turret drive.  A method is provided
+    to set the azimuth and elevation rate of the drive.  Methods are
+    provided to request the drive to run in stabilized or power mode
+    and set the drive speed to high or low.  A method is also provided
+    to allow for drift adjust.  
+
+    This object also tracks drive system on hours.  It gets the hours
+    from the EEPROM data, allows the hours to be set, updates the
+    hours via the activation and deactivation of the drive, and
+    provides access to the hours.
+
+    The object also provides methods to access azimuth and elevation
+    displacement, and methods to process changes in the hardware
+    discretes drive on, stabilized mode, thermal fault and fault.
+  
+    The object also provides methods to enable and disable
+    both the remote and turret power interlocks which determine if
+    the drive can be driven by the remote or turret handstation
+    respectively.   
+
+ATTRIBUTE DESCRIPTIONS:
+
+
+ATTRIBUTE:  Azimuth_displacement
+  DESCRIPTION:
+    This attribute represents the current azimuth displacement
+    of the turret.
+  TYPE: float constant
+  RANGE: 0-0
+  UNITS: degrees
+  REQUIREMENTS:
+    AFS 8.2.11 Receive Azimuth Displacement and Elevation Displacement
+    AIS 5.2.6 Azimuth Displacement analog
+
+
+ASSOCIATION DESCRIPTIONS: none
+
+ABSTRACT TYPE DESCRIPTIONS: none
+
+DATA ITEM DESCRIPTIONS: none
+
+METHOD DESCRIPTIONS: none
+
+TASK DESCRIPTIONS: none
+
+END OBJECT: Drive
