Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions INCHI-1-DOC/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
- Molecular Inorganics preprocessing now follows the revised bond-disconnection decision tree, preserving stereo-indicated metal bonds, linked-metal structures, and qualifying metal-containing chelate ring systems while applying the Group 1/2 and terminal-metal exceptions.
- The generated InChI for qualifying chelate complexes may intentionally change from a disconnected representation to a connected representation. In particular, the Ti bis-flavonoid regression case is now represented as connected `C30H18O14Ti` instead of disconnected `2C15H10O7.Ti` components.

### Fixed

- GHI #252: Explicit H/D/T atoms used as polymer crossing-bond end groups are now preserved during terminal-H processing when the `Polymers` option is enabled. This allows valid SRU/CRU structures with explicit hydrogen leaving groups to be processed instead of being rejected as unsupported. The retained H/D/T atom is represented explicitly as a numbered atom in the `/c` layer rather than being converted to an implicit hydrogen.

## v1.07.5 2026-02-17

### Changed
Expand Down
38 changes: 20 additions & 18 deletions INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll_a2.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ int Normalization_step( CANON_GLOBALS *pCG,
inp_ATOM *inp_at,
INP_ATOM_DATA *out_norm_data[2],
int num_inp_at,
const unsigned char *keep_explicit_HDT,
struct tagInchiTime *ulMaxTime,
INCHI_MODE *pbTautFlags,
INCHI_MODE *pbTautFlagsDone,
Expand Down Expand Up @@ -340,37 +341,37 @@ int NormOneStructureINChI( CANON_GLOBALS *pCG,
/* assign values to sd->num_components[] */

/* djb-rwth: MYREALLOC2( PINChI2, PINChI_Aux2, pINChI2[iINChI], pINChI_Aux2[iINChI], sd->num_components[iINChI], cur_prep_inp_data->num_components, k ) has been replaced and the whole block rewritten to address memory leaks and reading from freed memory locations */
do
{
if( (sd->num_components[iINChI]) <= (cur_prep_inp_data->num_components) )
do
{
if( (sd->num_components[iINChI]) <= (cur_prep_inp_data->num_components) )
{
PINChI2* newPTR1 = (PINChI2 *)inchi_calloc( (long long)cur_prep_inp_data->num_components + 1, sizeof(PINChI2) );
PINChI_Aux2* newPTR2 = (PINChI_Aux2*)inchi_calloc( (long long)cur_prep_inp_data->num_components + 1, sizeof(PINChI_Aux2) );
if ( newPTR1 && newPTR2 )
{
{
if (pINChI2[iINChI] && sd->num_components[iINChI] > 0)
memcpy( newPTR1, pINChI2[iINChI], (sd->num_components[iINChI]) * sizeof(PINChI2) );
if (pINChI_Aux2[iINChI] && sd->num_components[iINChI] > 0)
memcpy( newPTR2, pINChI_Aux2[iINChI], (sd->num_components[iINChI]) * sizeof(PINChI_Aux2) );
if (pINChI2[iINChI])
if (pINChI2[iINChI])
inchi_free(pINChI2[iINChI]);
if (pINChI_Aux2[iINChI])
inchi_free(pINChI_Aux2[iINChI]);
pINChI2[iINChI] = newPTR1;
pINChI_Aux2[iINChI] = newPTR2;
sd->num_components[iINChI] = cur_prep_inp_data->num_components;
k = 0;
}
else
{
inchi_free(newPTR1);
inchi_free(newPTR2);
}
else
{
inchi_free(newPTR1);
inchi_free(newPTR2);
k = 1;
}
}
else
{
k = 0;
}
}
else
{
k = 0;
}
} while (0);

Expand Down Expand Up @@ -790,7 +791,7 @@ int NormOneComponentINChI( CANON_GLOBALS *pCG,
{
num_at = Normalization_step(pCG, ic,
cur_INChI, cur_INChI_Aux,
inp_cur_data->at, inp_norm_data, inp_cur_data->num_at,
inp_cur_data->at, inp_norm_data, inp_cur_data->num_at, inp_cur_data->keep_explicit_HDT,
pulTEnd, &bTautFlags, &bTautFlagsDone, cti);

SetConnectedComponentNumber(inp_cur_data->at, inp_cur_data->num_at, i + 1); /* normalization alters structure component number */
Expand Down Expand Up @@ -1142,6 +1143,7 @@ int Normalization_step( CANON_GLOBALS *pCG,
inp_ATOM *inp_at,
INP_ATOM_DATA *out_norm_data[2],
int num_inp_at,
const unsigned char *keep_explicit_HDT,
struct tagInchiTime *ulMaxTime,
INCHI_MODE *pbTautFlags,
INCHI_MODE *pbTautFlagsDone,
Expand Down Expand Up @@ -1182,7 +1184,7 @@ int Normalization_step( CANON_GLOBALS *pCG,

memset( z->s, 0, sizeof( z->s ) ); /* djb-rwth: memset_s C11/Annex K variant? */

if (pBCN)
if (pBCN)
memset( pBCN, 0, sizeof( pBCN[0] ) ); /* djb-rwth: memset_s C11/Annex K variant? */

memset( t_group_info, 0, sizeof( *t_group_info ) ); /* djb-rwth: memset_s C11/Annex K variant? */
Expand Down Expand Up @@ -1251,7 +1253,7 @@ int Normalization_step( CANON_GLOBALS *pCG,
}
else
{
z->num_at_tg = z->num_atoms = remove_terminal_HDT( num_inp_at, z->out_at, z->fix_termhchrg );
z->num_at_tg = z->num_atoms = remove_terminal_HDT( num_inp_at, z->out_at, z->fix_termhchrg, keep_explicit_HDT);

z->num_deleted_H = num_inp_at - z->num_atoms;
t_group_info->tni.nNumRemovedExplicitH = z->num_deleted_H;
Expand Down
3 changes: 2 additions & 1 deletion INCHI-1-SRC/INCHI_BASE/src/ichimake.c
Original file line number Diff line number Diff line change
Expand Up @@ -3712,6 +3712,7 @@ int Create_INChI(CANON_GLOBALS* pCG,
inp_ATOM* inp_at,
INP_ATOM_DATA* out_norm_data[2],
int num_inp_at,
const unsigned char* keep_explicit_HDT,
INCHI_MODE nUserMode,
INCHI_MODE* pbTautFlags,
INCHI_MODE* pbTautFlagsDone,
Expand Down Expand Up @@ -3893,7 +3894,7 @@ int Create_INChI(CANON_GLOBALS* pCG,
}
else
{
num_at_tg = num_atoms = remove_terminal_HDT(num_inp_at, out_at, bFixTermHChrg);
num_at_tg = num_atoms = remove_terminal_HDT(num_inp_at, out_at, bFixTermHChrg, keep_explicit_HDT);
num_removed_H = num_inp_at - num_atoms;
t_group_info->tni.nNumRemovedExplicitH = num_removed_H;
add_DT_to_num_H(num_atoms, out_at);
Expand Down
6 changes: 3 additions & 3 deletions INCHI-1-SRC/INCHI_BASE/src/ichinorm.c
Original file line number Diff line number Diff line change
Expand Up @@ -5805,7 +5805,7 @@ int OAD_Edit_Underivatize( struct tagINCHI_CLOCK *ic,
/*set_R2C_el_numbers( );*/

#ifndef UNDERIV_ADD_EXPLICIT_H
num_atoms = remove_terminal_HDT( orig_inp_data->num_inp_atoms, at, 1 );
num_atoms = remove_terminal_HDT( orig_inp_data->num_inp_atoms, at, 1, NULL );
/*^^^^^ always accomodate accomodate FIX_TERM_H_CHRG_BUG - IPl, July 2008*/
num_removed_H = orig_inp_data->num_inp_atoms - num_atoms;
orig_inp_data->num_inp_atoms = num_atoms;
Expand Down Expand Up @@ -5833,7 +5833,7 @@ int OAD_Edit_Underivatize( struct tagINCHI_CLOCK *ic,
}

#ifdef UNDERIV_ADD_EXPLICIT_H
num_atoms = remove_terminal_HDT(inp_cur_data[i_component].num_at, inp_cur_data[i_component].at, 1);
num_atoms = remove_terminal_HDT(inp_cur_data[i_component].num_at, inp_cur_data[i_component].at, 1, NULL);
inp_cur_data[i_component].num_removed_H = inp_cur_data[i_component].num_at - num_atoms;
inp_cur_data[i_component].num_at = num_atoms;
#endif
Expand Down Expand Up @@ -7184,7 +7184,7 @@ int Ring2Chain( struct tagINCHI_CLOCK *ic,

/*set_R2C_el_numbers( );*/

num_atoms = remove_terminal_HDT( orig_inp_data->num_inp_atoms, at, 1 );
num_atoms = remove_terminal_HDT( orig_inp_data->num_inp_atoms, at, 1, NULL );
/*^^^^^ always accomodate accomodate FIX_TERM_H_CHRG_BUG - IPl, July 2008*/
orig_inp_data->num_inp_atoms = num_atoms;

Expand Down
3 changes: 2 additions & 1 deletion INCHI-1-SRC/INCHI_BASE/src/ichinorm.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ int MakeIsotopicHGroup( inp_ATOM *at,

int remove_terminal_HDT( int num_atoms,
inp_ATOM *at,
int bFixTermHChrg );
int bFixTermHChrg,
const unsigned char* keep_explicit_HDT);
int RemoveExcessiveImplicitH( int num_atoms,
int num_removed_H,
inp_ATOM *at );
Expand Down
1 change: 1 addition & 0 deletions INCHI-1-SRC/INCHI_BASE/src/ichirvr1.c
Original file line number Diff line number Diff line change
Expand Up @@ -5332,6 +5332,7 @@ int MakeOneInChIOutOfStrFromINChI( struct tagCANON_GLOBALS *pCG,
inp_cur_data->at,
inp_norm_data,
inp_cur_data->num_at + inp_cur_data->num_removed_H,
NULL,
ip->nMode,
&bTautFlags,
&bTautFlagsDone,
Expand Down
2 changes: 2 additions & 0 deletions INCHI-1-SRC/INCHI_BASE/src/inpdef.h
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,7 @@ typedef struct tagInfoAtomData
*
* @param at Array of input atoms
* @param at_fixed_bonds Array of input atoms with fixed bonds (tautomeric case, added or removed H)
* @param keep_explicit_HDT Component-local polymer crossing-bond endpoint mask used to prevent explicit H/D/T removal
* @param num_at Number of atoms
* @param num_removed_H Number of removed hydrogens
* @param num_bonds Number of bonds
Expand All @@ -588,6 +589,7 @@ typedef struct tagInputAtomData
{
inp_ATOM *at;
inp_ATOM *at_fixed_bonds; /* tautomeric case, added or removed H */
unsigned char *keep_explicit_HDT;
int num_at;
int num_removed_H;
int num_bonds;
Expand Down
18 changes: 12 additions & 6 deletions INCHI-1-SRC/INCHI_BASE/src/mol2atom.c
Original file line number Diff line number Diff line change
Expand Up @@ -1183,6 +1183,14 @@ void FreeInpAtomData(INP_ATOM_DATA *inp_at_data)
{
FreeInpAtom(&inp_at_data->at);
FreeInpAtom(&inp_at_data->at_fixed_bonds);

/*@nnuk*/
if (inp_at_data->keep_explicit_HDT)
{
inchi_free(inp_at_data->keep_explicit_HDT);
inp_at_data->keep_explicit_HDT = NULL;
}

memset(inp_at_data, 0, sizeof(*inp_at_data)); /* djb-rwth: memset_s C11/Annex K variant? */
}

Expand Down Expand Up @@ -1557,12 +1565,10 @@ int SetExtOrigAtDataByMolfileExtInput(MOL_FMT_DATA *mfdata,
ia2 = mfdata->ctab.bonds[ib - 1].atnum2;
unitk->blist[2 * m] = ia1;
unitk->blist[2 * m + 1] = ia2;
if (!strcmp(mfdata->ctab.atoms[ia1 - 1].symbol, "H") ||
!strcmp(mfdata->ctab.atoms[ia2 - 1].symbol, "H"))
{
TREAT_ERR(err, 9002, "Hydrogen as polymer end group is not supported");
Comment thread
nnuk marked this conversation as resolved.
goto exit_function;
}

/**
*@nnuk: GHI#252 addressed and redundant logic removed
*/
}
}
else
Expand Down
20 changes: 3 additions & 17 deletions INCHI-1-SRC/INCHI_BASE/src/runichi.c
Original file line number Diff line number Diff line change
Expand Up @@ -327,26 +327,13 @@ int ProcessOneStructure(INCHI_CLOCK* ic,
goto exit_function;
}

/*// Debugging: Print atom structure after disconnections
for (int i = 0; i < orig_inp_data->num_inp_atoms; i++)
{
printf("Atom %d: Element %s, Valence: %d, Charge: %d, Num of Implicit H: %d, Neighbors: ",
i + 1, orig_inp_data->at[i].elname, orig_inp_data->at[i].valence, orig_inp_data->at[i].charge, orig_inp_data->at[i].num_H);
for (int j = 0; j < orig_inp_data->at[i].valence; j++)
{
printf("%d ", orig_inp_data->at[i].neighbor[j] + 1);
}
printf("\n");
}*/

/* Preserve SDF output in Molecular Inorganics mode if requested */
ret1 = OrigAtData_SaveMolfile(orig_inp_data, sd, ip, num_inp, out_file);
if (ret1)
{
goto exit_function;
}

/*printf("Molecular inorganics preprocessing completed successfully.\n");*/
nRet1 = CreateOneStructureINChI(pCG, ic, sd, ip, szTitle,
pINChI, pINChI_Aux, INCHI_BAS,
inp_file, log_file, out_file, prb_file,
Expand All @@ -361,7 +348,6 @@ int ProcessOneStructure(INCHI_CLOCK* ic,
(sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) &&
(ip->bTautFlags & TG_FLAG_RECONNECT_COORD) )
{
/*printf("Generating reconnected InChI due to retained bonds.\n");*/

nRet1 = CreateOneStructureINChI(pCG, ic, sd, ip, szTitle,
pINChI, pINChI_Aux, INCHI_REC,
Expand Down Expand Up @@ -391,10 +377,9 @@ int ProcessOneStructure(INCHI_CLOCK* ic,
/*
* InChI is already generated via the Molecular Inorganics-specific path.
* Skip the standard structure-generation path to prevent duplicate
* CreateOneStructureINChI() calls, which previously caused redundant
* allocations and AddressSanitizer-reported memory leaks.
* CreateOneStructureINChI() calls and redundant allocations.
*
* pOrigStruct was populated above from the native input, so the shared
* pOrigStruct and OrigStruct were populated above from the native input, so the shared
* cleanup path (SortAndPrintINChI + OrigStruct_Free) emits the AuxInfo
* reversibility layers (/rA, /rB, /rC), including atom coordinates, and
* releases it.
Expand Down Expand Up @@ -1978,6 +1963,7 @@ int CreateOneComponentINChI(CANON_GLOBALS* pCG,
cur_INChI, cur_INChI_Aux,
orig_inp_data/* not used */,
inp_cur_data->at, inp_norm_data, inp_cur_data->num_at,
inp_cur_data->keep_explicit_HDT,
ip->nMode,
&bTautFlags, &bTautFlagsDone,
pulTEnd, NULL, sd->pStrErrStruct);
Expand Down
Loading