/* Copyright (C) 1995 Bjoern Beutel. */

/* Description. =============================================================*/

/* This file contains basic types, macros and functions used everywhere. */

/* Constants. ===============================================================*/

#undef NULL
#define NULL 0 /* Null pointer. */

enum {BITS_PER_BYTE = 8};

/* Attribute for a function that never returns. */
#ifdef __GNUC__
#define NO_RETURN __attribute__((noreturn))
#else
#define NO_RETURN
#endif

/* Basic types. =============================================================*/

/* Numeric types. */
typedef signed char byte_t; /* Signed 8 bits. */
typedef unsigned char u_byte_t; /* Unsigned 8 bits. */
typedef signed short short_t; /* Signed 16 bits. */
typedef unsigned short u_short_t; /* Unsigned 16 bits. */
typedef signed int int_t; /* Signed 32 bits. */
typedef unsigned int u_int_t; /* Unsigned 32 bits. */
typedef unsigned long ptr_t; /* Pointer in arithmetic expressions. */

/* Character types. */
typedef char char_t; /* A single char. */
typedef const char_t *string_t; /* A constant EOS-terminated C string. */
enum {EOS= '\0'}; /* End-Of-String control character. */
#define ORD(c) ((u_byte_t) (c)) /* The ordinal number of character C. */

/* Boolean type. */
#undef bool_t /* <rpc/rpc.h> conflicts with "bool_t" definition. */
#undef TRUE
#undef FALSE
typedef enum {FALSE, TRUE} bool_t;

/* Macros. ==================================================================*/

#undef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b)) /* Minimum of A and B. */
#undef MAX
#define MAX(a,b) ((a) > (b) ? (a) : (b)) /* Maximum of A and B. */
#undef ABS
#define ABS(a) ((a) >= 0 ? (a) : (-a)) /* Absolute value of A. */

#define ARRAY_LENGTH(a) (sizeof(a) / sizeof( (a)[0] ))

/* Global variables. ========================================================*/

extern bool_t user_break_requested;
/* Flag that indicates that user has send a Ctrl-C. */

extern bool_t split_hangul_syllables;

/* Read-only variables. =====================================================*/

extern char_t malaga_version[];
extern string_t program_name; /* This is set by "init_basic". */
extern bool_t in_emacs_malaga_mode;  /* This is set by "init_basic". */

/* Program message. =========================================================*/

extern void program_message( void );
/* Print some information about the program. */

/* Forward-linked lists. ====================================================*/
                                              
typedef struct list_node /* A node in a list of nodes. */
{ 
  struct list_node *next; /* Next list node. */
} list_node_t;

typedef struct /* A list of nodes. */
{ 
  list_node_t *first, *last; /* The first and last element in the list. */
} list_t;

typedef enum {LIST_START, LIST_END} position_t;
/* The position where a new element is added to a list. */

extern void clear_list( list_t *list );
/* Initialise LIST to be an empty list */

extern void add_node( list_t *list, list_node_t *node, position_t position );
/* Add NODE to LIST.
 * If POSITION = LIST_START, add it at the start of the list;
 * If POSITION = LIST_END, add it at the end. */

extern void insert_node( list_t *list, list_node_t *node, list_node_t *prev );
/* Insert NODE in LIST, behind PREV.
 * If PREV == NULL, insert NODE at the beginning of the list. */

extern void *remove_first_node( list_t *list );
/* Remove the first node in LIST and return it. */

extern void remove_node( list_t *list, list_node_t *node );
/* Remove NODE in LIST. */

extern void combine_lists( list_t *list1, list_t *list2 );
/* Append LIST2 to LIST1.
 * LIST1 will contain the concatenation; LIST2 will be empty. */

extern void *new_node( list_t *list, int_t size, position_t position );
/* Add a node of size SIZE to LIST.
 * If POSITION = LIST_START, add the element at the start of the list;
 * If POSITION = LIST_END, add the element at the end. 
 * Return the newly created node. */

extern void free_first_node( list_t *list );
/* Remove first node in LIST and free it. */

extern void free_node( list_t *list, list_node_t *node );
/* Remove NODE from LIST and free it. */

/* Iterate through a "list_t". */
#define FOREACH(var, list) \
  for ((var) = (void *) (list).first; \
       (var) != NULL; \
       (var) = (void *) (var)->next)

/* Iterate through a "list_t" and free every node in it. */
#define FOREACH_FREE(var, list) \
  for ((var) = (void *) (list).first; \
       (var) != NULL; \
       (var) = (void *) (var)->next, free_first_node( &(list) ))

/* Memory functions. ========================================================*/

extern void *new_mem( int_t item_size );
/* Allocate a memory block of ITEM_SIZE bytes, clear it and return it.
 * If memory is out, call the function "error". */

extern void *new_vector( int_t item_size, int_t item_count );
/* Allocate a memory block to contain ITEM_COUNT items of size ITEM_SIZE,
 * clear it and return it.
 * If memory is out, call the function "error". */

extern int_t renew_vector( void *block_p, int_t item_size, int_t item_count );
/* Realloc *BLOCK_P to contain ITEM_COUNT items of ITEM_SIZE bytes each.
 * Return ITEM_COUNT.
 * If memory is out, call the function "error". */

extern void free_mem( void *pointer );
/* Free memory *POINTER points to, and set *POINTER to NULL. */

/* String functions. ========================================================*/

extern char_t *new_string( string_t string, string_t end );
/* Allocate memory and copy STRING into it.
 * If END != NULL, it marks the end of the string.
 * The result string must be freed after use. */

extern char_t *new_string_readable( string_t from, string_t from_end );
/* Like "new_string", but copy a "\" in front of quotes
 * and copy any control chars in octal code: "\000". 
 * If FROM_END != NULL, it marks the end of the string. 
 * The result string must be freed after use. */

extern char_t *concat_strings( string_t first_string, ... );
/* Concatenate a list of strings and return the result string.
 * Must have NULL-terminated list of strings as parameters.
 * The result string must be freed after use. */

extern int_t strcmp_no_case( string_t str1, string_t str2 );
/* Return (case insensitive) lexical order of STR1 and STR2:
 * Result is -1 if STR1 < STR2,
 *            0 if STR1 = STR2,
 *            1 if STR1 > STR2. */

extern int_t strncmp_no_case( string_t str1, string_t str2, int_t n );
/* Return (case insensitive) lexical order of STR1 and STR2,
 * but compare only the first N characters.
 * Result is -1 if STR1 < STR2,
 *            0 if STR1 = STR2,
 *            1 if STR1 > STR2. */

extern string_t next_non_space( string_t string );
/* Return STRING, but without leading spaces. */

extern char_t *double_to_string( double number );
/* Convert NUMBER to a string. The string must be freed after use. */

extern char_t *int_to_string( int_t number );
/* Convert NUMBER to a string. The string must be freed after use. */

extern char_t *replace_arguments( string_t format, string_t chars, ... );
/* Create a new string with a copy of FORMAT.
 * Replace each sequence "%C" in FORMAT, where C is the N-th
 * char in CHARS, by the N-th additional string argument. 
 * The result string must be freed after use. */

/* Text functions. ==========================================================*/

/* A data structure that contains a string that may grow indefinitely. */
typedef struct
{
  char_t *buffer;
  int_t buffer_size;
  int_t string_size;
} text_t;

extern text_t *new_text( void );
/* Return a new text structure. */

extern void clear_text( text_t *text );
/* Initialize TEXT to an empty string. */

extern void free_text( text_t **text_p );
/* Free the content of *TEXT_P. */

extern void add_to_text( text_t *text, string_t string );
/* Add STRING to TEXT. */

extern void add_char_to_text( text_t *text, char_t character );
/* Add CHARACTER to TEXT. */

void add_unichar_to_text( text_t *text, gunichar c );
/* Add C to TEXT. */

extern void insert_in_text( text_t *text, string_t string, int_t position );
/* Insert STRING at POSITION in TEXT (position counts from 0 onward). */

extern void insert_char_in_text( text_t *text, 
				 char_t character, 
                                 int_t position );
/* Insert CHARACTER at POSITION in TEXT. */

extern void print_text( text_t *text, string_t format, ... );
/* Add FORMAT to TEXT, replace any "%s" with a "string_t" argument, 
 * and any "%d" with an "int_t" argument. */

extern char_t *text_to_string( text_t **text_p );
/* Return content of *TEXT_P as a string and delete *TEXT_P.
 * The string must be freed after use. */

/* Add CHARACTER to TEXT (macro version). */
#define ADD_CHAR_TO_TEXT( text, character ) \
do { \
  if (text->buffer_size < text->string_size + 2) \
  { \
    text->buffer_size = renew_vector( &text->buffer, sizeof( char_t ), \
				      2 * (text->string_size + 1) ); \
  } \
  text->buffer[ text->string_size++ ] = character; \
  text->buffer[ text->string_size ] = EOS; \
} while (FALSE)

/* Error handlers. ==========================================================*/

/* The syntax of an error handler is:
 * TRY STATEMENT1
 * IF_ERROR STATEMENT2
 * FINALLY STATEMENT3
 * END_TRY;
 *
 * The parts "IF_ERROR STATEMENT2" and "FINALLY STATEMENT3" are optional.
 *
 * First, STATEMENT1 is executed.
 * If the function "error" is called in STATEMENT1 or in a function 
 * called from there, STATEMENT2 and then STATEMENT3 will be executed
 * (if they exist).
 * If the function "error" is not called, STATEMENT3 will executed.
 * In STATEMENT2, you can use the statement "RESUME" to leave the error
 * state. */

#define TRY \
do { \
  jmp_buf error_handler, *older_error_handler = current_error_handler; \
  volatile bool_t rethrow; \
  current_error_handler = &error_handler; \
  if (! (rethrow = setjmp( error_handler )))

#define IF_ERROR else
#define FINALLY /* Nothing. */

#define END_TRY \
  current_error_handler = older_error_handler; \
  if (rethrow) \
    throw(); \
} while (FALSE)

#define RESUME rethrow = FALSE

extern text_t *error_text; /* The text of the last error. */

extern jmp_buf *current_error_handler; /* The active innermost error handler */

extern void throw( void ) NO_RETURN;
/* Call the current error handler. 
 * If there is no current error handler, print error and exit. */

extern void complain( string_t message, ... ) NO_RETURN;
/* Save the error MESSAGE in ERROR_TEXT.
 * Additional arguments to "complain" are inserted where 
 * "%s" (string_t ARGUMENT), "%c" (char_t ARGUMENT), "%u" (gunichar ARGUMENT),
 * or "%d" (int_t ARGUMENT) is part of MESSAGE. */

/* Module initialisation. ===================================================*/

extern void init_basic( string_t prog_name );
/* Initialise this module. PROG_NAME should be the name of the program. */

extern void terminate_basic( void );
/* Terminate this module. */

/* End of file. =============================================================*/
