This file is indexed.

/usr/include/mmdb/mmdb_mmcif.h is in libmmdb-dev 1.25.5-2+b2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
//  $Id: mmdb_mmcif.h,v 1.23 2012/01/26 17:52:20 ekr Exp $
//  =================================================================
//
//   CCP4 Coordinate Library: support of coordinate-related
//   functionality in protein crystallography applications.
//
//   Copyright (C) Eugene Krissinel 2000-2008.
//
//    This library is free software: you can redistribute it and/or
//    modify it under the terms of the GNU Lesser General Public
//    License version 3, modified in accordance with the provisions
//    of the license to address the requirements of UK law.
//
//    You should have received a copy of the modified GNU Lesser
//    General Public License along with this library. If not, copies
//    may be downloaded from http://www.ccp4.ac.uk/ccp4license.php
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU Lesser General Public License for more details.
//
//  =================================================================
//
//    16.01.13   <--  Date of Last Modification.
//                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//  -----------------------------------------------------------------
//
//  **** Module  :  MMDB_MMCIF <interface>
//       ~~~~~~~~~
//  **** Project :  MacroMolecular Data Base (MMDB)
//       ~~~~~~~~~
//  **** Classes :  CMMCIFCategory ( mmCIF category    )
//       ~~~~~~~~~  CMMCIFStruct   ( mmCIF structure   )
//                  CMMCIFLoop     ( mmCIF loop        )
//                  CMMCIFData     ( mmCIF data block  )
//                  CMMCIFFile     ( mmCIF file        )
//
//  (C) E. Krissinel 2000-2013
//
//  =================================================================
//


#ifndef __MMDB_MMCIF__
#define __MMDB_MMCIF__


#ifndef __Stream__
#include "stream_.h"
#endif



//  ======================  CMMCIFCategory  ==========================

#define MMCIF_Category 0
#define MMCIF_Struct   1
#define MMCIF_Loop     2
#define MMCIF_Data     3

DefineClass(CMMCIFCategory)
DefineStreamFunctions(CMMCIFCategory)

/// \brief CMMCIFCategory is a base class for CMMCIFStruct and
///        CMMCIFLoop, implementations of mmCIF's "structure" and
///        "loop" categories.
/*!
This class is not instantiated independently in any applications,
however, it provides a few public functions which work for
both CMMCIFStruct and CMMCIFLoop.

All data in mmCIF hierarchy is addressed using construct
"category.tag" plus row number (>=0) for loops. Category names
should always start from underscore, while tags normally start
with a letter, e.g. "_barrel.id".

See general principles of working with mmCIF files and mmCIF
hierarchies in Section \"\ref mmcif_handler\".
*/

class CMMCIFCategory : public CStream  {

  friend class  CMMCIFData;

  public :

    /// \brief Basic constructor.
    CMMCIFCategory ();

    /// \brief Constructor that assigns category name.
    /// \param[in] N category name (must start with underscore).
    CMMCIFCategory ( cpstr N );

    /// \brief Constructor for MMDB data streaming functions.
    CMMCIFCategory ( RPCStream Object );

    /// \brief Destructor.
    ~CMMCIFCategory();

    /// \brief Returns category name.
    /// \return NULL if name was not set
    /// \return pointer to character string if name was set
    pstr   GetCategoryName() { return name;           }

    /// \brief Sets category name.
    /// \param N new category name
    void   SetCategoryName ( cpstr N );

    /// \brief Returns category type.
    /// This function may be used when retrieving categories
    /// (structures and loops) from data blocks (CMMCIFData).
    /// \return MMCIF_Category for CMMCIFCategory
    /// \return MMCIF_Struct   for CMMCIFStruct
    /// \return MMCIF_Loop     for CMMCIFLoop
    virtual int  GetCategoryID() { return MMCIF_Category; }

    /// \brief Virtual function for writing category's content
    ///        into mmCIF file.
    /// Default implementation does nothing.
    virtual void WriteMMCIF ( RCFile ) {}

    /// \brief Virtual function for optimizig data structures.
    /// Optimized data structures take less RAM and their indexes
    /// are sorted for quicker access. Sorting is done automatically
    /// as new data is added to the category. If the
    /// category is edited (fields/data removed), it may need
    /// optimization and re-sorting for efficiency.\n\n
    /// The sorting preserves the order of actual appearance of
    /// tags in mmCIF file. If a category is created
    /// programmatically, the order of tags in mmCIF file will be
    /// the same as order of adding them to the category.
    virtual void Optimize();

    /// \brief Sorts category's data for quicker access.
    /// The sorting is essentially re-indexing of data for quicker
    /// access. It does not change the order of data in both mmCIF
    /// hierarchy and mmCIF file. E.g., if tag "serial_no" was 2nd
    /// one in given category before sorting, it will remain on 2nd
    /// place after it, therefore no change in tag number passed
    /// to functions in CMMCIFStruct, CMMCIFLoop and CMMCIFData.
    void  Sort();

    /// \brief Returns serial number of a tag in the category.
    /// \param[in]  ttag tag (or name of a field in category)
    /// \return \b >=0 : the tag is in given position
    /// \return \b <0 : the tag was not found, but it could be
    ///         inserted before tag with (-rc-1)th index, where
    ///         'rc' is the return.
    int   GetTagNo ( cpstr ttag );

    /// \brief Adds a tag to the category.
    /// Adding a tag in CMMCIFCategory does not reserve any
    /// placeholder for the corresponding value. All tags get
    /// automatically sorted (reindexed) for quicker access.
    /// Tags will appear in mmCIF file in order of their addition
    /// to the category.
    /// \param[in]  ttag tag to be added.
    /// \return \b >=0 the tag is already in the category, and return
    ///             is its serial number. No changes to the category
    ///             is done
    /// \return \b <0  the tag was added to the list of tags, and
    ///             return is minus total number of tags in the
    ///             category.
    int   AddTag ( cpstr ttag );

    /// \brief Returns the total number of tags in the category
    int   GetNofTags() { return nTags; }

    /// \brief Returns tag with the specified serial number.
    /// The tags are enumerated as 0..GetNofTags()-1.
    /// \param tagNo tag's serial number
    /// \return \b NULL: tagNo is outside the range
    ///                  of 0..GetNofTags()-1
    /// \return \b not \b NULL: tag in tagNo'th position
    pstr  GetTag ( int tagNo );  // 0..nTags-1

    /// \brief Prints list of tags to stdout.
    /// Both sorted and unsorted tags are printed to standard
    /// output. This function may be used for debugging.
    void  PrintTags();

    /// \brief Returns True if all tags from the list are found
    ///        in the category.
    /// The size of the list of tags may be less than the number
    /// of tags in the category, and order of tags is disregarded.
    /// \param[in] tagList  list of tags to be checked for presence
    ///                 in the category. The list must end with NULL
    ///                 pointer, or your program will crash.
    /// \return \b True  if all tags from the list were found in the
    ///               category
    /// \return \b False if one or more tags from the list were not
    ///               found in the category.
    ///
    /// Example:
    /// \code
    ///  cpstr tagList[] = {"id","type","date",NULL};
    ///  CMMCIFStruct cifStruct;
    ///  if (cifStruct.CheckTags(tagList))
    ///    printf ( " all tags are found in category %s\n",
    ///             cifStruct.GetCategoryName() );
    /// \endcode
    /// This function is useful for finding categories in
    /// "dirty cifs", where category name is not given.
    Boolean CheckTags ( cpstr * tagList );

    /// \brief Deep copy of categories.
    /// Deep copy duplicates all data and memory allocations,
    /// producing a genuine clone of the original. Only deep copy
    /// should be used for copying MMDB objects, a mere assignment
    /// operator will fail you.
    /// \param[in] Category a pointer to CMMCIFCategory, the content of
    ///                 which is copied into 'this' category.
    virtual void Copy ( PCMMCIFCategory Category );

    /// \brief MMDB stream writer.
    void  write ( RCFile f );

    /// \brief MMDB stream reader.
    void  read  ( RCFile f );

  protected:
    int      nTags;
    pstr     name;
    psvector tag;
    ivector  index;
    int      nAllocTags;

    void          InitMMCIFCategory ();
    virtual void  FreeMemory        ();
    void          ExpandTags        ( int nTagsNew );
    void          PutCategoryName   ( cpstr newName );

};



//  ======================  CMMCIFStruct  ============================

DefineClass(CMMCIFStruct)
DefineStreamFunctions(CMMCIFStruct)

/// \brief Constants used to specify mmCIF's \"data not given\" and
/// \"data not available\" data types.
#define CIF_NODATA_DOT           0
#define CIF_NODATA_QUESTION      1
#define CIF_NODATA_DOT_FIELD      pstr("\x02" ".")
#define CIF_NODATA_QUESTION_FIELD pstr("\x02" "?")

/// \brief CMMCIFStruct represents mmCIF's \"structure\" category,
///        where data follows directly the corresponding tag.
/*!
mmCIF's \"structure\" category has the following form:
\code
_structure_name.tag0   value0
_structure_name.tag1   value1
...........
_structure_name.tagN   valueN
\endcode
CMMCIFStruct represents this construct by keeping category name
(\"_structure_name\") and associated lists of tags
(\"tag0,tag1...tagN\") and their values (\"value0,value1...valueN\").

The structure is created automatically when an mmCIF file is read,
or it may be created programatically and then pushed into file.

Access to data is provided via tags. Internally, all values are kept
as character fields, and it is only on the retrieval stage that they
are converted to other data types (integers, floats or strings).
If conversion is not possible, an error code is returned by the
corresponding functions, which should be checked by the application.

See general principles of working with mmCIF files and mmCIF
hierarchies, as well as some code samples, in Section
\"\ref mmcif_handler\".
*/

class CMMCIFStruct : public CMMCIFCategory  {

  public :

    /// \brief Basic constructor
    CMMCIFStruct ();

    /// \brief Constructor that assigns structure name.
    /// \param[in] N structure name (must start with underscore).
    CMMCIFStruct ( cpstr N );

    /// \brief Constructor for MMDB data streaming functions
    CMMCIFStruct ( RPCStream Object );

    /// \brief Destructor
    ~CMMCIFStruct();

    /// \brief Adds field to the structure.
    /// \param[in] F field value
    /// \param[in] T tag name
    /// \param[in] Concatenate flag to concatenate existing field
    ///            with the value of \b F. If tag \b T is already in
    ///            the structure and \b Concatenate=True, then
    ///            value of \b F is appended to the existing field.
    ///            Otherwise, the field is replaced with the value
    ///            of \b F
    void AddField ( cpstr F, cpstr T, Boolean Concatenate=False );

    /// \brief Returns category type \b MMCIF_Struct.
    int  GetCategoryID()  { return MMCIF_Struct; }

    /// \brief Optimizes structure for RAM and data access speed.
    /// Optimized data structures take less RAM and their indexes
    /// are sorted for quicker access. Sorting is done automatically
    /// as new data is added to the category. If the structure
    /// is edited (fields/data removed), it may need
    /// optimization and re-sorting for efficiency.\n\n
    /// The sorting preserves the order of actual appearance of
    /// tags in mmCIF file. If a structure is created
    /// programmatically, the order of tags in mmCIF file will be
    /// the same as order of adding them to the structure.
    void Optimize();

    /// \brief Returns value of field corresponding to tag in the
    ///        specified position.
    /// Tag positions are defined by the order of their appearance in
    /// mmCIF file (if structure was read from a file), or by the
    /// order of their addition to the structure (if structure was
    /// created programmatically). Tags are numbered as
    /// 0...GetNofTags()-1.
    /// \param[in] tagNo tag number (position in the structure)
    /// \return \b NULL: tag does not exist
    /// \return \b CIF_NODATA_DOT_FIELD the field contains
    ///            \"data not given\" value
    /// \return \b CIF_NODATA_QUESTION_FIELD the field contains
    ///            \"data not available\" value
    /// \return \b not \b NULL: string value of the field
    pstr GetField ( int tagNo );  // 0..nTags-1

    /// \brief Fetches value, corresponding to the given tag, as
    ///        a string
    /// \param[out] S pointer to string, which will point to newly
    ///               allocated character string, containing value
    ///               associated with tag \b TName. If tag or value
    ///               is not found, or if value corresponds to
    ///               mmCIF's \"data not given\" or
    ///               \"data not available\", \b S returns NULL.
    /// \param[in] TName character string with tag name
    /// \param[in] Remove flag to remove the tag and its value from
    ///               structure after it is read.
    /// \return \b CIFRC_NoTag: tag is not found
    /// \return \b CIFRC_NoField: value is not found
    /// \return \b CIFRC_Ok: success. If \b S returns NULL, then
    ///                the value corresponds to either
    ///                \"data not available\" or
    ///                \"data not given\".
    /// \remarks If \b S!=NULL at time of call, the function will
    ///  try to dispose the string it points on. This allows a slick
    ///  re-use of the same pointer in consequitive calls. This also
    ///  means that one should never pass unallocated pointer to
    ///  this function. Safe use assumes the following patern:
    ///  \code
    ///  CMMCIFStruct mmCIFStruct;
    ///  pstr S;  // this is merely "char *S"
    ///  int  rc;
    ///
    ///    S  = NULL; // null pointer before first use
    ///    rc = mmCIFStruct.GetString ( S,"id" );
    ///    if (rc)  CreateCopy ( S,"*** data not found" );
    ///    if (!S)  CreateCopy ( S,"*** data not given or not available" );
    ///    printf ( " rc=%i, S='%s'\n",rc,S );
    ///
    ///    rc = mmCIFStruct.GetString ( S,"property" );
    ///    if (rc)  CreateCopy ( S,"*** data not found" );
    ///    if (!S)  CreateCopy ( S,"*** data not given or not available" );
    ///    printf ( " rc=%i, S='%s'\n",rc,S );
    ///
    ///    // etc etc etc
    ///
    ///    delete[] S;  // application is responsible for final
    ///                 // disposal of memory
    ///  \endcode
    int  GetString ( pstr & S, cpstr TName, Boolean Remove=False );

    /// \brief Returns pointer to value associated with given tag.
    /// \param[in] TName character string with tag name
    /// \param[out] RC return code:
    ///    \arg \b CIFRC_NoTag: tag is not found
    ///    \arg \b CIFRC_NoField: value is not found
    ///    \arg \b CIFRC_Ok: success. If function returns NULL, then
    ///                the value corresponds to either
    ///                \"data not available\" or
    ///                \"data not given\".
    /// \return \b NULL: either tag or value is not found, or the
    ///    value is \"data not available\" or \"data not given\".
    ///    Read return code \b RC in order to interpret NULL return.
    /// \return \b not \b NULL: pointer (\c char \c *) to value
    ///    associated with \b TName.
    /// \remarks Never try to dispose memory pointed by the return
    /// value, or your program will crash.
    pstr GetString ( cpstr TName, int & RC ); // NULL if TName
                                                     // is not there

    /// \brief Deletes field associated with given tag.
    /// \param[in] TName character string with tag name
    /// \return \b >=0: field deleted
    /// \return \b <0: either field or tag is not found
    int  DeleteField ( cpstr TName );  // <0 the field was not there

    /// \brief Fetches value, corresponding to the given tag, as
    ///        a real number.
    /// \param[out] R reference to real number to accept the value.
    ///        In case of failure, \b R returns zero.
    /// \param[in] TName character string with tag name
    /// \param[in] Remove flag to remove the tag and its value from
    ///               structure after it is read.
    /// \return \b CIFRC_NoTag: tag is not found
    /// \return \b CIFRC_NoField: field is not found
    /// \return \b CIFRC_WrongFormat: value is not a real or integer
    ///            number.
    /// \return \b CIFRC_NoData: value is either
    ///            \"data not available\" or
    ///            \"data not given\".
    /// \return \b CIFRC_Ok: success.
    int  GetReal ( realtype & R, cpstr TName, Boolean Remove=False );

    /// \brief Fetches value, corresponding to the given tag, as
    ///        an integer number.
    /// \param[out] I reference to integer number to accept the
    ///        value. In case of failure, \b I returns zero, except
    ///        when value is \"data not available\" or
    ///        \"data not given\", when I returns \c MinInt4.
    /// \param[in] TName character string with tag name
    /// \param[in] Remove flag to remove the tag and its value from
    ///               structure after it is read.
    /// \return \arg \b CIFRC_NoTag: tag is not found
    /// \return \b CIFRC_NoField: field is not found
    /// \return \b CIFRC_WrongFormat: value is not an integer number.
    /// \return \b CIFRC_NoData: value is either
    ///            \"data not available\" or
    ///            \"data not given\".
    /// \return \b CIFRC_Ok: success.
    int  GetInteger ( int & I, cpstr TName, Boolean Remove=False );

    /// \brief Sets string value for given tag.
    /// \param[in] S character string with value to be set.
    ///            If \b S==NULL, the \"data not given\" value
    ///            will be set. If \b S==\"\" (empty string), the
    ///            \"data not available\" value is stored.
    /// \param[in] TName character string with tag name. If tag
    ///            is not found, it will be added to the structure.
    /// \param[in] NonBlankOnly flag to treat white-space-only
    ///            strings:
    ///   \arg \b False: set as is
    ///   \arg \b True:  set \"data not available\" value instead.
    void PutString   ( cpstr S, cpstr TName,
                       Boolean NonBlankOnly=False );

    /// \brief Sets current date in format YYYY-MM-DD as a value
    ///        for given tag.
    /// \param[in] T character string with tag name. If tag
    ///            is not found, it will be added to the structure.
    void PutDate     ( cpstr T );

    /// \brief Sets \"data not given\" or \"data not available\"
    ///        values for given tag.
    /// \param[in] NoDataType can be either
    ///   \arg \b CIF_NODATA_DOT for \"data not given\"
    ///   \arg \b CIF_NODATA_QUESTION for \"data not available\"
    /// \param[in] T character string with tag name. If tag
    ///            is not found, it will be added to the structure.
    void PutNoData   ( int NoDataType, cpstr T  );

    /// \brief Sets float-point value for given tag.
    /// \param[in] R real number with value to be set.
    /// \param[in] TName character string with tag name. If tag
    ///            is not found, it will be added to the structure.
    /// \param[in] prec float-point precision; g-format with given
    ///            precision will be used
    void PutReal     ( realtype R, cpstr TName, int prec=8 );

    /// \brief Sets float-point value for given tag.
    /// \param[in] R real number with value to be set.
    /// \param[in] TName character string with tag name. If tag
    ///            is not found, it will be added to the structure.
    /// \param[in] format format string to convert \b R.
    void PutReal     ( realtype R, cpstr TName, cpstr format );

    /// \brief Sets integer value for given tag.
    /// \param[in] I integer number with value to be set.
    /// \param[in] TName character string with tag name. If tag
    ///            is not found, it will be added to the structure.
    void PutInteger  ( int      I, cpstr TName );

    /// \brief Writes structure data in mmCIF format into file.
    /// \param[in] FName character string with file name.
    /// \param[in] gzipMode flag to controll compression of files:
    ///  \arg \b GZM_NONE: do not compress
    ///  \arg \b GZM_CHECK: check file name suffix and compress
    ///                     (or not) accordingly
    ///  \arg \b GZM_ENFORCE_GZIP: force gzip compression despite
    ///                     suffix
    ///  \arg \b GZM_ENFORCE_COMPRESS: force using compress despite
    ///                     suffix
    ///  \return \b True: success
    ///  \return \b False: can not open file for writing.
    /// \remarks This function does not create a valid mmCIF file
    /// as \"data_XXX\" record will be missing. It may be used for
    /// debugging though.
    Boolean WriteMMCIFStruct ( cpstr FName,
                               byte gzipMode=GZM_CHECK );

    /// \brief Writes structure into given file.
    /// \param f reference to MMDB's file class. The file should be
    /// opened and closed by application.
    /// \remarks There is a very limited use of this function on
    /// application level. It is primarily used by CMMCIFData class.
    void    WriteMMCIF ( RCFile f   );

    /// \brief Deep copy of structures.
    /// Deep copy duplicates all data and memory allocations,
    /// producing a genuine clone of the original. Only deep copy
    /// should be used for copying MMDB objects, a mere assignment
    /// operator will fail you.
    /// \param[in] Struct a pointer to CMMCIFStruct, the content of
    ///                 which is copied into 'this' structure.
    void Copy ( PCMMCIFCategory Struct );

    /// \brief MMDB stream writer.
    void write ( RCFile f );

    /// \brief MMDB stream reader.
    void read  ( RCFile f );

  protected:
    psvector field;

    void InitMMCIFStruct();
    void FreeMemory();

};



//  ======================  CMMCIFLoop  ==============================

DefineClass(CMMCIFLoop)
DefineStreamFunctions(CMMCIFLoop)

/// \brief CMMCIFLoop represents mmCIF's \"loop\" category, which keeps
///        rows of data values associated with tags.
/*!
mmCIF's \"loop\" category has the following form:
\code
loop_
_loop_name.tag0   value0
_loop_name.tag1   value1
...........
_loop_name.tagN   valueN
value00 value10 ... valueN0
value01 value11 ... valueN1
...........
value0M value1M ... valueNM
\endcode
CMMCIFLoop represents this construct by keeping category name
(\"_loop_name\") and associated lists of tags
(\"tag0,tag1...tagN\") and data vectors
(\"[value00...value0M],[value10...value1M]...[valueN0...valueNM]\").

The loop object is created automatically when an mmCIF file is read,
or it may be created programatically and then pushed into file.

Access to data is provided via tags and data indexes. Internally,
all values are kept as character fields, and it is only on the
retrieval stage that they are converted to other data types
(integers, floats or strings). If conversion is not possible, an
error code is returned by the corresponding functions, which should
be checked by the application.

The following code gives an example of creating mmCIF loop category
and populating it with data:
\code
CMMCIFLoop loop;
char       S[100];
int        i;

  // Specify loop name:
  loop.SetCategoryName ( "_sample_loop" );

  // Create loop structure, i.e., list of tags first:
  loop.AddLoopTag ( "id"    );
  loop.AddLoopTag ( "name"  );
  loop.AddLoopTag ( "value" );

  // Now populate it with data. This my be done in 2 ways.
  // Here is how you write loop data in stream fashion,
  // value-after-value:
  for (i=0;i<3;i++)  {
    loop.AddInteger ( i );
    sprintf ( S,"1st_way-%i",i );
    loop.AddString ( S );
    loop.AddReal ( 2.5*(i+1) );
  }

  // Here is how you populate loop data using direct-access
  // functions:
  for (i=3;i<6;i++)  {
    loop.PutReal ( 2.5*(i+1),"value",i );
    loop.PutInteger ( i,"id" );
    sprintf ( S,"2nd way: %i",i );
    loop.PutString ( S,"name" );
  }

  loop.WriteMMCIFLoop ( "sample_loop.cif" );

\endcode

The resulting file \b sample_loop.cif will contain:

\code

loop_
_sample_loop.id
_sample_loop.name
_sample_loop.value
0   1st_way-0     2.5
1   1st_way-1     5.0
2   1st_way-2     7.5
3   "2nd way: 3"  10.0
4   "2nd way: 4"  12.5
5   "2nd way: 5"  15.0

\endcode

See general principles of working with mmCIF files and mmCIF
hierarchies, as well as some code samples, in Section
\"\ref mmcif_handler\".
*/

class CMMCIFLoop : public CMMCIFCategory  {

  friend class CMMCIFData;

  public :

    /// \brief Basic constructor
    CMMCIFLoop ();

    /// \brief Constructor that assigns structure name.
    /// \param[in] N structure name (must start with underscore).
    CMMCIFLoop ( cpstr N );

    /// \brief Constructor for MMDB data streaming functions
    CMMCIFLoop ( RPCStream Object );

    /// \brief Destructor
    ~CMMCIFLoop();

    /// \brief Adds tag to the loop.
    /// The tag is appended to the list of existing tags. The order
    /// of tags cannot be changed.
    /// \param[in] T tag name
    /// \param[in] Remove flag to remove all fields in the loop.
    void  AddLoopTag ( cpstr T, Boolean Remove=True );

    /// \brief Sets string value at current loop position.
    /// When \b CMMCIFLoop::Add[Data] functions use internal loop
    /// pointer. When category is created or cleared (by using
    /// \b CMMCIFTLoop::AddLoopTag ( T,True )) the pointer is set to
    /// 0th row and 0th column (tag). After each call to
    /// \b CMMCIFLoop::Add[Data] function, internal pointer advances
    /// to next column (tag), and wraps over to next row, 0th tag,
    /// if list of tags is exhausted. Any remaining fields in last
    /// row will be populated with \"data not given\" value.
    /// \param[in] S character string with value to be set.
    ///            If \b S==NULL, the \"data not given\" value
    ///            will be set. If \b S==\"\" (empty string), the
    ///            \"data not available\" value is stored.
    /// \param[in] NonBlankOnly flag to treat white-space-only
    ///            strings:
    ///   \arg \b False: set as is
    ///   \arg \b True:  set \"data not available\" value instead.
    void  AddString ( cpstr S, Boolean NonBlankOnly=False );

    /// \brief Sets \"data not given\" or \"data not available\" at
    ///        current loop position.
    /// When \b CMMCIFLoop::Add[Data] functions use internal loop
    /// pointer. When category is created or cleared (by using
    /// \b CMMCIFTLoop::AddLoopTag ( T,True )) the pointer is set to
    /// 0th row and 0th column (tag). After each call to
    /// \b CMMCIFLoop::Add[Data] function, internal pointer advances
    /// to next column (tag), and wraps over to next row, 0th tag,
    /// if list of tags is exhausted. Any remaining fields in last
    /// row will be populated with \"data not given\" value.
    /// \param[in] NoDataType integer key specifying which type of
    ///            data absence should be set as a value:
    ///   \arg \b CIF_NODATA_DOT for \"data not given\"
    ///   \arg \b CIF_NODATA_QUESTION for \"data not available\"
    void  AddNoData ( int NoDataType );

    /// \brief Sets float-point value at current loop position.
    /// When \b CMMCIFLoop::Add[Data] functions use internal loop
    /// pointer. When category is created or cleared (by using
    /// \b CMMCIFTLoop::AddLoopTag ( T,True )) the pointer is set to
    /// 0th row and 0th column (tag). After each call to
    /// \b CMMCIFLoop::Add[Data] function, internal pointer advances
    /// to next column (tag), and wraps over to next row, 0th tag,
    /// if list of tags is exhausted. Any remaining fields in last
    /// row will be populated with \"data not given\" value.
    /// \param[in] R real number with value to be set.
    /// \param[in] prec float-point precision; g-format with given
    ///            precision will be used
    void  AddReal ( realtype R, int prec=8 );

    /// \brief Sets float-point value at current loop position in
    ///        given format.
    /// When \b CMMCIFLoop::Add[Data] functions use internal loop
    /// pointer. When category is created or cleared (by using
    /// \b CMMCIFTLoop::AddLoopTag ( T,True )) the pointer is set to
    /// 0th row and 0th column (tag). After each call to
    /// \b CMMCIFLoop::Add[Data] function, internal pointer advances
    /// to next column (tag), and wraps over to next row, 0th tag,
    /// if list of tags is exhausted. Any remaining fields in last
    /// row will be populated with \"data not given\" value.
    /// \brief Sets float-point value for given tag.
    /// \param[in] R real number with value to be set.
    /// \param[in] format format string to convert \b R.
    void  AddReal ( realtype R, cpstr format );

    /// \brief Sets integer value at current loop position in given
    ///        format.
    /// When \b CMMCIFLoop::Add[Data] functions use internal loop
    /// pointer. When category is created or cleared (by using
    /// \b CMMCIFTLoop::AddLoopTag ( T,True )) the pointer is set to
    /// 0th row and 0th column (tag). After each call to
    /// \b CMMCIFLoop::Add[Data] function, internal pointer advances
    /// to next column (tag), and wraps over to next row, 0th tag,
    /// if list of tags is exhausted. Any remaining fields in last
    /// row will be populated with \"data not given\" value.
    /// \param[in] I integer number with value to be set.
    void  AddInteger ( int I );

    /// \brief Returns current length of the loop (i.e. the number
    ///        of rows).
    /// \return number of data rows in the loop.
    int   GetLoopLength() { return nRows; }

    /// \brief Returns string pointer on the field corresponding to
    ///        tag in the specified position, in the specified row.
    /// Tag positions are defined by the order of their appearance in
    /// mmCIF file (if loop was read from a file), or by the
    /// order of their addition to the loop (if loop was
    /// created programmatically).
    /// \param[in] rowNo row number (0...GetLoopLength()-1)
    /// \param[in] tagNo tag number (0...GetNofTags()-1)
    /// \return \b NULL: tag or row do not exist
    /// \return \b CIF_NODATA_DOT_FIELD the field contains
    ///            \"data not given\" value
    /// \return \b CIF_NODATA_QUESTION_FIELD the field contains
    ///            \"data not available\" value
    /// \return \b not \b NULL: string value of the field
    /// \remarks Never try to dispose memory pointed by the return
    /// value, or your program will crash.
    pstr  GetField ( int rowNo, int tagNo );

    /// \brief Fetches value, corresponding to the given tag, in
    ///        the given row, as a string
    /// \param[out] S pointer to string, which will point to newly
    ///               allocated character string, containing value
    ///               associated with tag \b TName and row \b nrow.
    ///               If tag, row or value
    ///               is not found, or if value corresponds to
    ///               mmCIF's \"data not given\" or
    ///               \"data not available\", \b S returns NULL.
    /// \param[in] TName character string with tag name
    /// \param[in] nrow  row number (0...GetLoopLength()-1)
    /// \param[in] Remove flag to remove the field from
    ///               structure after it is read.
    /// \return \b CIFRC_NoTag: tag is not found
    /// \return \b CIFRC_WrongIndex: row is not found
    /// \return \b CIFRC_NoField: value is not found
    /// \return \b CIFRC_Ok: success. If \b S returns NULL, then
    ///                the value corresponds to either
    ///                \"data not available\" or
    ///                \"data not given\".
    /// \remarks If \b S!=NULL at time of call, the function will
    ///  try to dispose the string it points on. This allows a slick
    ///  re-use of the same pointer in consequitive calls. This also
    ///  means that one should never pass unallocated pointer to
    ///  this function. Safe use assumes the following patern:
    ///  \code
    ///  CMMCIFLoop mmCIFLoop;
    ///  pstr S;  // this is merely "char *S"
    ///  int  rc;
    ///
    ///    S  = NULL; // null pointer before first use
    ///    rc = mmCIFLoop.GetString ( S,"id",1 );
    ///    if (rc)  CreateCopy ( S,"*** data not found" );
    ///    if (!S)  CreateCopy ( S,"*** data not given or not available" );
    ///    printf ( " rc=%i, S='%s'\n",rc,S );
    ///
    ///    rc = mmCIFLoop.GetString ( S,"property",0 );
    ///    if (rc)  CreateCopy ( S,"*** data not found" );
    ///    if (!S)  CreateCopy ( S,"*** data not given or not available" );
    ///    printf ( " rc=%i, S='%s'\n",rc,S );
    ///
    ///    // etc etc etc
    ///
    ///    delete[] S;  // application is responsible for final
    ///                 // disposal of memory
    ///  \endcode
    int   GetString ( pstr & S, cpstr TName, int nrow,
                                       Boolean Remove=False );

    /// \brief Returns pointer to value associated with given tag,
    ///        in the given row of the loop.
    /// \param[in] TName character string with tag name
    /// \param[in] nrow  row number (0...GetLoopLength()-1)
    /// \param[out] RC return code:
    ///    \arg \b CIFRC_NoTag: tag is not found
    ///    \arg \b CIFRC_WrongIndex: row is not found
    ///    \arg \b CIFRC_NoField: value is not found
    ///    \arg \b CIFRC_Ok: success. If function returns NULL, then
    ///                the value corresponds to either
    ///                \"data not available\" or
    ///                \"data not given\".
    /// \return \b NULL: either tag, row or value is not found, or the
    ///    value is \"data not available\" or \"data not given\".
    ///    Read return code \b RC in order to interpret NULL return.
    /// \return \b not \b NULL: pointer (\c char \c *) to value
    ///    associated with \b TName.
    /// \remarks Never try to dispose memory pointed by the return
    /// value, or your program will crash.
    pstr  GetString    ( cpstr TName, int nrow, int & RC );

    /// \brief Copies value, associated with given tag,
    ///        in the given row, into specified buffer.
    ///  Terminating NULL character is appended.
    /// \param[out] buf character string to accept the value
    /// \param[in] maxlength maximum number of bytes to copy
    /// \param[in] TName character string with tag name
    /// \param[in] nrow  row number (0...GetLoopLength()-1)
    /// \param[out] RC return code:
    ///    \arg \b CIFRC_NoTag: tag is not found
    ///    \arg \b CIFRC_WrongIndex: row is not found
    ///    \arg \b CIFRC_NoField: value is not found
    ///    \arg \b CIFRC_Ok: success.
    /// \remarks Destination string \b buf is not modified if
    /// \b RC!=CIFRC_Ok .
    void  CopyString   ( pstr  buf,   int maxlength,
                         cpstr TName, int nrow, int & RC );

    /// \brief Deletes field associated with given tag in
    ///          the given row.
    /// \param[in] TName character string with tag name
    /// \param[in] nrow  row number (0...GetLoopLength()-1)
    /// \return \b >=0: field deleted
    /// \return \b <0: either field or tag is not found
    int   DeleteField  ( cpstr TName, int nrow );

    /// \brief Deletes all fields in given row.
    /// \param[in] nrow  row number (0...GetLoopLength()-1)
    /// \return \b CIFRC_Ok: fields deleted
    /// \return \b CIFRC_WrongIndex: row not found
    /// \remarks Note that this function delets just the fields, but
    /// not the row. If you wish the row to be deleted, call
    /// CMMCIFLoop::Optimize() function after this one.
    int   DeleteRow    ( int nrow );

    /// \brief Fetches value, corresponding to the given tag,
    ///        in the given row, as a real number.
    /// \param[out] R reference to real number to accept the value.
    ///        In case of failure, \b R returns zero.
    /// \param[in] TName character string with tag name
    /// \param[in] nrow  row number (0...GetLoopLength()-1)
    /// \param[in] Remove flag to remove the field from
    ///               the loop after it is read.
    /// \return \b CIFRC_NoTag: tag is not found
    /// \return \b CIFRC_WrongIndex: row not found
    /// \return \b CIFRC_NoField: field is not found
    /// \return \b CIFRC_WrongFormat: value is not a real or integer
    ///            number.
    /// \return \b CIFRC_NoData: value is either
    ///            \"data not available\" or
    ///            \"data not given\".
    /// \return \b CIFRC_Ok: success.
    int   GetReal ( realtype & R, cpstr TName, int nrow,
                                       Boolean Remove=False );

    /// \brief Copies value, associated with given tag,
    ///        in the given row, into specified destination as
    ///        a real number.
    /// \param[out] R reference to real number to accept the value
    /// \param[in] TName character string with tag name
    /// \param[in] nrow  row number (0...GetLoopLength()-1)
    /// \param[out] RC return code:
    ///    \arg \b CIFRC_NoTag: tag is not found
    ///    \arg \b CIFRC_WrongIndex: row is not found
    ///    \arg \b CIFRC_NoField: value is not found
    ///    \arg \b CIFRC_Ok: success.
    /// \remarks Destination \b R is set 0 if \b RC!=CIFRC_Ok.
    void  CopyReal ( realtype & R, cpstr TName, int nrow, int & RC );

    /// \brief Copies value, associated with given tag,
    ///        in the given row, into specified destination as
    ///        an integer number.
    /// \param[out] I reference to integer number to accept the value
    /// \param[in] TName character string with tag name
    /// \param[in] nrow  row number (0...GetLoopLength()-1)
    /// \param[out] RC return code:
    ///    \arg \b CIFRC_NoTag: tag is not found
    ///    \arg \b CIFRC_WrongIndex: row is not found
    ///    \arg \b CIFRC_NoField: value is not found
    ///    \arg \b CIFRC_Ok: success.
    /// \remarks Destination \b I is set 0 if \b RC!=CIFRC_Ok.
    void  CopyInteger ( int & I, cpstr TName, int nrow, int & RC );

    /// \brief Fetches value, corresponding to the given tag,
    ///        in the given row, as an integer number.
    /// \param[out] I reference to integer number to accept the value.
    ///        In case of failure, \b R returns zero.
    /// \param[in] TName character string with tag name
    /// \param[in] nrow  row number (0...GetLoopLength()-1)
    /// \param[in] Remove flag to remove the field from
    ///               the loop after it is read.
    /// \return \b CIFRC_NoTag: tag is not found
    /// \return \b CIFRC_WrongIndex: row not found
    /// \return \b CIFRC_NoField: field is not found
    /// \return \b CIFRC_WrongFormat: value is not a real or integer
    ///            number.
    /// \return \b CIFRC_NoData: value is either
    ///            \"data not available\" or
    ///            \"data not given\".
    /// \return \b CIFRC_Ok: success.
    int   GetInteger   ( int & I, cpstr TName, int nrow,
                                       Boolean Remove=False );

    /// \brief Fetches set of values, corresponding to the given
    ///        tag, in the given range of rows, as a vector of
    ///        strings.
    /// \param[out] S reference to string vector to accept
    ///        the values. if \b S==NULL , the vector will be
    ///        allocated with starting index of \b i1.
    /// \param[in] TName character string with tag name
    /// \param[in] i1  minimum row number to fetch, the actual
    ///            index will be calculated as \b max(0,min(i1,i2))
    /// \param[in] i2  maximum row number to fetch, the actual
    ///            index will be calculated as
    ///            \b min(GetLoopLength()-1,max(i1,i2))
    /// \param[in] Remove flag to remove fetched fields from
    ///               the loop after they are read.
    /// \return \b CIFRC_NoTag: tag is not found
    /// \return \b CIFRC_WrongIndex: invalid range of rows
    /// \return \b CIFRC_Ok: success.
    ///
    /// For safe use, \b S should be pre-allocated by calling
    /// process. Only elements \b S[i1] to \b S[i2] will contain
    /// fetched data, others remain untouched. The calling
    /// process is responsible for the disposal of \b S. Example:
    /// \code
    /// CMMCIFLoop loop;
    /// psvector   S;  // equivalent to char **S
    /// int        i,i1,i2,rc,n;
    ///
    ///    // ... get loop data
    ///
    ///    n  = loop.GetLoopLength();
    ///    i1 = 5;  i2 = n - 5;  // could be wrong!
    ///
    ///    //  allocate vector of strings
    ///    GetVectorMemory ( S,n,0 );  // "0" for starting index
    ///    for (i=0;i<n;i++)
    ///      S[i] = NULL;  // initialize NULL string pointers
    ///
    ///    loop.GetSVector ( S,"name",i1,i2 );
    ///    printf ( " Fetched with return code rc=%i\n",rc );
    ///        // you may want a more thorough treatment of
    ///        // the return code here
    ///    for (i=i1;i<=i2;i++)
    ///      if (S[i])  printf ( " %4i. name='%s'\n",i,S[i] );
    ///           else  printf ( " %4i. name is not available\n",i );
    ///
    ///    //  S[] may be re-used for as many fetches as necessary
    ///    //  without cleaning or disposals
    ///
    ///    //  dispose of vector of strings
    ///    for (i=0;i<n;i++)
    ///      if (S[i])  delete[] S[i];
    ///    FreeVectorMemory ( S,0 );  // "0" for starting index
    ///
    /// \endcode
    int  GetSVector ( psvector & S, cpstr TName,
                      int i1=0, int i2=MaxInt4,
                      Boolean Remove=False );

    /// \brief Fetches set of values, corresponding to the given
    ///        tag, in the given range of rows, as a vector of
    ///        float-point numbers.
    /// \param[out] R reference to float-point vector to accept
    ///        the values. if \b R==NULL , the vector will be
    ///        allocated with starting index of \b i1.
    /// \param[in] TName character string with tag name
    /// \param[in] i1  minimum row number to fetch, the actual
    ///            index will be calculated as \b max(0,min(i1,i2))
    /// \param[in] i2  maximum row number to fetch, the actual
    ///            index will be calculated as
    ///            \b min(GetLoopLength()-1,max(i1,i2))
    /// \param[in] Remove flag to remove fetched fields from
    ///               the loop after they are read.
    /// \return \b CIFRC_NoTag: tag is not found
    /// \return \b CIFRC_WrongIndex: invalid range of rows
    /// \return \b CIFRC_Ok: success.
    ///
    /// For safe use, \b R should be pre-allocated by calling
    /// process. Only elements \b R[i1] to \b R[i2] will contain
    /// fetched data, others remain untouched. The calling
    /// process is responsible for the disposal of \b R. Example:
    /// \code
    /// CMMCIFLoop loop;
    /// rvector    R;  // equivalent to realtype *R
    /// int        i,i1,i2,rc,n;
    ///
    ///    // ... get loop data
    ///
    ///    n  = loop.GetLoopLength();
    ///    i1 = 5;  i2 = n - 5;  // could be wrong!
    ///
    ///    //  allocate a vector of real numbers
    ///    GetVectorMemory ( R,n,0 );  // "0" for starting index
    ///    // no need to initiaize unless required for the
    ///    // application
    ///
    ///    rc = loop.GetRVector ( R,"value",i1,i2 );
    ///    printf ( " Fetched with return code rc=%i\n",rc );
    ///        // you may want a more thorough treatment of
    ///        // the return code here
    ///    for (i=i1;i<=i2;i++)
    ///      printf ( " value[%4i] = %15.7g\n",i,R[i] );
    ///
    ///    //  R[] may be re-used for as many fetches as necessary
    ///    //  without cleaning or disposals
    ///
    ///    //  dispose of the vector
    ///    FreeVectorMemory ( R,0 );  // "0" for starting index
    ///
    /// \endcode
    int  GetRVector ( rvector  & R, cpstr TName,
                      int i1=0, int i2=MaxInt4,
                      Boolean Remove=False );

    /// \brief Fetches set of values, corresponding to the given
    ///        tag, in the given range of rows, as a vector of
    ///        integer numbers.
    /// \param[out] I reference to float-point vector to accept
    ///        the values. if \b I==NULL , the vector will be
    ///        allocated with starting index of \b i1.
    /// \param[in] TName character string with tag name
    /// \param[in] i1  minimum row number to fetch, the actual
    ///            index will be calculated as \b max(0,min(i1,i2))
    /// \param[in] i2  maximum row number to fetch, the actual
    ///            index will be calculated as
    ///            \b min(GetLoopLength()-1,max(i1,i2))
    /// \param[in] Remove flag to remove fetched fields from
    ///               the loop after they are read.
    /// \return \b CIFRC_NoTag: tag is not found
    /// \return \b CIFRC_WrongIndex: invalid range of rows
    /// \return \b CIFRC_Ok: success.
    ///
    /// For safe use, \b I should be pre-allocated by calling
    /// process. Only elements \b I[i1] to \b I[i2] will contain
    /// fetched data, others remain untouched. The calling
    /// process is responsible for the disposal of \b I.
    /// See example in CMMCIFLoop::GetRVector documentation
    /// for details.
    int  GetIVector ( ivector  & I, cpstr TName,
                      int i1=0, int i2=MaxInt4,
                      Boolean Remove=False );

    /// \brief Sets string value for given tag and row.
    /// \param[in] S character string with value to be set.
    ///            If \b S==NULL, the \"data not given\" value
    ///            will be set. If \b S==\"\" (empty string), the
    ///            \"data not available\" value is stored.
    /// \param[in] T character string with tag name. If tag
    ///            is not found, it will be added, and all data in
    ///            the loop will be reindexed accordingly.
    /// \param[in] nrow  row number. If the row does not exist then
    ///            it will be created, along with all other rows
    ///            between GetLoopLength()-1 and \b nrow as
    ///            necessary. All newly created fields will be
    ///            initialised with \"data not given\" value.
    void  PutString ( cpstr S, cpstr T, int nrow );

    /// \brief Sets \"data not given\" or \"data not available\"
    ///        values for given tag and row.
    /// \param[in] NoDataType can be either
    ///   \arg \b CIF_NODATA_DOT for \"data not given\"
    ///   \arg \b CIF_NODATA_QUESTION for \"data not available\"
    /// \param[in] T character string with tag name. If tag
    ///            is not found, it will be added, and all data in
    ///            the loop will be reindexed accordingly.
    /// \param[in] nrow  row number. If the row does not exist then
    ///            it will be created, along with all other rows
    ///            between GetLoopLength()-1 and \b nrow as
    ///            necessary. All newly created fields will be
    ///            initialised with \"data not given\" value.
    void  PutNoData ( int NoDataType, cpstr T, int nrow );

    /// \brief Sets float-point value for given tag and row.
    /// \param[in] R real number with value to be set.
    /// \param[in] T character string with tag name. If tag
    ///            is not found, it will be added, and all data in
    ///            the loop will be reindexed accordingly.
    /// \param[in] nrow  row number. If the row does not exist then
    ///            it will be created, along with all other rows
    ///            between GetLoopLength()-1 and \b nrow as
    ///            necessary. All newly created fields will be
    ///            initialised with \"data not given\" value.
    /// \param[in] prec float-point precision; g-format with given
    ///            precision will be used
    void  PutReal ( realtype R, cpstr T, int nrow, int prec=8 );

    /// \brief Sets float-point value for given tag and row.
    /// \param[in] R real number with value to be set.
    /// \param[in] T character string with tag name. If tag
    ///            is not found, it will be added, and all data in
    ///            the loop will be reindexed accordingly.
    /// \param[in] nrow  row number. If the row does not exist then
    ///            it will be created, along with all other rows
    ///            between GetLoopLength()-1 and \b nrow as
    ///            necessary. All newly created fields will be
    ///            initialised with \"data not given\" value.
    /// \param[in] format format string to convert \b R.
    void  PutReal ( realtype R, cpstr T, int nrow, cpstr format );

    /// \brief Sets integer value for given tag.
    /// \param[in] I integer number with value to be set.
    /// \param[in] T character string with tag name. If tag
    ///            is not found, it will be added, and all data in
    ///            the loop will be reindexed accordingly.
    /// \param[in] nrow  row number. If the row does not exist then
    ///            it will be created, along with all other rows
    ///            between GetLoopLength()-1 and \b nrow as
    ///            necessary. All newly created fields will be
    ///            initialised with \"data not given\" value.
    void  PutInteger ( int I, cpstr T, int nrow );

    /// \brief Sets a set of string values for the given tag and
    ///        range of rows.
    /// \param[in] S string vector with values to store in the loop
    /// \param[in] T character string with tag name. If tag
    ///            is not found, it will be added, and all data in
    ///            the loop will be reindexed accordingly.
    /// \param[in] i1  minimum data index in \b S to set in the loop
    /// \param[in] i2  maximum data index in \b S to set in the loop.
    ///
    /// The data will be set in rows \b i1 to \b i2 (inclusive) in
    /// the loop. If range \b [i1,i2] is not contained in the loop,
    /// all missing rows will be created and initialised to
    /// \"data not given\" value. Example:
    /// \code
    /// CMMCIFLoop loop("_sample_loop");
    /// pstr       S[100];
    /// int        i;
    ///
    ///    //  initialize vector of strings
    ///    for (i=0;i<100;i++)  {
    ///      S[i] = new char[20];
    ///      sprintf ( S[i],"value i=%i",i );
    ///    }
    ///
    ///    //  put data in loop
    ///    loop.PutSVector ( S,"made_up_string_value",0,99 );
    ///
    ///    //  dispose of vector of strings
    ///    for (i=0;i<100;i++)
    ///      if (S[i])  delete[] S[i];
    ///
    /// \endcode
    void  PutSVector   ( psvector S, cpstr T, int i1, int i2 );

    /// \brief Sets a set of float-point values for the given tag and
    ///        range of rows.
    /// \param[in] R vector of real numbers to store in the loop
    /// \param[in] T character string with tag name. If tag
    ///            is not found, it will be added, and all data in
    ///            the loop will be reindexed accordingly.
    /// \param[in] i1  minimum data index in \b S to set in the loop
    /// \param[in] i2  maximum data index in \b S to set in the loop
    /// \param[in] prec float-point precision; g-format with given
    ///            precision will be used.
    ///
    /// The data will be set in rows \b i1 to \b i2 (inclusive) in
    /// the loop. If range \b [i1,i2] is not contained in the loop,
    /// all missing rows will be created and initialised to
    /// \"data not given\" value.
    void  PutRVector   ( rvector  R, cpstr T, int i1, int i2,
                                                   int prec=8 );

    /// \brief Sets a set of integer values for the given tag and
    ///        range of rows.
    /// \param[in] I vector of integers to store in the loop
    /// \param[in] T character string with tag name. If tag
    ///            is not found, it will be added, and all data in
    ///            the loop will be reindexed accordingly.
    /// \param[in] i1  minimum data index in \b S to set in the loop
    /// \param[in] i2  maximum data index in \b S to set in the loop.
    ///
    /// The data will be set in rows \b i1 to \b i2 (inclusive) in
    /// the loop. If range \b [i1,i2] is not contained in the loop,
    /// all missing rows will be created and initialised to
    /// \"data not given\" value.
    void  PutIVector   ( ivector  I, cpstr T, int i1, int i2 );

    /// \brief Returns category type \b MMCIF_Loop.
    int   GetCategoryID() { return MMCIF_Loop; }

    /// \brief Optimizes loop for RAM and data access speed.
    /// Optimized data structures take less RAM and their indexes
    /// are sorted for quicker access. Sorting is done automatically
    /// as new data is added to the category. If the structure
    /// is edited (fields/data removed), it may need
    /// optimization and re-sorting for efficiency.\n\n
    /// The sorting preserves the order of actual appearance of
    /// tags and rows in mmCIF file. If a loop is created
    /// programmatically, the order of tags and rows in mmCIF file
    /// will be the same as order of adding them to the loop.
    void  Optimize();

    /// \brief Writes loop data in mmCIF format into file.
    /// \param[in] FName character string with file name.
    /// \param[in] gzipMode flag to controll compression of files:
    ///  \arg \b GZM_NONE: do not compress
    ///  \arg \b GZM_CHECK: check file name suffix and compress
    ///                     (or not) accordingly
    ///  \arg \b GZM_ENFORCE_GZIP: force gzip compression despite
    ///                     suffix
    ///  \arg \b GZM_ENFORCE_COMPRESS: force using compress despite
    ///                     suffix
    /// \return \b True: success
    /// \return \b False: can not open file for writing.
    /// \remarks This function does not create a valid mmCIF file
    /// as \"data_XXX\" record will be missing. It may be used for
    /// debugging though.
    Boolean WriteMMCIFLoop ( cpstr FName,
                             byte gzipMode=GZM_CHECK );

    /// \brief Writes loop data into given file.
    /// \param f reference to MMDB's file class. The file should be
    /// opened and closed by application.
    /// \remarks There is a very limited use of this function on
    /// application level. It is primarily used by CMMCIFData class.
    void  WriteMMCIF ( RCFile f );

    /// \brief Deep copy of loops.
    /// Deep copy duplicates all data and memory allocations,
    /// producing a genuine clone of the original. Only deep copy
    /// should be used for copying MMDB objects, a mere assignment
    /// operator will fail you.
    /// \param[in] Loop a pointer to CMMCIFLoop, the content of
    ///                 which is copied into 'this' loop.
    void  Copy ( PCMMCIFCategory Loop );

    /// \brief MMDB stream writer.
    void write ( RCFile f );

    /// \brief MMDB stream reader.
    void read  ( RCFile f );

  protected:
    int      nRows;
    psmatrix field;
    int      iColumn,nAllocRows;

    void  InitMMCIFLoop();
    void  FreeMemory   ();
    void  DeleteFields ();
    void  ExpandRows   ( int nRowsNew );

};



//  ======================  CMMCIFData  =============================


//    CIFW are warnings which may be issued on reading the CIF file.
// Each of them means actually a CIF syntax error.
#define CIFW_UnrecognizedItems 0x00000020
#define CIFW_MissingField      0x00000040
#define CIFW_EmptyLoop         0x00000080
#define CIFW_UnexpectedEOF     0x00000100
#define CIFW_LoopFieldMissing  0x00000200
#define CIFW_NotAStructure     0x00000400
#define CIFW_NotALoop          0x00000800
#define CIFW_DuplicateTag      0x00001000

//    CIFRC are return codes from procedures of extracting data from
// the read CIF file. Negative returns reflect unsuccessful and
// not accomplished operation.
#define CIFRC_Loop              2
#define CIFRC_Structure         1
#define CIFRC_Ok                0
#define CIFRC_StructureNoTag   -1
#define CIFRC_LoopNoTag        -2
#define CIFRC_NoCategory       -3
#define CIFRC_WrongFormat      -4
#define CIFRC_NoTag            -5
#define CIFRC_NotAStructure    -6
#define CIFRC_NotALoop         -7
#define CIFRC_WrongIndex       -8
#define CIFRC_NoField          -9
#define CIFRC_Created         -12
#define CIFRC_CantOpenFile    -13
#define CIFRC_NoDataLine      -14
#define CIFRC_NoData          -15


//
//  Functional flags:
//  ~~~~~~~~~~~~~~~~~
//
//  CIFFL_PrintWarnings      when reading CIF file, all warning
//                           messages will be printed. If the flag
//                           is off, the warnings will be bit-encoded
//                           in the return code
//  CIFFL_StopOnWarnings     reading CIF file will stop at first
//                           warning issued
//  CIFFL_SuggestCategories  allows reading CIF file with loops having
//                           no categories. Hidden category names
//                           will be automatically generated for
//                           internal consistency of the system.
//                           These names will not appear in output.
//                           As these names are hidden, they cannot
//                           be used to access data. It is therefore
//                           assumed that all tags in all loops without
//                           categories are unique. Simply specify ""
//                           for category when accessing such data
//                           (it cannot be accessed through CMMCIFLoop,
//                           but only through CMMCIFData functions
//                           taking both Category and Tag; note that
//                           CIFFL_SuggestCategories flag must be on
//                           while accessing such data).
//  CIFFL_SuggestTags        allows for identical tags in a category
//                           (including a hidden category). Hidden
//                           suffixes to tag names will be generated
//                           for internal consistency. At present,
//                           only data for first non-unique tag may be
//                           accessed.
//
#define CIFFL_PrintWarnings      0x00000001
#define CIFFL_StopOnWarnings     0x00000002
#define CIFFL_SuggestCategories  0x00000004
#define CIFFL_SuggestTags        0x00000008


DefineClass(CMMCIFData)
DefineStreamFunctions(CMMCIFData)


/// \brief CMMCIFData represents mmCIF's \"data\" category, which keeps
///        structures and loops and is mandatory element of mmCIF file.
/*!
mmCIF's \"data\" category has the following form:
\code
data_DataName

_structure1.tag1  value1
..........

loop_
..........

\endcode
In the above example, all structures and loops that follow \b data_
keyword until next \b data_ or end of file are part of data category
with name \b DataName.

CMMCIFData represents this construct by keeping a list of CMMCIFStruct
and CMMCIFLoop class instances associated with the corresponding
categories in the data block.

The data object is created automatically when an mmCIF file is read,
or it may be created programatically and then pushed into file.

Access to data is provided via category (structures and loops) names,
tags and data indexes (in case of loops). Alternatively, pointers to
contained structures and loops may be obtained first, an used for
fetching data using CMMCIFStruct's and CMMCIFLoop's interface
functions.

The following code gives an example of creating mmCIF's data category
and populating it:
\code
CMMCIFData data;

  // Specify data name:
  data.PutDataName ( "Sample_Data" );

  // the following statement:
  data.PutInteger ( 12345,"_category1","id" );
  // creates structure "_category1" with tag "id" and assigns it
  // the integer value of 12345.

  data.PutString ( "a name","_category1","name" );

  // Loops may be created quite similarly:
  data.PutLoopInteger ( 12345   ,"_loop1","id"  ,2 );
  data.PutLoopInteger ( "a name","_loop1","name",0 );

  // push data into a file
  data.WriteMMCIFData ( "sample.cif" );

\endcode

The resulting file \b sample.cif will contain:

\code
data_Sample_Data

_category1.id   12345
_category1.name "a name"

loop_
_loop1.id
_loop1.name
.      "a name"
.      .
12345  .
\endcode

The same result may be achieved differently:

\code
CMMCIFData    data;
PCMMCIFStruct mmCIFStruct;  // equivalent to CMMCIFStruct *mmCIFStruct
PCMMCIFLoop   mmCIFLoop;    // equivalent to CMMCIFLoop   *mmCIFLoop

  // Specify data name:
  data.PutDataName ( "Sample_Data" );

  // create new mmCIF's structure in the data block:
  data.AddStructure ( "_category1",mmCIFStruct );
  if (mmCIFStruct)  {
    mmCIFStruct->PutInteger ( 12345   ,"id"   );
    mmCIFStruct->PutString  ( "a name","name" );
  }

  // similarly for the loop:
  data.AddLoop ( "_loop1",mmCIFLoop );
  if (mmCIFLoop)  {
    mmCIFLoop->PutInteger ( 12345   ,"id"  ,2 );
    mmCIFLoop->PutString  ( "a name","name",0 );
  }

  // push data into a file
  data.WriteMMCIFData ( "sample.cif" );

\endcode

See general principles of working with mmCIF files and mmCIF
hierarchies, as well as some code samples, in Section
\"\ref mmcif_handler\".
*/

class CMMCIFData : public CStream  {

  friend class CMMCIFFile;

  public :

    /// \brief Basic constructor.
    CMMCIFData ();

    /// \brief Constructor that assigns data block name.
    /// \param[in] N data block name.
    CMMCIFData ( cpstr N );

    /// \brief Constructor for MMDB data streaming functions.
    CMMCIFData ( RPCStream Object );

    /// \brief Destructor.
    ~CMMCIFData();


    // -------- General I/O functions

    /// \brief Sets flag to print warnings on reading mmCIF files.
    /// \param[in] SPW flag to print warnings:
    ///    \arg \b True : warnings will be printed to stdout
    ///    \arg \b False : warnings will not be printed but returned
    ///                    in return code (default)
    void  SetPrintWarnings ( Boolean SPW );

    /// \brief Sets flag to stop on warning when reading an mmCIF file.
    /// \param[in] SOW flag to stop on warning:
    ///    \arg \b True : reading will stop on first warning encountered
    ///    \arg \b False : warnings will not stop reading (default)
    void  SetStopOnWarning ( Boolean SOW );

    /// \brief Sets optional flag(s) for reading mmCIF files.
    /// By default, no flags are set.
    /// \param[in] F flag or logical \"or\" of several flags to be set:
    ///  \arg \b CIFFL_PrintWarnings  toggles printing warning messages
    ///               at reading an mmCIF file, in stdout. If this
    ///               flag is not set (default), the warnings will
    ///               be returned in the bit-encoded return code
    ///  \arg \b CIFFL_StopOnWarnings  if set, reading an mmCIF file
    ///               will stop at first warning issued
    ///  \arg \b CIFFL_SuggestCategories  allows for reading of mmCIF
    ///               files with loops and structures having no
    ///               category names (\"dirty CIFs\"). If this flag is
    ///               set, then hidden category names will be
    ///               automatically generated. These names will not
    ///               appear in the output. As these names are hidden,
    ///               they cannot be used to access data. In order to
    ///               access data in such categories, consider whether
    ///               they are structures or loops. In case of a
    ///               unnamed structure, simply specify \"\" (empty
    ///               string) for structure name in all access
    ///               functions ( note that \b CIFFL_SuggestCategories
    ///               flag must be on while accessing such data). In
    ///               case of a loop, first use the CMMCIFData::FindLoop
    ///               function to retrieve pointer on the hidden loop,
    ///               and then use CMMCIFLoop's interface function to
    ///               fetch data from the loop.
    ///  \arg \b CIFFL_SuggestTags  allows for duplicate tags in a
    ///               category (structure or loop, including hidden
    ///               categories). This may help reading \"dirty CIFs\".
    ///               At present, only data for first non-unique tag
    ///               may be accessed.
    void  SetFlag ( int F );

    /// \brief Removes optional flag(s) for reading mmCIF files.
    /// By default, no flags are set.
    /// \param[in] F flag or logical \"or\" of several flags to be
    ///              removed (unset):
    ///  \arg \b CIFFL_PrintWarnings  no wornings will be printed in
    ///               stdout, but rather returned in the bit-encoded
    ///               return code
    ///  \arg \b CIFFL_StopOnWarnings  warnings will not stop reading
    ///  \arg \b CIFFL_SuggestCategories  loops without names will
    ///               count as errors and stop reading
    ///  \arg \b CIFFL_SuggestTags  duplicate tags in structures and
    ///               loops will count as errors and stop reading.
    ///
    /// See more detail flag description in CMMCIFData::SetFlag().
    void  RemoveFlag ( int F );

    /// \brief Returns bit-encoded warnings issued at last file read.
    /// \return an integer number, which is an or-superposition of
    ///         warning flags:
    /// \arg \b CIFW_UnrecognizedItems: unrecognized items were found
    /// \arg \b CIFW_MissingField: expected data field not found
    /// \arg \b CIFW_EmptyLoop: loop category was defined but has no
    ///                         data
    /// \arg \b CIFW_UnexpectedEOF: mmCIF construct finished prematurely
    /// \arg \b CIFW_LoopFieldMissing: loop category has wrong number
    ///                         of data fields
    /// \arg \b CIFW_NotAStructure: attempt to use a category name,
    ///                         which was once defined as a structure,
    ///                         as a loop
    /// \arg \b CIFW_NotALoop: attempt to use a category name, which was
    ///                         once defined as a loop, as a structure
    /// \arg \b CIFW_DuplicateTag: duplicate tag was found in a
    ///                         structure or loop
    int  GetWarnings() { return Warning; }

    /// \brief Sets category names and tags that are to be ignored
    ///        on file read.
    /// \param[in] cats list of categories, terminated by NULL
    /// \param[in] tags list of tags, terminated by NULL.
    ///
    /// This special function is to aid reading corrupt mmCIF files.
    /// The input lists should be of equal length 'n', and specify
    /// 'n' \"wrong fields\" that should be ignored on input. E.g.,
    /// ith \"wrong field\" is identified as \"cats[i].taga[i]\".
    /// If \"wrong field\" belongs to a loop, then all the corresponding
    /// column is assumed to be absent. This corrects for mmCIF errors
    /// when defined tags in loops or structures do not have actual data
    /// associated with them.
    ///
    /// In order to remove settings, call SetWrongFields(NULL,NULL).
    ///
    /// Example:
    /*!
    \code
    // assume data for "_category.item1" and "_category.item2"
    // missed in a file to-be-read
    CMMCIFData data;
    cpstr cats[] = { "_category", "_category", NULL };
    cpstr tags[] = { "item1"    , "item2"    , NULL };

       data.SetWrongFields ( cats,tags );
       data.ReadMMCIFData  ( "corrupt.cif" );
    \endcode
    */
    void  SetWrongFields ( cpstr *cats, cpstr *tags );

    /// \brief Reads mmCIF data block from file.
    /// \param FName character null-terminated string with file name
    /// \param gzipMode flag to read compressed files:
    /// \arg \b GZM_NONE: do not assume any compression
    /// \arg \b GZM_CHECK: check compression type by file extension
    /// \arg \b GZM_ENFORCE: same as \b GZM_ENFORCE_GZIP
    /// \arg \b GZM_ENFORCE_GZIP: assume gzip compression (*.gz files)
    /// \arg \b GZM_ENFORCE_COMPRESS: assume compression with 'compress'
    ///         (*.Z files).
    /// \return \b CIFRC_Ok: no errors
    /// \return \b negative: there were errors
    /// \return \b positive: there were warnings.
    ///
    /// This function will read 1st data block from the specified file.
    /// In case of non-zero return, use GetCIFMessage() function to
    /// print the corresponding error message or warning:
    /*!
    \code
    CMMCIFData data;
    char       errLog[500];
    int        rc;
       rc = data.ReadMMCIFData  ( "myfile.cif" );
       if (rc<0)
         printf ( " There was an error:\n %s\n",
                  GetCIFMessage(errLog,rc) );
       else if (rc>0)
         printf ( " There were warnings:\n %s\n",
                  GetCIFMessage(errLog,rc) );
       else
         printf ( " mmCIF file has be read in successfully.\n" );
    \endcode
    */
    int  ReadMMCIFData ( cpstr FName, byte gzipMode=GZM_CHECK );

    /// \brief Reads sequential mmCIF data blocks from file.
    /// \param RCFile reference to a CFile object opened on a file
    /// \param S buffer string which represent a sliding read window.
    ///          The string should be at least 500 characters long,
    ///          initialized with empty-string value before first read,
    ///          and passed unchanged between the reads
    /// \param lcount line counter, should be set zero before first
    ///          read and passed unchanged between the reads.
    /// \return \b CIFRC_Ok: no errors
    /// \return \b negative: there were errors
    /// \return \b positive: there were warnings.
    ///
    /// This function will read 1st data block from the current position
    /// of the file. The function is useful if a file contains more than
    /// a single data block, which should be read sequentially.
    ///
    /// \note Alternatively, files with multiple data blocks can be
    /// read using CMMCIFFile class.
    ///
    /// In case of non-zero return, use GetCIFMessage() function to
    /// print the corresponding error message or warning:
    /*!
    \code
  CMMCIFData    mmCIFData;
  CFile         f;
  char          S[1000];
  int           rc,lcount;

    // open file first
    f.assign ( "/path/example.cif" );
    if (!f.reset(True))  {
      printf ( " *** cannot open file '%s' for reading.\n",
               f.FileName() );
      return -1;
    }

    lcount = 0;         // global line counter through the file
    S[0]   = char(0);   // buffer string
    while (!f.FileEnd())  {

      rc = mmCIFData.ReadMMCIFData ( f,S,lcount );

      if (rc!=CIFRC_Ok)  {  // error or warning
        if ((rc<0) && (!f.FileEnd()))  { // error
          printf ( " *** error reading file %s:\n"
                   "     %s\n",f.FileName(),GetCIFMessage(S,rc) );
          return rc;
        } else if (rc>0)  { // warning
          printf ( " ... warning on reading file %s:\n"
                   "     %s\n",f.FileName(),GetCIFMessage(S,rc) );
        }
      } else  {
        // fetch needful values from the data block
        // ........
      }

    }

    f.shut();  // close file

    // NOTE: do not delete CMMCIFStruct/CMMCIFLoop
    // classes obtained from CMMCIFData. If you do, get a crash.
    // All these structures are containers that dispose their
    // content automatically.
    \endcode
    */
    int  ReadMMCIFData ( RCFile f, pstr S, int & lcount );

    /// \brief Writes mmCIF data block into file.
    /// \param FName character null-terminated string with file name
    /// \param gzipMode flag to read compressed files:
    /// \arg \b GZM_NONE: do not compress
    /// \arg \b GZM_CHECK: compress according to file extension
    /// \arg \b GZM_ENFORCE: same as \b GZM_ENFORCE_GZIP
    /// \arg \b GZM_ENFORCE_GZIP: compress with gzip
    /// \arg \b GZM_ENFORCE_COMPRESS: compression with 'compress'.
    /// \return \b True: no errors
    /// \return \b False: file cannot be open for writing.
    Boolean WriteMMCIFData   ( cpstr FName,
                               byte gzipMode=GZM_CHECK );

    /// \brief Writes (next) mmCIF data block into file.
    /// \param RCFile reference to a CFile object opened on a file.
    ///
    /// This function allows for sequential write of mmCIF data blocks
    /// into a file.
    ///
    /// \note Alternatively, files with multiple data blocks can be
    /// created using CMMCIFFile class.
    ///
    /// Example:
    /*!
  \code
  CFile       f;
  CMMCIFData  cifData;

    // open file first
    f.assign ( "/path/example.cif" );
    if (!f.rewrite())  {
      printf ( " *** cannot open file '%s' for writing.\n",
               f.FileName() );
      return -1;
    }

    cifData.PutDataName ( "name1" );
    // fill cifData with all data needed
    cifData.WriteMMCIF ( f ); // first data block written

    cifData.FreeMemory  ( 0 );  // reset data block to empty
    cifData.PutDataName ( "name2" );
    // fill cifData with all data needed
    cifData.WriteMMCIF ( f );  // second data block written

    // add as many data blocks as needed

    // now close the file
    f.shut();

  \endcode

    */
    void  WriteMMCIF ( RCFile f );


    // -------- Retrieving data

    /// \brief Returns the number of categories (structures and loops)
    ///        in data block.
    int   GetNumberOfCategories ()  { return nCategories; }

    /// \brief Retrieves pointer to category (a structure or a loop) by
    ///        category number.
    /// \param categoryNo category number to retrieve. Categories are
    ///        numbered from 0 to GetNumberOfCategories()-1.
    /// \return pointer to category, if \b categoryNo is in the right
    ///        range, or \b NULL otherwise.
    ///
    /// \note The category type (structure or loop) is returned by
    /// function CMMCIFCategory::GetCategoryID().
    /// \note The application should never attempt to deallocate
    /// the category returned. It will be properly disposed of by
    /// CMMCIFData's destructor.
    PCMMCIFCategory GetCategory ( int categoryNo ); // 0..nCategories-1

    /// \brief Retrieves mmCIF structure with given name.
    /// \param CName character string with name of the structure (must
    ///        start with underscore).
    /// \return pointer to structure if structure with given name was
    ///        found, and \b NULL otherwise.
    /// \note The application should never attempt to deallocate
    /// the structure returned. It will be properly disposed of by
    /// CMMCIFData's destructor.
    PCMMCIFStruct GetStructure  ( cpstr CName );

    /// \brief Retrieves mmCIF loop with given name.
    /// \param CName character string with name of the loop (must
    ///        start with underscore).
    /// \return pointer to loop if loop with given name was
    ///        found, and \b NULL otherwise.
    /// \note The application should never attempt to deallocate
    /// the loop returned. It will be properly disposed of by
    /// CMMCIFData's destructor.
    PCMMCIFLoop GetLoop ( cpstr CName );

    /// \brief Finds loop containing all tags from the tag list
    ///        provided.
    /// \param tagList list of tags to be looked for. The list should
    ///        be terminated by empty string \"\". The order of tags
    ///        is not significant.
    /// \return pointer to loop if loop with given tags was found, and
    ///         \b NULL otherwise.
    ///
    /// The function will look for first loop that includes all tags
    /// from the list. The list does not have to include all tags for
    /// that loop in order for function to succeed. This function is
    /// useful for reading \"dirty cifs\" that may contain loops without
    /// a name.
    PCMMCIFLoop FindLoop ( cpstr * tagList );

    /// \brief Retrieves data block name into dynamically-allocated
    ///        string.
    /// \param dname pointer reference to a string that accepts data
    ///        block name. If \b dname is not \b NULL, it is treated
    ///        as a pre-allocated string, which is disposed before
    ///        copying. The application is responsible for deallocating
    ///        \b dname.
    /// \param Remove flag to remove name from the data block.
    void GetDataName ( pstr & dname, Boolean Remove=False );

    /// \brief Returns data block name.
    pstr GetDataName()  { return name; }

    //   CheckData(..) returns positive value if the field is in the
    // file:
    //   CIFRC_Structure  category CName is a structure
    //   CIFRC_Loop       category CName is a loop
    // Negative returns mean:
    //   CIFRC_StructureNoTag  category CName is present,
    //                        it is a structure, but it does not
    //                        have tag TName
    //   CIFRC_LoopNoTag       category CName is present,
    //                        it is a loop, but it does not have
    //                        tag TName
    //   CIFRC_NoCategory      category CName is not present.
    // If TName is set to NULL then only the CName is checked and
    // possible returns are CIFRC_Structure, CIFRC_Loop and
    // CIFRC_NoCategory.
    int  CheckData       ( cpstr CName, cpstr TName );

    int  DeleteCategory  ( cpstr CName );
    int  DeleteStructure ( cpstr CName );
    int  DeleteLoop      ( cpstr CName );

    //   Optimize() optimizes the CIF data in memory allocation. It is
    // a good idea to call it once after extraction of data (GetXXXXXX
    // functions) with Remove flag set on has been completed.
    void Optimize();

    //   GetString(..), GetReal(..) and GetInteger(..) return 0 if the
    // requested field was found and successfully converted. Negative
    // returns mean:
    //    CIFRC_WrongFormat   the field was found but failed to convert
    //                        due to improper numeric format
    //    CIFRC_NoTag         category CName was found, but it does not
    //                        have tag TName
    //    CIFRC_NoCategory    category CName was not found
    //    CIFRC_NotAStructure category CName was found, but it is
    //                        a loop rather than a structure.
    //   GetString(..) will try to dispose Dest unless it is assigned
    // NULL value before the call. The string will be then dynamically
    // allocated and copied.
    //   If Remove is set to True, the field will be removed after
    // extraction.
    int  GetString   ( pstr & Dest, cpstr CName, cpstr TName,
                                    Boolean Remove=False );
    pstr GetString   ( cpstr CName, cpstr TName, int & RC );
    int  DeleteField ( cpstr CName, cpstr TName );
    int  GetReal     ( realtype & R, cpstr CName,
                       cpstr TName, Boolean Remove=False );
    int  GetInteger  ( int & I, cpstr CName, cpstr TName,
                                Boolean Remove=False );

    //   GetLoopLength(..) returns CIFRC_NotALoop if the category CName
    // is not a loop, CIFRC_NoCategory if the category CName is not
    // found. Non-negative returns give the length of the loop (may be
    // 0 if the loop is empty).
    int  GetLoopLength ( cpstr CName );

    //   GetLoopString(..), GetLoopReal(..) and GetLoopInteger(..) act
    // like GetString(..), GetReal(..) and GetInteger(..) above for
    // nrow-th element of the 'loop_' (indexed like 0..N-1 where N
    // is obtained through GetLoopLength(..)). They will return
    // CIFRC_WrongIndex if nrow is out of range.
    //   If Remove is set to True, the field will be removed after
    // extraction.
    int  GetLoopString   ( pstr & Dest, cpstr CName,
                                        cpstr TName, int nrow,
                                        Boolean Remove=False );
    pstr GetLoopString   ( cpstr CName, cpstr TName,
                           int nrow, int & RC );
    int  DeleteLoopField ( cpstr CName, cpstr TName,
                           int nrow );
    int  GetLoopReal     ( realtype & R, cpstr CName,
                                         cpstr TName, int nrow,
                                         Boolean Remove=False );
    int  GetLoopInteger  ( int & I, cpstr CName,
                                    cpstr TName, int nrow,
                                    Boolean Remove=False );

    //   GetLoopSVector(..), GetLoopRVector(..) and GetLoopIVector(..)
    // read CIF 'loop_' data into allocated vectors of strings, reals
    // and integers, correspondingly. The vectors may be deallocated
    // prior to call and assigned NULL, in which case they will be
    // allocated with offsets of i1, which is also the lower index of
    // the 'loop_' data transferred into it. The upper vector index is
    // given by i2 or by the loop's length whichever is less. If
    // vectors are not assigned NULL prior the call, it is assumed
    // that they are properly (i1-offset, i2-i1+1 length) allocated.
    //   The return codes are same as those of GetLoopString(..),
    // GetLoopReal(..) and GetLoopInteger(..).
    int  GetLoopSVector ( psvector & S, cpstr CName,
                          cpstr TName, int i1=0, int i2=MaxInt4,
                          Boolean Remove=False );
    int  GetLoopRVector ( rvector  & R, cpstr CName,
                          cpstr TName, int i1=0, int i2=MaxInt4,
                          Boolean Remove=False );
    int  GetLoopIVector ( ivector  & I, cpstr CName,
                          cpstr TName, int i1=0, int i2=MaxInt4,
                          Boolean Remove=False );


    // -------- Storing data

    //   Unless the data are to be added to the existing CIF structure,
    // FreeMemory() should be called once before creating a new
    // CIF data set.
    void FreeMemory ( int key );

    void PutDataName ( cpstr dname ); // stores name for 'data_'
                                      // record

    //   PutString(..), PutReal(..) and PutInteger(..) will put the
    // values given into the specified category (CName) under the
    // specified tag (TName). The category, tag and field are created
    // automatically; the field will be replaced silently if identical
    // CName.TName is specified in two calls. Calls of these functions
    // may follow in random order; however CIF file will have all tags
    // grouped by categories and catgories will follow in the order
    // of first appearance in PutString(..), PutReal(..) or
    // PutInteger(..).
    //   Return code - one of CIFRC_Ok or CIFRC_NotAStruct
    int  PutNoData   ( int NoDataType, cpstr CName,
                       cpstr TName );
    int  PutString   ( cpstr S, cpstr CName,
                       cpstr TName, Boolean Concatenate=False );
    int  PutDate     ( cpstr CName, cpstr TName );
    int  PutReal     ( realtype R, cpstr CName, cpstr TName,
                                   int prec=8 );
    int  PutInteger  ( int I, cpstr CName, cpstr TName );

    //   If loop category CName is not present in the CIF data
    // structure, AddLoop(..) creates an empty one and returns
    // its pointer in Loop. If loop category CName is already in
    // the CIF data structure, its pointer is returned, and any
    // data which might be contained in it, remains untouched.
    //   To stuff the loop with data, first the data tags have to
    // be specified by calling  Loop->AddLoopTag(..). After all
    // tags are given, the data comes as a stream of calls
    // Loop->AddString(..), Loop->AddReal(..) and
    // Loop->AddInteger(..) which should provide data for every
    // tag in sequence in strictly the same order as the tags
    // were given. This essentially reflects reading a CIF loop
    // from a file.
    //   Alternatively, the loop data may be stored with PutLoopXXX()
    // functions given below, although this way may be less
    // efficient (but more flexible).
    //   AddLoop(..) may return
    //     CIFRC_Ok       category was present
    //     CIFRC_Created  category was not present but it has
    //                    been created; the category is empty
    //     CIFRC_NotALoop category was present as a structure, but
    //                    has been replaced for a loop;
    //                    the category is empty.
    int  AddLoop      ( cpstr CName, PCMMCIFLoop   & Loop   );
    int  AddStructure ( cpstr CName, PCMMCIFStruct & Struct );

    //   PutLoopString(..), PutLoopReal(..) and PutLoopInteger(..) act
    // like PutString(..), PutReal(..) and PutInteger(..) above for
    // nrow-th element of the 'loop_' CName (indexed begining from 0).
    // In consequitive calls, given values of nrow does not have to be
    // ordered; the most efficient way is to start with HIGHEST value
    // for nrow in the loop and move down to 0. The least efficient way
    // is to start with nrow=0 and move up.
    //   These functions allow to form loops in arbitrary way.
    //   The functions may return CIFRC_Ok or CIFRC_NotALoop.
    int  PutLoopNoData  ( int NoDataType, cpstr CName,
                                          cpstr TName, int nrow );
    int  PutLoopString  ( cpstr S,   cpstr CName,
                                          cpstr TName, int nrow );
    int  PutLoopReal    ( realtype R, cpstr CName,
                                      cpstr TName, int nrow,
                                      int  prec=8 );
    int  PutLoopInteger ( int I, cpstr CName, cpstr TName,
                                 int nrow );

    //   PutLoopSVector(..), PutLoopRVector(..) and PutLoopIVector(..)
    // put vectors of values into specified loop fields. Parameters i1
    // and i2 give the range of indices of values which are to be
    // transfered. To transfer an entire vector allocated as [0..N-1]
    // i1 shoudl be set to 0 and i2 - to N-1. Note that the loop is
    // always indexed as starting form 0 on, therefore negative i1 and
    // i2 are not allowed, and specifying i1>0 will leave first i1
    // elements of the CIF loop for the corresponding tag undefined
    // (will be output like '?').
    //   These functions allow to form loops in arbitrary way.
    int  PutLoopSVector ( psvector S, cpstr CName,
                          cpstr TName, int i1, int i2 );
    int  PutLoopRVector ( rvector  R, cpstr CName,
                          cpstr TName, int i1, int i2,
                          int prec=8 );
    int  PutLoopIVector ( ivector  I, cpstr CName,
                          cpstr TName, int i1, int i2 );

    int  RenameCategory ( cpstr CName, cpstr newCName );

    // --------

    void Copy         ( PCMMCIFData Data );
    int  CopyCategory ( PCMMCIFData Data, cpstr CName,
                                          cpstr newCName=NULL );

    void PrintCategories();  // for debuging only

    void write ( RCFile f );
    void read  ( RCFile f );

  protected:
    pstr             name;
    int              nCategories;
    PPCMMCIFCategory Category;
    ivector          index;
    int              flags;
    int              Warning;
    int              loopNo;  // used locally for suggesting categories
    int              tagNo;   // used locally for suggesting tags
    psvector         WrongCat;
    psvector         WrongTag;
    int              nWrongFields;

    void    InitMMCIFData   ();
    void    FreeWrongFields ();
    Boolean CheckWrongField ( cpstr C, cpstr T );
    void    Sort            ();

    //   GetCategoryNo searches for index of category cname
    // in Category[]. Return:
    //    >=0 : position of the category found
    //     <0 : the category was not found, it could be inserted before
    //          (-RC-1)th element, where RC is the return value
    int  GetCategoryNo  ( cpstr cname );
    int  AddCategory    ( cpstr cname );
    int  DeleteCategory ( int  CatNo );

    void GetDataItem    ( RCFile f, pstr S, pstr & L, pstr & p,
                                    int & lcount, int & llen );
    void GetLoop        ( RCFile f, pstr S, pstr & L, pstr & p,
                                    int & lcount, int & llen );
    int  GetField       ( RCFile f, pstr S, pstr & L, pstr & p,
                                    int & lcount, int & llen );

};



//  ======================  CMMCIFFile  =============================

DefineClass(CMMCIFFile)
DefineStreamFunctions(CMMCIFFile)

class CMMCIFFile : public CStream  {

  public :
    int          nData;
    ivector      index;
    PPCMMCIFData data;

    CMMCIFFile ();
    CMMCIFFile ( cpstr FName, byte gzipMode=GZM_CHECK );
    CMMCIFFile ( RPCStream Object );
    ~CMMCIFFile();

    void  SetPrintWarnings ( Boolean SPW ) { PrintWarnings = SPW; }
    void  SetStopOnWarning ( Boolean SOW ) { StopOnWarning = SOW; }

    int   ReadMMCIFFile    ( cpstr FName,byte gzipMode=GZM_CHECK);
    int   WriteMMCIFFile   ( cpstr FName,byte gzipMode=GZM_CHECK);

    int   GetNofData()  { return nData; }
    PCMMCIFData GetCIFData ( int   dataNo );  // 0..nData-1
    PCMMCIFData GetCIFData ( cpstr DName  );
    int   AddMMCIFData     ( cpstr DName  );
    int   DeleteMMCIFData  ( cpstr DName  );
    int   DeleteMMCIFData  ( int   dataNo );
    int   GetCIFDataNo     ( cpstr DName  );

    void  WriteMMCIF       ( RCFile f    );

    void  Copy  ( PCMMCIFFile File );

    void  write ( RCFile f );
    void  read  ( RCFile f );

  protected:
    int     nAllocData;
    Boolean PrintWarnings;
    Boolean StopOnWarning;

    void  InitMMCIFFile();
    void  FreeMemory   ();
    void  Sort         ();
    void  ExpandData   ( int nDataNew );

};

extern pstr GetMMCIFInputBuffer ( int & LineNo );

//  isCIF will return
//    -1   if file FName does not exist
//     0   if file FName is likely a CIF file ( 'data_' is present )
//     1   if file FName is not a CIF file ( 'data_' is absent )
extern int isCIF ( cpstr FName, byte gzipMode=GZM_CHECK );
extern int isCIF ( RCFile f );

pstr GetCIFMessage ( pstr M, int RC );


#endif