/* 
 * Motif
 *
 * Copyright (c) 1987-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these librararies and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
*/ 
#ifdef REV_INFO
#ifndef lint
static char rcsid[] = "$TOG: UilSrcSrc.c /main/13 1997/03/12 15:21:40 dbl $"
#endif
#endif

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif


/*
**++
**  FACILITY:
**
**      User Interface Language Compiler (UIL)
**
**  ABSTRACT:
**
**      This module contains the procedure for managing the UIL source
**	files.  
**
**--
**/


/*
**
**  INCLUDE FILES
**
**/

#include "UilDefI.h"

/* %COMPLETE */
#include <sys/stat.h>



/*
**
**  DEFINE and MACRO DEFINITIONS
**
**/



/*
**
**  EXTERNAL VARIABLE DECLARATIONS
**
**/


/*
**
**  GLOBAL VARIABLE DECLARATIONS
**
**/

/*
**  define the source buffer data structures
*/

externaldef(uil_comp_glbl) src_source_buffer_type	*src_az_current_source_buffer;
externaldef(uil_comp_glbl) src_source_buffer_type	*src_az_avail_source_buffer;
externaldef(uil_comp_glbl) src_message_item_type	*src_az_orphan_messages;
/* %COMPLETE */
externaldef(uil_comp_glbl) long Uil_file_size;
struct stat stbuf;



/*
**  define the source record data structures
*/

externaldef(uil_comp_glbl) src_source_record_type
	*src_az_current_source_record;
externaldef(uil_comp_glbl) src_source_record_type
	*src_az_first_source_record;

externaldef(uil_comp_glbl) uil_fcb_type
 	*src_az_source_file_table[src_k_max_source_files];
externaldef(uil_comp_glbl) int
	src_l_last_source_file_number;

/*
**
**  OWN VARIABLE DECLARATIONS
**
**/
    static uil_fcb_type	    *main_fcb;
    static char		    *include_dir;
    static unsigned short   main_dir_len;




/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      This procedure initializes the reading of the UIL source program.
**
**  FORMAL PARAMETERS:
**
**      none
**
**  IMPLICIT INPUTS:
**
**      Uil_cmd_z_command
**
**  IMPLICIT OUTPUTS:
**
**      src_az_first_source_buffer
**      src_az_current_source_buffer
**	src_l_last_source_file_number
**	src_az_source_file_table
**      src_az_current_source_record
**	main_fcb
**	include_dir 
**	main_dir_len
**
**  FUNCTION VALUE:
**
**      void
**
**  SIDE EFFECTS:
**
**      source file is opened
**	source buffer structure is setup
**	source record structure is setup
**
**--
**/

void
src_initialize_source(void)
{
    /* initialize source data structures */

    src_az_current_source_buffer = NULL;
    src_az_avail_source_buffer = NULL;
    src_l_last_source_file_number = -1;
    src_az_first_source_record = NULL;
    src_az_current_source_record =
	(src_source_record_type *) &src_az_first_source_record;

    /*  Initialize Own storage    */

    main_fcb = NULL;
    include_dir = NULL;
    main_dir_len = 0;


    /* open the source file */
    if ( Uil_cmd_z_command.ac_source_file == NULL )
	diag_issue_diagnostic (d_src_open,
			       diag_k_no_source, diag_k_no_column,
			       "<null file name>");

    src_open_file ( Uil_cmd_z_command.ac_source_file, NULL );

    /* fixes initial filename is NULL bug in callable UIL */
    Uil_current_file = Uil_cmd_z_command.ac_source_file;

    return;
}

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**	This procedure does the cleanup processing of the source files
**	structures.
**
**  FORMAL PARAMETERS:
**
**      none
**
**  IMPLICIT INPUTS:
**
**	Uil_cmd_z_command
**      src_az_first_source_buffer
**      src_az_current_source_buffer
**	src_l_last_source_file_number
**	src_az_source_file_table
**      src_az_current_source_record
**
**  IMPLICIT OUTPUTS:
**
**      none
**
**  FUNCTION VALUE:
**
**      void
**
**  SIDE EFFECTS:
**
**	structures are freed
**
**--
**/

void
Uil_src_cleanup_source(void)
{

    int				i;		    /* index over fcbs */
    src_source_buffer_type	*buffer_to_free;   
    src_source_record_type	*record_to_free;
    src_machine_code_type	*first_code_item; 
    src_machine_code_type	*code_item_to_free; 
    status			l_close_status;

    /*
    **  Loop through all open files freeing their fcbs
    */
    for (i = 0; i <= src_l_last_source_file_number; i++)
	{
	/* it is possible to get an error before the files are open,
	   so check and see if table is NULL before opening */
	if (src_az_source_file_table[i] == NULL)
		continue;
	l_close_status = close_source_file (src_az_source_file_table[i]);
	if ( l_close_status == src_k_close_error )
	    {
	    diag_issue_diagnostic (d_src_close,
				   diag_k_no_source, diag_k_no_column,
				   src_az_source_file_table[i]->expanded_name);
	    }
	_free_memory ((char*)src_az_source_file_table [i]);
	src_az_source_file_table[i] = NULL;
	}

    /*
    **  Loop through list of current source buffers, freeing them
    */
    while (src_az_current_source_buffer != NULL)
	{
    	buffer_to_free = src_az_current_source_buffer;
    	src_az_current_source_buffer = 
	    src_az_current_source_buffer->az_prior_source_buffer;
	_free_memory ((char*)buffer_to_free);
	}

    /*
    **  Loop through list of source records, freeing them
    */
    while (src_az_first_source_record != NULL)
	{
	record_to_free = src_az_first_source_record;
	first_code_item = record_to_free->az_machine_code_list;
	
	while (first_code_item != NULL)
	  {
	    code_item_to_free = first_code_item;
	    first_code_item = first_code_item->az_next_machine_code;
	    _free_memory((char *)code_item_to_free);
	  }
	
	src_az_first_source_record =
	    src_az_first_source_record->az_next_source_record;
	_free_memory ((char*)record_to_free);
	}

    /*
    **  Free Own storage
    */
/* BEGIN OSF FIX pir 2240 */
    /* Memory pointed to by main_fcb already freed. */
/* END OSF FIX pir 2240 */
    _free_memory (include_dir);

    return;
}

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      This procedure opens a file and sets up the static pointers to
**	read from this file.
**
**  FORMAL PARAMETERS:
**
**      c_file_name	    file to open
**
**  IMPLICIT INPUTS:
**
**      src_az_first_source_buffer
**      src_az_current_source_buffer
**	src_l_last_source_file_number
**	src_az_source_file_table
**
**  IMPLICIT OUTPUTS:
**
**      src_az_first_source_buffer
**      src_az_current_source_buffer
**	src_l_last_source_file_number
**	src_az_source_file_table
**
**  FUNCTION VALUE:
**
**      void
**
**  SIDE EFFECTS:
**
**      input file is opened
**	input buffer structure is setup
**	input record structure is setup
**
**--
**/

void
src_open_file (XmConst char *c_file_name,
               char         *full_file_name)
{
    uil_fcb_type		*az_fcb;	    /* file control block ptr */
    status			l_open_status;	    /* status variable */
    src_source_buffer_type	*az_source_buffer;  /* source buffer ptr */

    /* allocate fcb and source buffer */

    az_fcb = (uil_fcb_type *) _get_memory (sizeof (uil_fcb_type));

    if (src_az_avail_source_buffer != NULL) {
	az_source_buffer = src_az_avail_source_buffer;
	src_az_avail_source_buffer = 
		src_az_avail_source_buffer->az_prior_source_buffer;
    } else {
	az_source_buffer =
	    (src_source_buffer_type *)
			_get_memory (sizeof (src_source_buffer_type));
    }

    /* Call the OS-specific open file procedure */

    l_open_status = open_source_file (
			c_file_name,
			az_fcb,
			az_source_buffer );

    /*	If the file is not found, a fatal error is generated.	*/

    if ( l_open_status == src_k_open_error ) {
	diag_issue_diagnostic( d_src_open,
			       diag_k_no_source, diag_k_no_column,
			       c_file_name );
    }

    /* put fcb in the file table */

    src_l_last_source_file_number++;

    if (src_l_last_source_file_number >= src_k_max_source_files) {
	diag_issue_diagnostic (
		d_src_limit,
		src_az_current_source_record,
		src_az_current_source_buffer -> w_current_position - 1,
		az_fcb->expanded_name );
    }

    src_az_source_file_table[ src_l_last_source_file_number ] = az_fcb;

    /* Complete the OS-independent initialization. Get the size of the file
    ** for %complete info then initialize a source
    ** buffer placing a null in the buffer will cause the lexical analyzer
    ** to start by reading the first line of the file
    */

    /* %COMPLETE */
    if (stat(az_fcb->expanded_name, &stbuf) == -1) {
	diag_issue_diagnostic( d_src_open,
			       diag_k_no_source, diag_k_no_column,
			       az_fcb->expanded_name );
    }

    Uil_file_size = stbuf.st_size;

    if (full_file_name != NULL)
	strcpy (full_file_name, az_fcb->expanded_name);

    az_fcb->v_position_before_get = FALSE;

    az_source_buffer->w_current_line_number = 0;
    az_source_buffer->b_file_number = src_l_last_source_file_number;
    az_source_buffer->w_current_position = 0;
    az_source_buffer->c_text[ 0 ] = 0;

    /* make the source buffer current */

    az_source_buffer->az_prior_source_buffer =
				src_az_current_source_buffer;
    src_az_current_source_buffer = az_source_buffer;

    return;
}

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      This procedure reads the next source line;
**
**  FORMAL PARAMETERS:
**
**      none
**
**  IMPLICIT INPUTS:
**
**      src_az_source_file_table
**
**  IMPLICIT OUTPUTS:
**
**      src_az_current_source_buffer
**      src_az_current_source_record
**
**  FUNCTION VALUE:
**
**      src_k_end_source	no more source lines
**      src_k_read_normal	new line in the source buffer
**
**  SIDE EFFECTS:
**
**      may issue diagnostics if error occurs reading record
**	may restore previous source upon reaching end of current source
**
**--
**/

status
src_get_source_line(void)
{
    uil_fcb_type	    *az_fcb;
    src_source_record_type  *az_source_record;
    status		    l_read_status;

    /* Return if already at the end of file */

    if (src_az_current_source_buffer == NULL)
	return src_k_end_source;

    /* Find the current fcb */

    az_fcb = src_az_source_file_table
		[ (int)src_az_current_source_buffer->b_file_number ];

    /* Read the next record */

    l_read_status = get_line( az_fcb );

    /* Increment lines processed count, and update current file */
    Uil_lines_processed++;
    Uil_current_file = az_fcb->expanded_name;

    if ( (l_read_status == src_k_read_normal) ||
	 (l_read_status == src_k_read_truncated) )
    {
	/* Read was successful
	 * Set position to the start of the record */

	src_az_current_source_buffer->w_current_position = 0;

	/* Allocate and initialize a source record */
	
	az_source_record = 
	    (src_source_record_type *)
		_get_memory( sizeof( src_source_record_type ) );

	az_source_record->az_next_source_record = NULL;
	az_source_record->w_line_number = 
	    ++src_az_current_source_buffer->w_current_line_number;
	az_source_record->b_file_number = 
	    src_az_current_source_buffer->b_file_number;
	az_source_record->az_message_list = NULL;
	az_source_record->az_machine_code_list = NULL;
	az_source_record->w_machine_code_cnt = 0;
	az_source_record->z_access_key = az_fcb->last_key;

	/* was uninitialized; fixes listing problem on HP  (RAP) */
        az_source_record->b_flags = 0;

	/* Link the source record to the end of source record list */

	src_az_current_source_record->az_next_source_record =
	    az_source_record;
	src_az_current_source_record = az_source_record;

	if (l_read_status == src_k_read_truncated)
	    diag_issue_diagnostic( d_src_truncate,
			      	   src_az_current_source_record,
			      	   diag_k_no_column,
				   src_k_max_source_line_length );
    
	return src_k_read_normal;
    }

    /* Check for end of file */

    if (l_read_status == src_k_end_source)
    {
	src_source_buffer_type	*az_prior_source_buffer;
	
	/* get prior source buffer */

	az_prior_source_buffer = 
	    src_az_current_source_buffer->az_prior_source_buffer;

	/* place current source buffer on the available list */

	src_az_current_source_buffer->az_prior_source_buffer =
	    src_az_avail_source_buffer;
	src_az_avail_source_buffer = src_az_current_source_buffer;

	/* if there is no prior source buffer - return end of source */

	if (az_prior_source_buffer == NULL)
	    return src_k_end_source;

	/* restore the prior buffer as current */

	src_az_current_source_buffer = az_prior_source_buffer;

	return src_k_read_normal;
    }

    /* must have been an error */

    diag_issue_diagnostic( d_src_read,
		      	   src_az_current_source_record,
		      	   diag_k_no_column,
			   az_fcb->expanded_name );

    _assert( FALSE, "read past source error" );
	return(src_k_read_error);
}







/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      open the source file.
**
**  FORMAL PARAMETERS:
**
**      c_file_name	    source file to open
**	az_fcb		    file control block for the file
**	az_source_buffer    source buffer for the file
**
**  IMPLICIT INPUTS:
**
**      none
**
**  IMPLICIT OUTPUTS:
**
**      none
**
**  FUNCTION VALUE:
**
**      src_k_open_normal
**      src_k_open_error
**
**  SIDE EFFECTS:
**
**      file is opened and has a source buffer associated with it
**
**--
**/

status
open_source_file( XmConst char           *c_file_name, 
                  uil_fcb_type           *az_fcb, 
                  src_source_buffer_type *az_source_buffer )
{

    static unsigned short	main_dir_len = 0;
    boolean			main_file;
    int				i;  /* loop index through include files */
    char			buffer[256];


    /* place the file name in the expanded_name buffer */

    strncpy(buffer, c_file_name, sizeof(buffer));
    buffer[sizeof(buffer)-1] = '\0';

/*    Determine if this is the main file or an include file.  */

    main_file = (main_fcb == NULL);

    if (main_file) {

	char XmConst		* ptr;
	unsigned short		len;

/*    Save the directory info for the main file.    */

	for (len = strlen (c_file_name),
	     ptr = & c_file_name [len - 1];
	     len > 0; len--, ptr--) {
	    if ((* ptr) == '/') {
		break;
	    }
	}

	main_dir_len = len;
	main_fcb = az_fcb;

/*    Open the main file.    */

	az_fcb->az_file_ptr = fopen(c_file_name, "r");

    } else {
	static char XmConst	c_include_dir[]= "/usr/include/";
	Boolean			search_user_include=True;
	Boolean			specific_directory=False;

/*    See if the file name has a leading slash and set the flag. 
      Look in the specified directory for the include file.  If the dir
      is not specified (leading slash), look in the main file's directory  */

	if (c_file_name[0] == '/') {
	    specific_directory = True;
	    }

	if (!specific_directory) {
	    _move (buffer, main_fcb -> expanded_name, main_dir_len);
	    _move (& buffer [main_dir_len],
		   c_file_name, strlen (c_file_name) + 1);  /* + NULL */
	} else {
	    strcpy (buffer, c_file_name);
	}

/*    Open the include file.    */

	az_fcb->az_file_ptr = fopen (buffer, "r");

/*    If a specific directory was specified, or if the file was found,
      then we are done.	*/

	if ( (specific_directory) || (az_fcb -> az_file_ptr != NULL) ) {
	  goto open_label;
	}

/*    Look in the command line specified include directories, if any.    */

	for (i = 0; i < Uil_cmd_z_command.include_dir_count; i++) {	     
	    int		inc_dir_len;

	    inc_dir_len = strlen (Uil_cmd_z_command.ac_include_dir[i]);
	    if (inc_dir_len == 0) {
		search_user_include = False;
		}
	    _move (buffer, Uil_cmd_z_command.ac_include_dir[i], inc_dir_len);

	/*  Add '/' if not specified at end of directory  */

	    if (Uil_cmd_z_command.ac_include_dir[i][inc_dir_len - 1] != '/') {
		buffer [inc_dir_len] = '/';
		inc_dir_len++;
	    };

	    _move (& buffer [inc_dir_len],
		   c_file_name, strlen (c_file_name) + 1);  /* + NULL */

	/*    Open the include file.  If found, we are done.    */

	    az_fcb->az_file_ptr = fopen (buffer, "r");

	    if (az_fcb -> az_file_ptr != NULL) {
		goto open_label;
	    }
	}

/*    Look in the default include directory.    */
	if (search_user_include) {
	  _move(buffer, c_include_dir, sizeof c_include_dir - 1); /* no NULL */
	  _move(&buffer[sizeof c_include_dir - 1], 
		c_file_name, strlen (c_file_name) + 1);  /* + NULL */

/*    Open the include file.    */
	  az_fcb->az_file_ptr = fopen (buffer, "r");
	}
    }

open_label:

    /* check the open status. */

    if (az_fcb->az_file_ptr == NULL)
	return src_k_open_error;

    /* open succeeded - place buffer address in fcb */

    az_fcb->c_buffer = az_source_buffer->c_text;
    az_fcb->c_buffer[ src_k_max_source_line_length ] = 0;
    strcpy(az_fcb->expanded_name, buffer);

    return src_k_open_normal;
}


/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      close the source file.
**
**  FORMAL PARAMETERS:
**
**	az_fcb		    file control block for the file
**
**  IMPLICIT INPUTS:
**
**      none
**
**  IMPLICIT OUTPUTS:
**
**      none
**
**  FUNCTION VALUE:
**
**      src_k_close_normal
**      src_k_close_error
**
**  SIDE EFFECTS:
**
**	none
**
**--
**/

status
close_source_file( uil_fcb_type *az_fcb )
{
    status	l_close_status;

    /*
    ** Close  the file
    */

    l_close_status = fclose (az_fcb->az_file_ptr);

    if ( l_close_status != EOF )
	return src_k_close_normal;
    else
	return src_k_close_error;
}

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      read line of the source file.
**
**  FORMAL PARAMETERS:
**
**	az_fcb		    file control block for the file
**
**  IMPLICIT INPUTS:
**
**      none
**
**  IMPLICIT OUTPUTS:
**
**      none
**
**  FUNCTION VALUE:
**
**      src_k_read_normal
**      src_k_read_error
**	src_k_read_truncated
**	src_k_end_source
**
**  SIDE EFFECTS:
**
**      next record in file is read
**
**--
**/

status
get_line( uil_fcb_type *az_fcb )
{
    status	l_read_status;
    char	*c_new_line;

    /*	
    **	if v_position_before_get is true, we need to reposition
    **	before the get because another retrieve has altered the
    **	current record.
    */

    if (az_fcb->v_position_before_get)
    {
	fseek( az_fcb->az_file_ptr,
	       az_fcb->last_key.l_key,
	       0 );
	l_read_status = (status) (fgets(az_fcb->c_buffer, 
					src_k_max_source_line_length, 
					az_fcb->az_file_ptr) != NULL);
	az_fcb->v_position_before_get = FALSE;
    }

    /* get the current offset */

    az_fcb->last_key.l_key = ftell(az_fcb->az_file_ptr);
    
    /* read the next line */

    l_read_status = (status) (fgets(az_fcb->c_buffer, 
				    src_k_max_source_line_length, 
				    az_fcb->az_file_ptr) != NULL );

    if ( l_read_status != 0 )
    {
	/* Read was successful
	 * Find \n character an replace with a null */

	c_new_line = (char *) strchr( az_fcb->c_buffer, '\n' );

	if (c_new_line == NULL) {

/* Fix for CR 3044 -- only return truncated if not at eof */

	    if (!feof(az_fcb->az_file_ptr))
	        return src_k_read_truncated;
	} else {
	    *c_new_line = 0;
	} 

	return src_k_read_normal;
    }

    /* Check for end of file */

    if (feof(az_fcb->az_file_ptr))
    {
	if (sym_az_current_section_entry->prev_section != NULL)
	    {
	    sym_include_file_entry_type	*include_entry;

	    /*
	    ** This is the end of an include file.  Set the pointers so that the sections
	    ** in the include file hang off the previous list correctly.
	    */
	    include_entry = (sym_include_file_entry_type *)
				sym_az_current_section_entry->prev_section->entries;
	    include_entry->sections = sym_az_current_section_entry;
	    sym_az_current_section_entry = sym_az_current_section_entry->prev_section;
	    }
	return src_k_end_source;
    }

    /* must have been an error */

    return src_k_read_error;
}

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      re-read line of the source file.
**
**  FORMAL PARAMETERS:
**
**	az_fcb		    file control block for the file
**	c_buffer	    pointer to buffer to hold the source line
**	z_access_key	    key to retrieve the source line
**  
**  IMPLICIT INPUTS:
**
**      none
**
**  IMPLICIT OUTPUTS:
**
**      v_position_before_read = TRUE
**
**  FUNCTION VALUE:
**
**      true		if the record can be retrieved
**      false		if the record cannot be retrieved
**
**  SIDE EFFECTS:
**
**      change next record for the file
**
**--
**/

boolean
reget_line( uil_fcb_type  *az_fcb, 
            char          *c_buffer, 
            XmConst z_key *z_access_key )
{
    status	l_read_status;
    char	*c_new_line;

    fseek( az_fcb->az_file_ptr,
	   z_access_key->l_key,
	   0 );

    l_read_status = (status) (fgets(c_buffer, 
				    src_k_max_source_line_length, 
				    az_fcb->az_file_ptr) != NULL );

    az_fcb->v_position_before_get = TRUE;

    if ( l_read_status != 0 )
    {
	/* Read was successful
	 * Find \n character an replace with a null */

	c_new_line = (char *) strchr( c_buffer, '\n' );

        if (c_new_line == NULL) {

/* Fix for CR 3044 -- only return truncated if not at eof */

            if (!feof(az_fcb->az_file_ptr))
                return src_k_read_truncated;
        } else {
            *c_new_line = 0;
        }        

	return TRUE;
    }

    /* must have been an error */

    return FALSE;
}


/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      Given a source record, this function returns the file name of
**	the file containing that source record.
**
**  FORMAL PARAMETERS:
**
**      az_src_rec	pointer to a source record structure
**
**  IMPLICIT INPUTS:
**
**      src_az_source_file_table
**
**  IMPLICIT OUTPUTS:
**
**      none
**
**  FUNCTION VALUE:
**
**      pointer to the file name string
**
**  SIDE EFFECTS:
**
**      none
**
**--
**/

char
*src_get_file_name(XmConst src_source_record_type *az_src_rec)
{
    uil_fcb_type    *fcb;

    /* Find the correct fcb */

    fcb = src_az_source_file_table[ az_src_rec->b_file_number ];
    
    /* Return a pointer to file name */

    return fcb->expanded_name;
}

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      Given a source record, this function retrieves the text of the
**	line corresponding to that source line.
**
**  FORMAL PARAMETERS:
**
**      az_src_rec	pointer to a source record structure
**	c_buffer	pointer to buffer to hold the text
**
**  IMPLICIT INPUTS:
**
**      src_az_source_file_table
**
**  IMPLICIT OUTPUTS:
**
**      none
**
**  FUNCTION VALUE:
**
**      true		if there is a source line
**      false		if there is no source line
**
**  SIDE EFFECTS:
**
**      buffer is set to the contents of source line
**
**--
**/

static char XmConst no_source[] = "[ source not available ]";



boolean	src_retrieve_source

	(XmConst src_source_record_type *az_src_rec,
	char *c_buffer)

{
    uil_fcb_type    *fcb;

    /* check if there is any source */

    if (az_src_rec == diag_k_no_source)
    {
	_move( c_buffer, no_source, sizeof no_source );
	return FALSE;
    }

    /* 
    **	check if we are dealing with the current source record
    **	in which case we don't need to reread the source 
    */

    if ((az_src_rec->b_file_number == 
	 src_az_current_source_buffer->b_file_number)
	 &&
	(az_src_rec->w_line_number ==
	 src_az_current_source_buffer->w_current_line_number)
       )
    {
	strcpy( c_buffer, 
	       src_az_current_source_buffer->c_text);
	return TRUE;
    }

    /*
    **	Will have to reread the data from the file.
    */

    /* Find the correct fcb */

    fcb = src_az_source_file_table[ az_src_rec->b_file_number ];
    
    /* get the line */

    if (reget_line( fcb, c_buffer, (z_key *) &(az_src_rec->z_access_key) ))
	return TRUE;

    _move( c_buffer, no_source, sizeof no_source );
    return FALSE;

}


/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      This function adds diagnostic information to the source representation.
**	This permit diagnostics to be placed in the listing.
**
**  FORMAL PARAMETERS:
**
**      az_src_rec	    source line diagnostic issued against
**	l_src_pos	    offset of diagnostic in the source line
**	c_msg_text	    text of diagnostic
**	l_msg_number	    message number for diagnostic
**
**  IMPLICIT INPUTS:
**
**      none
**
**  IMPLICIT OUTPUTS:
**
**      none
**
**  FUNCTION VALUE:
**
**      void
**
**  SIDE EFFECTS:
**
**      diagnostic stuff away in the source structure
**
**--
**/

void
src_append_diag_info( XmConst src_source_record_type *az_src_rec, 
                      XmConst int                     l_src_pos, 
                      XmConst char                   *c_msg_text, 
                      XmConst int                     l_msg_number )
{
    src_message_item_type	    *az_msg_item;
    int			    	    l_msg_length;
    src_message_item_type	    *current;
    src_message_item_type   	    **prior;

    /*
    **	create the message item and fill it in.
    */

    l_msg_length = strlen( c_msg_text ) + 1;	/* includes null */

    az_msg_item = (src_message_item_type *)
	_get_memory( sizeof( src_message_item_type ) + l_msg_length );

    az_msg_item->l_message_number = l_msg_number;
    az_msg_item->b_source_pos = l_src_pos;

    _move( (az_msg_item->c_text), c_msg_text, l_msg_length );

    /*
    **  Link the message from its source line
    **	    Messages are in ascending column order for a line with
    **	      messages without column info at the end
    **	    Messages without source are appended to a list of orphans
    */

    if (az_src_rec == diag_k_no_source)
	prior = &src_az_orphan_messages;
    else
	prior = (src_message_item_type **)&(az_src_rec->az_message_list);

    current = *prior;

    for (;  
	 current != NULL;
	 prior = &(current->az_next_message),  
	 current = *prior )
    {
	if (l_src_pos < (int)current->b_source_pos)
	    break;
    }

    az_msg_item->az_next_message = current;
    *prior = az_msg_item;
    
    return;
}

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      This function adds machine code information to the source
**	representation.  This permits machine code to be placed in the listing.
**
**  FORMAL PARAMETERS:
**
**      az_src_rec	    source line machine code is associated with.
**	l_offset	    offset in the record for this code element
**	l_code_len	    length of the binary machine code buffer
**	c_code		    buffer containing the machine code in binary form
**	c_text_arg	    text of machine code; optional
**
**  IMPLICIT INPUTS:
**
**      none
**
**  IMPLICIT OUTPUTS:
**
**      none
**
**  FUNCTION VALUE:
**
**      void
**
**  SIDE EFFECTS:
**
**      Machine code stuffed away in the source structure
**
**--
**/

void
src_append_machine_code ( src_source_record_type *az_src_rec, 
                          XmConst int             l_offset, 
                          XmConst int             l_code_len, 
                          XmConst char           *c_code, 
                          XmConst char           *c_text_arg )
{
    src_machine_code_type   *az_code_item;
    int			    l_text_len;
    XmConst char	    *c_text;

    if (c_text_arg == NULL) {
	c_text = "";
    } else {
	c_text = c_text_arg;
    }

    /*
    **	create the machine code item and fill it in.
    */

    l_text_len = strlen( c_text ) + 1;	/* includes null */

    az_code_item = (src_machine_code_type *) _get_memory(
		sizeof( src_machine_code_type ) + l_text_len + l_code_len );

    az_code_item -> w_offset = l_offset;
    az_code_item -> w_code_len = l_code_len;
    _move( (az_code_item->data.c_data), c_code, l_code_len );
    _move( &(az_code_item->data.c_data [l_code_len]), c_text, l_text_len );

    /*
    **  Link the machine code to its source line, at the head of
    **  the machine code list.
    */

    az_code_item->az_next_machine_code = az_src_rec->az_machine_code_list;
    az_src_rec->az_machine_code_list = az_code_item;
    az_src_rec->w_machine_code_cnt++;
    
    return;
}
