/*
 * International Chemical Identifier (InChI)
 * Version 1
 * Software version 1.07
 * April 30, 2024
 *
 * MIT License
 *
 * Copyright (c) 2024 IUPAC and InChI Trust
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
*
* The InChI library and programs are free software developed under the
 * auspices of the International Union of Pure and Applied Chemistry (IUPAC).
 * Originally developed at NIST.
 * Modifications and additions by IUPAC and the InChI Trust.
 * Some portions of code were developed/changed by external contributors
 * (either contractor or volunteer) which are listed in the file
 * 'External-contributors' included in this distribution.
 *
 * info@inchi-trust.org
 *
*/


#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <float.h>
#include <string.h>
#include <ctype.h>

#include "e_mode.h"

#include "../../../../INCHI_BASE/src/inchi_api.h"
#include "../../../../INCHI_BASE/src/bcf_s.h"

#include "e_ctl_data.h"

#include "e_ichisize.h"
#include "e_comdef.h"
#include "e_util.h"
#include "e_readmol.h"
#include "e_ichicomp.h"
#include "e_ichi_io.h"
#include "e_readstru.h"
#include "e_inpdef.h"

#define AddMOLfileError        e_AddMOLfileError
#define remove_one_lf          e_remove_one_lf
#define RemoveNonPrintable     e_RemoveNonPrintable
#define mystrncpy              e_mystrncpy
#define read_mol_file          e_read_mol_file
#define bypass_sdf_data_items  e_bypass_sdf_data_items
#define extract_ChargeRadical  e_extract_ChargeRadical
#define delete_mol_data        e_delete_mol_data
#define remove_trailing_spaces e_remove_trailing_spaces
#define normalize_name         e_normalize_name
#define read_sdfile_segment    e_read_sdfile_segment
#define CopyMOLfile            e_CopyMOLfile
#define LtrimRtrim             e_LtrimRtrim

#ifndef inchi_calloc
#define inchi_calloc            e_inchi_calloc
#endif

#ifndef inchi_free
#define inchi_free              e_inchi_free
#endif

/*
    Former lreadmol.h follows below


#include "lreadmol.h"
*/



/* local prototypes */
int bypass_sdf_data_items( FILE* inp, long *cas_reg_no, char* comment, int lcomment, char *name, int lname, int prev_err,
                           const char *pSdfLabel, char *pSdfValue, char *pStrErr );
MOL_DATA* read_mol_file( FILE* inp, MOL_HEADER_BLOCK *OnlyHeaderBlock, MOL_CTAB *OnlyCtab,
                         int bGetOrigCoord, int *err, char *pStrErr );


static int mol_read_hdr( MOL_HEADER_BLOCK *hdr, FILE* inp, char *pStrErr );
static int mol_read_counts_line( MOL_CTAB* ctab, FILE *inp, char *pStrErr );
static int read_atom_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr );
static int read_bonds_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr );
static int read_stext_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr );
static int read_properties_block( MOL_CTAB* ctab, MOL_HEADER_BLOCK *pHdr, FILE *inp, int err, char *pStrErr );

static int identify_sdf_label( char* inp_line, const char *pSdfLabel );
static long extract_cas_rn( char *line );
static int mol_copy_check_empty( char* dest, char* source, int len, char **first_space );
static int mol_read_datum( void* data, int field_len, int  data_type, char** line_ptr );

static int RemoveNonPrintable( char *line );


/******/
#ifndef MOLFILE_ERR_FIN
#define MOLFILE_ERR_FIN(err, new_err, err_fin, msg) \
        if ( !(err) && (new_err) ) { (err) = (new_err);} AddMOLfileError(pStrErr, (msg)); goto err_fin
#endif
#ifndef MOLFILE_ERR_SET
#define MOLFILE_ERR_SET(err, new_err, msg) \
        if ( !(err) && (new_err) ) { (err) = (new_err);} AddMOLfileError(pStrErr, (msg))
#endif


/****************************************************************************/
int AddMOLfileError( char *pStrErr, const char *szMsg )
{
    if (pStrErr && szMsg && szMsg[0])
    {
        int lenStrErr = strlen( pStrErr );
        int lenMsg = strlen( szMsg );
        char *p = strstr( pStrErr, szMsg );
        if (p && ( p == pStrErr || (*( p - 1 ) == ' ' && ( *( p - 2 ) == ';' || *( p - 2 ) == ':' )) ) &&
            ( p + lenMsg == pStrErr + lenStrErr ||
                (p[lenMsg] == ';' && p[lenMsg + 1] == ' ') ||
                (p[lenMsg - 1] == ':' && p[lenMsg] == ' ') )) /* djb-rwth: addressing LLVM warnings */
        {
            return 1; /*  reject duplicates */
        }
        if (lenStrErr + lenMsg + 2 * ( lenStrErr > 0 ) < STR_ERR_LEN)
        {
            /*  enough room to add */
            if (lenStrErr > 0)
            {
                if (pStrErr[lenStrErr - 1] != ':')
                {
                    strcat( pStrErr, ";" );
                }
                strcat( pStrErr, " " );
            }
            strcat( pStrErr, szMsg );
            return 1;
        }
        /*  no room */
        if (strstr( pStrErr, "..." ))
        {
            return 0; /*  no room mark has already been set */
        }
        if (lenStrErr + 3 < STR_ERR_LEN)
        {
            strcat( pStrErr, "..." );
        }
    }

    return 0;
}


/****************************************************************************/
int mol_copy_check_empty( char* dest, char* source, int len, char **first_space )
{
    int i, c;   /* required len >= 0; dest must have at least len+1 bytes */
    if (len > 0)
    {
        strncpy( dest, source, len );
    }
    dest[len] = '\0';
    len = ( len > 0 ) ? (int) strlen( dest ) : 0;
    for (i = ( len - 1 ); i >= 0 && 0 != ( c = source[i] ) && isspace( UCINT c ); i--)
    {
        ;
    }
    *first_space = dest + ((long long)i + 1); /* first blank or zero terminating byte in dest */ /* djb-rwth: cast operator added */

    return len; /* number of actually processed bytes; zero termination not included */
}


/****************************************************************************/
int mol_read_datum( void* data, int field_len, int  data_type, char** line_ptr )
{

    /* 1. 'field_len' for MOL_STRING_DATA does not include trailing zero,
     *     that is actual length of the string pointed by 'data'
     *     should be at least field_len+1 bytes.
     *     For numerical data 'field_len' is length of input data field
     *     For numerical integral data field_len <= 0 means read up to first
     *     non-numeric character as strtod() does ("free format")
     * 2.  return value: for MOL_STRING_DATA: number of bytes excluding trailing zero
     *                   for all others:  1=success; 0 = empty; -1= error
     * 3.  on exit *line_ptr points to the next byte after the last entered
     */

    char *p = *line_ptr, *q, *p_end;
    int  i, ret = 1, c, len;
    long   ldata;
    double ddata;

    switch (data_type)
    {
        case MOL_STRING_DATA:
            for (i = 0; i < field_len && 0 != ( c = p[i] ) && isspace( UCINT c ); i++) /* pass by all leading spaces */
            {
                ;
            }
            len = mol_copy_check_empty( (char*) data, &p[i], field_len - i, &q );
            ret = ( q - (char*) data );/* actual data length */
            *q = '\0';                /* add zero termination to data if it is not there yet*/
            *line_ptr += ( (long long)len + i );     /* ptr to the 1st byte of the next input field or to zero termination */ /* djb-rwth: cast operator added */
            break;

        case MOL_CHAR_INT_DATA:
        case MOL_SHORT_INT_DATA:
        case MOL_LONG_INT_DATA:
        {
            /* block start */
            char str[MOL_MAX_VALUE_LEN + 1];
            ldata = 0L;
            if (field_len > MOL_MAX_VALUE_LEN)
            {
                ret = -1;
            }
            else
            {
                if (field_len > 0)
                {
                    /* fixed length */
                    *line_ptr += ( len = mol_copy_check_empty( str, p, field_len, &q ) );
                    *q = '\0';
                    if (!len || !( q - str ))
                    {
                        /* empty string */
                        ret = 0;
                    }
                    else
                    {
                        if (( ldata = strtol( str, &p_end, 10 ), p_end != q ))
                        {
                            /* wrong data: incompletely interpreted */
                            ret = -1;
                        }
                    }
                }
                else
                {
                    /* free format: field_len <= 0 */
                    ldata = strtol( p, &p_end, 10 );
                    *line_ptr += ( len = p_end - p );
                    if (len == 0)
                    {
                        ret = 0;
                    }
                }
            }

            switch (data_type)
            {
                case MOL_CHAR_INT_DATA:
                    if (SCHAR_MIN <= ldata  && ldata <= SCHAR_MAX)
                    { /* from || to &&: 11-19-96 */
                        *(S_CHAR*) data = (S_CHAR) ldata;
                    }
                    else
                    {
                        *(S_CHAR*) data = (S_CHAR) 0;
                        ret = -1;
                    }
                    break;
                case MOL_SHORT_INT_DATA:
                    if (SHRT_MIN <= ldata && ldata <= SHRT_MAX)
                    {
                        *(S_SHORT*) data = (S_SHORT) ldata;
                    }
                    else
                    {
                        *(S_SHORT*) data = (S_SHORT) 0;
                        ret = -1;
                    }
                    break;
                case MOL_LONG_INT_DATA:
                    if (LONG_MIN < ldata && ldata < LONG_MAX)
                    {
                        *(long*) data = (long) ldata;
                    }
                    else
                    {
                        *(long*) data = 0L;
                        ret = -1;
                    }
                    break;
                default:
                    ret = -1;
            }
        } /* block end */

        break;
        case MOL_DOUBLE_DATA:
        case MOL_FLOAT_DATA:
        { /* block start */
            char str[MOL_MAX_VALUE_LEN + 1];
            if (field_len > MOL_MAX_VALUE_LEN)
            {
                ret = -1;
                ddata = 0.0;
            }
            else
            {
                if (field_len > 0)
                {
                    *line_ptr += ( len = mol_copy_check_empty( str, p, field_len, &q ) );
                    *q = '\0';
                    if (!len || !( q - str ))
                    { /* empty string */
                        ddata = 0.0;
                        ret = 0;
                    }
                    else
                    {
                        if (( ddata = strtod( str, &p_end ), p_end != q ))
                        { /* wrong data */
                            ret = -1;
                        }
                    }
                }
                else
                {
                    /* free format */
                    ddata = strtod( p, &p_end );
                    *line_ptr += ( len = p_end - p );
                    if (len == 0)
                    {
                        ret = 0;
                    }
                }
            }
            switch (data_type)
            {
                case MOL_DOUBLE_DATA:
                    if (ddata != HUGE_VAL && /*ldata*/ ddata != -HUGE_VAL)
                    { /* replaced ldata with ddata 6-30-98 DCh */
                        *(double*) data = ddata;
                    }
                    else
                    {
                        *(double*) data = 0.0;
                        ret = -1;
                    }
                    break;
                case MOL_FLOAT_DATA:
                    if (fabs( ddata ) <= (double) FLT_MIN)
                    {
                        *(float*) data = 0.0;
                    }
                    else
                    {
                        if (fabs( ddata ) >= (double) FLT_MAX)
                        {
                            *(float*) data = 0.0;
                            ret = -1;
                        }
                        else
                        {
                            *(float*) data = (float) ddata;
                        }
                    }
                    break;
            }
        } /* block end */
        break;

        case MOL_JUMP_TO_RIGHT:
            for (i = 0; i < field_len && p[i]; i++)
            {
                ;
            }
            *line_ptr += i;
            ret = i;
            break;

        default:
            ret = -1;
    }

    return ret;
}


/****************************************************************************/
int mol_read_hdr( MOL_HEADER_BLOCK *hdr, FILE* inp, char *pStrErr )
{
    /* All input lines can have are up 80 characters */
    /* Header Block */
    char line[MOLFILEINPLINELEN]; /* + cr +lf +zero termination + reserve */
    int  err = 0, len; /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
    const int  line_len = sizeof( line );
    char *p;

    /* memset( &hdr, 0, sizeof( MOL_HEADER_BLOCK ) ); */
    /*------------ header line #1: name ----------------*/
    if (NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ))
    {
        err = 1;             /* can't read the input file line */
        /* AddMOLfileError( pStrErr, "Can't read header block name line" ); */
        goto err_fin;
    }
    remove_one_lf( line );
    /* -- Disabled to relax strictness: allow > 80 chars names.
    if ( line[MOLFILEMAXLINELEN] ){
        err = 2;             // too long line
        goto err_fin;
    }
    */
    len = mol_read_datum( hdr->szMoleculeName, sizeof( hdr->szMoleculeName ) - 1, MOL_STRING_DATA, &p ); /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
    /*----------- header line #2 -----------------------*/
    if (NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ))
    {
        err = 3;             /* can't read the input file line */
        /* AddMOLfileError( pStrErr, "Can't read header block line 2" ); */
        goto err_fin;
    }
    remove_one_lf( line );
    /* -- Disabled to relax strictness: allow > 80 chars names.
    if ( line[MOLFILEMAXLINELEN] ){
        err = 4;             // too long input file line
        goto err_fin;
    }
    */
    /* djb-rwth: ignoring LLVM warning: variable used to store function return values */
    len = mol_read_datum( hdr->szUserInitials, sizeof( hdr->szUserInitials ) - 1, MOL_STRING_DATA, &p );
    len = mol_read_datum( hdr->szProgramName, sizeof( hdr->szProgramName ) - 1, MOL_STRING_DATA, &p );

    /*------------ Relax strictness -----------------------*/
    /* djb-rwth: ignoring LLVM warning: variable used to store function return values */
    len = mol_read_datum( &hdr->cMonth, 2, MOL_CHAR_INT_DATA, &p );
    len = mol_read_datum( &hdr->cDay, 2, MOL_CHAR_INT_DATA, &p );
    len = mol_read_datum( &hdr->cYear, 2, MOL_CHAR_INT_DATA, &p );
    len = mol_read_datum( &hdr->cHour, 2, MOL_CHAR_INT_DATA, &p );
    len = mol_read_datum( &hdr->cMinute, 2, MOL_CHAR_INT_DATA, &p );
    len = mol_read_datum( hdr->szDimCode, sizeof( hdr->szDimCode ) - 1, MOL_STRING_DATA, &p );
    len = mol_read_datum( &hdr->nScalingFactor1, 2, MOL_SHORT_INT_DATA, &p );
    len = mol_read_datum( &hdr->dScalingFactor2, 10, MOL_DOUBLE_DATA, &p );
    len = mol_read_datum( &hdr->dEnergy, 12, MOL_DOUBLE_DATA, &p );
    len = mol_read_datum( &hdr->lInternalRegistryNumber, 6, MOL_LONG_INT_DATA, &p );

    /* save the whole line 2 */
    p = line;
    len = mol_read_datum( hdr->szMoleculeLine2, sizeof( hdr->szMoleculeLine2 ) - 1, MOL_STRING_DATA, &p ); /* djb-rwth: ignoring LLVM warning: variable used to store function return value */

    /*------------ header line #3: comment ----------------*/
    if (NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ))
    {
        err = 7;             /* can't read the line */
        /* AddMOLfileError( pStrErr, "Can't read header block comment line" ); */
        goto err_fin;
    }
    remove_one_lf( line );
    /* -- Disabled to relax strictness: allow > 80 chars comments.
    if ( line[MOLFILEMAXLINELEN] ){
        err = 8;             // too long line
        goto err_fin;
    }
    */
    len = mol_read_datum( hdr->szComment, sizeof( hdr->szComment ) - 1, MOL_STRING_DATA, &p ); /* djb-rwth: ignoring LLVM warning: variable used to store function return value */

err_fin:

    return err;
}


/****************************************************************************/
int RemoveNonPrintable( char *line )
{
    int i, c, num = 0;
    if (line)
    {
        for (i = 0; (c = UCINT line[i]); i++) /* djb-rwth: addressing LLVM warning */
        {
            /* assuming ASCII charset */
            if (c < ' ' || c >= 0x7F)
            {
                line[i] = '.';
                num++;
            }
        }
    }

    return num;
}


/****************************************************************************/
int mol_read_counts_line( MOL_CTAB* ctab, FILE *inp, char *pStrErr )
{
    char *p;
    char line[MOLFILEINPLINELEN];
    const int line_len = sizeof( line );
    int   err = 0, len; /* djb-rwth: ignoring LLVM warning: variable used to store function return value */

    if (NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ))
    {
        MOLFILE_ERR_FIN( err, 1, err_fin, "Cannot read counts line" );
        /* can't read the input file line */
    }
    remove_one_lf( line );
    if (line[MOLFILEMAXLINELEN])
    {
        MOLFILE_ERR_SET( err, 0, "Too long counts line" );  /* too long input file line */
    }
    if (0 > mol_read_datum( &ctab->nNumberOfAtoms, 3, MOL_SHORT_INT_DATA, &p )
         || 0 > mol_read_datum( &ctab->nNumberOfBonds, 3, MOL_SHORT_INT_DATA, &p )
#if ( MOL_QUERY == MOL_PRESENT )
         || 0 > mol_read_datum( &ctab->nNumberOfAtomsLists, 3, MOL_SHORT_INT_DATA, &p )
#else
         || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p )
#endif
         || 0 > mol_read_datum( NULL, /*obsolete*/             3, MOL_JUMP_TO_RIGHT, &p )
         || 0 > mol_read_datum( &ctab->cChiralFlag, 3, MOL_CHAR_INT_DATA, &p )
         || 0 > mol_read_datum( &ctab->nNumberOfStextEntries, 3, MOL_SHORT_INT_DATA, &p )
#if ( MOL_CPSS == MOL_PRESENT )
         || 0 > mol_read_datum( &ctab->nNumberOfReactionComponentsPlus1, 3, MOL_SHORT_INT_DATA, &p )
         || 0 > mol_read_datum( &ctab->nNumberOfReactants, 3, MOL_SHORT_INT_DATA, &p )
         || 0 > mol_read_datum( &ctab->nNumberOfProducts, 3, MOL_SHORT_INT_DATA, &p )
         || 0 > mol_read_datum( &ctab->nNumberOfIntermediates, 3, MOL_SHORT_INT_DATA, &p )
#else
         || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p )
         || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p )
         || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p )
         || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p )
#endif
         || 0 > mol_read_datum( &ctab->nNumberOfPropertyLines, 3, MOL_SHORT_INT_DATA, &p ))
    {
        err = 3;  /* can't interpret counts line */
        MOLFILE_ERR_SET( err, 3, "Cannot interpret counts line:" );  /* too long input file line */
        RemoveNonPrintable( line );
        AddMOLfileError( pStrErr, line );
        goto err_fin;
    }

    len = mol_read_datum( ctab->csCurrentCtabVersion, sizeof( ctab->csCurrentCtabVersion ) - 1, MOL_STRING_DATA, &p ); /* djb-rwth: ignoring LLVM warning: variable used to store function return value */

err_fin:

    return err;
}


/****************************************************************************/
int read_atom_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr )
{
    char *p;
    char line[MOLFILEINPLINELEN];
    const int line_len = sizeof( line );
    S_SHORT i, chg;
    static S_SHORT charge_val[] = { 0, 3, 2, 1, 'R', -1, -2, -3 };
    /*                           0  1  2  3   4    5   6   7 */
    /*
      if ( NULL == ctab->MolAtom ){
          err = 1;
          goto err_fin; // internal error: MolAtom structure has not been allocated
      }
     */

    for (i = 0; i < ctab->nNumberOfAtoms; i++)
    {

        if (NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ))
        {
            if (!err)
            {
                MOLFILE_ERR_SET( err, 2, "Cannot read atom block line" );
            }
            break;
        }
        remove_one_lf( line );
        if (line[MOLFILEMAXLINELEN])
        {
            MOLFILE_ERR_SET( err, 0, "Too long atom block line" );
        }
        if (err)
        {
            if (!strcmp( line, SDF_END_OF_DATA ))
            {
                err = -abs( err );
                break;
            }
            continue; /* bypass the rest of the Atom block */
        }
        if (NULL != ctab->szCoord)
        {
            mystrncpy( ctab->szCoord[i], p, 31 ); /* original coordinates */
        }

        if (NULL != ctab->MolAtom)
        {
            if (0 > mol_read_datum( &ctab->MolAtom[i].fX, 10, MOL_DOUBLE_DATA, &p )
                || 0 > mol_read_datum( &ctab->MolAtom[i].fY, 10, MOL_DOUBLE_DATA, &p )
                || 0 > mol_read_datum( &ctab->MolAtom[i].fZ, 10, MOL_DOUBLE_DATA, &p )
                || 0 > mol_read_datum( NULL, /* undescribed in article*/    1, MOL_JUMP_TO_RIGHT, &p )
                || 0 == mol_read_datum( &ctab->MolAtom[i].szAtomSymbol, 3, MOL_STRING_DATA, &p ) /* was sizeof(ctab->MolAtom[0].szAtomSymbol)-1 */
                || 0 > mol_read_datum( &ctab->MolAtom[i].cMassDifference, 2, MOL_SHORT_INT_DATA, &p )
                || 0 > mol_read_datum( &ctab->MolAtom[i].cCharge, 3, MOL_CHAR_INT_DATA, &p )
                || 0 > mol_read_datum( &ctab->MolAtom[i].cStereoParity, 3, MOL_CHAR_INT_DATA, &p )
#if ( MOL_QUERY == MOL_PRESENT )
                || 0 > mol_read_datum( &ctab->MolAtom[i].cH_countPlus1, 3, MOL_CHAR_INT_DATA, &p )
                || 0 > mol_read_datum( &ctab->MolAtom[i].cStereoCare, 3, MOL_CHAR_INT_DATA, &p )
#else
                || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p )
                || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p )
#endif
                || 0 > mol_read_datum( &ctab->MolAtom[i].cValence, 3, MOL_CHAR_INT_DATA, &p ))
            {

                err = 4;
                MOLFILE_ERR_SET( err, 4, "Cannot interpret atom block line:" );
                RemoveNonPrintable( line );
                AddMOLfileError( pStrErr, line );
                if (!strcmp( line, SDF_END_OF_DATA ))
                {
                    err = -abs( err );
                    break;
                }
                continue; /* can't interpret a first half of atom block line */
            }
            if (2 == strlen( ctab->MolAtom[i].szAtomSymbol ) && isupper( UCINT ctab->MolAtom[i].szAtomSymbol[1] ))
                ctab->MolAtom[i].szAtomSymbol[1] = (char) tolower( UCINT ctab->MolAtom[i].szAtomSymbol[1] ); /* 5-4-99 DCh*/

            if (( chg = (S_SHORT) ctab->MolAtom[i].cCharge ) < 0 || chg >= (int) ( sizeof( charge_val ) / sizeof( charge_val[0] ) ))
            {
                /* ctab->MolAtom[i].cCharge = 0; */ /* error; ignore for now */
                ctab->MolAtom[i].cCharge = (S_CHAR) ( 4 - chg ); /*  allow greater charges to accommodate NCI structures. 8-20-2002 */
                ctab->MolAtom[i].cRadical = 0;
            }
            else
                if ('R' == ( chg = charge_val[chg] ))
                {
                    ctab->MolAtom[i].cCharge = 0;
                    ctab->MolAtom[i].cRadical = RADICAL_DOUBLET;
                }
                else
                {
                    ctab->MolAtom[i].cCharge = (S_CHAR) chg; /* actual charge value */
                    ctab->MolAtom[i].cRadical = 0;
                }
            if (ctab->MolAtom[i].cMassDifference)
            {
                /* e_ReadMOL.c specific */
                ctab->MolAtom[i].cMassDifference += ISOTOPIC_SHIFT_FLAG;
            }

            if (
#if ( MOL_CPSS == MOL_PRESENT )
                   0 > mol_read_datum( &ctab->MolAtom[i].cH0_designator, 3, MOL_CHAR_INT_DATA, &p )
                || 0 > mol_read_datum( &ctab->MolAtom[i].cReactionComponentType, 3, MOL_CHAR_INT_DATA, &p )
                || 0 > mol_read_datum( &ctab->MolAtom[i].cReactionComponentNumber, 3, MOL_CHAR_INT_DATA, &p )
#else
                   0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p )
                || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p )
                || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p )
#endif
#if ( MOL_REACT == MOL_PRESENT )
                || 0 > mol_read_datum( &ctab->MolAtom[i].nAtomAtomMappingNumber, 3, MOL_SHORT_INT_DATA, &p )
                || 0 > mol_read_datum( &ctab->MolAtom[i].cReactionComponentType, 3, MOL_CHAR_INT_DATA, &p )
#else
                || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p )
                || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p )
#endif
#if ( MOL_REACT == MOL_PRESENT || MOL_QUERY == MOL_PRESENT )
                || 0 > mol_read_datum( &ctab->MolAtom[i].cExactChargeFlag, 3, MOL_CHAR_INT_DATA, &p )
#else
                || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p )
#endif
            )
            {
                err = 5; /* can't interpret a second half of atom block line */
                MOLFILE_ERR_SET( err, 5, "Cannot interpret atom block line:" );
                RemoveNonPrintable( line );
                AddMOLfileError( pStrErr, line );
                if (!strcmp( line, SDF_END_OF_DATA ))
                {
                    err = -abs( err );
                    break;
                }
                continue;
            }
        }
    }
/* err_fin: */

    return err;
}

/****************************************************************************/
int read_bonds_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr )
{
    char *p;
    char line[MOLFILEINPLINELEN];
    const int line_len = sizeof( line );
    S_SHORT i;

    /*
      if ( NULL == ctab->MolBond ){
          err = 1;
          goto err_fin;    // internal error: memory has not been allocated for MolBond structure
      }
    */

    for (i = 0; i < ctab->nNumberOfBonds; i++)
    {

        if (NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ))
        {
            if (!err)
            {
                MOLFILE_ERR_SET( err, 2, "Cannot read bond block line" );
            }
            break;
        }
        remove_one_lf( line );
        if (line[MOLFILEMAXLINELEN])
        {
            err = err ? err : 3;             /* too long input file line */
        }
        if (err)
        {
            if (!strcmp( line, SDF_END_OF_DATA ))
            {
                err = -abs( err );
                break;
            }
            continue;
        }

        if (ctab->MolBond)
        {
            if (0 > mol_read_datum( &ctab->MolBond[i].nAtomNo1, 3, MOL_SHORT_INT_DATA, &p )
                || 0 > mol_read_datum( &ctab->MolBond[i].nAtomNo2, 3, MOL_SHORT_INT_DATA, &p )
                || 0 > mol_read_datum( &ctab->MolBond[i].cBondType, 3, MOL_CHAR_INT_DATA, &p )
                || 0 > mol_read_datum( &ctab->MolBond[i].cBondStereo, 3, MOL_CHAR_INT_DATA, &p )
#if ( MOL_QUERY == MOL_PRESENT )
                || 0 > mol_read_datum( &ctab->MolBond[i].cBondTopology, 3, MOL_CHAR_INT_DATA, &p ) /* ring/chain */
#else
                || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p )
#endif
#if ( MOL_REACT == MOL_PRESENT )
                || 0 > mol_read_datum( &ctab->MolBond[i].cReactingCenterStatus, 3, MOL_CHAR_INT_DATA, &p )
#else
                || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p )
#endif
            )
            {
                if (!err)
                {
                    /* can't interpret bonds block line */
                    MOLFILE_ERR_SET( err, 4, "Cannot interpret bond block line:" );
                    RemoveNonPrintable( line );
                    AddMOLfileError( pStrErr, line );
                }
                if (!strcmp( line, SDF_END_OF_DATA ))
                {
                    err = -abs( err );
                    break;
                }
            }
        }
    }
/* err_fin: */

    return err;
}


/****************************************************************************/
int read_stext_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr )
{
    /* just pass by all stext enties without attemp to interpret */
    char *p;
    char line[MOLFILEINPLINELEN];
    const int line_len = sizeof( line );
    S_SHORT i;

    for (i = 0; i < 2 * ctab->nNumberOfStextEntries; i++)
    {
        p = inchi_fgetsLf(line, line_len, inp); /* djb-rwth: addressing LLVM warning */
        if (NULL == p)
        {
            if (!err)
            {
                MOLFILE_ERR_FIN( err, 2, err_fin, "Cannot read STEXT block line" );
            }
            break;
            /* can't read the input file line */
        }
        /*
        remove_one_lf( line );
        if ( line[MOLFILEMAXLINELEN] ){
            MOLFILE_ERR_SET (err, 2, "Warning: Too long STEXT block line");
            // too long input file line
        }
        */
    }

err_fin:

    return err;
}


/****************************************************************************/
int read_properties_block( MOL_CTAB* ctab,
                           MOL_HEADER_BLOCK *pHdr,
                           FILE *inp,
                           int err,
                           char *pStrErr )
{
    enum { MULTI_LINE_MODE_NO_MODE, MULTI_LINE_MODE_ISIS_ALIAS };
    char *p;
    char line[MOLFILEINPLINELEN];
    const int line_len = sizeof( line );
    int   nMultiLineMode = MULTI_LINE_MODE_NO_MODE, nAtomNumber = 0;
    S_SHORT i, j;
    char  charM[2];
    char  szBlank[3];
    char  szType[4] = {'\0'}; /* djb-rwth: adding zero termination */
    S_SHORT  skip_lines = 0;
    S_SHORT  num_entries;
    S_SHORT  num_atoms = ctab->nNumberOfAtoms;

    int  charge_encountered = 0;
    int  radical_encountered = 0;
    int  isotope_encountered = 0;
    /*
      if ( NULL == ctab->MolAtom ){
          err = 1;
          goto err_fin;    internal error: memory has not been allocated for MolAtom structure
      }
     */
    for (i = 0; ctab->csCurrentCtabVersion[0] ? 1 : ( i < ctab->nNumberOfPropertyLines ); i++)
    {
        /* the last line should be M END */
        /* ctab->csCurrentCtabVersion[0] == 0:
        exactly ctab->nNumberOfPropertyLines lines including M END */
        /* ctab->csCurrentCtabVersion[0] != 0:
        read until M END line was encountered */
        if (NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ))
        {
            if (!err)
            {
                MOLFILE_ERR_SET( err, 2, "Cannot read properties block line" );
            }
            goto err_fin;
        }
        remove_one_lf( line );
        if (line[MOLFILEMAXLINELEN])
        {
            MOLFILE_ERR_SET( err, 3, "Too long properties block line" );
            continue;
        }
        if (skip_lines > 0)
        {
            skip_lines--;
            continue;
        }
        /* alias. */
        if (nMultiLineMode == MULTI_LINE_MODE_ISIS_ALIAS && nAtomNumber)
        {
            int  len;
            nMultiLineMode = MULTI_LINE_MODE_NO_MODE;
            if (0 >= ( len = normalize_name( p ) ))
            {
                nAtomNumber = 0;
                continue;
            }
            if (0 < len && len < (int) ( sizeof( ctab->MolAtom->szAtomSymbol ) ))
            {
                int  nCharge, nRad;
                MOL_ATOM*  MolAtom = ctab->MolAtom + nAtomNumber - 1;
                /* ctab->MolAtom[nAtomNumber-1].cAtomAliasedFlag = 1; */
                /*  extract radicals & charges */
                extract_ChargeRadical( p, &nRad, &nCharge );
                /*  Aliased atom cannot have charge, radical & mass difference */
                /*  in the atom table or "M  CHG", "M  RAD", "M  ISO" */
                /* if ( nCharge ) */
                MolAtom->cCharge = (S_CHAR) nCharge;
                /* if ( nRad ) */
                MolAtom->cRadical = (char) nRad;

                if (1 == len && 'D' == p[0])
                {
                    /*  H isotope */
                    p[0] = 'H';
                    MolAtom->cMassDifference = ( 1 + ISOTOPIC_SHIFT_FLAG );
                }
                else
                {
                    if (1 == len && 'T' == p[0])
                    {
                        /*  H isotope */
                        p[0] = 'H';
                        MolAtom->cMassDifference = ( 2 + ISOTOPIC_SHIFT_FLAG );
                    }
                    else
                    {
                        MolAtom->cMassDifference = 0;
                    }
                }
                if (strlen( p ) < sizeof( ctab->MolAtom[0].szAtomSymbol ))
                {
                    strcpy( MolAtom->szAtomSymbol, p );
                }
                else
                {
                    strcpy( MolAtom->szAtomSymbol, "???" );
                }
                MolAtom->cAtomAliasedFlag++;
            }
            skip_lines = 0;
            nAtomNumber = 0;
            continue;
        }

        if (1 != mol_read_datum( charM, sizeof( charM ) - 1, MOL_STRING_DATA, &p )
            || 0 != mol_read_datum( szBlank, sizeof( szBlank ) - 1, MOL_STRING_DATA, &p ) /* must contain 0 bytes */
            || 0 >= mol_read_datum( szType, sizeof( szType ) - 1, MOL_STRING_DATA, &p ) /* must contain 3 bytes */
        )
        {
            if (!strcmp( line, SDF_END_OF_DATA ))
            {
                err = err ? -abs( err ) : -4;
                break;
            }
            continue;  /* ignore because cannot recognize */
        }
        if (charM[0] == 'V')
        {
            skip_lines = 0;   /* ISIS/Desktop Atom Value: one-line property */
            continue;
        }
        if (charM[0] == 'G')
        {
            skip_lines = 1;   /* ISIS/Desktop Group abbreviation: two-line property */
            continue;
        }
        if (charM[0] == 'A')
        {
            if (NULL != ctab->MolAtom &&
                 0 < ( nAtomNumber = (int) strtol( szType, NULL, 10 ) ) &&
                 nAtomNumber <= ctab->nNumberOfAtoms)
            {
/* Atom Alias [ISIS/Desktop] two-line property */
                nMultiLineMode = MULTI_LINE_MODE_ISIS_ALIAS;
                continue;
            }
            else
            {
                nAtomNumber = 0;
                skip_lines = 1;
                continue;
            }
        }
        if (charM[0] == 'S' && !strcmp( szType, "SKP" ))
        {  /* skip lines */
            if (0 >= mol_read_datum( &skip_lines, 3, MOL_SHORT_INT_DATA, &p ))
            {
                skip_lines = 0;
            }
            continue;
        }
        if (charM[0] != 'M')
        {/* cannot recognize a line */
            continue;
        }
        if (!strcmp( szType, "REG" ))
        {
            int len;
            p = p + strspn( p, " " );
            len = strcspn( p, " " );
            len = inchi_min( len, MOL_MAX_VALUE_LEN );
            mol_read_datum( &pHdr->lInternalRegistryNumber, len, MOL_LONG_INT_DATA, &p );
            continue;
        }

        if (!strcmp( szType, "END" ))
        {
            if (ctab->csCurrentCtabVersion[0])
            {
                break;  /* end of property lines */
            }
            continue;
        }

        if (NULL == ctab->MolAtom)
        {
            continue; /* ignore because the user requested to bypass all this stuff */
        }

        /*----------------------------------- charge: Generic */
        if (!strcmp( szType, "CHG" ) &&
             0 < mol_read_datum( &num_entries, 3, MOL_SHORT_INT_DATA, &p ) &&
             1 <= num_entries && num_entries <= 8)
        {
            S_SHORT atoms[8];
            S_SHORT charges[8];
            if (!charge_encountered && !radical_encountered)
            {
                /* first charge or radical record clears all Atom Block */
                /* entered charge and radical data to zeroes            */
                charge_encountered = -1;
            }
            for (j = 0; j < num_entries; j++)
            {
                if (0 > mol_read_datum( &atoms[j], 0, MOL_SHORT_INT_DATA, &p ) ||
                     0 > mol_read_datum( &charges[j], 0, MOL_SHORT_INT_DATA, &p ) ||
                     atoms[j] <= 0 || atoms[j] > num_atoms ||
                     charges[j] < -15 || charges[j]  > 15)
                {
                    goto charge_error;
                }
            }
            if (charge_encountered == -1)
            {
                for (j = 0; j < num_atoms; j++)
                {
                    if (!ctab->MolAtom[j].cAtomAliasedFlag) /* do not clear aliased atoms.*/
                    {
                        ctab->MolAtom[j].cCharge = ctab->MolAtom[j].cRadical = '\0';
                    }
                }
                charge_encountered = 1;
            }
            for (j = 0; j < num_entries; j++)
            {
                if (!ctab->MolAtom[atoms[j] - 1].cAtomAliasedFlag) /* do not change aliased atoms.*/
                    ctab->MolAtom[atoms[j] - 1].cCharge = (S_CHAR) charges[j];
            }
            continue;
        charge_error:
            MOLFILE_ERR_SET( err, 0, "Charge not recognized:" );
            RemoveNonPrintable( line );
            AddMOLfileError( pStrErr, line );
            continue; /* ignore for now */
        }
        /*-------------------------------------- radical: Generic */
        if (!strcmp( szType, "RAD" ) &&
             0 < mol_read_datum( &num_entries, 3, MOL_SHORT_INT_DATA, &p ) &&
             1 <= num_entries && num_entries <= 8)
        {
            S_SHORT atoms[8];
            S_SHORT radicals[8];
            if (!charge_encountered && !radical_encountered)
            {
                /* first charge or radical record clears all Atom Block */
                /* entered charge and radical data to zeroes            */
                radical_encountered = -1;
            }
            for (j = 0; j < num_entries; j++)
            {
                if (0 > mol_read_datum( &atoms[j], 0, MOL_SHORT_INT_DATA, &p ) ||
                     0 > mol_read_datum( &radicals[j], 0, MOL_SHORT_INT_DATA, &p ) ||
                     atoms[j] <= 0 || atoms[j] > num_atoms ||
                     radicals[j] < 0 || radicals[j]  > 3)
                {
                    goto radical_error;
                }
            }
            if (radical_encountered == -1)
            {
                for (j = 0; j < num_atoms; j++)
                {
                    if (!ctab->MolAtom[j].cAtomAliasedFlag)  /* do not clear aliased atoms. 5-3-99 DCh */
                        ctab->MolAtom[j].cCharge = ctab->MolAtom[j].cRadical = '\0';
                }
                radical_encountered = 1;
            }
            for (j = 0; j < num_entries; j++)
            {
                if (!ctab->MolAtom[atoms[j] - 1].cAtomAliasedFlag)
                { /* do not change aliased atoms. 5-3-99 DCh */
                    ctab->MolAtom[atoms[j] - 1].cRadical = (S_CHAR) radicals[j];
                }
            }
            continue;
        radical_error:
            MOLFILE_ERR_SET( err, 0, "Radical not recognized:" );
            RemoveNonPrintable( line );
            AddMOLfileError( pStrErr, line );
            continue; /* ignore error for now */
        }
        /*-------------------------------------- isotope: Generic */
        if (!strcmp( szType, "ISO" ) &&
             0 < mol_read_datum( &num_entries, 3, MOL_SHORT_INT_DATA, &p ) &&
             1 <= num_entries && num_entries <= 8)
        {
            S_SHORT atoms[8];
            S_SHORT iso_mass[8]; /*  contains istotope mass number, not difference. 7-14-00 DCh. */
            if (!isotope_encountered)
            {
                /* first charge or radical record clears all Atom Block */
                /* entered charge and radical data to zeroes            */
                isotope_encountered = -1;
            }
            for (j = 0; j < num_entries; j++)
            {
                if (0 > mol_read_datum( &atoms[j], 0, MOL_SHORT_INT_DATA, &p ) ||
                     0 > mol_read_datum( &iso_mass[j], 0, MOL_SHORT_INT_DATA, &p ) ||
                     atoms[j] <= 0 || atoms[j] > num_atoms
                     /*|| iso_mass[j] < -18 || iso_mass[j]  > 12*/)
                {
                    /* goto isotope_error; */
                    atoms[j] = -1; /*  flag error */
                    MOLFILE_ERR_SET( err, 0, "Isotopic data not recognized:" );
                    RemoveNonPrintable( line );
                    AddMOLfileError( pStrErr, line );
                    continue; /* ignore isotopic error for now */
                }
            }
            if (isotope_encountered == -1)
            {
                for (j = 0; j < num_atoms; j++)
                {
                    /*if ( !ctab->MolAtom[j].cAtomAliasedFlag )*/  /* clear even aliased atoms */
                    ctab->MolAtom[j].cMassDifference = 0;
                }
                isotope_encountered = 1;
            }
            for (j = 0; j < num_entries; j++)
            {
                if (atoms[j] <= 0)
                {
                    continue; /* ignore isotopic error for now */
                }
                if (1 /* !ctab->MolAtom[atoms[j]-1].cAtomAliasedFlag */)
                {
                    char *at = ctab->MolAtom[atoms[j] - 1].szAtomSymbol;
                    if (at[1] || (at[0] != 'D' && at[0] != 'T')) /* djb-rwth: addressing LLVM warning */
                    {
                        /*  D & T cannot have ISO */
                        /*  need atomic weight to calculate isotope difference. 7-14-00 DCh. */
                        /*^^^ Check added 5-10-2008 - IPl */
                        if (iso_mass[j] > 0)
                            /* According to MDL specification, p.12, only a positive
                            integer is allowed. And yes, there appeared some MOL/SD
                            files contaning here a negative value. This manifested
                            in mismatch in InChI_MAIN vs. cInChI-1/stdinchi-1 results.
                            */
                            ctab->MolAtom[atoms[j] - 1].cMassDifference = iso_mass[j]; /* mass, not difference */
                    }
                }
            }
            continue;
        }

        /*-------------------------------------- this demo must now reject polymers */
        if (!strcmp( szType, "STY" ) ||
             !strcmp( szType, "SAL" ))
        {
            if (!err)
            {
                MOLFILE_ERR_SET( err, 2, "This demo program can not treat polymers" );
            }
            goto err_fin;
        }

    }

err_fin:

    return err;
}


/****************************************************************************/
MOL_DATA* delete_mol_data( MOL_DATA* mol_data )
{
    if (mol_data)
    {
        if (mol_data->ctab.MolAtom)
        {
            inchi_free( mol_data->ctab.MolAtom );
        }
        if (mol_data->ctab.MolBond)
        {
            inchi_free( mol_data->ctab.MolBond );
        }
        if (mol_data->ctab.szCoord)
        {
            inchi_free( mol_data->ctab.szCoord );
        }
        inchi_free( mol_data );
        mol_data = NULL;
    }

    return mol_data;
}


/****************************************************************************
  Comletely ingnore STEXT block, queries, and 3D features
****************************************************************************/
MOL_DATA* read_mol_file( FILE* inp,
                         MOL_HEADER_BLOCK *OnlyHeaderBlock,
                         MOL_CTAB *OnlyCtab,
                         int bGetOrigCoord,
                         int *err,
                         char *pStrErr )
{
    MOL_DATA* mol_data = NULL;
    int       ret = 0, prev_ret, bEndOfData = 0;
    int       bReadAll = ( OnlyHeaderBlock == NULL );
    MOL_CTAB  ctab, *pCtab = NULL;
    MOL_HEADER_BLOCK *pHdr = NULL;

    *err = 0;
    if (bReadAll)
    {
        if (NULL == ( mol_data = (MOL_DATA*) inchi_calloc( 1, sizeof( MOL_DATA ) ) ))
        {
            ret = 1; /* can't allocate mol_data structure */
            AddMOLfileError( pStrErr, "Out of RAM" );
            goto err_fin;
        }
        pHdr = &mol_data->hdr;
        pCtab = &mol_data->ctab;
    }
    else
    {
        pHdr = OnlyHeaderBlock;
        pCtab = OnlyCtab ? OnlyCtab : &ctab;
        memset( pHdr, 0, sizeof( MOL_HEADER_BLOCK ) ); /* djb-rwth: memset_s C11/Annex K variant? */
        memset( pCtab, 0, sizeof( MOL_CTAB ) ); /* djb-rwth: memset_s C11/Annex K variant? */
    }
    pCtab->MolBond = NULL;
    pCtab->MolAtom = NULL;
    pCtab->szCoord = NULL;

    if (0 != ( ret = mol_read_hdr( pHdr, inp, pStrErr ) ))
    {
        ret += 10;
        goto err_fin; /*  most probably end of file */
    }
    if (0 != ( ret = mol_read_counts_line( pCtab, inp, pStrErr ) ))
    {
        ret += 20;
        goto err_fin;
    }

    if (bReadAll)
    {
        if (NULL == ( mol_data->ctab.MolAtom = (MOL_ATOM*) inchi_calloc( inchi_max( mol_data->ctab.nNumberOfAtoms, 1 ), sizeof( MOL_ATOM ) ) ))
        {
            ret = 2; /* can't allocate MolAtom structure */
            MOLFILE_ERR_FIN( ret, 2, err_fin, "Out of RAM" );
        }
        if (bGetOrigCoord &&
             NULL == ( mol_data->ctab.szCoord = (MOL_COORD*) inchi_calloc( inchi_max( mol_data->ctab.nNumberOfAtoms, 1 ), sizeof( MOL_COORD ) ) ))
        {
            ret = 2; /* can't allocate MolAtom structure */
            MOLFILE_ERR_FIN( ret, 2, err_fin, "Out of RAM" );
        }
    }
    if (0 != ( ret = read_atom_block( pCtab, inp, ret, pStrErr ) ))
    {
        if (ret < 0)
        {
            ret = -ret;
            bEndOfData = 1;
        }
        ret += 30;
        /* goto err_fin; */
    }

    if (bReadAll && ret < 30)
    {
        if (!bEndOfData && NULL == ( mol_data->ctab.MolBond = (MOL_BONDS*) inchi_calloc( inchi_max( mol_data->ctab.nNumberOfBonds, 1 ), sizeof( MOL_BONDS ) ) ))
        {
            ret = 3; /* can't allocate MolBond structure */
            MOLFILE_ERR_FIN( ret, 3, err_fin, "Out of RAM" );
        }
    }
    prev_ret = ret;
    if (!bEndOfData && 0 != ( ret = read_bonds_block( pCtab, inp, ret, pStrErr ) ))
    {
        if (ret < 0)
        {
            ret = -ret;
            bEndOfData = 1;
        }
        ret = prev_ret ? prev_ret : ret + 40;
    }
    prev_ret = ret;
    if (!bEndOfData && 0 != ( ret = read_stext_block( pCtab, inp, ret, pStrErr ) ))
    {
        ret = prev_ret ? prev_ret : ret + 50;
    }
    prev_ret = ret;
    if (!bEndOfData && 0 != ( ret = read_properties_block( pCtab, pHdr, inp, ret, pStrErr ) ))
    {
        if (ret < 0)
        {
            ret = -ret;
            bEndOfData = 1;
        }
        ret = prev_ret ? prev_ret : ret + 60;
    }

err_fin:
    *err = bEndOfData ? -ret : ret;
    if (bReadAll)
    {
        if (ret)
        {
            mol_data = delete_mol_data( mol_data ); /* delete all results */
        }
        return mol_data;
    }
    else
    {
        if (ret)
        {
            return NULL;
        }
        else
        {
            return (MOL_DATA*) OnlyHeaderBlock;
        }
    }
}


/****************************************************************************/
static const char sdf_data_hdr_name[] = "NAME";
static const char sdf_data_hdr_comm[] = "COMMENT";
enum {
    SDF_START, SDF_DATA_HEADER, SDF_DATA_HEADER_NAME
    , SDF_DATA_HEADER_COMMENT, SDF_DATA_HEADER_CAS
    , SDF_DATA_HEADER_USER, SDF_DATA_LINE
    , SDF_END_OF_DATA_ITEM, SDF_EMPTY_LINE, SDF_END_OF_DATA_BLOCK
};


/****************************************************************************/
long extract_cas_rn( char *line )
{
    int i, j;
    i = line[0] == '-' ? 1 : 0;
    for (j = i; line[i]; i++)
    {
        if (isdigit( UCINT line[i] ))
        {
            line[j++] = line[i];
        }
        else
            if (line[i] != '-')
            {
                break;
            }
    }
    line[j] = '\0';
    return strtol( line, NULL, 10 );
}
/********** static ********************************************************/
int identify_sdf_label( char* inp_line, const char *pSdfLabel )
{
    char line[MOLFILEMAXLINELEN];
    char *p, *q;
    int  i, j, len;
    if (( p = strchr( inp_line, '<' ) ) &&
        ( q = strchr( p, '>' ) ) &&
        ( len = q - p - 1 ) > 0 && len < ( int )sizeof( line ))
    {
        memcpy( line, p + 1, len );
        line[len] = '\0';
        for (i = 0; isspace( UCINT line[i] ); i++) /* djb-rwth: ui_rr */
        {
            ;
        }
        for (j = len - 1; j >= i && isspace( UCINT line[i] ); j--)
        {
            ;
        }
        len = j - i + 1;
        p = line + i;
        if (pSdfLabel && pSdfLabel[0] && len == (int) strlen( pSdfLabel ) && !e_inchi_memicmp( p, pSdfLabel, len ))
        {
            return SDF_DATA_HEADER_USER;
        }
        if (len == sizeof( sdf_data_hdr_name ) - 1 && !e_inchi_memicmp( p, sdf_data_hdr_name, len ))
        {
            return SDF_DATA_HEADER_NAME;
        }
        if (len == sizeof( sdf_data_hdr_comm ) - 1 && !e_inchi_memicmp( p, sdf_data_hdr_comm, len ))
        {
            return SDF_DATA_HEADER_COMMENT;
        }
        if (!e_inchi_memicmp( p, "CAS", 3 ))
        {
            return SDF_DATA_HEADER_CAS;
        }
    }

    return SDF_DATA_HEADER;
}


/****************************************************************************/
int bypass_sdf_data_items( FILE* inp, long *cas_reg_no, char* comment,
                           int lcomment, char *name, int lname, int prev_err,
                           const char *pSdfLabel, char *pSdfValue, char *pStrErr )
{
    char line[MOLFILEINPLINELEN];
    const int line_len = sizeof( line );
    int   err = 0;
    int   current_state = SDF_START;
    int   n_blank_lines = 0;
    int   n_lines = 0;
    char* p = NULL;
    int   bNeedsName = name && lname > 0 && !name[0];
    int   bNeedsComm = comment && lcomment > 0 && !comment[0];
    int   bNeedsUser = pSdfLabel && pSdfLabel[0] && pSdfValue;
    int   bNeedsCASrn = 0;
    int   bCASrnIsUser = 0;

    if (cas_reg_no != NULL)
    {
        bNeedsCASrn = 1;
        *cas_reg_no = 0;
        bCASrnIsUser = ( bNeedsUser && !e_inchi_memicmp( pSdfLabel, "CAS", 3 ) );
    }

    while (err == 0 &&
            current_state != SDF_END_OF_DATA_BLOCK &&
            NULL != ( p = inchi_fgetsLf( line, line_len, inp ) ))
    {

        if (!n_lines && !memcmp( line, "M  END", 6 ))
        {
            continue; /*  allow subtle errors */
        }
        n_lines++;

        remove_trailing_spaces( line );
        if (line[MOLFILEMAXLINELEN])
        {
            if (current_state != SDF_DATA_HEADER &&
                 current_state != SDF_DATA_LINE   &&
                 current_state != SDF_DATA_HEADER_NAME &&
                 current_state != SDF_DATA_HEADER_USER &&
                 current_state != SDF_DATA_HEADER_COMMENT)
            {
                line[MOLFILEMAXLINELEN] = '\0';
                if (!prev_err)
                {
                    MOLFILE_ERR_SET( err, 0, "Too long SData line truncated" );
                }
            }
            else
            {
                /* allow long lines in SDF data. 9-29-00 DCh */
                line[MOLFILEMAXLINELEN] = '\0';
            }
        }

        n_blank_lines += ( *line == '\0' );

        switch (current_state)
        {

            case SDF_START:
            case SDF_END_OF_DATA_ITEM:
            case SDF_EMPTY_LINE:              /* Added 9-25-97 DCh */
                if (0 == strcmp( line, SDF_END_OF_DATA ))
                {
                    current_state = SDF_END_OF_DATA_BLOCK;
                }
                else
                {
                    if ('>' == *line)
                    {
                        current_state = ( bNeedsName || bNeedsComm || bNeedsCASrn || bNeedsUser ) ? identify_sdf_label( line, pSdfLabel ) : SDF_DATA_HEADER;
                    }
                    else
                    {
                        if (*line == '\0')
                        {
                            /* Added 9-25-97 DCh */
                            /* Relax the strictness: Allow more than 1 empty line. */
                            current_state = SDF_EMPTY_LINE;
                        }
                        else
                        {
                            if (!prev_err)
                            {
                                MOLFILE_ERR_SET( err, 3, "Unexpected SData header line:" );
                                RemoveNonPrintable( line );
                                AddMOLfileError( pStrErr, line );
                                /* unexpected contents of data header line */
                            }
                            else
                            {
                                err = 3;
                            }
                        }
                    }
                }
                break;

            case SDF_DATA_HEADER_NAME:
                if (bNeedsName && 0 < normalize_name( line ))
                {
                    bNeedsName = 0;
                    mystrncpy( name, line, lname );
                }
                goto got_data_line;

            case SDF_DATA_HEADER_COMMENT:
                if (bNeedsComm && 0 < normalize_name( line ))
                {
                    bNeedsComm = 0;
                    mystrncpy( comment, line, lcomment );
                }
                goto got_data_line;

            case SDF_DATA_HEADER_USER:
                if (bNeedsUser && 0 < normalize_name( line ))
                {
                    bNeedsUser = 0;
                    mystrncpy( pSdfValue, line, MAX_SDF_VALUE + 1 );
                    if (bCASrnIsUser && bNeedsCASrn)
                    {
                        *cas_reg_no = extract_cas_rn( line );
                        bNeedsCASrn = ( 0 == *cas_reg_no );
                    }
                }
                goto got_data_line;

            case SDF_DATA_HEADER_CAS:
                if (bNeedsCASrn && 0 < normalize_name( line ))
                {
                    *cas_reg_no = extract_cas_rn( line );
                    bNeedsCASrn = ( 0 == *cas_reg_no );
                }
                goto got_data_line;

            case SDF_DATA_HEADER:
            case SDF_DATA_LINE:
            got_data_line:
                current_state = *line ? SDF_DATA_LINE : SDF_END_OF_DATA_ITEM;
                break;
        }
    }

    if (0 == err && SDF_END_OF_DATA_BLOCK != current_state && NULL == p)
    {
        ; /* err = 4; */ /* unexpected end of file: missing $$$$ */
    }
    else
    {
        if (err && ( n_blank_lines == n_lines && *line == '\0' ))
        {
            err = 5; /* empty lines -- do not know when this can happen */
        }
    }

    if (err && err != 5 && current_state != SDF_END_OF_DATA_BLOCK && p)
    {
        /*  bypass up to $$$$ */
        while (( p = inchi_fgetsLf( line, line_len, inp ) ) && memcmp( line, SDF_END_OF_DATA, 4 ))
        {
            ;
        }
        if (p)
        {
            err = 9; /*  bypassed to $$$$; non-fatal */
            AddMOLfileError( pStrErr, "Bypassing to next structure" );
        }
    }

    return err;
}


/****************************************************************************/
MOL_DATA* read_sdfile_segment( FILE* inp,
                               MOL_HEADER_BLOCK *OnlyHeaderBlock,
                               MOL_CTAB *OnlyCtab,
                               int bGetOrigCoord,
                               char *pname, int lname,
                               long *Id,
                               const char *pSdfLabel,
                               char *pSdfValue,
                               int *err,
                               char *pStrErr )
{
    MOL_DATA* mol_data = read_mol_file( inp, OnlyHeaderBlock, OnlyCtab, bGetOrigCoord, err, pStrErr );
    int       err_bypass_sdf = 0;

    if (pname && lname)
    {
        pname[0] = '\0';
    }
    if (Id)
    {
        *Id = 0L;  /* ignore for now */
    }
    /* if ( mol_data && !*err ) { */
    if (*err < 0)
    {
        *err = -*err; /* end of data encountered */
    }
    else
    {
        err_bypass_sdf = bypass_sdf_data_items( inp, Id, NULL, 0, pname, lname, *err, pSdfLabel, pSdfValue, pStrErr );
        if (err_bypass_sdf)
        {
            *err = err_bypass_sdf; /* important to continue to the next good structure */
        }
    }
    /* } */

    return mol_data;
}


/****************************************************************************/
int CopyMOLfile( FILE *inp_file,
                 long fPtrStart,
                 long fPtrEnd,
                 FILE *prb_file,
                 long lNumb )
{
    char line[MOLFILEINPLINELEN], *p;
    long fPtr;
    int  ret = 1;
    char szNumber[32];

    if (inp_file && prb_file && fPtrStart >= 0L &&
         fPtrEnd > fPtrStart &&
         0 == fseek( inp_file, fPtrStart, SEEK_SET ))
    {

        while ( fPtrEnd > ( fPtr = ftell( inp_file ) )
                && fPtr >= 0L
                && inchi_fgetsLf( line, sizeof( line ) - 1, inp_file ) )
        {
            line[sizeof( line ) - 1] = '\0'; /*  unnecessary extra precaution */
            if (fPtr == fPtrStart && lNumb)
            {
                int len;
                LtrimRtrim( line, &len );
                len = sprintf( szNumber, "#%ld%s", lNumb, len ? "/" : "" );
                mystrncpy( line + len, line, sizeof( line ) - len - 1 );
                memcpy( line, szNumber, len );
            }
            if (!strchr( line, '\n' ))
            {
                p = line + strlen( line );
                p[0] = '\n';
                p[1] = '\0';
            }
            fputs( line, prb_file );
        }
        ret = fseek( inp_file, fPtrEnd, SEEK_SET );
    }

    return ret;
}
