Branch data Line data Source code
1 : : /*
2 : : * SPDX-License-Identifier: MIT
3 : : * Copyright (c) 2024 IUPAC and InChI Trust
4 : : */
5 : :
6 : : #include <string.h>
7 : : #include <stdlib.h>
8 : : #include <ctype.h>
9 : :
10 : : #include "mode.h"
11 : :
12 : : #if defined(COMPILE_ANSI_ONLY) && defined(__APPLE__)
13 : : /* For build under OSX, advice from Burt Leland */
14 : : #include "ichicomp.h" /* Needed for __isascii define */
15 : : #endif
16 : :
17 : : /* djb-rwth: defining __isascii */
18 : : #if defined(__isascii)
19 : : #define is_ascii __isascii
20 : : #elif defined(isascii)
21 : : #define is_ascii isascii
22 : : #else
23 : : #define is_ascii(c) ((unsigned)(c) < 0x80)
24 : : #endif
25 : :
26 : : #include "util.h"
27 : : #include "eldata.h"
28 : : #include "extr_ct.h"
29 : :
30 : : #include "bcf_s.h"
31 : :
32 : :
33 : : /*
34 : : MISC. CHEMICAL-STRUCTURE RELATED UTILITIES AND HELPERS
35 : : */
36 : :
37 : :
38 : : /****************************************************************************
39 : : Finds chemical symbol for element of given number.
40 : : Returns 0 if OK and -1 if element was not found.
41 : : ****************************************************************************/
42 : 1514 : int get_element_chemical_symbol( int nAtNum, char *szElement )
43 : : {
44 : 1514 : nAtNum -= 1;
45 : :
46 [ + + ]: 1514 : if ( 0 < nAtNum )
47 : : {
48 : 1508 : nAtNum += 2; /* bypass D, T */
49 : : }
50 : :
51 [ + - + - ]: 1514 : if ( 0 <= nAtNum && nAtNum < nElDataLen )
52 : : {
53 : : /* valid element symbol found */
54 : 1514 : strcpy(szElement, ElData[nAtNum].szElName);
55 : 1514 : return 0;
56 : : }
57 : :
58 : : /* not found */
59 : 0 : strcpy(szElement, "??");
60 : 0 : return -1;
61 : : }
62 : :
63 : :
64 : : /****************************************************************************
65 : : Finds symbol for element of given number.
66 : : Accounts for (translates)pseudoelements.
67 : : Returns 0 if OK and -1 if element was not found.
68 : : ****************************************************************************/
69 : 465 : int get_element_or_pseudoelement_symbol( int nAtNum,
70 : : char *szElement )
71 : : {
72 : 465 : nAtNum -= 1;
73 : :
74 [ + + ]: 465 : if ( 0 < nAtNum )
75 : : {
76 : 459 : nAtNum += 2; /* bypass D, T */
77 : : }
78 : :
79 [ + - + - ]: 465 : if ( 0 <= nAtNum && nAtNum < nElDataLen )
80 : : {
81 : : /* valid element symbol found */
82 : 465 : strcpy(szElement, ElData[nAtNum].szElName);
83 : :
84 [ - + ]: 465 : if (!strcmp( szElement, "Zy" ))
85 : : {
86 : 0 : strcpy(szElement, "Zz");
87 : : }
88 : :
89 : 465 : return 0;
90 : : }
91 : :
92 : : /* not found */
93 : 0 : strcpy(szElement, "??");
94 : :
95 : 0 : return -1;
96 : : }
97 : :
98 : :
99 : : /****************************************************************************/
100 : 2667 : int el_number_in_internal_ref_table( const char* elname )
101 : : {
102 : : int i;
103 : : const char* p;
104 : :
105 [ + - + + ]: 36184 : for (i = 0; ( p = ElData[i].szElName )[0] && strcmp( p, elname ); i++)
106 : : {
107 : : ;
108 : : }
109 : :
110 [ + - ]: 2667 : return p[0] ? i : ERR_ELEM;
111 : : }
112 : :
113 : :
114 : : /****************************************************************************
115 : : Get element number by symbol
116 : : ****************************************************************************/
117 : 1616 : int get_periodic_table_number( const char* elname )
118 : : {
119 : : int num;
120 : :
121 [ - + ]: 1616 : if (elname == NULL)
122 : : {
123 : 0 : return ERR_ELEM;
124 : : }
125 : :
126 [ - + ]: 1616 : if (strlen(elname) == 0)
127 : : {
128 : 0 : return ERR_ELEM;
129 : : }
130 : :
131 : : /* the single letter (common) elements */
132 [ + + ]: 1616 : if (!elname[1])
133 : : {
134 [ + + + + : 1274 : switch (elname[0])
+ + + + +
+ ]
135 : : {
136 : 11 : case 'H':
137 : 11 : return EL_NUMBER_H;
138 : : break;
139 : 2 : case 'B':
140 : 2 : return EL_NUMBER_B;
141 : : break;
142 : 940 : case 'C':
143 : 940 : return EL_NUMBER_C;
144 : : break;
145 : 36 : case 'N':
146 : 36 : return EL_NUMBER_N;
147 : : break;
148 : 244 : case 'O':
149 : 244 : return EL_NUMBER_O;
150 : : break;
151 : 2 : case 'P':
152 : 2 : return EL_NUMBER_P;
153 : : break;
154 : 6 : case 'S':
155 : 6 : return EL_NUMBER_S;
156 : : break;
157 : 22 : case 'F':
158 : 22 : return EL_NUMBER_F;
159 : : break;
160 : 2 : case 'I':
161 : 2 : return EL_NUMBER_I;
162 : : break;
163 : : }
164 : : }
165 : :
166 : 351 : num = el_number_in_internal_ref_table( elname );
167 : :
168 [ + - ]: 351 : if ( num < ERR_ELEM )
169 : : {
170 : : /* account for D,T in internal table (but not Mendeleev's table) */
171 : 351 : num = inchi_max( 1, num - 1 );
172 : : }
173 : :
174 : 351 : return num;
175 : : }
176 : :
177 : :
178 : : /****************************************************************************
179 : : Check if no H addition allowed
180 : : ****************************************************************************/
181 : 1574 : int if_skip_add_H( int nPeriodicNum )
182 : : /* was called if_skip_add_H(, renamed to avoid confusion with other procedures */
183 : : {
184 : : return
185 [ + + ]: 1574 : ElData[nPeriodicNum > 1 ? nPeriodicNum + 1 : 0].bSkipAddingH;
186 : : }
187 : :
188 : :
189 : : /****************************************************************************
190 : : Get reference value of atom valence at given charge
191 : : ****************************************************************************/
192 : 18120 : int get_el_valence( int nPeriodicNum, int charge, int val_num )
193 : : {
194 [ + + ]: 18120 : int idx = ( nPeriodicNum > 1 ) ? nPeriodicNum + 1 : 0;
195 : :
196 : : /* Bounds-check every index before touching ElData[].cValence[][].
197 : : * The original code guarded only the upper bound of val_num and the
198 : : * charge range; a negative nPeriodicNum/val_num or an element number
199 : : * beyond the table would read out of bounds. Return 0 (no known
200 : : * valence) for any out-of-range input. */
201 [ + - + - : 18120 : if ( nPeriodicNum < 0 || idx > nElDataLen ||
+ - ]
202 [ + - + - ]: 18120 : val_num < 0 || val_num >= MAX_NUM_VALENCES ||
203 [ - + ]: 18120 : charge < MIN_ATOM_CHARGE || charge > MAX_ATOM_CHARGE )
204 : : {
205 : 0 : return 0;
206 : : }
207 : :
208 : 18120 : return ElData[idx].cValence[NEUTRAL_STATE + charge][val_num];
209 : : }
210 : :
211 : :
212 : : /****************************************************************************
213 : : Output valence needed to unambiguosly reconstruct bonds
214 : : ****************************************************************************/
215 : 750 : int get_unusual_el_valence( int nPeriodicNum,
216 : : int charge,
217 : : int radical,
218 : : int bonds_valence,
219 : : int num_H,
220 : : int num_bonds )
221 : : {
222 : : int i, num_found, chem_valence, rad_adj, known_chem_valence, exact_found;
223 : :
224 [ + + - + ]: 750 : if ( !num_bonds && !num_H )
225 : : {
226 : 0 : return 0;
227 : : }
228 : :
229 [ + - - + ]: 750 : if ( charge < MIN_ATOM_CHARGE || charge > MAX_ATOM_CHARGE )
230 : : {
231 [ # # ]: 0 : if ( bonds_valence == num_bonds )
232 : : {
233 : 0 : return 0; /* all single bonds */
234 : : }
235 : 0 : return bonds_valence;
236 : : }
237 : :
238 [ + + + - ]: 750 : if (!get_el_valence( nPeriodicNum, charge, 0 ) && bonds_valence == num_bonds)
239 : : {
240 : 2 : return 0;
241 : : }
242 : :
243 : 748 : chem_valence = bonds_valence + num_H;
244 : 748 : rad_adj = 0;
245 : 748 : num_found = 0;
246 : 748 : exact_found = 0;
247 : :
248 : : /* Take into account a radical */
249 [ - + ]: 748 : if ( radical == RADICAL_DOUBLET )
250 : : {
251 : 0 : rad_adj = 1;
252 : : }
253 [ - + ]: 748 : else if ( radical == RADICAL_TRIPLET )
254 : : {
255 : 0 : rad_adj = 2;
256 : : }
257 : :
258 [ + + ]: 791 : for ( i = 0; i < MAX_NUM_VALENCES; i++ )
259 : : {
260 [ + + + + ]: 785 : if (0 < ( known_chem_valence = get_el_valence( nPeriodicNum, charge, i ) - rad_adj ) &&
261 [ + + ]: 751 : num_bonds <= known_chem_valence && known_chem_valence <= chem_valence)
262 : : {
263 : 749 : num_found++;
264 [ + + ]: 749 : if ( known_chem_valence == chem_valence )
265 : : {
266 : 742 : exact_found = 1;
267 : 742 : break;
268 : : }
269 : : }
270 : : }
271 : :
272 [ + + + + ]: 748 : return ( exact_found && 1 == num_found ) ? 0 : chem_valence;
273 : : }
274 : :
275 : :
276 : : /****************************************************************************
277 : : Output valence needed to unambiguosly reconstruct number of H
278 : : ****************************************************************************/
279 : 1614 : int needed_unusual_el_valence( int nPeriodicNum,
280 : : int charge,
281 : : int radical,
282 : : int bonds_valence,
283 : : int actual_bonds_valence,
284 : : int num_H, int
285 : : num_bonds )
286 : : {
287 : : int chem_valence, num_H_expected; /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
288 : : char szElement[4];
289 : :
290 : : /*
291 : : if ( !num_bonds && !num_H )
292 : : return 0;
293 : : */
294 : :
295 [ + + + - ]: 1614 : if (num_bonds && get_element_chemical_symbol( nPeriodicNum, szElement ) != -1)
296 : : {
297 : 1514 : num_H_expected = get_num_H( szElement, 0, NULL, charge, radical, actual_bonds_valence, 0, 0, 0, 0 );
298 : : }
299 : : else
300 : : {
301 : 100 : num_H_expected = num_H;
302 : : }
303 : :
304 : 1614 : chem_valence = bonds_valence + num_H;
305 : :
306 : : #if ( (BUILD_WITH_ENG_OPTIONS==1) && (SDF_OUTPUT_HETERO_VALENCE==1) )
307 : : if ((nPeriodicNum == 1 && chem_valence != 1) /* H */ || (nPeriodicNum == 6 && chem_valence != 4) /* C */ ||
308 : : (nPeriodicNum != 1 && nPeriodicNum != 6) || charge || radical) /* djb-rwth: addressing LLVM warning */
309 : : {
310 : : return chem_valence ? chem_valence : -1;
311 : : }
312 : : else
313 : : {
314 : : return 0;
315 : : }
316 : : #else
317 : : {
318 : : int i, num_found, num_found_known, rad_adj, known_chem_valence, exact_found;
319 : :
320 [ + - + - : 3228 : if (charge < MIN_ATOM_CHARGE || charge > MAX_ATOM_CHARGE ||
+ + ]
321 [ + + ]: 3188 : !get_el_valence( nPeriodicNum, charge, 0 ) ||
322 [ + + + + ]: 3060 : if_skip_add_H( nPeriodicNum ) || bonds_valence != actual_bonds_valence ||
323 : : num_H_expected != num_H)
324 : : {
325 [ + + + + : 170 : if ( !num_H && !num_H_expected && bonds_valence == actual_bonds_valence )
+ + ]
326 : : {
327 : 96 : return 0; /* no H */
328 : : }
329 : 74 : return chem_valence; /* needs to add H-atoms */
330 : : }
331 : :
332 : : /* take into account radical */
333 [ - + ]: 1444 : if ( radical == RADICAL_DOUBLET )
334 : : {
335 : 0 : rad_adj = 1;
336 : : }
337 [ - + ]: 1444 : else if ( radical == RADICAL_TRIPLET )
338 : : {
339 : 0 : rad_adj = 2;
340 : : }
341 : : else
342 : : {
343 : 1444 : rad_adj = 0;
344 : : }
345 : :
346 : 1444 : num_found_known = 0;
347 : 1444 : num_found = 0;
348 : 1444 : exact_found = 0;
349 : :
350 [ + - ]: 1452 : for ( i = 0; i < MAX_NUM_VALENCES; i++ )
351 : : {
352 [ + - ]: 1452 : if (0 < ( known_chem_valence = get_el_valence( nPeriodicNum, charge, i ) ) &&
353 [ + + ]: 1452 : bonds_valence <= ( known_chem_valence -= rad_adj ))
354 : : {
355 : : /* found known valence that fits without H */
356 : 1444 : num_found_known++;
357 [ + - ]: 1444 : if ( known_chem_valence <= chem_valence )
358 : : {
359 : : /* known valence is large enough to accommodate (implicit) H */
360 : 1444 : num_found++;
361 : : }
362 [ + - ]: 1444 : if ( known_chem_valence == chem_valence )
363 : : {
364 : 1444 : exact_found = 1;
365 : 1444 : break;
366 : : }
367 : : }
368 : : }
369 : :
370 [ + - - + ]: 1444 : return ( exact_found && 1 == num_found && 1 == num_found_known )
371 : : ? 0
372 [ + - - - ]: 2888 : : chem_valence ? chem_valence : -1; /* needs zero */
373 : : }
374 : : #endif
375 : : }
376 : :
377 : :
378 : : /****************************************************************************
379 : : Output valence that does not fit any known valences
380 : : ****************************************************************************/
381 : 4686 : int detect_unusual_el_valence( int nPeriodicNum,
382 : : int charge,
383 : : int radical,
384 : : int bonds_valence,
385 : : int num_H,
386 : : int num_bonds )
387 : : {
388 : : int i, chem_valence, rad_adj, known_chem_valence;
389 : :
390 [ + + + + ]: 4686 : if ( !num_bonds && !num_H )
391 : : {
392 : 65 : return 0;
393 : : }
394 : :
395 [ + - - + ]: 4621 : if ( charge < MIN_ATOM_CHARGE || charge > MAX_ATOM_CHARGE )
396 : : {
397 [ # # ]: 0 : if ( bonds_valence == num_bonds )
398 : : {
399 : 0 : return 0; /* all single bonds */
400 : : }
401 : 0 : return bonds_valence;
402 : : }
403 : :
404 [ + + + - ]: 4621 : if (!get_el_valence( nPeriodicNum, charge, 0 ) && bonds_valence == num_bonds)
405 : : {
406 : 5 : return 0;
407 : : }
408 : :
409 : 4616 : chem_valence = bonds_valence + num_H;
410 : 4616 : rad_adj = 0;
411 : :
412 : : /* take into account radical */
413 [ - + ]: 4616 : if ( radical == RADICAL_DOUBLET )
414 : : {
415 : 0 : rad_adj = 1;
416 : : }
417 [ + - - + ]: 4616 : else if ( radical == RADICAL_TRIPLET || radical == RADICAL_SINGLET )
418 : : {
419 : 0 : rad_adj = 2;
420 : : }
421 : :
422 [ + + ]: 4870 : for ( i = 0; i < MAX_NUM_VALENCES; i++ )
423 : : {
424 [ + + ]: 4839 : if (0 < ( known_chem_valence = get_el_valence( nPeriodicNum, charge, i ) - rad_adj ))
425 : : {
426 [ + + ]: 4718 : if ( known_chem_valence == chem_valence )
427 : : {
428 : 4585 : return 0;
429 : : }
430 : : }
431 : : }
432 : :
433 : 31 : return chem_valence;
434 : : }
435 : :
436 : :
437 : : /****************************************************************************
438 : : Return element type
439 : : ****************************************************************************/
440 : 2005 : int get_el_type( int nPeriodicNum )
441 : : {
442 : 2005 : return ElData[nPeriodicNum + 1].nType;
443 : : }
444 : :
445 : :
446 : : /****************************************************************************
447 : : Check if element is metal
448 : : ****************************************************************************/
449 : 8424 : int is_el_a_metal( int nPeriodicNum )
450 : : {
451 : 8424 : return 0 != ( ElData[nPeriodicNum + 1].nType & IS_METAL );
452 : : }
453 : :
454 : :
455 : : /*#ifndef TARGET_API_LIB*/
456 : :
457 : :
458 : : /****************************************************************************
459 : : Extract radicals and charges
460 : : ****************************************************************************/
461 : 0 : int extract_charges_and_radicals( char *elname, int *pnRadical, int *pnCharge )
462 : : {
463 : : char *q, *r, *p;
464 : 0 : int nCharge = 0, nRad = 0, charge_len = 0, k, nVal, nSign, nLastSign = 1; /* djb-rwth: removing redundant variables */
465 : :
466 : 0 : p = elname;
467 : :
468 : : /* extract radicals & charges */
469 [ # # ]: 0 : while ( (q = strpbrk(p, "+-^")) ) /* djb-rwth: addressing LLVM warning */
470 : : {
471 [ # # # ]: 0 : switch ( *q )
472 : : {
473 : 0 : case '+':
474 : : case '-':
475 [ # # # # ]: 0 : for (k = 0, nVal = 0; ( nSign = ( '+' == q[k] ) ) || ( nSign = -( '-' == q[k] ) ); k++)
476 : : {
477 : 0 : nVal += ( nLastSign = nSign );
478 : 0 : charge_len++;
479 : : }
480 [ # # ]: 0 : if ((nSign = (int)strtol(q + k, &r, 10))) /* djb-rwth: addressing LLVM warning */
481 : : {
482 : : /* fixed 12-5-2001 */
483 : 0 : nVal += nLastSign * ( nSign - 1 );
484 : : }
485 : 0 : charge_len = (int) ( r - q );
486 : 0 : nCharge += nVal;
487 : 0 : break;
488 : : /* case '.': */ /* singlet '.' may be confused with '.' in formulas like CaO.H2O */
489 : 0 : case '^':
490 : 0 : nRad = 1; /* doublet here is 1. See below */
491 : 0 : charge_len = 1;
492 [ # # ]: 0 : for (k = 1; q[0] == q[k]; k++)
493 : : {
494 : 0 : nRad++;
495 : 0 : charge_len++;
496 : : }
497 : 0 : break;
498 : : }
499 : 0 : memmove(q, q + charge_len, strlen(q + charge_len) + 1);
500 : : }
501 : :
502 : : /* djb-rwth: removing redundant code */
503 : :
504 : : /* radical */
505 [ # # # # ]: 0 : if (( q = strrchr( p, ':' ) ) && !q[1])
506 : : {
507 : 0 : nRad = RADICAL_SINGLET;
508 : 0 : q[0] = '\0';
509 : : /* djb-rwth: removing redundant code */
510 : : }
511 : : else
512 : : {
513 [ # # # # ]: 0 : while (( q = strrchr( p, '.' ) ) && !q[1])
514 : : {
515 : 0 : nRad++;
516 : 0 : q[0] = '\0';
517 : : /* djb-rwth: removing redundant code */
518 : : }
519 : :
520 [ # # ]: 0 : nRad = nRad == 1 ? RADICAL_DOUBLET :
521 [ # # ]: 0 : nRad == 2 ? RADICAL_TRIPLET : 0;
522 : : }
523 : :
524 : 0 : *pnRadical = nRad;
525 : 0 : *pnCharge = nCharge;
526 : :
527 [ # # # # ]: 0 : return ( nRad || nCharge );
528 : : }
529 : :
530 : :
531 : : /*#endif*/
532 : :
533 : :
534 : : /****************************************************************************/
535 : 0 : int extract_H_atoms( char *elname, S_CHAR num_iso_H[] )
536 : : {
537 : : int i, len, c, k, num_H, val;
538 : : char* q;
539 : 0 : char elname1 = '\0';
540 : :
541 : 0 : i = 0;
542 : 0 : num_H = 0;
543 : 0 : len = (int) strlen( elname );
544 : 0 : c = UCINT elname[0];
545 : :
546 [ # # ]: 0 : if ( len > 1 )
547 : : {
548 : 0 : elname1 = elname[1];
549 : : }
550 : :
551 [ # # ]: 0 : while ( i < len )
552 : : {
553 [ # # # # ]: 0 : switch ( c )
554 : : {
555 : 0 : case 'H':
556 : 0 : k = 0;
557 : 0 : break;
558 : 0 : case 'D':
559 : 0 : k = 1;
560 : 0 : break;
561 : 0 : case 'T':
562 : 0 : k = 2;
563 : 0 : break;
564 : 0 : default:
565 : 0 : k = -1;
566 : 0 : break;
567 : : }
568 : :
569 : 0 : q = elname + i + 1; /* pointer to the next to elname[i] character */
570 : 0 : c = UCINT q[0];
571 : :
572 [ # # # # ]: 0 : if (k >= 0 && !islower( c ))
573 : : {
574 : : /* found a hydrogen */
575 [ # # ]: 0 : if (isdigit( c ))
576 : : {
577 : 0 : val = (int) strtol( q, &q, 10 );
578 : : /* q = pointer to the next to number of hydrogen atom(s) character */
579 : : }
580 : : else
581 : : {
582 : 0 : val = 1;
583 : : }
584 [ # # ]: 0 : if ( k )
585 : : {
586 : 0 : num_iso_H[k] += val;
587 : : }
588 : : else
589 : : {
590 : 0 : num_H += val;
591 : : }
592 : :
593 : : /* remove the hydrogen atom from the string */
594 : 0 : len -= (int) ( q - elname ) - i;
595 : 0 : memmove(elname + i, q, (long long)len + 1); /* djb-rwth: cast operator added */
596 : : /* c = UCINT elname[i]; */
597 : : }
598 : : else
599 : : {
600 : 0 : i++;
601 : : }
602 : :
603 : 0 : c = UCINT elname[i]; /* moved here 11-04-2002 */
604 : : }
605 : :
606 : 0 : len = (int) strlen( elname );
607 [ # # ]: 0 : if (len == 2)
608 : : {
609 [ # # ]: 0 : if ( elname[1] != elname1 )
610 : : /* Error, incorrect 2nd char of elname appears after 'subtracting' {H,D,T} */
611 : : /* See a bug reported to inchi-discuss by A. Dalke for alias atom "pH4d" */
612 : : /*^^^ 2017-01-06 */
613 : 0 : elname[1] = '?';
614 : : }
615 : :
616 : 0 : return num_H;
617 : : }
618 : :
619 : :
620 : : /****************************************************************************
621 : : Return number of attached hydrogens
622 : : ****************************************************************************/
623 : 2316 : int get_num_H( const char* elname,
624 : : int inp_num_H,
625 : : S_CHAR inp_num_iso_H[],
626 : : int charge,
627 : : int radical,
628 : : int chem_bonds_valence,
629 : : int atom_input_valence,
630 : : int bAliased,
631 : : int bDoNotAddH,
632 : : int bHasMetalNeighbor )
633 : : {
634 : 2316 : int val, i, el_number, num_H = 0, num_iso_H;
635 : : static int intl_el_number_N = 0, intl_el_number_S=0, intl_el_number_O=0, intl_el_number_C=0;
636 : :
637 [ + + ]: 2316 : if ( !intl_el_number_N )
638 : : {
639 : 6 : intl_el_number_N = el_number_in_internal_ref_table( "N" );
640 : : }
641 [ + + ]: 2316 : if ( !intl_el_number_S )
642 : : {
643 : 6 : intl_el_number_S = el_number_in_internal_ref_table( "S" );
644 : : }
645 [ + + ]: 2316 : if ( !intl_el_number_O )
646 : : {
647 : 6 : intl_el_number_O = el_number_in_internal_ref_table( "O" );
648 : : }
649 [ + + ]: 2316 : if ( !intl_el_number_C )
650 : : {
651 : 6 : intl_el_number_C = el_number_in_internal_ref_table( "C" );
652 : : }
653 : :
654 : :
655 : : /* atom_input_valence (cValence) cannot be specified in case of */
656 : : /* aliased MOLFile atom with known inp_num_H or inp_num_iso_H[] */
657 : :
658 [ - + ]: 2316 : if ( bAliased )
659 : : {
660 : 0 : num_H = inp_num_H;
661 : : }
662 [ + + + - ]: 2316 : else if (atom_input_valence && atom_input_valence != 15)
663 : : {
664 : 24 : num_H = inchi_max( 0, atom_input_valence - chem_bonds_valence );
665 : : }
666 [ - + ]: 2292 : else if ( atom_input_valence == 15 )
667 : : {
668 : : /* Explicit zero valence (V3000 VAL=-1 / V2000 valence field 15): the */
669 : : /* atom must carry neither implicit H nor (chemically) any bonds. Honour */
670 : : /* the zero valence regardless of whether bonds were drawn, instead of */
671 : : /* treating 15 as a literal target valence and adding 15 - chem_bonds_valence */
672 : : /* hydrogens (github #105). */
673 : 0 : num_H = 0;
674 : : }
675 [ + - + - ]: 2292 : else if (MIN_ATOM_CHARGE <= charge &&
676 : 2292 : MAX_ATOM_CHARGE >= charge &&
677 [ + - ]: 2292 : ERR_ELEM != ( el_number = el_number_in_internal_ref_table( elname ) ) &&
678 [ + + + + ]: 2292 : !ElData[el_number].bSkipAddingH && !bDoNotAddH)
679 : : {
680 : : /* add hydrogen atoms according to standard element valence */
681 [ - + - - ]: 2175 : if ( radical && radical != RADICAL_SINGLET )
682 : : {
683 [ # # ]: 0 : if ( (val = ElData[el_number].cValence[NEUTRAL_STATE + charge][0]) ) /* djb-rwth: addressing LLVM warning */
684 : : {
685 : 0 : val -= ( radical == RADICAL_DOUBLET ) ? 1
686 [ # # # # : 0 : : ( radical == RADICAL_SINGLET || radical == RADICAL_TRIPLET ) ? 2 : val;
# # ]
687 : : /* if unknown radical then do not add H */
688 : 0 : num_H = inchi_max( 0, val - chem_bonds_valence );
689 : : }
690 : : }
691 : : else
692 : : {
693 : : /* find the smallest valence that is greater than the sum of the chemical bond valences */
694 : 2175 : for (i = 0;
695 [ + + + + ]: 2187 : ( val = ElData[el_number].cValence[NEUTRAL_STATE + charge][i] ) &&
696 : : val < chem_bonds_valence;
697 : 12 : i++)
698 : : {
699 : : ;
700 : : }
701 : :
702 : : /* special case: do not add H to N(IV), S(III), S+(II), S-(II) */ /* S ions added 2004-05-10 */
703 [ + + + - : 2175 : if ( el_number == intl_el_number_N && !charge && !radical && val == 5 )
+ - - + ]
704 : : {
705 : 0 : val = 3;
706 : : }
707 : : /*else if ( el_number == el_number_N && !charge && !radical && val == 3 &&
708 : : chem_bonds_valence == 2 && bHasMetalNeighbor )
709 : : {
710 : : val = 2;
711 : : }
712 : : */
713 [ + + + - : 2175 : else if ( el_number == intl_el_number_S && !charge && !radical && val == 4 && chem_bonds_valence == 3 )
+ - - + -
- ]
714 : : {
715 : 0 : val = 3;
716 : : }
717 [ - + - - : 2175 : else if ( bHasMetalNeighbor && el_number != intl_el_number_C && val > 0 )
- - ]
718 : : {
719 : 0 : val--;
720 : : }
721 : : /*
722 : : if ( (el_number == el_number_S || el_number == el_number_O) &&
723 : : abs(charge)==1 && !radical && val == 3 && chem_bonds_valence == 2 && bHasMetalNeighbor )
724 : : {
725 : : val = 2;
726 : : }
727 : : else
728 : : */
729 : :
730 : 2175 : num_H = inchi_max( 0, val - chem_bonds_valence );
731 : : }
732 : :
733 : 2175 : num_iso_H = 0;
734 [ + + ]: 2175 : if ( inp_num_iso_H )
735 : : {
736 [ + + ]: 2868 : for ( i = 0; i < NUM_H_ISOTOPES; i++ )
737 : : {
738 : 2151 : num_iso_H += inp_num_iso_H[i];
739 : : }
740 : : }
741 : :
742 : : /* should not happen because atom here is not aliased */
743 [ - + ]: 2175 : if ( num_iso_H )
744 : : {
745 [ # # ]: 0 : if ( num_H >= num_iso_H )
746 : : {
747 : 0 : num_H -= num_iso_H;
748 : : }
749 : : else
750 : : {
751 : 0 : num_H = inp_num_H; /* as requested in the alias */
752 : : /* num_H = (num_iso_H - num_H) % 2; */ /* keep unchanged parity of the total number of H atoms */
753 : : }
754 : : }
755 : :
756 : : /* should not happen because atom here is not aliased */
757 [ - + ]: 2175 : if ( inp_num_H > num_H )
758 : : {
759 : 0 : num_H = inp_num_H; /* as requested in the alias */
760 : : /* num_H = inp_num_H + (inp_num_H - num_H)%2; */ /* keep unchanged parity of the number of non-isotopic H atoms */
761 : : }
762 : : }
763 : : else
764 : : {
765 : 117 : num_H = inp_num_H;
766 : : }
767 : :
768 : 2316 : return num_H;
769 : : }
770 : :
771 : :
772 : : /****************************************************************************/
773 : 0 : int get_atomic_mass_from_elnum( int nAtNum )
774 : : {
775 : 0 : nAtNum -= 1;
776 : :
777 [ # # ]: 0 : if ( 0 < nAtNum )
778 : : {
779 : 0 : nAtNum += 2; /* bypass D, T */
780 : : }
781 : :
782 [ # # # # ]: 0 : if ( 0 <= nAtNum && nAtNum < nElDataLen )
783 : : {
784 : 0 : return (int) ElData[nAtNum].nAtMass;
785 : : }
786 : :
787 : 0 : return 0;
788 : : }
789 : :
790 : :
791 : : /****************************************************************************
792 : : int get_mw(char elname[])
793 : : {
794 : : int i;
795 : :
796 : : for (i=0; i<NUMEL; i++)
797 : : if (strcmp(elname,elements[i])==0)
798 : : return(atomic_wt[i]);
799 : : return(0);
800 : : }
801 : : ****************************************************************************/
802 : :
803 : :
804 : :
805 : : /****************************************************************************/
806 : 0 : int get_atomic_mass( const char *elname )
807 : : {
808 : : int el_number, atw;
809 [ # # ]: 0 : if (ERR_ELEM != ( el_number = el_number_in_internal_ref_table( elname ) ))
810 : : {
811 : 0 : atw = ElData[el_number].nAtMass;
812 : : }
813 : : else
814 : : {
815 : 0 : atw = 0;
816 : : }
817 : :
818 : 0 : return atw;
819 : : }
820 : :
821 : :
822 : : /****************************************************************************
823 : : Check if atom is in the list
824 : : ****************************************************************************/
825 : 2840 : AT_NUMB *is_in_the_list( AT_NUMB *pathAtom, AT_NUMB nNextAtom, int nPathLen )
826 : : {
827 [ + + + + ]: 5572 : for ( ; nPathLen && *pathAtom != nNextAtom; nPathLen--, pathAtom++ )
828 : : {
829 : : ;
830 : : }
831 [ + + ]: 2840 : return nPathLen ? pathAtom : NULL;
832 : : }
833 : :
834 : : /****************************************************************************
835 : : Check if integer is in the list
836 : : ****************************************************************************/
837 : 44 : int *is_in_the_ilist( int *pathAtom, int nNextAtom, int nPathLen )
838 : : {
839 [ + + + + ]: 108 : for ( ; nPathLen && *pathAtom != nNextAtom; nPathLen--, pathAtom++ )
840 : : {
841 : : ;
842 : : }
843 [ + + ]: 44 : return nPathLen ? pathAtom : NULL;
844 : : }
845 : :
846 : :
847 : : /****************************************************************************
848 : : Check if list (of integers) is within the embedding list2
849 : : ****************************************************************************/
850 : 0 : int is_ilist_inside( int *ilist, int nlist, int *ilist2, int nlist2 )
851 : : {
852 : : int k;
853 [ # # ]: 0 : for ( k = 0; k < nlist; k++ )
854 : : {
855 [ # # ]: 0 : if (!is_in_the_ilist( ilist2, ilist[k], nlist2 ))
856 : : {
857 : 0 : return 0;
858 : : }
859 : : }
860 : 0 : return 1;
861 : : }
862 : :
863 : :
864 : : /****************************************************************************/
865 : 0 : int nBondsValToMetal( inp_ATOM* at, int iat )
866 : : {
867 : 0 : int i, bond_type, nVal2Metal = 0; /* djb-rwth: removing redundant variables */
868 : 0 : inp_ATOM* a = at + iat;
869 : :
870 [ # # ]: 0 : for ( i = 0; i < a->valence; i++ )
871 : : {
872 : : /* djb-rwth: removing redundant code */
873 : :
874 [ # # ]: 0 : if (is_el_a_metal( at[(int) a->neighbor[i]].el_number ))
875 : : {
876 : 0 : bond_type = a->bond_type[i];
877 : :
878 [ # # ]: 0 : if ( bond_type <= BOND_TYPE_TRIPLE )
879 : : {
880 : 0 : nVal2Metal += bond_type;
881 : : }
882 : : else
883 : : {
884 : 0 : return -1; /* bond to metal order is not well defined */
885 : : }
886 : : }
887 : : }
888 : :
889 : 0 : return nVal2Metal;
890 : : }
891 : :
892 : :
893 : : /****************************************************************************/
894 : 134 : int num_of_H( inp_ATOM *at, int iat )
895 : : {
896 : : static int el_number_H = (int)EL_NUMBER_H;
897 : 134 : int i, n, num_explicit_H = 0;
898 : 134 : inp_ATOM *a = at + iat;
899 : :
900 [ + + ]: 298 : for (i = 0; i < a->valence; i++)
901 : : {
902 : 164 : n = a->neighbor[i];
903 [ + + - + ]: 164 : num_explicit_H += ( 1 == at[n].valence && el_number_H == at[n].el_number );
904 : : }
905 : :
906 : 134 : return num_explicit_H + NUMH( at, iat );
907 : : }
908 : :
909 : : /****************************************************************************/
910 : : /* Get the element group of an element. The base element rather than the */
911 : : /* periodic group is used to aid readability. */
912 : : /* - NitrogenGroup = 7 (EL_NUMBER_N) */
913 : : /* - OxygenGroup = 8 (EL_NUMBER_O) */
914 : : /* - Cargbon = 6 (EL_NUMBER_C) */
915 : : /****************************************************************************/
916 : 164 : U_CHAR ion_el_group( int el )
917 : : {
918 [ + - + + ]: 164 : switch ( el )
919 : : {
920 : 4 : case EL_NUMBER_C: /* fallthrough */
921 : : #if ( FIX_REM_ION_PAIRS_Si_BUG == 1 )
922 : : case EL_NUMBER_SI:
923 : : #endif
924 : 4 : return EL_NUMBER_C;
925 : 0 : case EL_NUMBER_N: /* fallthrough */
926 : : case EL_NUMBER_P:
927 : : case EL_NUMBER_AS:
928 : : case EL_NUMBER_SB:
929 : 0 : return EL_NUMBER_N;
930 : 142 : case EL_NUMBER_O: /* fallthrough */
931 : : case EL_NUMBER_S:
932 : : case EL_NUMBER_SE:
933 : : case EL_NUMBER_TE:
934 : 142 : return EL_NUMBER_O;
935 : 18 : default:
936 : 18 : return 0;
937 : : }
938 : : }
939 : :
940 : 0 : int has_other_ion_neigh( inp_ATOM *at,
941 : : int iat,
942 : : int iat_ion_neigh)
943 : : {
944 : 0 : int charge = at[iat_ion_neigh].charge;
945 : : int i, neigh;
946 : :
947 [ # # ]: 0 : for ( i = 0; i < at[iat].valence; i++ )
948 : : {
949 : 0 : neigh = at[iat].neighbor[i];
950 : :
951 [ # # # # : 0 : if (neigh != iat_ion_neigh && at[neigh].charge == charge &&
# # ]
952 : 0 : ion_el_group( at[neigh].el_number ))
953 : : {
954 : 0 : return 1;
955 : : }
956 : : }
957 : :
958 : 0 : return 0;
959 : : }
960 : :
961 : :
962 : : /****************************************************************************
963 : : Check if has_other_ion_in_sphere_2
964 : : BFS r=2
965 : : ****************************************************************************/
966 : 0 : int has_other_ion_in_sphere_2( inp_ATOM *at, int iat,
967 : : int iat_ion_neigh )
968 : : {
969 : : #define MAXQ 16
970 : : AT_NUMB q[MAXQ];
971 : 0 : int lenq = 0, lenq2, dist = 0, i = 0, iq, neigh, j, nRet = 0;
972 : 0 : q[lenq++] = iat;
973 : 0 : at[iat].cFlags = 1;
974 : :
975 : 0 : iq = 0;
976 : 0 : dist = 1;
977 : : /* use at->cFlags as an indicator */
978 : :
979 [ # # ]: 0 : while ( dist <= 2 )
980 : : {
981 [ # # ]: 0 : for ( lenq2 = lenq; iq < lenq2; iq++ )
982 : : {
983 : 0 : i = q[iq];
984 : :
985 [ # # ]: 0 : for ( j = 0; j < at[i].valence; j++ )
986 : : {
987 : 0 : neigh = at[i].neighbor[j];
988 : :
989 [ # # ]: 0 : if (!at[neigh].cFlags &&
990 [ # # # # ]: 0 : at[neigh].valence <= 3 &&
991 : 0 : ion_el_group( at[neigh].el_number ))
992 : : {
993 : 0 : q[lenq++] = neigh;
994 : 0 : at[neigh].cFlags = 1;
995 [ # # ]: 0 : if (neigh != iat_ion_neigh &&
996 [ # # ]: 0 : at[iat_ion_neigh].charge == at[neigh].charge)
997 : : {
998 : 0 : nRet++;
999 : : }
1000 : : }
1001 : : }
1002 : : }
1003 : :
1004 : 0 : dist++;
1005 : : }
1006 : :
1007 [ # # ]: 0 : for ( iq = 0; iq < lenq; iq++ )
1008 : : {
1009 : 0 : i = q[iq];
1010 : 0 : at[i].cFlags = 0;
1011 : : }
1012 : :
1013 : 0 : return nRet;
1014 : : }
1015 : :
1016 : :
1017 : : /****************************************************************************/
1018 : 410 : int nNoMetalNumBonds( inp_ATOM *at, int at_no )
1019 : : {
1020 : : int i;
1021 : :
1022 : 410 : inp_ATOM *a = at + at_no;
1023 : 410 : int num_H = NUMH( a, 0 );
1024 : 410 : int std_chem_bonds_valence = get_el_valence( a->el_number, a->charge, 0 );
1025 : :
1026 [ + + ]: 410 : if ( a->chem_bonds_valence + num_H > std_chem_bonds_valence )
1027 : : {
1028 : 90 : int valence_to_metal = 0;
1029 : 90 : int num_bonds_to_metal = 0;
1030 : :
1031 [ + + ]: 370 : for ( i = 0; i < a->valence; i++ )
1032 : : {
1033 [ + + ]: 344 : if (is_el_a_metal( at[(int) a->neighbor[i]].el_number ))
1034 : : {
1035 [ + - ]: 64 : if (( a->bond_type[i] & BOND_TYPE_MASK ) >= BOND_TYPE_ALTERN)
1036 : : {
1037 : 64 : return a->valence; /* fall back */
1038 : : }
1039 : 0 : num_bonds_to_metal++;
1040 : 0 : valence_to_metal += ( a->bond_type[i] & BOND_TYPE_MASK );
1041 : : }
1042 : : }
1043 : :
1044 [ - + ]: 26 : if ( a->chem_bonds_valence + num_H - valence_to_metal == std_chem_bonds_valence )
1045 : : {
1046 : : /* removing bonds to metal produces standard valence */
1047 : 0 : return a->valence - num_bonds_to_metal;
1048 : : }
1049 : : }
1050 : :
1051 : : #if ( S_VI_O_PLUS_METAL_FIX_BOND == 1 )
1052 : : else
1053 : : {
1054 [ - + - - ]: 320 : if (1 == a->charge && 2 == get_endpoint_valence( a->el_number ) &&
1055 [ # # ]: 0 : a->chem_bonds_valence + num_H == std_chem_bonds_valence)
1056 : : {
1057 : 0 : int valence_to_metal = 0;
1058 : 0 : int num_bonds_to_metal = 0;
1059 [ # # ]: 0 : for ( i = 0; i < a->valence; i++ )
1060 : : {
1061 [ # # ]: 0 : if (is_el_a_metal( at[(int) a->neighbor[i]].el_number ))
1062 : : {
1063 [ # # ]: 0 : if (( a->bond_type[i] & BOND_TYPE_MASK ) >= BOND_TYPE_ALTERN)
1064 : : {
1065 : 0 : return a->valence; /* fall back */
1066 : : }
1067 : 0 : num_bonds_to_metal++;
1068 : 0 : valence_to_metal += ( a->bond_type[i] & BOND_TYPE_MASK );
1069 : : }
1070 : : }
1071 [ # # ]: 0 : if ( 1 == valence_to_metal )
1072 : : {
1073 : : /* removing bonds to metal produces standard valence */
1074 : 0 : return a->valence - num_bonds_to_metal;
1075 : : }
1076 : : }
1077 : : }
1078 : : #endif
1079 : :
1080 : 346 : return a->valence;
1081 : : }
1082 : :
1083 : :
1084 : : /****************************************************************************/
1085 : 154 : int nNoMetalBondsValence( inp_ATOM *at, int at_no )
1086 : : {
1087 : : int i;
1088 : :
1089 : 154 : inp_ATOM *a = at + at_no;
1090 : 154 : int num_H = NUMH( a, 0 );
1091 : 154 : int std_chem_bonds_valence = get_el_valence( a->el_number, a->charge, 0 );
1092 : :
1093 [ + + ]: 154 : if ( a->chem_bonds_valence + num_H > std_chem_bonds_valence )
1094 : : {
1095 : 10 : int valence_to_metal = 0;
1096 : : /*int num_bonds_to_metal = 0;*/
1097 : :
1098 [ + + ]: 50 : for ( i = 0; i < a->valence; i++ )
1099 : : {
1100 [ - + ]: 40 : if (is_el_a_metal( at[(int) a->neighbor[i]].el_number ))
1101 : : {
1102 [ # # ]: 0 : if (( a->bond_type[i] & BOND_TYPE_MASK ) >= BOND_TYPE_ALTERN)
1103 : : {
1104 : 0 : return a->valence; /* fall back */
1105 : : }
1106 : : /* num_bonds_to_metal ++;*/
1107 : 0 : valence_to_metal += ( a->bond_type[i] & BOND_TYPE_MASK );
1108 : : }
1109 : : }
1110 : :
1111 [ - + ]: 10 : if ( a->chem_bonds_valence + num_H - valence_to_metal == std_chem_bonds_valence )
1112 : : {
1113 : : /* removing bonds to metal produces standard valence */
1114 : 0 : return a->chem_bonds_valence - valence_to_metal;
1115 : : }
1116 : : }
1117 : :
1118 : : #if ( S_VI_O_PLUS_METAL_FIX_BOND == 1 )
1119 [ - + - - ]: 144 : else if (1 == a->charge && 2 == get_endpoint_valence( a->el_number ) &&
1120 [ # # ]: 0 : a->chem_bonds_valence + num_H == std_chem_bonds_valence)
1121 : : {
1122 : :
1123 : 0 : int valence_to_metal = 0;
1124 : : /* int num_bonds_to_metal = 0;*/
1125 : :
1126 [ # # ]: 0 : for ( i = 0; i < a->valence; i++ )
1127 : : {
1128 [ # # ]: 0 : if (is_el_a_metal( at[(int) a->neighbor[i]].el_number ))
1129 : : {
1130 [ # # ]: 0 : if (( a->bond_type[i] & BOND_TYPE_MASK ) >= BOND_TYPE_ALTERN)
1131 : : {
1132 : 0 : return a->valence; /* fall back */
1133 : : }
1134 : : /* num_bonds_to_metal ++;*/
1135 : 0 : valence_to_metal += ( a->bond_type[i] & BOND_TYPE_MASK );
1136 : : }
1137 : : }
1138 : :
1139 [ # # ]: 0 : if (1 == valence_to_metal)
1140 : : {/* removing bonds to metal produces standard valence */
1141 : 0 : return a->chem_bonds_valence - valence_to_metal;
1142 : : }
1143 : : }
1144 : : #endif
1145 : :
1146 : 154 : return a->chem_bonds_valence;
1147 : : }
1148 : :
1149 : :
1150 : : /****************************************************************************/
1151 : 112 : int nNoMetalNeighIndex( inp_ATOM *at, int at_no )
1152 : : {
1153 : : int i;
1154 : :
1155 : 112 : inp_ATOM* a = at + at_no;
1156 : :
1157 [ + - ]: 112 : for ( i = 0; i < a->valence; i++ )
1158 : : {
1159 [ + - ]: 112 : if (!is_el_a_metal( at[(int) a->neighbor[i]].el_number ))
1160 : : {
1161 : 112 : return i;
1162 : : }
1163 : : }
1164 : :
1165 : 0 : return -1;
1166 : : }
1167 : :
1168 : :
1169 : : /****************************************************************************/
1170 : 0 : int nNoMetalOtherNeighIndex( inp_ATOM *at, int at_no, int cur_neigh )
1171 : : {
1172 : : int i, neigh;
1173 : :
1174 : 0 : inp_ATOM* a = at + at_no;
1175 : :
1176 [ # # ]: 0 : for ( i = 0; i < a->valence; i++ )
1177 : : {
1178 : 0 : neigh = (int) a->neighbor[i];
1179 : :
1180 [ # # # # ]: 0 : if (neigh != cur_neigh && !is_el_a_metal( at[neigh].el_number ))
1181 : : {
1182 : 0 : return i;
1183 : : }
1184 : : }
1185 : :
1186 : 0 : return -1;
1187 : : }
1188 : :
1189 : :
1190 : : /****************************************************************************/
1191 : 0 : int nNoMetalOtherNeighIndex2( inp_ATOM *at,
1192 : : int at_no,
1193 : : int cur_neigh,
1194 : : int cur_neigh2 )
1195 : : {
1196 : : int i, neigh;
1197 : :
1198 : 0 : inp_ATOM* a = at + at_no;
1199 : :
1200 [ # # ]: 0 : for ( i = 0; i < a->valence; i++ )
1201 : : {
1202 : 0 : neigh = (int) a->neighbor[i];
1203 : :
1204 [ # # # # : 0 : if (neigh != cur_neigh && neigh != cur_neigh2 && !is_el_a_metal( at[neigh].el_number ))
# # ]
1205 : : {
1206 : 0 : return i;
1207 : : }
1208 : : }
1209 : :
1210 : 0 : return -1;
1211 : : }
1212 : :
1213 : :
1214 : :
1215 : : #ifndef COMPILE_ANSI_ONLY
1216 : :
1217 : :
1218 : : /****************************************************************************/
1219 : : int MakeRemovedProtonsString( int nNumRemovedProtons,
1220 : : NUM_H *nNumExchgIsotopicH,
1221 : : NUM_H *nNumRemovedProtonsIsotopic,
1222 : : int bIsotopic,
1223 : : char *szRemovedProtons,
1224 : : int *num_removed_iso_H )
1225 : : {
1226 : : int i, j, len, num;
1227 : :
1228 : : len = 0;
1229 : :
1230 : : if ( nNumRemovedProtons )
1231 : : {
1232 : : len = sprintf(szRemovedProtons, "Proton balance: %c %d H+",
1233 : : nNumRemovedProtons >= 0 ? '+' : '-', abs(nNumRemovedProtons));
1234 : : }
1235 : :
1236 : : if (bIsotopic && ( nNumRemovedProtonsIsotopic || nNumExchgIsotopicH ))
1237 : : {
1238 : :
1239 : : for ( i = 0, j = 0; i < NUM_H_ISOTOPES; i++ )
1240 : : {
1241 : :
1242 : : num = ( nNumExchgIsotopicH ? nNumExchgIsotopicH[i] : 0 ) +
1243 : : ( nNumRemovedProtonsIsotopic ? nNumRemovedProtonsIsotopic[i] : 0 );
1244 : :
1245 : : if ( num )
1246 : : {
1247 : : len += sprintf(szRemovedProtons + len, "%s %d^%dH",
1248 : : j ? ", " : " [ removed ", num, i + 1);
1249 : : j++;
1250 : : }
1251 : : }
1252 : :
1253 : : if ( j )
1254 : : {
1255 : : len += sprintf(szRemovedProtons + len, " ]");
1256 : : if ( num_removed_iso_H )
1257 : : {
1258 : : *num_removed_iso_H = j;
1259 : : }
1260 : : }
1261 : : }
1262 : :
1263 : : if ( !len )
1264 : : {
1265 : : szRemovedProtons[0] = '\0';
1266 : : }
1267 : :
1268 : : return len;
1269 : : }
1270 : : #endif
1271 : :
1272 : :
1273 : : /****************************************************************************/
1274 : 5164 : int get_endpoint_valence( U_CHAR el_number )
1275 : : {
1276 [ + + + ]: 5164 : switch (el_number)
1277 : : {
1278 : 1253 : case EL_NUMBER_O: /* fallthrough */
1279 : : case EL_NUMBER_S:
1280 : : case EL_NUMBER_SE:
1281 : : case EL_NUMBER_TE:
1282 : 1253 : return 2;
1283 : 107 : case EL_NUMBER_N:
1284 : 107 : return 3;
1285 : 3804 : default:
1286 : 3804 : return 0;
1287 : : }
1288 : : }
1289 : :
1290 : :
1291 : : #if ( KETO_ENOL_TAUT == 1 ) /* post v.1 feature */
1292 : :
1293 : :
1294 : : /****************************************************************************/
1295 : 0 : int get_endpoint_valence_KET( U_CHAR el_number )
1296 : : {
1297 [ # # # ]: 0 : switch (el_number)
1298 : : {
1299 : 0 : case EL_NUMBER_C:
1300 : 0 : return 4;
1301 : 0 : case EL_NUMBER_O:
1302 : 0 : return 2;
1303 : 0 : default:
1304 : 0 : return 0;
1305 : : }
1306 : : }
1307 : : #endif
1308 : :
1309 : :
1310 : : /*
1311 : : MEMORY MANAGEMENT
1312 : : */
1313 : :
1314 : :
1315 : : #ifndef inchi_malloc
1316 : : /****************************************************************************/
1317 : : void *inchi_malloc( size_t c )
1318 : : {
1319 : : return malloc( c );
1320 : : }
1321 : : #endif
1322 : :
1323 : :
1324 : : #ifndef inchi_calloc
1325 : : /****************************************************************************/
1326 : : void *inchi_calloc( size_t c, size_t n )
1327 : : {
1328 : : return calloc( c, n );
1329 : : }
1330 : : #endif
1331 : :
1332 : :
1333 : : #ifndef inchi_free
1334 : : /****************************************************************************/
1335 : : void inchi_free( void *p )
1336 : : {
1337 : : if ( p )
1338 : : {
1339 : : inchi_free( p ); /*added check if zero*/
1340 : : }
1341 : : }
1342 : : #endif
1343 : :
1344 : :
1345 : : /*
1346 : : STRINGS AND TEXT HANDLING
1347 : : */
1348 : :
1349 : :
1350 : : /****************************************************************************
1351 : : Remove leading & trailing spaces, replace consecutive
1352 : : spaces with a single space.
1353 : : ****************************************************************************/
1354 : 0 : int normalize_string( char* name )
1355 : : {
1356 : : int i, len, n;
1357 : :
1358 : 0 : len = (int) strlen( name );
1359 : :
1360 [ # # ]: 0 : for ( i = 0, n = 0; i < len; i++ )
1361 : : {
1362 [ # # ]: 0 : if (isspace( UCINT name[i] ) /*|| !isprint( UCINT name[i] )*/)
1363 : : {
1364 : 0 : name[i] = ' '; /* exterminate tabs !!! */
1365 : 0 : n++;
1366 : : }
1367 : : else
1368 : : {
1369 [ # # ]: 0 : if ( n > 0 )
1370 : : {
1371 : 0 : memmove((void*)&name[i - n], (void*)&name[i], (long long)len - (long long)i + 1); /* djb-rwth: cast operators added */
1372 : 0 : i -= n;
1373 : 0 : len -= n;
1374 : : }
1375 : 0 : n = -1;
1376 : : }
1377 : : }
1378 [ # # ]: 0 : if ( n == len ) /* empty line */
1379 : : {
1380 : 0 : name[len = 0] = '\0';
1381 : : }
1382 [ # # # # ]: 0 : else if ( ++n && n <= len )
1383 : : {
1384 : 0 : len -= n;
1385 : 0 : name[len] = '\0';
1386 : : }
1387 : :
1388 : 0 : return len;
1389 : : }
1390 : :
1391 : :
1392 : : /****************************************************************************
1393 : : Replace non-ASCII characters with '.', return number of replacements
1394 : : ****************************************************************************/
1395 : 0 : int dotify_non_printable_chars( char *line )
1396 : : {
1397 : 0 : int i, c, num = 0;
1398 : :
1399 [ # # ]: 0 : if ( line )
1400 : : {
1401 [ # # ]: 0 : for ( i = 0; (c = UCINT line[i]); i++ ) /* djb-rwth: addressing LLVM warning */
1402 : : {
1403 : : /* assuming ASCII charset */
1404 [ # # # # ]: 0 : if ( c < ' ' || c >= 0x7F )
1405 : : {
1406 : 0 : line[i] = '.';
1407 : 0 : num++;
1408 : : }
1409 : : }
1410 : : }
1411 : :
1412 : 0 : return num;
1413 : : }
1414 : :
1415 : :
1416 : : /****************************************************************************
1417 : : Reads char sequence pointed to by *pstring ( char *p = *ppstring) for
1418 : : not more than maxlen bytes) to 'field' up to first occurrence of any of
1419 : : delimiters in 'delims' or end of line, whichever occurs first.
1420 : : Sets *pstring to point to character which matches delimiter.
1421 : : Returns number of bytes copied, -1 on error.
1422 : : ****************************************************************************/
1423 : 8070 : int read_upto_delim( char **pstring, char *field, int maxlen, char* delims )
1424 : : {
1425 : : int i, n;
1426 : 8070 : char* p = *pstring;
1427 : :
1428 [ + + ]: 8070 : if ( !p )
1429 : : {
1430 : 1 : return -1;
1431 : : }
1432 : :
1433 : : /* skip leading spaces */
1434 [ + - + + ]: 14026 : for (i = 0; p[i] && isspace( UCINT p[i] ); i++)
1435 : : {
1436 : : ;
1437 : : }
1438 : 8069 : p += i;
1439 : :
1440 : : /* read up to next delim or eol */
1441 : 8069 : n = 0;
1442 [ + + + + ]: 31053 : while (p[n] && !is_matching_any_delim( p[n], delims ))
1443 : : {
1444 : 22984 : n++;
1445 : : }
1446 : :
1447 [ - + ]: 8069 : if ( n + 1 > maxlen )
1448 : : {
1449 : 0 : return -1;
1450 : : }
1451 : :
1452 : 8069 : mystrncpy( field, p, n + 1 );
1453 : 8069 : field[n + 1] = '\0';
1454 : :
1455 [ + + ]: 8069 : if ( !p[n] )
1456 : : {
1457 : : /* reached EOL */
1458 : 1414 : *pstring = NULL;
1459 : : }
1460 : : else
1461 : : {
1462 : : /* advance reading pos */
1463 : 6655 : *pstring = *pstring + i + n;
1464 : : }
1465 : :
1466 : 8069 : return n;
1467 : : }
1468 : :
1469 : :
1470 : : /****************************************************************************
1471 : : Check if a character is in the list of possible delimiters
1472 : : NB: same as isspace if delims is " \t\n\v\f\r"
1473 : : (0x20 and 0x09-0x0D)
1474 : : ****************************************************************************/
1475 : 29639 : int is_matching_any_delim( char c, char* delims )
1476 : : {
1477 : 29639 : int ic = UCINT c;
1478 [ + + ]: 174455 : while ( *delims )
1479 : : {
1480 [ + + ]: 151471 : if ( ic == *delims )
1481 : : {
1482 : 6655 : return 1;
1483 : : }
1484 : 144816 : delims++;
1485 : : }
1486 : 22984 : return 0;
1487 : : }
1488 : :
1489 : :
1490 : : /****************************************************************************
1491 : : Remove trailing spaces
1492 : : ****************************************************************************/
1493 : 1832 : void remove_trailing_spaces( char* p )
1494 : : {
1495 : : int len;
1496 [ + - + + ]: 22446 : for (len = (int) strlen( p ) - 1; len >= 0 && isspace( UCINT p[len] ); len--)
1497 : : {
1498 : : ;
1499 : : }
1500 : 1832 : p[++len] = '\0';
1501 : 1832 : }
1502 : :
1503 : :
1504 : : /****************************************************************************/
1505 : 2751 : void remove_one_lf( char* p )
1506 : : {
1507 : : size_t len;
1508 [ + - + - : 2751 : if (p && 0 < ( len = strlen( p ) ) && p[len - 1] == '\n')
+ + ]
1509 : : {
1510 : 862 : p[len - 1] = '\0';
1511 [ + + - + ]: 862 : if ( len >= 2 && p[len - 2] == '\r' )
1512 : : {
1513 : 0 : p[len - 2] = '\0';
1514 : : }
1515 : : }
1516 : 2751 : }
1517 : :
1518 : :
1519 : : /****************************************************************************
1520 : : Copies up to maxlen characters INCLUDING end null from source to target
1521 : : Fills out the rest of the target with null bytes
1522 : :
1523 : : protected from non-zero-terminated source and overlapped target/source.
1524 : : ****************************************************************************/
1525 : 10799 : int mystrncpy( char *target, const char *source, unsigned maxlen )
1526 : : {
1527 : : const char *p;
1528 : : unsigned len, source_len;
1529 : :
1530 [ + - + - : 10799 : if ( target == NULL || maxlen == 0 || source == NULL )
- + ]
1531 : : {
1532 : 0 : return 0;
1533 : : }
1534 : :
1535 : : /* giallu: PR #163 */
1536 : : /* Find actual source length first to limit memchr search */
1537 : 10799 : source_len = (unsigned)strlen(source);
1538 : :
1539 [ + + ]: 10799 : if (source_len < maxlen)
1540 : : {
1541 : : /* Source is shorter than maxlen, use actual source length */
1542 : 3990 : len = source_len;
1543 : : }
1544 [ - + ]: 6809 : else if ((p = (const char*)memchr(source, 0, maxlen))) /* djb-rwth: addressing LLVM warning */
1545 : : {
1546 : : /* maxlen does not include the found zero termination */
1547 : 0 : len = (int) ( p - source );
1548 : : }
1549 : : else
1550 : : {
1551 : : /* reduced length does not include one more byte for zero termination */
1552 : 6809 : len = maxlen - 1;
1553 : : }
1554 : :
1555 [ + - ]: 10799 : if ( len )
1556 : : {
1557 : 10799 : memmove(target, source, len);
1558 : : }
1559 : :
1560 : 10799 : memset(target + len, 0, maxlen - len); /* zero termination */ /* djb-rwth: memset_s C11/Annex K variant? */
1561 : :
1562 : 10799 : return 1;
1563 : : }
1564 : :
1565 : :
1566 : : /****************************************************************************
1567 : : Remove leading and trailing white spaces
1568 : : ****************************************************************************/
1569 : 4878 : char* lrtrim( char *p, int* nLen )
1570 : : {
1571 : 4878 : int i, len = 0;
1572 : :
1573 [ + - + - ]: 4878 : if (p && ( len = (int) strlen( p ) ))
1574 : : {
1575 [ + - + - : 27035 : for (i = 0; i < len && is_ascii( p[i] ) && isspace( p[i] ); i++)
+ + ]
1576 : : {
1577 : : ;
1578 : : }
1579 [ + + ]: 4878 : if ( i )
1580 : : {
1581 : 4875 : len -= i; /* djb-rwth: variable has to be decreased before memmove */
1582 : 4875 : (memmove)(p, p + i, ((long long)len + 1)); /* djb-rwth: now cast operator can be added */
1583 : : }
1584 : :
1585 [ + - + - : 4878 : for (; 0 < len && is_ascii( p[len - 1] ) && isspace( p[len - 1] ); len--)
- + ]
1586 : : {
1587 : : ;
1588 : : }
1589 : 4878 : p[len] = '\0';
1590 : : }
1591 : :
1592 [ + - ]: 4878 : if ( nLen )
1593 : : {
1594 : 4878 : *nLen = len;
1595 : : }
1596 : :
1597 : 4878 : return p;
1598 : : }
1599 : :
1600 : :
1601 : : /****************************************************************************
1602 : : Extract InChI substring embedded into a longer string.
1603 : :
1604 : : InChI should start from "InChI=".
1605 : :
1606 : : As for the end of InChI,
1607 : : consider that according to
1608 : : http://info-uri.info/registry/OAIHandler?verb=GetRecord&metadataPrefix=reg&identifier=info:inchi/
1609 : : an InChI identifier may contain the following characters:
1610 : : A-Z
1611 : : a-z
1612 : : 0-9
1613 : : ()*+,-./;=?@
1614 : :
1615 : : Here we treat any character not conforming this specification as a whitespace
1616 : : which marks the end of the InChI string.
1617 : : For example:
1618 : : "InChI=1/Ar%"
1619 : : "InChI=1/Ar\n"
1620 : : "InChI=1/Ar\r\t"
1621 : : all will be trimmed to
1622 : : "InChI=1/Ar"
1623 : :
1624 : : ****************************************************************************/
1625 : 1 : void extract_inchi_substring( char ** buf, const char *str, size_t slen )
1626 : : {
1627 : : size_t i;
1628 : : const char *p;
1629 : : char* bufp;
1630 : : char pp;
1631 : :
1632 : 1 : bufp = *buf;
1633 : 1 : *buf = NULL;
1634 : :
1635 [ - + ]: 1 : if ( str == NULL )
1636 : : {
1637 : 0 : return;
1638 : : }
1639 [ - + ]: 1 : if (strlen( str ) < 1)
1640 : : {
1641 : 0 : return;
1642 : : }
1643 : :
1644 : 1 : p = strstr( str, "InChI=" );
1645 [ - + ]: 1 : if (NULL == p)
1646 : 0 : return;
1647 : :
1648 [ + - ]: 57 : for ( i = 0; i < slen; i++ )
1649 : : {
1650 : 57 : pp = p[i];
1651 : :
1652 [ + + + + ]: 57 : if (pp >= 'A' && pp <= 'Z') continue;
1653 [ + + + - ]: 48 : if (pp >= 'a' && pp <= 'z') continue;
1654 [ + + + + ]: 43 : if (pp >= '0' && pp <= '9') continue;
1655 [ + + ]: 20 : switch (pp)
1656 : : {
1657 : 19 : case '(':
1658 : : case ')':
1659 : : case '*':
1660 : : case '+':
1661 : : case ',':
1662 : : case '-':
1663 : : case '.':
1664 : : case '/':
1665 : : case ';':
1666 : : case '=':
1667 : : case '?':
1668 : 19 : case '@': continue;
1669 : :
1670 : 1 : default: break;
1671 : : }
1672 : :
1673 : 1 : break;
1674 : : }
1675 : :
1676 : 1 : *buf = (char*) inchi_calloc( i + 1, sizeof( char ) );
1677 : 1 : memcpy(*buf, p, i);
1678 [ + - ]: 1 : if ( *buf )
1679 : 1 : (*buf)[i] = '\0';
1680 : :
1681 : 1 : return;
1682 : : }
1683 : :
1684 : :
1685 : : /****************************************************************************/
1686 : 1 : void extract_auxinfo_substring( char ** buf, const char *str, size_t slen )
1687 : : {
1688 : : size_t i;
1689 : : const char *p;
1690 : : char* bufp;
1691 : : char pp;
1692 : :
1693 : 1 : bufp = *buf;
1694 : 1 : *buf = NULL;
1695 : :
1696 [ - + ]: 1 : if ( str == NULL )
1697 : : {
1698 : 0 : return;
1699 : : }
1700 [ - + ]: 1 : if (strlen( str ) < 1)
1701 : : {
1702 : 0 : return;
1703 : : }
1704 : :
1705 : 1 : p = strstr( str, "AuxInfo=" );
1706 [ - + ]: 1 : if (NULL == p)
1707 : : {
1708 : 0 : return;
1709 : : }
1710 : :
1711 [ + - ]: 241 : for ( i = 0; i < slen; i++ )
1712 : : {
1713 : 241 : pp = p[i];
1714 [ + + ]: 241 : if (isspace( UCINT pp )) break;
1715 : : }
1716 : :
1717 : 1 : *buf = (char*) inchi_calloc( i + 1, sizeof( char ) );
1718 : 1 : memcpy(*buf, p, i);
1719 [ + - ]: 1 : if ( *buf )
1720 : 1 : (*buf)[i] = '\0';
1721 : :
1722 : 1 : return;
1723 : : }
1724 : :
1725 : :
1726 : : /****************************************************************************
1727 : : For compatibility: local implementation of non-ANSI
1728 : : (MS-specific) functions, prefixed with "inchi_"
1729 : : ****************************************************************************/
1730 : :
1731 : :
1732 : : #define __MYTOLOWER(c) ( ((c) >= 'A') && ((c) <= 'Z') ? ((c) - 'A' + 'a') : (c) )
1733 : :
1734 : :
1735 : : /****************************************************************************/
1736 : 356 : int inchi_memicmp( const void * p1, const void * p2, size_t length )
1737 : : {
1738 : 356 : const U_CHAR *s1 = (const U_CHAR*) p1;
1739 : 356 : const U_CHAR *s2 = (const U_CHAR*) p2;
1740 [ + - ]: 782 : while (length--)
1741 : : {
1742 [ + + + + ]: 1186 : if (*s1 == *s2 ||
1743 [ + + + + : 404 : __MYTOLOWER( (int) *s1 ) == __MYTOLOWER( (int) *s2 ))
+ + + + ]
1744 : : {
1745 : 426 : s1++;
1746 : 426 : s2++;
1747 : : }
1748 : : else
1749 : : {
1750 : : return
1751 [ + + + + : 356 : __MYTOLOWER( (int) *s1 ) - __MYTOLOWER( (int) *s2 );
+ + + + ]
1752 : : }
1753 : : }
1754 : :
1755 : 0 : return 0;
1756 : : }
1757 : :
1758 : :
1759 : : /****************************************************************************/
1760 : 1321 : int inchi_stricmp( const char *s1, const char *s2 )
1761 : : {
1762 [ + + ]: 2913 : while ( *s1 )
1763 : : {
1764 [ + + + + ]: 4096 : if (*s1 == *s2 ||
1765 [ + - + + : 1258 : __MYTOLOWER( (int) *s1 ) == __MYTOLOWER( (int) *s2 ))
+ + + + ]
1766 : : {
1767 : 1592 : s1++;
1768 : 1592 : s2++;
1769 : : }
1770 : : else
1771 : : {
1772 : : return
1773 [ + - + + : 1246 : __MYTOLOWER( (int) *s1 ) - __MYTOLOWER( (int) *s2 );
+ + + + ]
1774 : : }
1775 : : }
1776 : :
1777 [ - + ]: 75 : if ( *s2 )
1778 : : {
1779 : 0 : return -1;
1780 : : }
1781 : :
1782 : 75 : return 0;
1783 : : }
1784 : :
1785 : :
1786 : : /****************************************************************************/
1787 : 0 : char *inchi__strnset( char *s, int val, size_t length )
1788 : : {
1789 : 0 : char* ps = s;
1790 [ # # # # ]: 0 : while ( length-- && *ps )
1791 : : {
1792 : 0 : *ps++ = (char) val;
1793 : : }
1794 : :
1795 : 0 : return s;
1796 : : }
1797 : :
1798 : :
1799 : : /****************************************************************************/
1800 : 123 : char *inchi__strdup( const char *string )
1801 : : {
1802 : 123 : char* p = NULL;
1803 [ - + ]: 123 : if ( string )
1804 : : {
1805 : 0 : size_t length = strlen( string );
1806 : 0 : p = (char *) inchi_malloc( length + 1 );
1807 [ # # ]: 0 : if (p)
1808 : : {
1809 : 0 : strcpy(p, string);
1810 : : }
1811 : : }
1812 : :
1813 : 123 : return p;
1814 : : }
1815 : :
1816 : :
1817 : : #undef __MYTOLOWER
|