Coverage Report

Created: 2026-05-24 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/inchi/INCHI-1-SRC/INCHI_BASE/src/mol_fmt3.c
Line
Count
Source
1
/*
2
 * International Chemical Identifier (InChI)
3
 * Version 1
4
 * Software version 1.07
5
 * April 30, 2024
6
 *
7
 * MIT License
8
 *
9
 * Copyright (c) 2024 IUPAC and InChI Trust
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a copy
12
 * of this software and associated documentation files (the "Software"), to deal
13
 * in the Software without restriction, including without limitation the rights
14
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
 * copies of the Software, and to permit persons to whom the Software is
16
 * furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in all
19
 * copies or substantial portions of the Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
 * SOFTWARE.
28
 *
29
 * The InChI library and programs are free software developed under the
30
 * auspices of the International Union of Pure and Applied Chemistry (IUPAC).
31
 * Originally developed at NIST.
32
 * Modifications and additions by IUPAC and the InChI Trust.
33
 * Some portions of code were developed/changed by external contributors
34
 * (either contractor or volunteer) which are listed in the file
35
 * 'External-contributors' included in this distribution.
36
 *
37
 * info@inchi-trust.org
38
 *
39
 */
40
41
#include <stdlib.h>
42
#include <ctype.h>
43
#include <string.h>
44
#include <math.h>
45
#include <float.h>
46
#include <limits.h>
47
48
#include "mode.h"
49
#include "mol_fmt.h"
50
51
#include "ichierr.h"
52
#include "util.h"
53
#include "ichi_io.h"
54
55
#include "bcf_s.h"
56
#include "stb_sprintf.h"
57
58
/*
59
    Molfile V3000 related procedures
60
61
*/
62
63
static int get_actual_atom_number(int index, int n, int *orig, int *fin);
64
65
/****************************************************************************
66
 Init V3000 reader
67
****************************************************************************/
68
int MolfileV3000Init(MOL_FMT_CTAB *ctab,
69
                     char *pStrErr)
70
0
{
71
0
    int ret = 0;
72
0
    int i;
73
74
    /* STAR ATOMS */
75
0
    ctab->v3000->n_star_atoms = 0;
76
0
    ctab->v3000->n_non_star_atoms = 0;
77
78
0
    if (ctab->n_atoms)
79
0
    {
80
0
        ctab->v3000->atom_index_orig = (int *)inchi_calloc(ctab->n_atoms, sizeof(int));
81
0
        ctab->v3000->atom_index_fin = (int *)inchi_calloc(ctab->n_atoms, sizeof(int));
82
0
        if (ctab->v3000->atom_index_orig && ctab->v3000->atom_index_fin) /* djb-rwth: fixing a NULL pointer dereference */
83
0
        {
84
0
            for (i = 0; i < ctab->n_atoms; i++) /* protective */
85
0
            {
86
0
                ctab->v3000->atom_index_orig[i] = -1;
87
0
                ctab->v3000->atom_index_fin[i] = -1;
88
0
            }
89
0
        }
90
0
    }
91
0
    else
92
0
    {
93
0
        ctab->v3000->atom_index_orig = NULL;
94
0
        ctab->v3000->atom_index_fin = NULL;
95
0
    }
96
97
    /* HAPTIC BONDS */
98
0
    ctab->v3000->n_haptic_bonds = 0;
99
0
    ctab->v3000->haptic_bonds = (NUM_LISTS *)inchi_calloc(1, sizeof(NUM_LISTS));
100
0
    if (!ctab->v3000->haptic_bonds)
101
0
    {
102
0
        AddErrorMessage(pStrErr, "Out of RAM");
103
0
        return -1;
104
0
    }
105
0
    ret = NumLists_Alloc(ctab->v3000->haptic_bonds, 8);
106
0
    if (ret < 0)
107
0
    {
108
0
        ctab->v3000->haptic_bonds = NULL;
109
0
        AddErrorMessage(pStrErr, "Out of RAM");
110
0
        return -1;
111
0
    }
112
113
    /* STEABS */
114
0
    ctab->v3000->n_steabs = 0;
115
0
    ctab->v3000->steabs = (NUM_LISTS *)inchi_calloc(1, sizeof(NUM_LISTS));
116
0
    if (!ctab->v3000->steabs)
117
0
    {
118
0
        AddErrorMessage(pStrErr, "Out of RAM");
119
0
        return -1;
120
0
    }
121
0
    ret = NumLists_Alloc(ctab->v3000->steabs, 1);
122
0
    if (ret < 0)
123
0
    {
124
0
        ctab->v3000->steabs = NULL;
125
0
        AddErrorMessage(pStrErr, "Out of RAM");
126
0
        return -1;
127
0
    }
128
    /* STEREL */
129
0
    ctab->v3000->n_sterel = 0;
130
0
    ctab->v3000->sterel = (NUM_LISTS *)inchi_calloc(1, sizeof(NUM_LISTS));
131
0
    if (!ctab->v3000->sterel)
132
0
    {
133
0
        AddErrorMessage(pStrErr, "Out of RAM");
134
0
        return -1;
135
0
    }
136
0
    ret = NumLists_Alloc(ctab->v3000->sterel, 4);
137
0
    if (ret < 0)
138
0
    {
139
0
        ctab->v3000->sterel = NULL;
140
0
        AddErrorMessage(pStrErr, "Out of RAM");
141
0
        return -1;
142
0
    }
143
    /* STERAC */
144
0
    ctab->v3000->n_sterac = 0;
145
0
    ctab->v3000->sterac = (NUM_LISTS *)inchi_calloc(1, sizeof(NUM_LISTS));
146
0
    if (!ctab->v3000->sterac)
147
0
    {
148
0
        AddErrorMessage(pStrErr, "Out of RAM");
149
0
        return -1;
150
0
    }
151
0
    ret = NumLists_Alloc(ctab->v3000->sterac, 4);
152
0
    if (ret < 0)
153
0
    {
154
0
        ctab->v3000->sterac = NULL;
155
0
        AddErrorMessage(pStrErr, "Out of RAM");
156
0
        return -1;
157
0
    }
158
159
0
    return ret;
160
0
}
161
162
/****************************************************************************
163
 Delete V3000 reader data
164
****************************************************************************/
165
int DeleteMolfileV3000Info(MOL_FMT_v3000 *v3000)
166
0
{
167
0
    if (v3000)
168
0
    {
169
170
0
        if (v3000->atom_index_orig)
171
0
        {
172
0
            inchi_free(v3000->atom_index_orig);
173
0
        }
174
175
0
        if (v3000->atom_index_fin)
176
0
        {
177
0
            inchi_free(v3000->atom_index_fin);
178
0
        }
179
180
0
        if (v3000->haptic_bonds)
181
0
        {
182
0
            NumLists_Free(v3000->haptic_bonds);
183
0
            free(v3000->haptic_bonds);
184
0
        }
185
186
0
        if (v3000->steabs)
187
0
        {
188
0
            NumLists_Free(v3000->steabs);
189
0
            free(v3000->steabs);
190
0
        }
191
192
0
        if (v3000->sterel)
193
0
        {
194
0
            NumLists_Free(v3000->sterel);
195
0
            free(v3000->sterel);
196
0
        }
197
198
0
        if (v3000->sterac)
199
0
        {
200
0
            NumLists_Free(v3000->sterac);
201
0
            free(v3000->sterac);
202
0
        }
203
204
0
        inchi_free(v3000);
205
0
        v3000 = NULL;
206
0
    }
207
208
0
    return 0;
209
0
}
210
211
/****************************************************************************
212
    Extended version of inchi_fgetsLf which is able of reading
213
    concatenated lines (ending with '-') of V3000 Molfile.
214
    Also removes "M  V30 " prefix" and normalizes the rest of string
215
****************************************************************************/
216
char *inchi_fgetsLf_V3000(char *line, INCHI_IOSTREAM *inp_stream)
217
0
{
218
0
    char *p = NULL;
219
0
    int len = 0;
220
221
0
    p = inchi_fgetsLf(line, MOL_FMT_V3000_INPLINELEN, inp_stream);
222
0
    if (!p)
223
0
    {
224
0
        return NULL;
225
0
    }
226
227
0
    len = (int)strlen(p);
228
0
    if (len < 7)
229
0
    {
230
0
        return NULL;
231
0
    }
232
233
0
    if (strncmp(p, "M  V30 ", 7))
234
0
    {
235
0
        return NULL;
236
0
    }
237
238
0
    p += 7;
239
0
    len = normalize_string(p); /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
240
241
0
    return p;
242
0
}
243
244
/****************************************************************************
245
    Read V3000 field.
246
247
    It is MolfileReadField updated for V3000.
248
    It considers right whitespace as stop sign,
249
    no predefined len is used.
250
251
    Returns -1 on error otherwise number of bytes read.
252
253
    NB: ASSUMES THAT STRING HAS BEEN NORMALIZED with normalize_string()
254
255
    TODO: treat strings with spaces in double quotes
256
****************************************************************************/
257
int MolfileV3000ReadField(void *data,
258
                          int data_type,
259
                          char **line_ptr)
260
0
{
261
0
    int nread = 0;
262
0
    char field[MOL_FMT_V3000_MAXFIELDLEN];
263
0
    const int max_field_len = sizeof(field);
264
0
    long ldata = 0L;
265
0
    double ddata = 0.0;
266
0
    char *p_end;
267
268
0
    memset(field, 0, max_field_len); /* djb-rwth: memset_s C11/Annex K variant? */
269
270
0
    nread = read_upto_delim(line_ptr, field, max_field_len, " \t\n\v\f\r");
271
272
0
    switch (data_type)
273
0
    {
274
0
    case MOL_FMT_STRING_DATA:
275
0
    {
276
0
        if (nread && (nread <= ATOM_EL_LEN)) /* djb-rwth: fixing GHI #133 -- updated 28/09/2025 */
277
0
        {
278
0
            mystrncpy((char *)data, field, nread + 1);
279
0
        }
280
0
        else
281
0
        {
282
0
            ((char *)data)[0] = '\0';
283
0
        }
284
0
    }
285
0
    break;
286
287
0
    case MOL_FMT_CHAR_INT_DATA:
288
0
    case MOL_FMT_SHORT_INT_DATA:
289
0
    case MOL_FMT_LONG_INT_DATA:
290
0
    case MOL_FMT_INT_DATA:
291
0
    {
292
        /* assume that field ends at first non-digit */
293
0
        ldata = strtol(field, &p_end, 10);
294
295
0
        if (p_end == field)
296
0
        {
297
0
            nread = 0;
298
0
        }
299
300
0
        if (data_type == MOL_FMT_LONG_INT_DATA)
301
0
        {
302
0
            if (LONG_MIN < ldata && ldata < LONG_MAX)
303
0
            {
304
0
                *(long *)data = (long)ldata;
305
0
            }
306
0
            else
307
0
            {
308
0
                *(long *)data = 0L;
309
0
                nread = -1;
310
0
            }
311
0
        }
312
0
        else if (data_type == MOL_FMT_INT_DATA)
313
0
        {
314
0
            if (INT_MIN <= ldata && ldata <= INT_MAX) /* djb-rwth: addressing coverity ID #499496/499553 -- ldata check seems to be necessary */
315
0
            {
316
0
                *(int *)data = (int)ldata;
317
0
            }
318
0
            else
319
0
            {
320
0
                *(int *)data = (int)0;
321
0
                nread = -1;
322
0
            }
323
0
        }
324
325
0
        else if (data_type == MOL_FMT_CHAR_INT_DATA)
326
0
        {
327
0
            if (SCHAR_MIN <= ldata && ldata <= SCHAR_MAX)
328
0
            {
329
0
                *(S_CHAR *)data = (S_CHAR)ldata;
330
0
            }
331
0
            else
332
0
            {
333
0
                *(S_CHAR *)data = (S_CHAR)0;
334
0
                nread = -1;
335
0
            }
336
0
        }
337
338
0
        else if (data_type == MOL_FMT_SHORT_INT_DATA)
339
0
        {
340
0
            if (SHRT_MIN <= ldata && ldata <= SHRT_MAX)
341
0
            {
342
0
                *(S_SHORT *)data = (S_SHORT)ldata;
343
0
            }
344
0
            else
345
0
            {
346
0
                *(S_SHORT *)data = (S_SHORT)0;
347
0
                nread = -1;
348
0
            }
349
0
        }
350
0
        else
351
0
        {
352
0
            nread = -1;
353
0
        }
354
0
    }
355
0
    break; /* INT's */
356
357
0
    case MOL_FMT_DOUBLE_DATA:
358
0
    case MOL_FMT_FLOAT_DATA:
359
0
    {
360
        /* assume that field ends at first non-digit */
361
0
        ddata = strtod(field, &p_end);
362
363
0
        if (p_end == field)
364
0
        {
365
0
            nread = 0;
366
0
        }
367
368
0
        if (data_type == MOL_FMT_DOUBLE_DATA)
369
0
        {
370
0
            if (ddata != HUGE_VAL && /*ldata*/ ddata != -HUGE_VAL)
371
0
            {
372
0
                *(double *)data = ddata;
373
0
            }
374
0
            else
375
0
            {
376
0
                *(double *)data = 0.0;
377
0
                nread = -1;
378
0
            }
379
0
        }
380
0
        else if (data_type == MOL_FMT_FLOAT_DATA)
381
0
        {
382
0
            if (fabs(ddata) <= (double)FLT_MIN)
383
0
            {
384
0
                *(float *)data = 0.0;
385
0
            }
386
0
            else if (fabs(ddata) >= (double)FLT_MAX)
387
0
            {
388
0
                *(float *)data = 0.0;
389
0
                nread = -1;
390
0
            }
391
0
        }
392
0
        else
393
0
        {
394
0
            *(float *)data = (float)ddata; /* djb-rwth: addressing coverity ID #499519 -- probably never reached */
395
0
        }
396
0
    }
397
0
    break; /* REAL's */
398
399
0
    default:
400
0
        nread = -1;
401
0
    }
402
403
0
    return nread;
404
0
}
405
406
/****************************************************************************
407
    Read V3000 keyword.
408
    TODO: treat strings with spaces in double quotes
409
****************************************************************************/
410
int MolfileV3000ReadKeyword(char *key,
411
                            char **line_ptr)
412
0
{
413
0
    int nread = 0;
414
0
    char field[MOL_FMT_V3000_MAXFIELDLEN];
415
0
    const int max_field_len = sizeof(field);
416
417
0
    memset(field, 0, max_field_len); /* djb-rwth: memset_s C11/Annex K variant? */
418
419
0
    nread = read_upto_delim(line_ptr, field, max_field_len, "= \t\n\v\f\r");
420
421
0
    if (nread)
422
0
    {
423
0
        mystrncpy(key, field, nread + 1);
424
        /* consume '=' sign if present */
425
0
        if (*line_ptr)
426
0
        {
427
0
            if (*line_ptr[0] == '=')
428
0
            {
429
0
                *line_ptr = *line_ptr + 1;
430
0
            }
431
0
        }
432
0
    }
433
0
    else
434
0
    {
435
0
        key[0] = '\0';
436
0
    }
437
438
0
    return nread;
439
0
}
440
441
/****************************************************************************
442
 Read V3000 head of CTab
443
****************************************************************************/
444
int MolfileV3000ReadCTABBeginAndCountsLine(MOL_FMT_CTAB *ctab,
445
                                           INCHI_IOSTREAM *inp_file,
446
                                           char *pStrErr)
447
0
{
448
0
    char field[MOL_FMT_V3000_MAXFIELDLEN];
449
0
    int err = 0, len; /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
450
0
    int failed = 0;
451
452
0
    int nc;
453
0
    char *p = NULL, *line = NULL;
454
0
    INCHI_IOSTREAM tmpin;
455
0
    INCHI_IOS_STRING *pin = &tmpin.s;
456
0
    inchi_ios_init(&tmpin, INCHI_IOS_TYPE_STRING, NULL);
457
458
0
    field[0] = '\0'; /* djb-rwth: adding zero termination */
459
460
    /* Check for proper start */
461
462
    /*p = inchi_fgetsLf_V3000( line, inp_file );*/
463
0
    inchi_strbuf_reset(pin);
464
0
    nc = get_V3000_input_line_to_strbuf(pin, inp_file);
465
0
    if (nc < 1)
466
0
    {
467
0
        p = NULL;
468
0
    }
469
0
    else
470
0
    {
471
0
        p = line = pin->pStr;
472
0
    }
473
0
    if (!p || strcmp(p, "BEGIN CTAB"))
474
0
    {
475
0
        TREAT_ERR_AND_FIN(err, 1, err_fin, "Error: No V3000 CTab start marker");
476
0
    }
477
0
    remove_one_lf(line);
478
479
    /* Reset all previosly read data from quasi-counts line            */
480
    /* (which contains only single meaningful value, 'V3000' marker    */
481
0
    ctab->n_atoms = -1;
482
0
    ctab->n_bonds = -1;
483
0
    ctab->chiral_flag = -1;
484
0
    ctab->n_stext_entries = -1;
485
    /* Relax stricthness of V3000 conformance: */
486
    /* Do not check if '999' supplied, just use this. */
487
0
    ctab->n_property_lines = 999;
488
489
    /* Read counts line */
490
    /*p = inchi_fgetsLf_V3000( line, inp_file );*/
491
0
    inchi_strbuf_reset(pin);
492
0
    nc = get_V3000_input_line_to_strbuf(pin, inp_file);
493
0
    if (nc < 1)
494
0
    {
495
0
        p = NULL;
496
0
    }
497
0
    else
498
0
    {
499
0
        p = line = pin->pStr;
500
0
    }
501
0
    if (!p)
502
0
    {
503
0
        TREAT_ERR_AND_FIN(err, 1, err_fin, "Cannot read V3000 counts line");
504
0
    }
505
0
    remove_one_lf(line);
506
507
    /* Parse counts line */
508
0
    len = MolfileV3000ReadField(field, MOL_FMT_STRING_DATA, &p); /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
509
0
    if (strcmp(field, "COUNTS"))
510
0
    {
511
0
        TREAT_ERR_AND_FIN(err, 1, err_fin, "Cannot read V3000 counts line");
512
0
    }
513
0
    failed = 0;
514
0
    if (0 > MolfileV3000ReadField(&ctab->n_atoms, MOL_FMT_INT_DATA, &p))
515
0
    {
516
0
        failed = 2;
517
0
    }
518
0
    else if (0 > MolfileV3000ReadField(&ctab->n_bonds, MOL_FMT_INT_DATA, &p))
519
0
    {
520
0
        failed = 1;
521
0
    }
522
0
    else if (0 > MolfileV3000ReadField(&ctab->v3000->n_sgroups, MOL_FMT_INT_DATA, &p))
523
0
    {
524
0
        failed = 1;
525
0
    }
526
0
    else if (0 > MolfileV3000ReadField(&ctab->v3000->n_3d_constraints, MOL_FMT_INT_DATA, &p))
527
0
    {
528
0
        failed = 1;
529
0
    }
530
0
    else if (0 > MolfileV3000ReadField(&ctab->chiral_flag, MOL_FMT_CHAR_INT_DATA, &p))
531
0
    {
532
0
        failed = 1;
533
0
    }
534
535
0
    if (failed)
536
0
    {
537
0
        err = 3;
538
0
        if (failed == 2)
539
0
        {
540
0
            TREAT_ERR(err, 3, "Number of atoms too large. V3000 counts line:");
541
0
        }
542
0
        else
543
0
        {
544
            /* too long input file line or other value min-max range mismatch */
545
0
            TREAT_ERR(err, 3, "Cannot interpret V3000 counts line:");
546
0
        }
547
0
        dotify_non_printable_chars(line);
548
0
        AddErrorMessage(pStrErr, line);
549
0
        goto err_fin;
550
0
    }
551
552
0
err_fin:
553
0
    inchi_strbuf_close(pin);
554
555
0
    return err;
556
0
}
557
558
/****************************************************************************
559
 Read V3000 SGroup
560
****************************************************************************/
561
int MolfileV3000ReadSGroup(MOL_FMT_CTAB *ctab,
562
                           INCHI_IOSTREAM *inp_file,
563
                           int err,
564
                           char *pStrErr)
565
0
{
566
0
    int nc;
567
0
    char *p = NULL, *line = NULL;
568
0
    INCHI_IOSTREAM tmpin;
569
0
    INCHI_IOS_STRING *pin = &tmpin.s;
570
571
0
    inchi_ios_init(&tmpin, INCHI_IOS_TYPE_STRING, NULL);
572
    /*p = inchi_fgetsLf_V3000( line, inp_file );*/
573
574
0
    while (1)
575
0
    {
576
0
        nc = get_V3000_input_line_to_strbuf(pin, inp_file);
577
578
0
        if (nc < 1)
579
0
        {
580
0
            p = NULL;
581
0
        }
582
0
        else
583
0
        {
584
0
            p = line = pin->pStr;
585
0
            remove_one_lf(line);
586
0
        }
587
0
        if (p && !strcmp(p, "END SGROUP"))
588
0
        {
589
0
            inchi_ios_close(&tmpin); /* ricrogz: fixing memory leak */
590
0
            return 0;
591
0
        }
592
0
    }
593
594
    /* if (  !p  || strcmp(p, "END SGROUP") ) */
595
0
    {
596
0
        TREAT_ERR_AND_FIN(err, 1, err_fin, "Error: No V3000 SGroup end marker");
597
0
    }
598
599
0
err_fin:
600
601
0
    inchi_ios_close(&tmpin); /* ricrogz: fixing memory leak */
602
0
    return err;
603
0
}
604
605
/****************************************************************************
606
 Read V3000 3DBlock
607
****************************************************************************/
608
int MolfileV3000Read3DBlock(MOL_FMT_CTAB *ctab,
609
                            INCHI_IOSTREAM *inp_file,
610
                            int err,
611
                            char *pStrErr)
612
0
{
613
0
    int nc;
614
0
    char *p = NULL, *line = NULL;
615
0
    INCHI_IOSTREAM tmpin;
616
0
    INCHI_IOS_STRING *pin = &tmpin.s;
617
0
    inchi_ios_init(&tmpin, INCHI_IOS_TYPE_STRING, NULL);
618
    /*p = inchi_fgetsLf_V3000( line, inp_file );*/
619
620
0
    nc = get_V3000_input_line_to_strbuf(pin, inp_file);
621
622
0
    if (nc < 1)
623
0
    {
624
0
        p = NULL;
625
0
    }
626
0
    else
627
0
    {
628
0
        p = line = pin->pStr;
629
0
    }
630
0
    remove_one_lf(line);
631
632
0
    if (!p || strcmp(p, "END OBJ3D"))
633
0
    {
634
0
        TREAT_ERR_AND_FIN(err, 1, err_fin, "Error: No V3000 3DBlock end marker");
635
0
    }
636
0
    goto err_fin;
637
638
0
err_fin:
639
640
0
    inchi_ios_close(&tmpin); /* ricrogz: fixing memory leak */
641
0
    return err;
642
0
}
643
644
/****************************************************************************
645
 Read V3000 collections
646
****************************************************************************/
647
int MolfileV3000ReadCollections(MOL_FMT_CTAB *ctab,
648
                                INCHI_IOSTREAM *inp_file,
649
                                int err,
650
                                char *pStrErr)
651
0
{
652
0
    char field[MOL_FMT_V3000_MAXFIELDLEN];
653
0
    const int max_field_len = sizeof(field);
654
0
    int nread, len, n_coll = 0;
655
0
    int failed = 0;
656
0
    int nc;
657
0
    char *p = NULL, *line = NULL;
658
0
    INCHI_IOSTREAM tmpin;
659
0
    INCHI_IOS_STRING *pin = &tmpin.s;
660
661
0
    inchi_ios_init(&tmpin, INCHI_IOS_TYPE_STRING, NULL);
662
    /*p = inchi_fgetsLf_V3000( line, inp_file );*/
663
664
0
    nc = get_V3000_input_line_to_strbuf(pin, inp_file);
665
666
0
    if (nc < 1)
667
0
    {
668
0
        p = NULL;
669
0
    }
670
0
    else
671
0
    {
672
0
        p = line = pin->pStr;
673
0
    }
674
0
    remove_one_lf(line);
675
676
0
    while (p && strcmp(p, "END COLLECTION"))
677
0
    {
678
0
        int stereo_kind = MOL_FMT_V3000_STENON;
679
        /* stereo collection of interest */
680
0
        NUM_LISTS *ste_coll = NULL;
681
682
0
        nread = read_upto_delim(&p, field, max_field_len, "/");
683
0
        if (nread < 6)
684
0
        {
685
0
            failed = 1;
686
0
            break;
687
0
        }
688
0
        if (strcmp(field, "MDLV30"))
689
0
        {
690
0
            failed = 1;
691
0
            break;
692
0
        }
693
694
0
        nread = read_upto_delim(&p, field, max_field_len, "1234567890 \t\n\v\f\r"); /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
695
0
        if (!strcmp(field, "/STEABS"))
696
0
        {
697
0
            n_coll = 1;
698
0
            stereo_kind = MOL_FMT_V3000_STEABS;
699
0
            ste_coll = ctab->v3000->steabs;
700
0
        }
701
0
        else if (!strcmp(field, "/STEREL"))
702
0
        {
703
            /* get number of collection */
704
0
            if (0 > MolfileV3000ReadField(&n_coll, MOL_FMT_CHAR_INT_DATA, &p))
705
0
            {
706
0
                failed = 1;
707
0
                break;
708
0
            }
709
0
            stereo_kind = MOL_FMT_V3000_STEREL;
710
0
            ste_coll = ctab->v3000->sterel;
711
0
        }
712
0
        else if (!strcmp(field, "/STERAC"))
713
0
        {
714
            /* get number of collection */
715
0
            if (0 > MolfileV3000ReadField(&n_coll, MOL_FMT_CHAR_INT_DATA, &p))
716
0
            {
717
0
                failed = 1;
718
0
                break;
719
0
            }
720
0
            stereo_kind = MOL_FMT_V3000_STERAC;
721
0
            ste_coll = ctab->v3000->sterac;
722
0
        }
723
0
        else
724
0
        {
725
0
            ;
726
0
        }
727
728
0
        if (stereo_kind != MOL_FMT_V3000_STENON)
729
        /* currently skip non-stereo collections */
730
0
        {
731
            /* consume atoms= */
732
0
            if ((len = MolfileV3000ReadKeyword(field, &p) > 0)) /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
733
0
            {
734
0
                if (!strcmp(field, "ATOMS"))
735
0
                {
736
0
                    int res, *num_list = NULL;
737
738
0
                    if (0 > MolfileV3000ReadStereoCollection(ctab, &p, &num_list, pStrErr))
739
0
                    {
740
0
                        failed = 1;
741
0
                    }
742
0
                    else if (!num_list)
743
0
                    {
744
0
                        failed = 1;
745
0
                    }
746
0
                    else
747
0
                    {
748
0
                        int k, nnum;
749
0
                        num_list[0] = n_coll;
750
0
                        nnum = num_list[1];
751
0
                        for (k = 2; k < nnum; k++)
752
0
                        {
753
0
                            num_list[k] =
754
0
                                get_actual_atom_number(num_list[k],
755
0
                                                       ctab->v3000->n_non_star_atoms + ctab->v3000->n_star_atoms,
756
0
                                                       ctab->v3000->atom_index_orig,
757
0
                                                       ctab->v3000->atom_index_fin);
758
0
                        }
759
0
                        res = NumLists_Append(ste_coll, num_list);
760
0
                        if (res < 0)
761
0
                        {
762
0
                            failed = 1;
763
0
                        }
764
0
                        else
765
0
                        {
766
0
                            if (stereo_kind == MOL_FMT_V3000_STEABS)
767
0
                            {
768
0
                                ctab->v3000->n_steabs++;
769
0
                                ctab->v3000->n_collections++;
770
0
                            }
771
0
                            else if (stereo_kind == MOL_FMT_V3000_STEREL)
772
0
                            {
773
0
                                ctab->v3000->n_sterel++;
774
0
                                ctab->v3000->n_collections++;
775
0
                            }
776
0
                            else if (stereo_kind == MOL_FMT_V3000_STERAC)
777
0
                            {
778
0
                                ctab->v3000->n_sterac++;
779
0
                                ctab->v3000->n_collections++;
780
0
                            }
781
0
                        }
782
0
                    }
783
0
                }
784
0
            }
785
0
            else
786
0
            {
787
0
                failed = 1;
788
0
                break;
789
0
            }
790
0
        }
791
792
        /*next_line:*/
793
        /*p = inchi_fgetsLf_V3000( line, inp_file );*/
794
0
        inchi_strbuf_reset(pin);
795
796
0
        nc = get_V3000_input_line_to_strbuf(pin, inp_file);
797
798
0
        if (nc < 1)
799
0
        {
800
0
            p = NULL;
801
0
        }
802
0
        else
803
0
        {
804
0
            p = line = pin->pStr;
805
0
            remove_one_lf(line);
806
0
        }
807
0
    }
808
809
0
    if (failed)
810
0
    {
811
        /*p = inchi_fgetsLf_V3000( line, inp_file );*/
812
0
        inchi_strbuf_reset(pin);
813
0
        line = NULL; /* Reset line pointer since buffer was freed */
814
815
0
        nc = get_V3000_input_line_to_strbuf(pin, inp_file);
816
817
0
        if (nc < 1)
818
0
        {
819
0
            p = NULL;
820
0
        }
821
0
        else
822
0
        {
823
0
            p = line = pin->pStr;
824
0
            remove_one_lf(line);
825
0
        }
826
0
    }
827
828
0
    if (!p)
829
0
    {
830
0
        failed = 1;
831
0
    }
832
833
0
    if (failed)
834
0
    {
835
0
        err = 7;
836
0
        TREAT_ERR(err, 7, "Cannot interpret V3000 collection line(s)"); /* djb-rwth: addressing coverity ID #499531 -- TREAT_ERR properly used */
837
0
        if (line)
838
0
        {
839
0
            dotify_non_printable_chars(line);
840
0
            AddErrorMessage(pStrErr, line);
841
0
        }
842
0
        goto err_fin;
843
0
    }
844
845
    // /* Error: No V3000 Collection end marker */
846
    // if (ctab->v3000->n_steabs ||
847
    //     ctab->v3000->n_sterel ||
848
    //     ctab->v3000->n_sterac)
849
    // {
850
    //     AddErrorMessage(pStrErr, "V3000 enhanced stereo read/stored but ignored");
851
    // }
852
853
0
err_fin:
854
855
0
    inchi_ios_close(&tmpin); /* ricrogz: fixing memory leak */
856
0
    return err;
857
0
}
858
859
/****************************************************************************
860
 Read V3000 atoms
861
****************************************************************************/
862
int MolfileV3000ReadAtomsBlock(MOL_FMT_CTAB *ctab,
863
                               INCHI_IOSTREAM *inp_file,
864
                               int err,
865
                               char *pStrErr)
866
0
{
867
0
    int i;
868
    /* djb-rwth: removing redundant variables */
869
0
    char field[MOL_FMT_V3000_MAXFIELDLEN];
870
0
    int nc, failed = 0;
871
0
    char *p = NULL, *line = NULL;
872
0
    INCHI_IOSTREAM tmpin;
873
0
    INCHI_IOS_STRING *pin = &tmpin.s;
874
0
    inchi_ios_init(&tmpin, INCHI_IOS_TYPE_STRING, NULL);
875
876
    /* Check for proper start */
877
    /*p = inchi_fgetsLf_V3000( line, inp_file );*/
878
879
0
    nc = get_V3000_input_line_to_strbuf(pin, inp_file);
880
881
0
    if (nc < 1)
882
0
    {
883
0
        p = NULL;
884
0
    }
885
0
    else
886
0
    {
887
0
        p = line = pin->pStr;
888
0
    }
889
0
    if (!p || strcmp(p, "BEGIN ATOM"))
890
0
    {
891
0
        TREAT_ERR_AND_FIN(err, 1, err_fin, "Error: No V3000 Atom block start marker");
892
0
    }
893
0
    remove_one_lf(line);
894
895
0
    ctab->v3000->n_non_star_atoms = 0;
896
0
    for (i = 0; i < ctab->n_atoms; i++)
897
0
    {
898
0
        int ii = -1;
899
900
        /*p = inchi_fgetsLf_V3000( line, inp_file );*/
901
0
        inchi_strbuf_reset(pin);
902
903
0
        nc = get_V3000_input_line_to_strbuf(pin, inp_file);
904
905
0
        if (nc < 1)
906
0
        {
907
0
            p = NULL;
908
0
        }
909
0
        else
910
0
        {
911
0
            p = line = pin->pStr;
912
0
        }
913
0
        if (!p)
914
0
        {
915
0
            if (!err)
916
0
            {
917
0
                TREAT_ERR(err, 2, "Cannot read V3000 atom block line");
918
0
            }
919
0
            break;
920
0
        }
921
0
        remove_one_lf(line);
922
923
0
        if (err)
924
0
        {
925
0
            if (!strcmp(line, SD_FMT_END_OF_DATA))
926
0
            {
927
0
                err = -abs(err);
928
0
                break;
929
0
            }
930
0
            continue; /* bypass the rest of the Atom block */
931
0
        }
932
933
0
        if (ctab->atoms)
934
0
        {
935
0
            int index, aamap; /* not used actually, just read them */
936
0
            int len;
937
0
            char symbol[6]; /* TODO: treat possibly long V3000 atom names */
938
0
            double fx = 0.0, fy = 0.0, fz = 0.0;
939
#ifdef GHI100_FIX
940
#if (SPRINTF_FLAG == 2)
941
            char *fxs, *fys, *fzs;
942
            int rfxs, rfys, rfzs;
943
944
            fxs = (char *)inchi_malloc((10 + 3) * sizeof(double));
945
            fys = (char *)inchi_malloc((10 + 3) * sizeof(double));
946
            fzs = (char *)inchi_malloc((10 + 3) * sizeof(double));
947
948
            if (fxs || fys || fzs)
949
            {
950
                failed = 1;
951
            }
952
#endif
953
#endif
954
0
            symbol[0] = '\0'; /* djb-rwth: adding zero termination */
955
956
            /* Read positional parameters */
957
0
            failed = 0;
958
959
0
            if (0 > MolfileV3000ReadField(&index, MOL_FMT_INT_DATA, &p))
960
0
            {
961
0
                failed = 1;
962
0
            }
963
0
            else if (0 > MolfileV3000ReadField(&symbol, MOL_FMT_STRING_DATA, &p))
964
0
            {
965
0
                failed = 1;
966
0
            }
967
0
            else if (0 > MolfileV3000ReadField(&fx, MOL_FMT_DOUBLE_DATA, &p))
968
0
            {
969
0
                failed = 1;
970
0
            }
971
0
            else if (0 > MolfileV3000ReadField(&fy, MOL_FMT_DOUBLE_DATA, &p))
972
0
            {
973
0
                failed = 1;
974
0
            }
975
0
            else if (0 > MolfileV3000ReadField(&fz, MOL_FMT_DOUBLE_DATA, &p))
976
0
            {
977
0
                failed = 1;
978
0
            }
979
0
            else if (0 > MolfileV3000ReadField(&aamap, MOL_FMT_INT_DATA, &p))
980
0
            {
981
0
                failed = 1;
982
0
            }
983
984
0
            if (failed)
985
0
            {
986
987
0
                err = 4;
988
0
                TREAT_ERR(err, 4, "Cannot interpret V3000 atom block line:"); /* djb-rwth: addressing coverity ID #499547 -- TREAT_ERR properly used */
989
0
                dotify_non_printable_chars(line);
990
0
                AddErrorMessage(pStrErr, line);
991
992
0
                if (!strcmp(line, SD_FMT_END_OF_DATA))
993
0
                {
994
0
                    err = -abs(err);
995
0
                    break;
996
0
                }
997
0
                continue; /* can't interpret a first half of atom block line */
998
0
            }
999
1000
            /* emulate V2000 coordinates substring */
1001
0
            if (ctab->coords)
1002
0
            {
1003
0
                char szcoords[40];
1004
#ifdef GHI100_FIX
1005
#if (SPRINTF_FLAG == 2)
1006
                if (fxs)
1007
                {
1008
                    rfxs = dbl2int(fxs, 10, -1, 'g', fx);
1009
                }
1010
                if (fys)
1011
                {
1012
                    rfys = dbl2int(fys, 10, -1, 'g', fy);
1013
                }
1014
                if (fzs)
1015
                {
1016
                    rfzs = dbl2int(fzs, 10, -1, 'g', fz);
1017
                }
1018
                if ((rfxs >= 0) && (rfys >= 0) && (rfzs >= 0))
1019
                {
1020
                    sprintf(szcoords, "%s%s%s", fxs, fys, fzs);
1021
                }
1022
                else
1023
                {
1024
                    failed = 1;
1025
                }
1026
                inchi_free(fxs);
1027
                inchi_free(fys);
1028
                inchi_free(fzs);
1029
#elif (SPRINTF_FLAG == 2)
1030
                stbsp_sprintf(szcoords, "%10g%10g%10g", fx, fy, fz);
1031
#else
1032
                sprintf(szcoords, "%10g%10g%10g", fx, fy, fz);
1033
#endif
1034
#endif
1035
0
                sprintf(szcoords, "%10g%10g%10g", fx, fy, fz);
1036
0
                strcpy(ctab->coords[i], szcoords);
1037
0
            }
1038
1039
0
            if (!strcmp(symbol, "*"))
1040
0
            {
1041
                /* ignore star atoms but save index info */
1042
0
                ctab->v3000->atom_index_orig[i] = index;
1043
0
                ctab->v3000->atom_index_fin[i] = -1;
1044
0
                ctab->v3000->n_star_atoms++;
1045
0
                continue;
1046
0
            }
1047
1048
0
            ctab->v3000->n_non_star_atoms++;
1049
0
            ctab->v3000->atom_index_orig[i] = index;
1050
0
            ctab->v3000->atom_index_fin[i] = ctab->v3000->n_non_star_atoms;
1051
0
            ii = ctab->v3000->n_non_star_atoms - 1;
1052
1053
0
            mystrncpy(ctab->atoms[ii].symbol, symbol, sizeof(ctab->atoms[ii].symbol));
1054
0
            if (2 == strlen(ctab->atoms[ii].symbol) && isupper(UCINT ctab->atoms[ii].symbol[1]))
1055
0
            {
1056
0
                ctab->atoms[ii].symbol[1] = (char)tolower(UCINT ctab->atoms[ii].symbol[1]); /* 5-4-99 DCh*/
1057
0
            }
1058
0
            ctab->atoms[ii].fx = fx;
1059
0
            ctab->atoms[ii].fy = fy;
1060
0
            ctab->atoms[ii].fz = fz;
1061
1062
            /* Read key-val pairs if any */
1063
0
            while (p && (len = MolfileV3000ReadKeyword(field, &p)) > 0) /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
1064
0
            {
1065
1066
0
                int itmp;
1067
0
                char ctmp;
1068
0
                char stmp[MOL_FMT_V3000_MAXFIELDLEN];
1069
1070
0
                failed = 0;
1071
0
                if (!strcmp(field, "CHG"))
1072
0
                {
1073
0
                    if (0 > MolfileV3000ReadField(&ctab->atoms[ii].charge, MOL_FMT_CHAR_INT_DATA, &p))
1074
0
                    {
1075
0
                        failed = 1;
1076
0
                    }
1077
0
                }
1078
0
                else if (!strcmp(field, "RAD"))
1079
0
                {
1080
0
                    if (0 > MolfileV3000ReadField(&ctab->atoms[ii].radical, MOL_FMT_CHAR_INT_DATA, &p))
1081
0
                    {
1082
0
                        failed = 1;
1083
0
                    }
1084
0
                }
1085
0
                else if (!strcmp(field, "CFG"))
1086
0
                {
1087
0
                    if (0 > MolfileV3000ReadField(&ctab->atoms[ii].stereo_parity, MOL_FMT_CHAR_INT_DATA, &p))
1088
0
                    {
1089
0
                        failed = 1;
1090
0
                    }
1091
0
                }
1092
1093
0
                else if (!strcmp(field, "MASS"))
1094
0
                {
1095
                    /*
1096
                        Default = natural abundance
1097
                        A specified value indicates the absolute
1098
                        atomic weight of the designated atom.
1099
                    */
1100
0
                    S_SHORT iso_mass;
1101
0
                    if (0 > MolfileV3000ReadField(&iso_mass, MOL_FMT_SHORT_INT_DATA, &p))
1102
0
                    {
1103
0
                        failed = 1;
1104
0
                        TREAT_ERR(err, 0, "Isotopic data not recognized:");
1105
0
                        AddErrorMessage(pStrErr, line);
1106
                        /* ignore isotopic error for now */
1107
0
                    }
1108
0
                    else
1109
0
                    {
1110
                        /*  What we read is an absolute isotopic mass, by V3000 spec.
1111
                            Adjust this to old convention for further processing:
1112
                            set 'ctab->atoms[ii].mass_difference' to 127
1113
                            if isotopic mass is the same as element mass
1114
                            in Periodic Table (rounded avg by all isotopes), 'atw'
1115
                            delta otherwise, the value of difference 'delta' = ( isotopic mass - 'atw')
1116
                        */
1117
0
                        int atw, delta;
1118
0
                        atw = get_atomic_mass(ctab->atoms[ii].symbol);
1119
0
                        delta = (int)iso_mass - atw;
1120
0
                        ctab->atoms[ii].mass_difference = (char)(delta ? delta : ZERO_ATW_DIFF);
1121
0
                    }
1122
0
                }
1123
1124
0
                else if (!strcmp(field, "VAL"))
1125
0
                {
1126
0
                    if (0 > MolfileV3000ReadField(&itmp, MOL_FMT_INT_DATA, &p))
1127
0
                    {
1128
0
                        failed = 1;
1129
0
                    }
1130
0
                    else
1131
0
                    {
1132
                        /* adjust to old convention: was 15 for zero, now -1 for zero */
1133
0
                        if (itmp == -1)
1134
0
                        {
1135
0
                            ctmp = 15;
1136
0
                        }
1137
0
                        else
1138
0
                        {
1139
0
                            ctmp = (char)itmp;
1140
0
                        }
1141
0
                        ctab->atoms[ii].valence = ctmp;
1142
0
                    }
1143
0
                }
1144
0
                else if (!strcmp(field, "HCOUNT"))
1145
0
                {
1146
0
                    if (0 > MolfileV3000ReadField(&itmp, MOL_FMT_INT_DATA, &p))
1147
0
                    {
1148
0
                        ; /* skip query-related stuff */
1149
0
                    }
1150
0
                }
1151
0
                else if (!strcmp(field, "STBOX"))
1152
0
                {
1153
0
                    if (0 > MolfileV3000ReadField(&itmp, MOL_FMT_INT_DATA, &p))
1154
0
                    {
1155
0
                        ; /* skip for now */
1156
0
                    }
1157
0
                }
1158
0
                else if (!strcmp(field, "INVRET") || !strcmp(field, "EXACHG"))
1159
0
                {
1160
0
                    if (0 > MolfileV3000ReadField(&itmp, MOL_FMT_INT_DATA, &p))
1161
0
                    {
1162
0
                        ; /* skip reaction-related stuff */
1163
0
                    }
1164
0
                }
1165
0
                else if (!strcmp(field, "SUBST") || !strcmp(field, "UNSAT") || !strcmp(field, "RBCNT"))
1166
0
                {
1167
0
                    if (0 > MolfileV3000ReadField(&itmp, MOL_FMT_INT_DATA, &p))
1168
0
                    {
1169
0
                        ; /* skip query-related stuff */
1170
0
                    }
1171
0
                }
1172
0
                else if (!strcmp(field, "ATTCHPT"))
1173
0
                {
1174
0
                    if (0 > MolfileV3000ReadField(&itmp, MOL_FMT_INT_DATA, &p))
1175
0
                    {
1176
0
                        ;
1177
0
                    }
1178
0
                }
1179
0
                else if (!strcmp(field, "RGROUPS"))
1180
0
                {
1181
0
                    if (0 > MolfileV3000ReadField(&stmp, MOL_FMT_STRING_DATA, &p))
1182
0
                    {
1183
0
                        ;
1184
0
                    }
1185
0
                }
1186
0
                else if (!strcmp(field, "ATTCHORD"))
1187
0
                {
1188
0
                    if (0 > MolfileV3000ReadField(&stmp, MOL_FMT_STRING_DATA, &p))
1189
0
                    {
1190
0
                        ;
1191
0
                    }
1192
0
                }
1193
0
                else if (!strcmp(field, "CLASS"))
1194
0
                {
1195
0
                    if (0 > MolfileV3000ReadField(&stmp, MOL_FMT_STRING_DATA, &p))
1196
0
                    {
1197
0
                        ;
1198
0
                    }
1199
0
                }
1200
0
                else if (!strcmp(field, "SEQID"))
1201
0
                {
1202
0
                    if (0 > MolfileV3000ReadField(&itmp, MOL_FMT_INT_DATA, &p))
1203
0
                    {
1204
0
                        ;
1205
0
                    }
1206
0
                }
1207
1208
0
                if (failed)
1209
0
                {
1210
0
                    err = 4;
1211
0
                    TREAT_ERR(err, 4, "Cannot interpret V3000 atom block key-value pair");
1212
0
                    dotify_non_printable_chars(line);
1213
0
                    AddErrorMessage(pStrErr, line);
1214
1215
0
                    if (!strcmp(line, SD_FMT_END_OF_DATA))
1216
0
                    {
1217
0
                        err = -abs(err);
1218
0
                        break;
1219
0
                    }
1220
0
                    continue;
1221
0
                }
1222
0
            }
1223
0
        } /* if ( NULL != ctab->atoms )  */
1224
0
    } /* for ( i = 0; i < ctab->n_atoms; i++ )  */
1225
1226
0
    if (ctab->v3000->n_star_atoms)
1227
0
    {
1228
0
        AddErrorMessage(pStrErr, "V3000 star atoms ignored if MolecularInorganics or NPZz parameter not used");        /* @nnuk : when using /MolecularInorganics or /NPZz, star atoms will not be ignored */
1229
0
        ctab->n_atoms = ctab->v3000->n_non_star_atoms;
1230
0
    }
1231
1232
    /* Check for proper finish */
1233
1234
    /*p = inchi_fgetsLf_V3000( line, inp_file );*/
1235
0
    inchi_strbuf_reset(pin);
1236
1237
0
    nc = get_V3000_input_line_to_strbuf(pin, inp_file);
1238
1239
0
    if (nc < 1)
1240
0
    {
1241
0
        p = NULL;
1242
0
    }
1243
0
    else
1244
0
    {
1245
0
        p = line = pin->pStr;
1246
0
    }
1247
0
    if (!p || strcmp(p, "END ATOM"))
1248
0
    {
1249
0
        TREAT_ERR_AND_FIN(err, 1, err_fin, "Error: No V3000 Atom block end marker");
1250
0
    }
1251
0
    remove_one_lf(line);
1252
1253
0
err_fin:
1254
0
    inchi_strbuf_close(pin);
1255
1256
0
    return err;
1257
0
}
1258
1259
/****************************************************************************
1260
 Read V3000 bonds
1261
****************************************************************************/
1262
int MolfileV3000ReadBondsBlock(MOL_FMT_CTAB *ctab,
1263
                               INCHI_IOSTREAM *inp_file,
1264
                               int err,
1265
                               char *pStrErr)
1266
0
{
1267
0
    int i;
1268
0
    char field[MOL_FMT_V3000_MAXFIELDLEN];
1269
0
    int nc;
1270
0
    char *p = NULL, *line = NULL;
1271
0
    INCHI_IOSTREAM tmpin;
1272
0
    INCHI_IOS_STRING *pin = &tmpin.s;
1273
1274
0
    if (!ctab->n_bonds)
1275
0
    {
1276
0
        return 0;
1277
0
    }
1278
0
    inchi_ios_init(&tmpin, INCHI_IOS_TYPE_STRING, NULL);
1279
1280
    /* Check for proper start */
1281
    /*p = inchi_fgetsLf_V3000( line, inp_file );*/
1282
1283
0
    nc = get_V3000_input_line_to_strbuf(pin, inp_file);
1284
1285
0
    if (nc < 1)
1286
0
    {
1287
0
        p = NULL;
1288
0
    }
1289
0
    else
1290
0
    {
1291
0
        p = line = pin->pStr;
1292
0
    }
1293
0
    if (!p || strcmp(p, "BEGIN BOND"))
1294
0
    {
1295
0
        TREAT_ERR_AND_FIN(err, 1, err_fin, "Error: No V3000 Bond block start marker");
1296
0
    }
1297
0
    remove_one_lf(line);
1298
1299
0
    ctab->v3000->n_haptic_bonds = 0;
1300
0
    ctab->v3000->n_non_haptic_bonds = 0;
1301
1302
0
    for (i = 0; i < ctab->n_bonds; i++)
1303
0
    {
1304
0
        int is_haptic = 0;
1305
1306
        /*p = inchi_fgetsLf_V3000( line, inp_file );*/
1307
0
        inchi_strbuf_reset(pin);
1308
1309
0
        nc = get_V3000_input_line_to_strbuf(pin, inp_file);
1310
1311
0
        if (nc < 1)
1312
0
        {
1313
0
            p = NULL;
1314
0
        }
1315
0
        else
1316
0
        {
1317
0
            p = line = pin->pStr;
1318
0
        }
1319
0
        if (!p)
1320
0
        {
1321
0
            if (!err)
1322
0
            {
1323
0
                TREAT_ERR(err, 2, "Cannot read V3000 bond block line"); /* djb-rwth: addressing coverity ID #499565 -- TREAT_ERR properly used */
1324
0
            }
1325
0
            break;
1326
0
        }
1327
0
        remove_one_lf(line);
1328
1329
0
        if (err)
1330
0
        {
1331
0
            if (!strcmp(line, SD_FMT_END_OF_DATA))
1332
0
            {
1333
0
                err = -abs(err);
1334
0
                break;
1335
0
            }
1336
0
            continue;
1337
0
        }
1338
1339
0
        if (ctab->bonds)
1340
0
        {
1341
0
            int index, n_orig_at, len;
1342
0
            short int atnum1 = -1, atnum2 = -1;
1343
0
            char bond_type = 0, stereo = 0;
1344
0
            int failed = 0;
1345
            /* djb-rwth: removing redundant variables */
1346
1347
0
            n_orig_at = ctab->v3000->n_non_star_atoms + ctab->v3000->n_star_atoms;
1348
1349
            /* read positional parameters */
1350
0
            if (0 > MolfileV3000ReadField(&index, MOL_FMT_INT_DATA, &p))
1351
0
            {
1352
0
                failed = 1;
1353
0
            }
1354
0
            else if (0 > MolfileV3000ReadField(&bond_type, MOL_FMT_CHAR_INT_DATA, &p))
1355
0
            {
1356
0
                failed = 1;
1357
0
            }
1358
0
            else if (0 > MolfileV3000ReadField(&atnum1, MOL_FMT_SHORT_INT_DATA, &p))
1359
0
            {
1360
0
                failed = 1;
1361
0
            }
1362
0
            else if (0 > MolfileV3000ReadField(&atnum2, MOL_FMT_SHORT_INT_DATA, &p))
1363
0
            {
1364
0
                failed = 1;
1365
0
            }
1366
1367
0
            atnum1 = get_actual_atom_number(atnum1, n_orig_at,
1368
0
                                            ctab->v3000->atom_index_orig,
1369
0
                                            ctab->v3000->atom_index_fin);
1370
1371
0
            atnum2 = get_actual_atom_number(atnum2, n_orig_at,
1372
0
                                            ctab->v3000->atom_index_orig,
1373
0
                                            ctab->v3000->atom_index_fin);
1374
1375
0
            if ((atnum1 < 0) && (atnum2 < 0))
1376
0
            {
1377
0
                failed = 1;
1378
0
            }
1379
            /* djb-rwth: removing redundant code */
1380
1381
0
            if (failed)
1382
0
            {
1383
1384
0
                if (!err)
1385
0
                {
1386
                    /* can't interpret bonds block line */
1387
0
                    TREAT_ERR(err, 4, "Cannot interpret V3000 bond block line:");
1388
0
                    dotify_non_printable_chars(line);
1389
0
                    AddErrorMessage(pStrErr, line);
1390
0
                }
1391
0
                if (!strcmp(line, SD_FMT_END_OF_DATA))
1392
0
                {
1393
0
                    err = -abs(err);
1394
0
                    break;
1395
0
                }
1396
0
            }
1397
1398
            /* TODO: treat new bond types  9 10 */
1399
            /* read key-val pairs if any */
1400
0
            while (p && (len = MolfileV3000ReadKeyword(field, &p)) > 0) /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
1401
0
            {
1402
1403
0
                int itmp;
1404
0
                char stmp[MOL_FMT_V3000_MAXFIELDLEN];
1405
0
                failed = 0;
1406
1407
0
                if (!strcmp(field, "CFG"))
1408
0
                {
1409
0
                    if (0 > MolfileV3000ReadField(&stereo, MOL_FMT_CHAR_INT_DATA, &p))
1410
0
                    {
1411
0
                        failed = 1;
1412
0
                    }
1413
0
                    else
1414
0
                    {
1415
                        /*    adjust stereo to old convention for wedges which was:
1416
                                0 = not stereo, 1 = Up,  4 = Either, 6 = Down
1417
                            now:
1418
                                0 = none (default), 1 = up, 2 = either, 3 = down
1419
                        */
1420
0
                        if (stereo == 2)
1421
0
                        {
1422
0
                            stereo = 4;
1423
0
                        }
1424
0
                        else if (stereo == 3)
1425
0
                        {
1426
0
                            stereo = 6;
1427
0
                        }
1428
0
                    }
1429
0
                }
1430
0
                else if (!strcmp(field, "TOPO"))
1431
0
                {
1432
0
                    if (0 > MolfileV3000ReadField(&itmp, MOL_FMT_INT_DATA, &p))
1433
0
                    {
1434
0
                        ; /* skip query-related stuff */
1435
0
                    }
1436
0
                }
1437
0
                else if (!strcmp(field, "RXCTR"))
1438
0
                {
1439
0
                    if (0 > MolfileV3000ReadField(&itmp, MOL_FMT_INT_DATA, &p))
1440
0
                    {
1441
0
                        ; /* skip reaction-related stuff */
1442
0
                    }
1443
0
                }
1444
0
                else if (!strcmp(field, "STBOX"))
1445
0
                {
1446
0
                    if (0 > MolfileV3000ReadField(&itmp, MOL_FMT_INT_DATA, &p))
1447
0
                    {
1448
0
                        ; /* skip for now */
1449
0
                    }
1450
0
                }
1451
0
                else if (!strcmp(field, "ENDPTS"))
1452
0
                {
1453
0
                    int res, *num_list = NULL;
1454
0
                    if (0 > MolfileV3000ReadHapticBond(ctab, &p, &num_list, pStrErr))
1455
0
                    {
1456
0
                        failed = 1;
1457
0
                    }
1458
0
                    else if (!num_list)
1459
0
                    {
1460
0
                        failed = 1;
1461
0
                    }
1462
0
                    else
1463
0
                    {
1464
0
                        int existent_atom = atnum1;
1465
0
                        if (existent_atom < 0)
1466
0
                        {
1467
0
                            existent_atom = atnum2;
1468
0
                        }
1469
0
                        if (existent_atom < 0) /* should not be here */
1470
0
                         {
1471
0
                            failed = 1;
1472
0
                        }
1473
0
                        else
1474
0
                        {
1475
0
                            int k, nnum;
1476
0
                            nnum = num_list[2];
1477
0
                            num_list[1] = existent_atom;
1478
0
                            for (k = 3; k < nnum + 3; k++)              /* @nnuk : wrong mapping of endpoints (+3 added for full endpoints mapping for all haptic bonds) */
1479
0
                            {
1480
0
                                num_list[k] = get_actual_atom_number(num_list[k],
1481
0
                                                                     n_orig_at,
1482
0
                                                                     ctab->v3000->atom_index_orig,
1483
0
                                                                     ctab->v3000->atom_index_fin);
1484
0
                            }
1485
0
                            res = NumLists_Append(ctab->v3000->haptic_bonds, num_list);
1486
0
                            if (res < 0)
1487
0
                            {
1488
0
                                failed = 1;
1489
0
                            }
1490
0
                            else
1491
0
                            {
1492
0
                                is_haptic = 1;
1493
0
                            }
1494
0
                        }
1495
0
                    }
1496
                    /* djb-rwth: addressing coverity ID #499489 -- false positive as num_atoms allocated in MolfileV3000ReadHapticBond and returns a value in this block */
1497
0
                }
1498
0
                else if (!strcmp(field, "DISP"))
1499
0
                {
1500
0
                    if (0 > MolfileV3000ReadField(&stmp, MOL_FMT_STRING_DATA, &p))
1501
0
                    {
1502
0
                        ;
1503
0
                    }
1504
0
                }
1505
0
                else if (!strcmp(field, "ATTACH"))
1506
0
                {
1507
0
                    if (0 > MolfileV3000ReadField(&stmp, MOL_FMT_STRING_DATA, &p))
1508
0
                    {
1509
0
                        ;
1510
0
                    }
1511
0
                }
1512
1513
0
                if (failed)
1514
0
                {
1515
0
                    if (!err)
1516
0
                    {
1517
                        /* can't interpret bonds block line */
1518
0
                        TREAT_ERR(err, 4, "Cannot interpret V3000 bond block line:");
1519
0
                        dotify_non_printable_chars(line);
1520
0
                        AddErrorMessage(pStrErr, line);
1521
0
                    }
1522
0
                    if (!strcmp(line, SD_FMT_END_OF_DATA))
1523
0
                    {
1524
0
                        err = -abs(err);
1525
0
                        break;
1526
0
                    }
1527
0
                }
1528
0
            } /* while ( p && (len=MolfileV3000ReadKeyword(field, &p)) > 0 ) */
1529
1530
0
            if (is_haptic)
1531
0
            {
1532
0
                int ii = ctab->v3000->n_haptic_bonds;
1533
0
                ctab->v3000->haptic_bonds->lists[ii][0] = bond_type;
1534
0
                ctab->v3000->n_haptic_bonds++;
1535
0
                continue;
1536
0
            }
1537
0
            else
1538
0
            {
1539
0
                int ii = ctab->v3000->n_non_haptic_bonds;
1540
0
                ctab->bonds[ii].atnum1 = atnum1;
1541
0
                ctab->bonds[ii].atnum2 = atnum2;
1542
0
                ctab->bonds[ii].bond_type = bond_type;
1543
0
                ctab->bonds[ii].bond_stereo = stereo;
1544
0
                ctab->v3000->n_non_haptic_bonds++;
1545
0
            }
1546
0
        } /* if ctab->bonds */
1547
0
    } /* for ( i = 0; i < ctab->n_bonds; i++ )  */
1548
1549
0
    if (ctab->v3000->n_haptic_bonds)
1550
0
    {
1551
0
        AddErrorMessage(pStrErr, "V3000 haptic bonds read/stored and ignored if MolecularInorganics or NPZz parameter is not used");    /* @nnuk : when using /MolecularInorganics or /NPZz, haptic bonds will not be ignored */
1552
0
        ctab->n_bonds = ctab->v3000->n_non_haptic_bonds;
1553
0
    }
1554
1555
    /* Check for proper finish */
1556
    /*p = inchi_fgetsLf_V3000( line, inp_file );*/
1557
0
    inchi_strbuf_reset(pin);
1558
1559
0
    nc = get_V3000_input_line_to_strbuf(pin, inp_file);
1560
1561
0
    if (nc < 1)
1562
0
    {
1563
0
        p = NULL;
1564
0
    }
1565
0
    else
1566
0
    {
1567
0
        p = line = pin->pStr;
1568
0
    }
1569
0
    if (!p || strcmp(p, "END BOND"))
1570
0
    {
1571
0
        TREAT_ERR_AND_FIN(err, 1, err_fin, "Error: No V3000 Bond block end marker");
1572
0
    }
1573
0
    remove_one_lf(line);
1574
1575
0
err_fin:
1576
1577
0
    inchi_ios_close(&tmpin); /* ricrogz: fixing memory leak */
1578
0
    return err;
1579
0
}
1580
1581
/****************************************************************************
1582
 Convert atom index to the final consequitive atom number (starting from 1)
1583
 Returns -1 for star atom or not found index
1584
****************************************************************************/
1585
int get_actual_atom_number(int index, int n, int *orig, int *fin)
1586
0
{
1587
0
    int i;
1588
0
    for (i = 0; i < n; i++)
1589
0
    {
1590
0
        if (orig[i] == index)
1591
0
        {
1592
0
            return fin[i];
1593
0
        }
1594
0
    }
1595
1596
0
    return -1;
1597
0
}
1598
1599
/****************************************************************************
1600
 Read V3000 tail of CTab
1601
****************************************************************************/
1602
int MolfileV3000ReadTailOfCTAB(MOL_FMT_CTAB *ctab,
1603
                               INCHI_IOSTREAM *inp_file,
1604
                               int err,
1605
                               char *pStrErr)
1606
0
{
1607
0
    int retcode = err;
1608
0
    int nc;
1609
0
    char *p = NULL, *line = NULL;
1610
0
    INCHI_IOSTREAM tmpin;
1611
0
    INCHI_IOS_STRING *pin = &tmpin.s;
1612
0
    inchi_ios_init(&tmpin, INCHI_IOS_TYPE_STRING, NULL);
1613
1614
    /*p = inchi_fgetsLf_V3000( line, inp_file );*/
1615
1616
0
    nc = get_V3000_input_line_to_strbuf(pin, inp_file);
1617
1618
0
    if (nc < 1)
1619
0
    {
1620
0
        p = NULL;
1621
0
    }
1622
0
    else
1623
0
    {
1624
0
        p = line = pin->pStr;
1625
0
    }
1626
0
    remove_one_lf(line);
1627
1628
0
    if (p && !strcmp(p, "BEGIN SGROUP"))
1629
0
    {
1630
0
        retcode = MolfileV3000ReadSGroup(ctab, inp_file, retcode, pStrErr);
1631
0
        if (retcode)
1632
0
        {
1633
0
            retcode += 70;
1634
0
            TREAT_ERR_AND_FIN(retcode, 1, err_fin, pStrErr);
1635
0
        }
1636
        /*p = inchi_fgetsLf_V3000( line, inp_file );*/
1637
0
        inchi_strbuf_reset(pin);
1638
0
        nc = get_V3000_input_line_to_strbuf(pin, inp_file);
1639
0
        if (nc < 1)
1640
0
        {
1641
0
            p = NULL;
1642
0
        }
1643
0
        else
1644
0
        {
1645
0
            p = line = pin->pStr;
1646
0
            remove_one_lf(line);
1647
0
        }
1648
0
    }
1649
1650
0
    if (p && !strcmp(p, "BEGIN OBJ3D"))
1651
0
    {
1652
0
        retcode = MolfileV3000Read3DBlock(ctab, inp_file, retcode, pStrErr);
1653
0
        if (retcode)
1654
0
        {
1655
0
            retcode += 70;
1656
0
            TREAT_ERR_AND_FIN(retcode, 1, err_fin, pStrErr);
1657
0
        }
1658
        /*p = inchi_fgetsLf_V3000( line, inp_file );*/
1659
0
        inchi_strbuf_reset(pin);
1660
1661
0
        nc = get_V3000_input_line_to_strbuf(pin, inp_file);
1662
0
        if (nc < 1)
1663
0
        {
1664
0
            p = NULL;
1665
0
        }
1666
0
        else
1667
0
        {
1668
0
            p = line = pin->pStr;
1669
0
            remove_one_lf(line);
1670
0
        }
1671
0
    }
1672
1673
0
    while (p && !strcmp(p, "LINKNODE"))
1674
0
    {
1675
        /* skip for now */
1676
        /*p = inchi_fgetsLf_V3000( line, inp_file );*/
1677
0
        inchi_strbuf_reset(pin);
1678
1679
0
        nc = get_V3000_input_line_to_strbuf(pin, inp_file);
1680
0
        if (nc < 1)
1681
0
        {
1682
0
            p = NULL;
1683
0
        }
1684
0
        else
1685
0
        {
1686
0
            p = line = pin->pStr;
1687
0
            remove_one_lf(line);
1688
0
        }
1689
0
    }
1690
1691
    /* Collections */
1692
0
    while (p && !strcmp(p, "BEGIN COLLECTION"))
1693
0
    {
1694
0
        retcode = MolfileV3000ReadCollections(ctab, inp_file, retcode, pStrErr);
1695
0
        if (retcode)
1696
0
        {
1697
0
            retcode += 70;
1698
0
            TREAT_ERR_AND_FIN(retcode, 1, err_fin, pStrErr);
1699
0
        }
1700
        /*p = inchi_fgetsLf_V3000( line, inp_file );*/
1701
0
        inchi_strbuf_reset(pin);
1702
1703
0
        nc = get_V3000_input_line_to_strbuf(pin, inp_file);
1704
1705
0
        if (nc < 1)
1706
0
        {
1707
0
            p = NULL;
1708
0
        }
1709
0
        else
1710
0
        {
1711
0
            p = line = pin->pStr;
1712
0
            remove_one_lf(line);
1713
0
        }
1714
0
    }
1715
1716
0
    if (!p || strcmp(p, "END CTAB"))
1717
0
    {
1718
0
        TREAT_ERR_AND_FIN(err, 1, err_fin, "Error: No V3000 CTAB end marker");
1719
0
    }
1720
1721
0
    remove_one_lf(line);
1722
1723
0
err_fin:
1724
0
    inchi_strbuf_close(pin);
1725
1726
0
    return err;
1727
0
}
1728
1729
/****************************************************************************
1730
 Read haptic bond info
1731
****************************************************************************/
1732
int MolfileV3000ReadHapticBond(MOL_FMT_CTAB *ctab,
1733
                               char **line_ptr,
1734
                               int **num_list,
1735
                               char *pStrErr)
1736
0
{
1737
0
    int nread = 0;
1738
0
    char field[MOL_FMT_V3000_MAXFIELDLEN];
1739
0
    const int max_field_len = sizeof(field);
1740
0
    char *p_end;
1741
0
    int i, nnum = 0;
1742
1743
0
    *num_list = NULL;
1744
1745
0
    memset(field, 0, max_field_len); /* djb-rwth: memset_s C11/Annex K variant? */
1746
1747
0
    nread = read_upto_delim(line_ptr, field, max_field_len, "1234567890 \t\n\v\f\r"); /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
1748
0
    if (strcmp(field, "("))
1749
0
    {
1750
0
        return -1;
1751
0
    }
1752
1753
0
    nread = read_upto_delim(line_ptr, field, max_field_len, " \t\n\v\f\r"); /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
1754
1755
0
    nnum = strtol(field, &p_end, 10);
1756
1757
0
    if (p_end == field)
1758
0
    {
1759
0
        return -1; /* paranoia */
1760
0
    }
1761
0
    if (nnum < 0)
1762
0
    {
1763
0
        return -1;
1764
0
    }
1765
1766
0
    *num_list = (int *)inchi_calloc((long long)nnum + 3, sizeof(int)); /* djb-rwth: cast operator added */
1767
1768
0
    if (!*num_list)
1769
0
    {
1770
0
        nread = -1;
1771
0
        goto ret;
1772
0
    }
1773
1774
0
    (*num_list)[0] = -1; /* will be bond type, to be filled by caller */
1775
0
    (*num_list)[1] = -1; /* will be atom number, to be filled by caller */
1776
0
    (*num_list)[2] = nnum;
1777
1778
0
    for (i = 3; i < nnum + 3; i++)
1779
0
    {
1780
0
        if (0 > MolfileV3000ReadField(&((*num_list)[i]), MOL_FMT_INT_DATA, line_ptr))
1781
0
        {
1782
0
            nread = -1;
1783
0
            goto ret;
1784
0
        }
1785
0
    }
1786
1787
    /* ')' should have been consumed  by strtol */
1788
1789
    /* check for ATTACH=ALL */
1790
1791
0
    nread = read_upto_delim(line_ptr, field, max_field_len, " \t\n\v\f\r");
1792
0
    if (nread > 0)
1793
0
    {
1794
0
        if (strcmp(field, "ATTACH=ALL"))
1795
0
        {
1796
0
            nread = -1;
1797
0
            goto ret;
1798
0
        }
1799
0
    }
1800
1801
0
ret:
1802
0
    if (nread < 0)
1803
0
    {
1804
0
        if (*num_list)
1805
0
        {
1806
0
            inchi_free(*num_list);
1807
0
            *num_list = NULL;
1808
0
        }
1809
0
    }
1810
1811
0
    return nread;
1812
0
}
1813
1814
/****************************************************************************
1815
 Read V3000 stereo collection
1816
****************************************************************************/
1817
int MolfileV3000ReadStereoCollection(MOL_FMT_CTAB *ctab,
1818
                                     char **line_ptr,
1819
                                     int **num_list,
1820
                                     char *pStrErr)
1821
0
{
1822
0
    int nread = 0;
1823
0
    char field[MOL_FMT_V3000_MAXFIELDLEN];
1824
0
    const int max_field_len = sizeof(field);
1825
0
    char *p_end;
1826
0
    int i, nnum = 0;
1827
1828
0
    *num_list = NULL;
1829
1830
0
    memset(field, 0, max_field_len); /* djb-rwth: memset_s C11/Annex K variant? */
1831
1832
0
    nread = read_upto_delim(line_ptr, field, max_field_len, "1234567890 \t\n\v\f\r"); /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
1833
0
    if (strcmp(field, "("))
1834
0
    {
1835
0
        return -1;
1836
0
    }
1837
1838
0
    nread = read_upto_delim(line_ptr, field, max_field_len, " \t\n\v\f\r");
1839
1840
0
    nnum = strtol(field, &p_end, 10);
1841
1842
0
    if (p_end == field)
1843
0
    {
1844
0
        return -1; /* paranoia */
1845
0
    }
1846
0
    if (nnum < 0)
1847
0
    {
1848
0
        return -1;
1849
0
    }
1850
1851
0
    *num_list = (int *)inchi_calloc((long long)nnum + 3, sizeof(int)); /* djb-rwth: cast operator added */
1852
1853
0
    if (!*num_list)
1854
0
    {
1855
0
        nread = -1;
1856
0
        goto ret;
1857
0
    }
1858
1859
0
    (*num_list)[0] = -1; /* reserved, may be filled by caller */
1860
0
    (*num_list)[1] = nnum;
1861
1862
0
    for (i = 2; i < nnum + 2; i++)
1863
0
    {
1864
0
        if (0 > MolfileV3000ReadField(&((*num_list)[i]), MOL_FMT_INT_DATA, line_ptr))
1865
0
        {
1866
0
            nread = -1;
1867
0
            goto ret;
1868
0
        }
1869
0
    }
1870
1871
    /* ')' should have been consumed  by strtol */
1872
1873
0
ret:
1874
0
    if (nread < 0)
1875
0
    {
1876
0
        if (*num_list)
1877
0
        {
1878
0
            inchi_free(*num_list);
1879
0
            *num_list = NULL;
1880
0
        }
1881
0
    }
1882
1883
0
    return nread;
1884
0
}
1885
1886
/****************************************************************************
1887
    Returns -1 @ error
1888
****************************************************************************/
1889
int get_V3000_input_line_to_strbuf(INCHI_IOS_STRING *buf,
1890
                                   INCHI_IOSTREAM *inp_stream)
1891
0
{
1892
0
    const int prefix_len = 7; /* "M  V30 " */
1893
0
    int old_used, crlf2lf = 1, preserve_lf = 0;
1894
1895
0
    inchi_strbuf_reset(buf);
1896
1897
0
    old_used = buf->nUsedLength;
1898
0
    while (1)
1899
0
    {
1900
0
        inchi_strbuf_addline(buf, inp_stream, crlf2lf, preserve_lf);
1901
1902
0
        if (buf->nUsedLength - old_used < 8)
1903
0
        {
1904
0
            return -1;
1905
0
        }
1906
0
        if (strncmp(buf->pStr + old_used, "M  V30 ", prefix_len))
1907
0
        {
1908
0
            return -1;
1909
0
        }
1910
1911
0
        memmove((void *)(buf->pStr + old_used), (void *)(buf->pStr + old_used + prefix_len), (long long)buf->nUsedLength - (long long)old_used - (long long)prefix_len + 1); /* djb-rwth: cast operators added */ /* ricrogz: fixing memory overflow error */
1912
0
        buf->nUsedLength -= prefix_len;
1913
1914
0
        if (buf->pStr[buf->nUsedLength - 1] != '-')
1915
0
        {
1916
0
            break;
1917
0
        }
1918
0
        buf->pStr[--buf->nUsedLength] = '\0';
1919
1920
0
        old_used = buf->nUsedLength;
1921
0
    }
1922
1923
0
    remove_trailing_spaces(buf->pStr);
1924
0
    buf->nUsedLength = strlen(buf->pStr);
1925
1926
0
    return buf->nUsedLength;
1927
0
}