1 //  $Id: mmdb_mmcif_.cpp $
2 //  =================================================================
3 //
4 //   CCP4 Coordinate Library: support of coordinate-related
5 //   functionality in protein crystallography applications.
6 //
7 //   Copyright (C) Eugene Krissinel 2000-2015.
8 //
9 //    This library is free software: you can redistribute it and/or
10 //    modify it under the terms of the GNU Lesser General Public
11 //    License version 3, modified in accordance with the provisions
12 //    of the license to address the requirements of UK law.
13 //
14 //    You should have received a copy of the modified GNU Lesser
15 //    General Public License along with this library. If not, copies
16 //    may be downloaded from http://www.ccp4.ac.uk/ccp4license.php
17 //
18 //    This program is distributed in the hope that it will be useful,
19 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
20 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 //    GNU Lesser General Public License for more details.
22 //
23 //  =================================================================
24 //
25 //    28.09.15   <--  Date of Last Modification.
26 //                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
27 //  -----------------------------------------------------------------
28 //
29 //  **** Module  :  MMDB_MMCIF <implementation>
30 //       ~~~~~~~~~
31 //  **** Project :  MacroMolecular Data Base (MMDB)
32 //       ~~~~~~~~~
33 //  **** Classes :  mmdb::mmcif::Category ( mmCIF category    )
34 //       ~~~~~~~~~  mmdb::mmcif::Struct   ( mmCIF structure   )
35 //                  mmdb::mmcif::Loop     ( mmCIF loop        )
36 //                  mmdb::mmcif::Data     ( mmCIF data block  )
37 //                  mmdb::mmcif::File     ( mmCIF file        )
38 //
39 //  (C) E. Krissinel 2000-2015
40 //
41 //  =================================================================
42 //
43 
44 #include <string.h>
45 #include <stdlib.h>
46 #include <time.h>
47 
48 #include "mmdb_mmcif_.h"
49 
50 namespace mmdb  {
51 
52   namespace mmcif  {
53 
54     //  ======================  SortTags  ===============================
55 
SortTags(psvector tag,int len,ivector index)56     void  SortTags ( psvector tag, int len, ivector index )  {
57     int  i,k,l,l1,l2;
58       if (len==1)  {
59         index[0] = 0;
60         return;
61       }
62       if (strcasecmp(tag[0],tag[1])<0)  {
63         index[0] = 0;
64         index[1] = 1;
65       } else  {
66         index[0] = 1;
67         index[1] = 0;
68       }
69       for (k=2;k<len;k++)  {
70         l2 = k-1;
71         if (strcasecmp(tag[k],tag[index[0]])<0)       l2 = 0;
72         else if (strcasecmp(tag[k],tag[index[l2]])>0) l2 = k;
73         else  {
74           l1 = 0;
75           while (l1<l2-1)  {
76             l = (l1+l2)/2;
77             if (strcasecmp(tag[k],tag[index[l]])<0)  l2 = l;
78                                                else  l1 = l;
79           }
80         }
81         for (i=k;i>l2;i--)
82           index[i] = index[i-1];
83         index[l2] = k;
84       }
85     }
86 
87 
88     //  ======================  Category  ==========================
89 
90     const int CIF_NODATA_DOT        = 0;
91     const int CIF_NODATA_QUESTION   = 1;
92     cpstr CIF_NODATA_DOT_FIELD      = pstr("\x02" ".");
93     cpstr CIF_NODATA_QUESTION_FIELD = pstr("\x02" "?");
94 
Category()95     Category::Category() : io::Stream()  {
96       InitCategory();
97     }
98 
Category(cpstr N)99     Category::Category ( cpstr N ) : io::Stream()  {
100       InitCategory();
101       SetCategoryName ( N );
102     }
103 
Category(io::RPStream Object)104     Category::Category ( io::RPStream Object ) : io::Stream(Object)  {
105       InitCategory();
106     }
107 
~Category()108     Category::~Category()  {
109       FreeMemory();
110     }
111 
InitCategory()112     void Category::InitCategory()  {
113       name       = NULL;
114       nTags      = 0;
115       tag        = NULL;
116       index      = NULL;
117       nAllocTags = 0;
118     }
119 
FreeMemory()120     void Category::FreeMemory()  {
121     int i;
122       if (name)  delete[] name;
123       name = NULL;
124       for (i=0;i<nAllocTags;i++)
125         if (tag[i])  delete[] tag[i];
126       FreeVectorMemory ( tag  ,0 );
127       FreeVectorMemory ( index,0 );
128       nTags      = 0;
129       nAllocTags = 0;
130     }
131 
SetCategoryName(cpstr N)132     void Category::SetCategoryName ( cpstr N )  {
133       if (N[0])  CreateCopy ( name,N );
134       else  {
135         CreateCopy ( name,pstr(" ") );
136         name[0] = char(1);  // no category name
137       }
138     }
139 
ExpandTags(int nTagsNew)140     void Category::ExpandTags ( int nTagsNew )  {
141     int      i,nAT;
142     psvector tag1;
143     ivector  index1;
144       if (nTagsNew>nAllocTags)  {
145         nAT = nTagsNew + IMin(nAllocTags/2+1,20);
146         GetVectorMemory ( tag1  ,nAT,0 );
147         GetVectorMemory ( index1,nAT,0 );
148         for (i=0;i<nAllocTags;i++)  {
149           tag1  [i] = tag  [i];
150           index1[i] = index[i];
151         }
152         for (i=nAllocTags;i<nAT;i++)  {
153           tag1  [i] = NULL;
154           index1[i] = i;
155         }
156         FreeVectorMemory ( tag  ,0 );
157         FreeVectorMemory ( index,0 );
158         tag        = tag1;
159         index      = index1;
160         nAllocTags = nAT;
161       }
162     }
163 
GetTag(int tagNo)164     pstr Category::GetTag ( int tagNo )  {
165       if ((tagNo>=0) && (tagNo<nTags))  return tag[tagNo];
166       return NULL;
167     }
168 
Sort()169     void Category::Sort()  {
170     //  Sorts tags for easing the search
171     int i,k;
172       if (nAllocTags>0)  {
173         k = 0;
174         if (!index)
175           GetVectorMemory ( index,nAllocTags,0 );
176         for (i=0;i<nTags;i++)
177           if (tag[i])  {
178             if (k<i)  {
179               tag[k] = tag[i];
180               tag[i] = NULL;
181             }
182             k++;
183           }
184         nTags = k;
185         SortTags ( tag,nTags,index );
186       }
187     }
188 
Optimize()189     void Category::Optimize()  {
190     int      i,k;
191     psvector tag1;
192       k = 0;
193       for (i=0;i<nTags;i++)
194         if (tag[i])  k++;
195       if (k<=0)  FreeMemory();
196       else if (k!=nAllocTags)  {
197         GetVectorMemory  ( tag1,k,0 );
198         FreeVectorMemory ( index,0 );
199         k = 0;
200         for (i=0;i<nTags;i++)
201           if (tag[i])
202             tag1[k++] = tag[i];
203         FreeVectorMemory ( tag,0 );
204         tag        = tag1;
205         nTags      = k;
206         nAllocTags = nTags;
207         Sort();
208       }
209     }
210 
GetTagNo(cpstr ttag)211     int  Category::GetTagNo ( cpstr ttag )  {
212     //   Binary search for index of tag ttag in tag[].
213     // Return:
214     //    >=0 : position of the tag found
215     //     <0 : the tag was not found, it could be inserted before
216     //          (-RC-1)th element, where RC is the return value
217     int l1,l2,l,k;
218 
219       if (!tag)    return -1;
220 
221       if (!index)  Sort();
222 
223       l = 0;
224       l1 = 0;
225       l2 = nTags-1;
226       k  = 1;
227       while (l1<l2-1)  {
228         l = (l1+l2)/2;
229         k = strcasecmp ( ttag,tag[index[l]] );
230         if (k<0)      l2 = l;
231         else if (k>0) l1 = l;
232         else {
233           l1 = l;
234           break;
235         }
236       }
237 
238       if (k==0)  return index[l];    // is at RCth position
239       k = strcasecmp ( ttag,tag[index[l1]] );
240       if (k==0)  return index[l1];   // is at RCth position
241       if (k<0)   return -1;          // would be at (-RC-1)th position
242       if (l2!=l1)  {
243         k = strcasecmp ( ttag,tag[index[l2]] );
244         if (k==0)  return index[l2]; // is at RCth position
245         if (k>0)   return -2-l2;     // would be at l2+1=(-RC-1)th position
246       }
247 
248       return -2-l1;                  // would be at l1+1=(-RC-1)th position
249 
250     }
251 
AddTag(cpstr ttag)252     int  Category::AddTag ( cpstr ttag )  {
253     //  return -1: the tag has been added on the top of array;
254     //             index is added and sorted automatically
255     //        >=0: the tag is already in the array -- its position
256     //             is returned
257     int  i1,i;
258       if (!tag)  {
259         ExpandTags ( 3 );  // get space for first 3 tags
260         CreateCopy ( tag[0],ttag );
261         nTags  = 1;
262         return -nTags;  // the tag has been added on the top of array
263       }
264       i1 = GetTagNo ( ttag );
265       if (i1>=0)  return i1;  // non-negative returns mean that
266                               // the tag is already in the array
267       i1 = -i1-1;  // otherwise the tag has to be added and indexed at here
268       // put new tag on the top of array and update index
269       ExpandTags ( nTags+1 );
270       CreateCopy ( tag[nTags],ttag );
271       for (i=nTags;i>i1;i--)
272         index[i] = index[i-1];
273       index[i1] = nTags;
274       nTags++;
275       return -nTags; // the tag has been added on the top of array
276     }
277 
PrintTags()278     void Category::PrintTags()  {
279     int i;
280       Sort();
281       printf ( " Unsorted tags:\n" );
282       for (i=0;i<nTags;i++)
283         if (tag[i])
284           printf ( "  %s.%s\n",name,tag[i] );
285       if (index)  {
286         printf ( " Sorted tags:\n" );
287         for (i=0;i<nTags;i++)
288           if (tag[index[i]])
289             printf ( "  %s.%s\n",name,tag[index[i]] );
290       }
291     }
292 
CheckTags(cpstr * tagList)293     bool Category::CheckTags ( cpstr * tagList )  {
294     int i;
295       i = 0;
296       while (tagList[i][0])  {
297         if (GetTagNo(tagList[i])<0)  return false;
298         i++;
299       }
300       return true;
301     }
302 
PutCategoryName(cpstr newName)303     void  Category::PutCategoryName ( cpstr newName )  {
304       CreateCopy ( name,newName );
305     }
306 
Copy(PCategory Category)307     void  Category::Copy ( PCategory Category )  {
308     int i;
309       FreeMemory();
310       if (Category)  {
311         CreateCopy ( name,Category->name );
312         nTags      = Category->nTags;
313         nAllocTags = nTags;
314         if (nTags>0) {
315           GetVectorMemory ( tag  ,nAllocTags,0 );
316           GetVectorMemory ( index,nAllocTags,0 );
317           for (i=0;i<nTags;i++)  {
318             tag[i]   = NULL;
319             CreateCopy ( tag[i],Category->tag[i] );
320             index[i] = Category->index[i];
321           }
322         }
323       }
324     }
325 
326 
write(io::RFile f)327     void Category::write ( io::RFile f )  {
328     int i;
329       if (!index)  Sort();
330       f.CreateWrite ( name   );
331       f.WriteInt    ( &nTags );
332       for (i=0;i<nTags;i++)
333         f.CreateWrite ( tag[i] );
334       f.WriteVector ( index,nTags,0 );
335     }
336 
read(io::RFile f)337     void Category::read ( io::RFile f )  {
338     int i;
339       FreeMemory   ();
340       f.CreateRead ( name   );
341       f.ReadInt    ( &nTags );
342       nAllocTags = nTags;
343       if (nTags>0) {
344         GetVectorMemory ( tag,nTags,0 );
345         for (i=0;i<nTags;i++)  {
346           tag[i] = NULL;
347           f.CreateRead ( tag[i] );
348         }
349       }
350       f.CreateReadVector ( index,0 );
351     }
352 
MakeStreamFunctions(Category)353     MakeStreamFunctions(Category)
354 
355 
356 
357     //  ======================  Struct  ===========================
358 
359 
360     Struct::Struct() : Category() {
361       InitStruct();
362     }
363 
Struct(cpstr N)364     Struct::Struct ( cpstr N ) : Category(N) {
365       InitStruct();
366     }
367 
Struct(io::RPStream Object)368     Struct::Struct ( io::RPStream Object ) : Category(Object)  {
369       InitStruct();
370     }
371 
~Struct()372     Struct::~Struct()  {
373       FreeMemory();
374     }
375 
FreeMemory()376     void Struct::FreeMemory()  {
377     int i;
378       for (i=0;i<nAllocTags;i++)
379         if (field[i]) delete[] field[i];
380       FreeVectorMemory ( field,0 );
381       Category::FreeMemory();
382     }
383 
InitStruct()384     void Struct::InitStruct()  {
385       field = NULL;
386     }
387 
Optimize()388     void Struct::Optimize()  {
389     int      i,k;
390     psvector f1;
391       k = 0;
392       for (i=0;i<nTags;i++)
393         if (!tag[i])  {
394           if (field[i])  delete[] field[i];
395           field[i] = NULL;
396         } else if (!field[i])  {
397           delete[] tag[i];
398           tag[i] = NULL;
399         } else
400           k++;
401       if (k<=0)  FreeMemory();
402       else if (k!=nAllocTags)  {
403         f1 = new pstr[k];
404         k  = 0;
405         for (i=0;i<nTags;i++)
406           if (tag[i])
407             f1[k++] = field[i];
408         FreeVectorMemory ( field,0 );
409         field = f1;
410         Category::Optimize();
411       }
412     }
413 
AddField(cpstr F,cpstr T,bool Concatenate)414     void Struct::AddField ( cpstr F, cpstr T, bool Concatenate )  {
415     psvector field1;
416     int      i,nAT;
417     pstr     nf;
418 
419       nAT = nAllocTags;
420       i   = AddTag ( T );
421 
422       if (i<0) {
423         // The tag was not in the list, but has been added on the top
424         // of list. Now expand the field list and put new field on
425         // the top of it.
426         if (nAllocTags>nAT)  {
427           GetVectorMemory ( field1,nAllocTags,0 );
428           for (i=0;i<nTags-1;i++)
429             field1[i] = field[i];
430           for (i=nTags-1;i<nAllocTags;i++)
431             field1[i] = NULL;
432           FreeVectorMemory ( field,0 );
433           field = field1;
434         }
435         i        = nTags-1;
436         field[i] = NULL;
437       }
438 
439       if (!F)  {
440         if ((!Concatenate) || (!field[i]))  {
441           CreateCopy ( field[i],pstr(" ?") );
442           field[i][0] = char(2);
443         }
444       } else if ((!Concatenate) || (!field[i]))
445         CreateCopy ( field[i],F );
446       else  {
447         nf = new char[strlen(field[i])+strlen(F)+1];
448         strcpy ( nf,field[i] );
449         strcat ( nf,F        );
450         delete[] field[i];
451         field[i] = nf;
452       }
453 
454     }
455 
GetField(int tagNo)456     pstr Struct::GetField ( int tagNo )  {
457       if ((tagNo>=0) && (tagNo<nTags))  return field[tagNo];
458       return NULL;
459     }
460 
GetString(pstr & S,cpstr TName,bool Remove)461     int  Struct::GetString ( pstr & S, cpstr TName,
462                                    bool Remove )  {
463     int k = GetTagNo ( TName );
464       if (S)  delete[] S;
465       S = NULL;
466       if (!field)     return CIFRC_NoField;
467       if (k<0)        return CIFRC_NoTag;
468       if (!field[k])  return CIFRC_NoField;
469       if (field[k][0]==char(2))  {
470         if (Remove)  {
471           delete[] field[k];
472           field[k] = NULL;
473         }
474       } else if (Remove)  {
475         S = field[k];
476         field[k] = NULL;
477       } else
478         CreateCopy ( S,field[k] );
479       return 0;
480     }
481 
GetString(cpstr TName,int & RC)482     pstr Struct::GetString ( cpstr TName, int & RC )  {
483     int k = GetTagNo ( TName );
484       if (k<0)  {
485         RC = CIFRC_NoTag;
486         return NULL;
487       }
488       if (!field)  {
489         RC = CIFRC_NoField;
490         return NULL;
491       }
492       if (!field[k])  {
493         RC = CIFRC_NoField;
494         return NULL;
495       }
496       RC = 0;
497       if (field[k][0]==char(2))  return NULL;
498       return field[k];
499     }
500 
DeleteField(cpstr TName)501     int  Struct::DeleteField ( cpstr TName )  {
502     int k = GetTagNo ( TName );
503       if ((k>=0) && (field)) {
504         if (field[k])  delete[] field[k];
505         field[k] = NULL;
506       }
507       return k;
508     }
509 
GetReal(realtype & R,cpstr TName,bool Remove)510     int  Struct::GetReal ( realtype & R, cpstr TName,
511                                  bool Remove )  {
512     pstr endptr;
513     int  RC;
514     int  k = GetTagNo ( TName );
515       R = 0.0;
516       if (!field)                return CIFRC_NoField;
517       if (k<0)                   return CIFRC_NoTag;
518       if (!field[k])             return CIFRC_NoField;
519       if (field[k][0]==char(2))  return CIFRC_NoData;
520       R = strtod ( field[k],&endptr );
521       if (endptr==field[k])  RC = CIFRC_WrongFormat;
522       else  {
523         RC = 0;
524         if (Remove)  {
525           delete[] field[k];
526           field[k] = NULL;
527         }
528       }
529       return RC;
530     }
531 
GetInteger(int & I,cpstr TName,bool Remove)532     int  Struct::GetInteger ( int & I, cpstr TName,
533                                     bool Remove )  {
534     pstr endptr;
535     int  RC;
536     int  k = GetTagNo ( TName );
537       I = 0;
538       if (!field)                return CIFRC_NoField;
539       if (k<0)                   return CIFRC_NoTag;
540       if (!field[k])             return CIFRC_NoField;
541       if (field[k][0]==char(2))  {
542         if (field[k][1]=='.')  I = MinInt4;
543         return CIFRC_NoData;
544       }
545       I = mround ( strtod(field[k],&endptr) );
546       if (endptr==field[k])  RC = CIFRC_WrongFormat;
547       else  {
548         RC = 0;
549         if (Remove)  {
550           delete[] field[k];
551           field[k] = NULL;
552         }
553       }
554       return RC;
555     }
556 
557 
PutString(cpstr S,cpstr T,bool NonBlankOnly)558     void Struct::PutString ( cpstr S, cpstr T,
559                                    bool NonBlankOnly )  {
560     pstr p;
561       if (!S)  PutNoData ( CIF_NODATA_QUESTION,T );
562       else  {
563         p = pstr(S);
564         if (NonBlankOnly)
565           while (*p==' ')  p++;
566         if (!(*p))  PutNoData ( CIF_NODATA_DOT,T );
567               else  AddField  ( S,T,false );
568       }
569     }
570 
PutDate(cpstr T)571     void Struct::PutDate ( cpstr T )  {
572     time_t t;
573     tm *   tstruct;
574      char   S[100];
575       t       = time ( NULL );
576       tstruct = localtime(&t);
577       if (tstruct)
578             sprintf ( S,"%4i-%02i-%02i",
579                 tstruct->tm_year+1900,tstruct->tm_mon+1,tstruct->tm_mday );
580       else  strcpy  ( S,"YYYY-MM-DD" );
581       AddField ( S,T,false );
582     }
583 
584 
PutNoData(int NoDataType,cpstr T)585     void Struct::PutNoData ( int NoDataType, cpstr T )  {
586     char S[10];
587       S[0] = char(2);
588       if (NoDataType==CIF_NODATA_DOT)  S[1] = '.';
589                                  else  S[1] = '?';
590       S[2] = char(0);
591       AddField ( S,T,false );
592     }
593 
594 
PutReal(realtype R,cpstr T,int prec)595     void Struct::PutReal ( realtype R, cpstr T, int prec )  {
596     char rS[100];
597       sprintf  ( rS,"%.*g",prec,R );
598       AddField ( rS,T,false );
599     }
600 
PutReal(realtype R,cpstr T,cpstr format)601     void Struct::PutReal ( realtype R, cpstr T, cpstr format )  {
602     char rS[100];
603       sprintf  ( rS,format,R );
604       AddField ( DelSpaces(rS,' '),T,false );
605     }
606 
PutInteger(int I,cpstr T)607     void Struct::PutInteger ( int I, cpstr T )  {
608     char iS[100];
609       if (I>MinInt4)  {
610         sprintf  ( iS,"%i",I );
611         AddField ( iS,T,false );
612       } else
613         PutNoData ( CIF_NODATA_DOT,T );
614     }
615 
616 
617 
618     #define  NODATA_Q  pstr("?")
619     #define  NODATA_P  pstr(".")
620 
WriteMMCIFStruct(cpstr FName,io::GZ_MODE gzipMode)621     bool Struct::WriteMMCIFStruct ( cpstr FName,
622                                     io::GZ_MODE gzipMode )  {
623     io::File f;
624       f.assign ( FName,true,false,gzipMode );
625       if (f.rewrite())  {
626         WriteMMCIF ( f );
627         f.shut();
628         return true;
629       } else
630         return false;
631     }
632 
633     #define _max_output_line_width 256
634 
WriteMMCIF(io::RFile f)635     void Struct::WriteMMCIF ( io::RFile f )  {
636     int   i,j,k,l,m,n;
637     pstr  F;
638 
639       // calculate maximal length of tags
640       l = 0;
641       for (i=0;i<nTags;i++)
642         l = IMax(l,strlen(tag[i]));
643       l += 1;  // add one space separator
644 
645       // calculate maximal space left for data
646       m  = _max_output_line_width - l;
647       // subtract category name width
648       if (name[0]!=char(1))  m -= strlen(name);
649 
650       // start outout
651       f.LF();
652       for (i=0;i<nTags;i++)  {  // for each tag
653 
654         // print category name, if not hidden, and dot
655         if (name[0]!=char(1))  {
656           f.Write ( name      );
657           f.Write ( pstr(".") );
658         }
659 
660         // print tag, checking for duplicate tag flag
661         F = FirstOccurence ( tag[i],'\1' );
662         if (F)  {
663           *F = char(0);
664           f.Write ( tag[i] );
665           *F = '\1';
666         } else
667           f.Write ( tag[i] );
668 
669         // print field
670         if (field[i])  {  // field is defined
671           F = field[i];
672           if (FirstOccurence(F,'\n') || strstr(F,"\" "))  {
673             f.Write ( pstr("\n;")   );
674             f.Write ( F             );
675             f.Write ( pstr("\n;\n") );
676           } else {
677             n = strlen(F);
678             if (n>m)  // wrap around if field is too long
679               f.Write ( pstr("\n ") );
680             else {
681               k = l-strlen(tag[i]);
682               for (j=0;j<k;j++)
683                 f.Write ( pstr(" ") );
684             }
685             if ((((F[0]=='.') || (F[0]=='?')) && (!F[1])) ||
686                 FirstOccurence(F,' '))  {
687               f.Write ( pstr("\"")   );
688               f.Write ( field[i]     );
689               f.Write ( pstr("\"\n") );
690             } else if (field[i][0]==char(2))  {
691               f.WriteLine ( &(field[i][1]) );
692             } else if (!field[i][0])  {
693               f.WriteLine ( NODATA_P );
694             } else
695               f.WriteLine ( field[i] );
696           }
697 
698         } else  {  // field if not defined, put question mark
699 
700           k = l-strlen(tag[i]);
701           for (j=0;j<k;j++)
702             f.Write ( pstr(" ") );
703           f.WriteLine ( NODATA_Q );
704 
705         }
706 
707       }
708 
709     }
710 
Copy(PCategory Struct)711     void Struct::Copy ( PCategory Struct )  {
712     int i;
713       Category::Copy ( Struct );
714       if (nTags>0)  {
715         GetVectorMemory ( field,nTags,0 );
716         for (i=0;i<nTags;i++)  {
717           field[i] = NULL;
718           CreateCopy ( field[i],PStruct(Struct)->field[i] );
719         }
720       }
721     }
722 
write(io::RFile f)723     void Struct::write ( io::RFile f )  {
724     int i;
725       Category::write ( f );
726       for (i=0;i<nTags;i++)
727         f.CreateWrite ( field[i] );
728     }
729 
read(io::RFile f)730     void Struct::read ( io::RFile f )  {
731     int i;
732       Category::read ( f );
733       if (nTags>0)  {
734         GetVectorMemory ( field,nTags,0 );
735         for (i=0;i<nTags;i++)  {
736           field[i] = NULL;
737           f.CreateRead ( field[i] );
738         }
739       }
740     }
741 
742 
MakeStreamFunctions(Struct)743     MakeStreamFunctions(Struct)
744 
745 
746 
747     //  ======================  Loop  ==============================
748 
749 
750     Loop::Loop() : Category()  {
751       InitLoop();
752     }
753 
Loop(cpstr N)754     Loop::Loop ( cpstr N ) : Category(N)  {
755       InitLoop();
756     }
757 
Loop(io::RPStream Object)758     Loop::Loop ( io::RPStream Object ) : Category(Object)  {
759       InitLoop();
760     }
761 
~Loop()762     Loop::~Loop()  {
763       FreeMemory();
764     }
765 
InitLoop()766     void Loop::InitLoop()  {
767       nRows      = 0;
768       field      = NULL;
769       iColumn    = 0;
770       nAllocRows = 0;
771     }
772 
FreeMemory()773     void Loop::FreeMemory()  {
774       DeleteFields();
775       Category::FreeMemory();
776     }
777 
Optimize()778     void Loop::Optimize()  {
779     int      i,j,nT,nR,k,m;
780     bool  empty;
781     psmatrix f1;
782 
783       if (!field)  {
784         Category::Optimize();  // optimize tags
785         return;
786       }
787 
788       // first check for empty columns
789       nT = 0;
790       for (i=0;i<nTags;i++)
791         if (!tag[i])  {
792           for (j=0;j<nRows;j++)  // delete ith column of field
793             if (field[j])  {
794               if (field[j][i])
795                 delete[] field[j][i];
796               field[j][i] = NULL;
797             }
798         } else  {
799           empty = true;
800           j = 0;
801           while ((j<nRows) && empty)  {  // check if ith column is empty
802             if (field[j])
803               empty = !field[j][i];
804             j++;
805           }
806           if (empty)  {    // if ith column is empty, delete its tag
807             delete[] tag[i];
808             tag[i] = NULL;
809           } else           // otherwise count ith tag
810             nT++;
811         }
812 
813       // now check for empty rows
814       nR = 0;
815       for (j=0;j<nRows;j++)
816         if (field[j])  {
817           i = 0;
818           while ((i<nTags) && (!field[j][i])) i++;
819           if (i>=nTags)  {
820             delete[] field[j];  // delete empty row
821             field[j] = NULL;
822           } else
823             nR++;             // count non-empty row
824         }
825       if ((nT<=0) || (nR<=0))
826         FreeMemory();  // the loop is completely empty
827       else if ((nT!=nTags) || (nR!=nAllocRows))  {
828         f1 = new psvector[nR];
829         m  = 0;
830         for (j=0;j<nRows;j++)
831           if (field[j])  {
832             f1[m] = new pstr[nT];
833             k = 0;
834             for (i=0;i<nTags;i++)
835               if (tag[i])
836                 f1[m][k++] = field[j][i];
837             m++;
838             delete[] field[j];
839           }
840         if (field)  delete[] field;
841         field = f1;
842         nRows = nR;
843         nAllocRows = nRows;
844         Category::Optimize();  // optimize tags
845       }
846 
847     }
848 
DeleteFields()849     void Loop::DeleteFields()  {
850     int i,j;
851       if (field)  {
852         for (i=0;i<nAllocRows;i++)
853           if (field[i])  {
854             for (j=0;j<nTags;j++)
855               if (field[i][j])  delete[] field[i][j];
856             delete[] field[i];
857           }
858         delete[] field;
859         field      = NULL;
860         nRows      = 0;
861         nAllocRows = 0;
862       }
863     }
864 
AddLoopTag(cpstr T,bool Remove)865     void Loop::AddLoopTag ( cpstr T, bool Remove )  {
866     psmatrix  f1;
867     int       i,j,nT1;
868       if (Remove)  {
869         DeleteFields();
870         AddTag ( T );
871       } else  {
872         f1    = field;
873         field = NULL;
874         i     = AddTag ( T );
875         if ((f1) && (i<0))  {
876           // The tag was added on the top of tag array. Create
877           // and fill new fields.
878           field = new psvector[nAllocRows];
879           nT1   = nTags-1;
880           for (i=0;i<nAllocRows;i++)
881             if (f1[i])  {
882               field[i] = new pstr[nTags];
883               for (j=0;j<nT1;j++)
884                 field[i][j] = f1[i][j];
885               field[i][nT1] = NULL;
886               f1[i] = NULL;
887             } else
888               field[i] = NULL;
889           delete[] f1;
890         } else
891           // The tag was already in the category. Just restore fields.
892           field = f1;
893       }
894     }
895 
896 
ExpandRows(int nRowsNew)897     void Loop::ExpandRows ( int nRowsNew )  {
898     int      nAR,i;
899     psmatrix field1;
900       if (nRowsNew>nAllocRows)  {
901         nAR    = nRowsNew + IMin(nAllocRows/2+10,2000);
902         field1 = new psvector[nAR];
903         for (i=0;i<nAllocRows;i++)
904           field1[i] = field[i];
905         for (i=nAllocRows;i<nAR;i++)
906           field1[i] = NULL;
907         if (field)  delete[] field;
908         field      = field1;
909         nAllocRows = nAR;
910       }
911     }
912 
AddString(cpstr S,bool NonBlankOnly)913     void Loop::AddString ( cpstr S, bool NonBlankOnly )  {
914     int  i;
915     pstr p;
916       if (!S)  AddNoData ( CIF_NODATA_QUESTION );
917       else  {
918         p = pstr(S);
919         if (NonBlankOnly)
920           while (*p==' ')  p++;
921         if (!(*p))  AddNoData ( CIF_NODATA_DOT );
922         else  {
923           if (iColumn==0)  {  // start a new row
924             ExpandRows ( nRows+1 );
925             field[nRows] = new pstr[nTags];
926             for (i=0;i<nTags;i++)
927               field[nRows][i] = NULL;
928             nRows++;
929           }
930           CreateCopy ( field[nRows-1][iColumn],S );
931           iColumn++;
932           if (iColumn>=nTags) iColumn = 0;
933         }
934       }
935     }
936 
AddNoData(int NoDataType)937     void Loop::AddNoData ( int NoDataType )  {
938     char S[10];
939       S[0] = char(2);
940       if (NoDataType==CIF_NODATA_DOT)  S[1] = '.';
941                                  else  S[1] = '?';
942       S[2] = char(0);
943       AddString ( S );
944     }
945 
AddReal(realtype R,int prec)946     void Loop::AddReal ( realtype R, int prec )  {
947     char rS[100];
948       sprintf ( rS,"%.*g",prec,R );
949       AddString ( rS );
950     }
951 
AddReal(realtype R,cpstr format)952     void Loop::AddReal ( realtype R, cpstr format )  {
953     char rS[100];
954       sprintf ( rS,format,R );
955       AddString ( DelSpaces(rS,' ') );
956     }
957 
AddInteger(int I)958     void Loop::AddInteger ( int I )  {
959     char iS[100];
960       if (I>MinInt4)  {
961         sprintf ( iS,"%i",I );
962         AddString ( iS );
963       } else
964         AddNoData ( CIF_NODATA_DOT );
965     }
966 
967 
GetField(int rowNo,int tagNo)968     pstr Loop::GetField ( int rowNo, int tagNo )  {
969       if ((tagNo>=0) && (tagNo<nTags) &&
970           (rowNo>=0) && (rowNo<nRows))  {
971         if (field[rowNo])  return field[rowNo][tagNo];
972       }
973       return NULL;
974     }
975 
GetString(pstr & S,cpstr TName,int nrow,bool Remove)976     int  Loop::GetString ( pstr & S, cpstr TName, int nrow,
977                                  bool Remove)  {
978     int k = GetTagNo ( TName );
979       if (S)  delete[] S;
980       S = NULL;
981       if (k<0)                       return CIFRC_NoTag;
982       if ((nrow<0) || (nrow>=nRows)) return CIFRC_WrongIndex;
983       if (!field[nrow])              return CIFRC_NoField;
984       if (!field[nrow][k])           return CIFRC_NoField;
985       if (field[nrow][k][0]==char(2))  {
986         if (Remove)  {
987           delete[] field[nrow][k];
988           field[nrow][k] = NULL;
989         }
990       } else if (Remove)  {
991         S = field[nrow][k];
992         field[nrow][k] = NULL;
993       } else
994         CreateCopy ( S,field[nrow][k] );
995       return 0;
996     }
997 
GetString(cpstr TName,int nrow,int & RC)998     pstr Loop::GetString ( cpstr TName, int nrow, int & RC )  {
999     int k = GetTagNo ( TName );
1000       if (k<0)  {
1001         RC = CIFRC_NoTag;
1002         return NULL;
1003       }
1004       if ((nrow<0) || (nrow>=nRows))  {
1005         RC = CIFRC_WrongIndex;
1006         return NULL;
1007       }
1008       if (!field[nrow])  {
1009         RC = CIFRC_NoField;
1010         return NULL;
1011       }
1012       if (!field[nrow][k])  {
1013         RC = CIFRC_NoField;
1014         return NULL;
1015       }
1016       RC = 0;
1017       // char(2) means the field was either '.' or '?'
1018       if (field[nrow][k][0]==char(2))  return NULL;
1019       return field[nrow][k];
1020     }
1021 
1022     //  CopyString() does nothing if RC is not 0
CopyString(pstr buf,int maxlength,cpstr TName,int nrow,int & RC)1023     void Loop::CopyString   ( pstr  buf,   int maxlength,
1024                                     cpstr TName, int nrow, int & RC )  {
1025     pstr p;
1026     int  k;
1027 
1028       if (RC)  return;
1029 
1030       k = GetTagNo ( TName );
1031       if (k<0)  {
1032         RC = CIFRC_NoTag;
1033         buf[0] = char(0);
1034         return;
1035       }
1036       if ((nrow<0) || (nrow>=nRows))  {
1037         RC = CIFRC_WrongIndex;
1038         buf[0] = char(0);
1039         return;
1040       }
1041       if (!field[nrow])  {
1042         RC = CIFRC_NoField;
1043         buf[0] = char(0);
1044         return;
1045       }
1046       p = field[nrow][k];
1047       if (!p)  {
1048         RC = CIFRC_NoField;
1049         buf[0] = char(0);
1050         return;
1051       }
1052 
1053       // char(2) means the field was either '.' or '?'
1054       if (p[0]==char(2))  {
1055         buf[0] = p[0];
1056         buf[1] = char(0);
1057       } else
1058         strncpy ( buf,p,IMin(maxlength,strlen(p)+1) );
1059 
1060     }
1061 
1062 
1063 
DeleteField(cpstr TName,int nrow)1064     int Loop::DeleteField ( cpstr TName, int nrow )  {
1065     int k = GetTagNo ( TName );
1066       if (k<0)  return CIFRC_NoTag;
1067       if ((nrow<0) || (nrow>=nRows))
1068                 return CIFRC_WrongIndex;
1069       if (field[nrow])  {
1070         if (field[nrow][k])  delete[] field[nrow][k];
1071         field[nrow][k] = NULL;
1072       }
1073       return k;
1074     }
1075 
DeleteRow(int nrow)1076     int Loop::DeleteRow ( int nrow )  {
1077     int i;
1078       if ((nrow<0) || (nrow>=nRows))
1079                 return CIFRC_WrongIndex;
1080       if (field[nrow])  {
1081         for (i=0;i<nTags;i++)
1082           if (field[nrow][i])  {
1083             delete[] field[nrow][i];
1084             field[nrow][i] = NULL;
1085           }
1086         delete[] field[nrow];
1087         field[nrow] = NULL;
1088       }
1089       return 0;
1090     }
1091 
GetReal(realtype & R,cpstr TName,int nrow,bool Remove)1092     int  Loop::GetReal ( realtype & R, cpstr TName, int nrow,
1093                                bool Remove )  {
1094     pstr endptr;
1095     int  k = GetTagNo ( TName );
1096       if (k<0)  return CIFRC_NoTag;
1097       if ((nrow<0) || (nrow>=nRows))
1098                 return CIFRC_WrongIndex;
1099       R = 0.0;
1100       if (!field[nrow])                return CIFRC_NoField;
1101       if (!field[nrow][k])             return CIFRC_NoField;
1102       if (field[nrow][k][0]==char(2))  return CIFRC_NoField;
1103       R = strtod ( field[nrow][k],&endptr );
1104       if (endptr==field[nrow][k])      return CIFRC_WrongFormat;
1105       if (Remove)  {
1106         delete[] field[nrow][k];
1107         field[nrow][k] = NULL;
1108       }
1109       return 0;
1110     }
1111 
CopyReal(realtype & R,cpstr TName,int nrow,int & RC)1112     void Loop::CopyReal ( realtype & R, cpstr TName, int nrow,
1113                                 int & RC )  {
1114     pstr endptr;
1115     int  k;
1116 
1117       if (RC)  return;
1118 
1119     //  R = 0.0;
1120       k = GetTagNo ( TName );
1121 
1122       if (k<0)                              RC = CIFRC_NoTag;
1123       else if ((nrow<0) || (nrow>=nRows))   RC = CIFRC_WrongIndex;
1124       else if (!field[nrow])                RC = CIFRC_NoField;
1125       else if (!field[nrow][k])             RC = CIFRC_NoField;
1126       else if (field[nrow][k][0]==char(2))  RC = CIFRC_NoField;
1127       else  {
1128         R = strtod ( field[nrow][k],&endptr );
1129         if (endptr==field[nrow][k])  RC = CIFRC_WrongFormat;
1130       }
1131 
1132     }
1133 
CopyInteger(int & I,cpstr TName,int nrow,int & RC)1134     void Loop::CopyInteger ( int & I, cpstr TName, int nrow,
1135                                    int & RC )  {
1136     pstr endptr;
1137     int  k;
1138 
1139       if (RC)  return;
1140 
1141       I = 0;
1142       k = GetTagNo ( TName );
1143 
1144       if (k<0)                              RC = CIFRC_NoTag;
1145       else if ((nrow<0) || (nrow>=nRows))   RC = CIFRC_WrongIndex;
1146       else if (!field[nrow])                RC = CIFRC_NoField;
1147       else if (!field[nrow][k])             RC = CIFRC_NoField;
1148       else if (field[nrow][k][0]==char(2))  RC = CIFRC_NoField;
1149       else  {
1150         I = mround ( strtod ( field[nrow][k],&endptr ) );
1151         if (endptr==field[nrow][k])  RC = CIFRC_WrongFormat;
1152       }
1153 
1154     }
1155 
GetInteger(int & I,cpstr TName,int nrow,bool Remove)1156     int  Loop::GetInteger ( int & I, cpstr TName, int nrow,
1157                                   bool Remove )  {
1158     pstr endptr;
1159     int  k = GetTagNo ( TName );
1160       if (k<0)  return CIFRC_NoTag;
1161       if ((nrow<0) || (nrow>=nRows))
1162                 return CIFRC_WrongIndex;
1163       I = 0;
1164       if (!field[nrow])                return CIFRC_NoField;
1165       if (!field[nrow][k])             return CIFRC_NoField;
1166       if (field[nrow][k][0]==char(2))  {
1167         if (field[nrow][k][1]=='.')  I = MinInt4;
1168         return CIFRC_NoField;
1169       }
1170       I = mround ( strtod(field[nrow][k],&endptr) );
1171       if (endptr==field[nrow][k])      return CIFRC_WrongFormat;
1172       if (Remove)  {
1173         delete[] field[nrow][k];
1174         field[nrow][k] = NULL;
1175       }
1176       return 0;
1177     }
1178 
1179 
GetSVector(psvector & S,cpstr TName,int i1,int i2,bool Remove)1180     int  Loop::GetSVector ( psvector & S, cpstr TName,
1181                                   int i1, int i2, bool Remove )  {
1182     int j,k,r1,r2;
1183       r1 = IMin(i1,i2);
1184       r2 = IMin(IMax(i1,i2),nRows-1);
1185       if ((r1<0) || (r1>=nRows) || (r2<0)) return CIFRC_WrongIndex;
1186       k = GetTagNo ( TName );
1187       if (k<0)  return CIFRC_NoTag;
1188       if (!S)
1189         GetVectorMemory ( S,r2-r1+1,r1 );
1190       if (Remove)  {
1191         for (j=r1;j<=r2;j++)
1192           if (field[j])  {
1193             S[j] = field[j][k];
1194             field[j][k] = NULL;
1195             if (S[j])  {
1196               if (S[j][0]==char(2))  {
1197                 delete[] S[j];
1198                 S[j] = NULL;
1199               }
1200             }
1201           } else
1202             S[j] = NULL;
1203       } else  {
1204         for (j=r1;j<=r2;j++)  {
1205           S[j] = NULL;
1206           if (field[j])  {
1207             if (field[j][k])  {
1208               if (field[j][k][0]!=char(2))
1209                 CreateCopy ( S[j],field[j][k] );
1210             }
1211           }
1212         }
1213       }
1214       return 0;
1215     }
1216 
GetRVector(rvector & R,cpstr TName,int i1,int i2,bool Remove)1217     int  Loop::GetRVector ( rvector & R, cpstr TName,
1218                                   int i1, int i2, bool Remove )  {
1219     int  j,k,r1,r2,RC;
1220     pstr endptr;
1221       r1 = IMin(i1,i2);
1222       r2 = IMin(IMax(i1,i2),nRows-1);
1223       if ((r1<0) || (r1>=nRows) || (r2<0)) return CIFRC_WrongIndex;
1224       k = GetTagNo ( TName );
1225       if (k<0)  return CIFRC_NoTag;
1226       if (!R)
1227         GetVectorMemory ( R,r2-r1+1,r1 );
1228       RC = 0;
1229       for (j=r1;j<=r2;j++)  {
1230         R[j] = 0.0;
1231         if (field[j])  {
1232           if (field[j][k])  {
1233             R[j] = strtod ( field[j][k],&endptr );
1234             if (endptr==field[j][k])  RC = CIFRC_WrongFormat;
1235             if (Remove)  {
1236               delete[] field[j][k];
1237               field[j][k] = NULL;
1238             }
1239           }
1240         }
1241       }
1242       return RC;
1243     }
1244 
GetIVector(ivector & I,cpstr TName,int i1,int i2,bool Remove)1245     int  Loop::GetIVector ( ivector & I, cpstr TName,
1246                                   int i1, int i2, bool Remove )  {
1247     int  j,k,r1,r2,RC;
1248     pstr endptr;
1249       r1 = IMin(i1,i2);
1250       r2 = IMin(IMax(i1,i2),nRows-1);
1251       if ((r1<0) || (r1>=nRows) || (r2<0)) return CIFRC_WrongIndex;
1252       k = GetTagNo ( TName );
1253       if (k<0)    return CIFRC_NoTag;
1254       if (!I)
1255         GetVectorMemory ( I,r2-r1+1,r1 );
1256       RC = 0;
1257       for (j=r1;j<=r2;j++)  {
1258         I[j] = 0;
1259         if (field[j])  {
1260           if (field[j][k])  {
1261             I[j] = mround ( strtod(field[j][k],&endptr) );
1262             if (endptr==field[j][k]) RC = CIFRC_WrongFormat;
1263             if (Remove)  {
1264               delete[] field[j][k];
1265               field[j][k] = NULL;
1266             }
1267           }
1268         }
1269       }
1270       return RC;
1271     }
1272 
1273 
PutString(cpstr S,cpstr T,int nrow)1274     void Loop::PutString ( cpstr S, cpstr T, int nrow )  {
1275     psmatrix field1;
1276     int      nT,nR,iT,i,j;
1277       nT = nTags;
1278       nR = nRows;
1279       iT = AddTag ( T );
1280       if (iT<0)  iT = nTags-1;
1281       if (nTags>nT)  {
1282         // a new tag has been added; all field must be reallocated.
1283         nRows      = IMax(nR,nrow+1);  // nrow is indexed like 0,1,...
1284         nAllocRows = IMax(nR,nrow+IMin(nR/2+1,2000));
1285         field1     = new psvector[nAllocRows];
1286         for (i=0;i<nR;i++)
1287           if (field[i])  {
1288             field1[i] = new pstr[nTags];
1289             for (j=0;j<nT;j++)
1290               field1[i][j] = field[i][j];
1291             for (j=nT;j<nTags;j++)
1292               field1[i][j] = NULL;
1293             delete[] field[i];
1294           } else
1295             field1[i] = NULL;
1296         for (i=nR;i<nRows;i++)
1297           field1[i] = NULL;
1298         if (field)  delete[] field;
1299         field = field1;
1300       } else if (nrow>=nR)  {
1301         // only new rows are to be added
1302         ExpandRows ( nrow+1 );
1303         nRows++;
1304       }
1305       if (!field[nrow])  {
1306         field[nrow] = new pstr[nTags];
1307         for (j=0;j<nTags;j++)
1308           field[nrow][j] = NULL;
1309       }
1310       CreateCopy ( field[nrow][iT],S );
1311       iColumn = iT+1;
1312       if (iColumn>=nTags) iColumn = 0;
1313     }
1314 
1315 
PutNoData(int NoDataType,cpstr T,int nrow)1316     void Loop::PutNoData ( int NoDataType, cpstr T, int nrow )  {
1317     char S[10];
1318       S[0] = char(2);
1319       if (NoDataType==CIF_NODATA_DOT)  S[1] = '.';
1320                                  else  S[1] = '?';
1321       S[2] = char(0);
1322       PutString ( S,T,nrow );
1323     }
1324 
1325 
PutReal(realtype R,cpstr T,int nrow,int prec)1326     void Loop::PutReal ( realtype R, cpstr T, int nrow, int prec )  {
1327     char rS[100];
1328       sprintf ( rS,"%.*g",prec,R );
1329       PutString ( rS,T,nrow );
1330     }
1331 
PutReal(realtype R,cpstr T,int nrow,cpstr format)1332     void Loop::PutReal ( realtype R, cpstr T, int nrow,
1333                                cpstr format )  {
1334     char rS[100];
1335       sprintf ( rS,format,R );
1336       PutString ( DelSpaces(rS,' '),T,nrow );
1337     }
1338 
PutInteger(int I,cpstr T,int nrow)1339     void Loop::PutInteger ( int I, cpstr T, int nrow )  {
1340     char iS[100];
1341       if (I>MinInt4)  {
1342         sprintf ( iS,"%i",I );
1343         PutString ( iS,T,nrow );
1344       } else
1345         PutNoData ( CIF_NODATA_DOT,T,nrow );
1346     }
1347 
PutSVector(psvector S,cpstr T,int i1,int i2)1348     void Loop::PutSVector ( psvector S, cpstr T, int i1, int i2 )  {
1349     int i,j,k;
1350       PutString ( S[i2],T,i2 );
1351       if (iColumn==0)  k = nTags-1;
1352                  else  k = iColumn-1;
1353       for (i=i2-1;i>=i1;i--)  {
1354         if (!field[i])  {
1355           field[i] = new pstr[nTags];
1356           for (j=0;j<nTags;j++)
1357             field[i][j] = NULL;
1358         }
1359         CreateCopy ( field[i][k],S[i] );
1360       }
1361     }
1362 
PutRVector(rvector R,cpstr T,int i1,int i2,int prec)1363     void Loop::PutRVector ( rvector R, cpstr T,
1364                                   int i1, int i2, int prec )  {
1365     int  i,j,k;
1366     char rS[100];
1367       PutReal ( R[i2],T,i2,prec );
1368       if (iColumn==0)  k = nTags-1;
1369                  else  k = iColumn-1;
1370       for (i=i2-1;i>=i1;i--)  {
1371         if (!field[i])  {
1372           field[i] = new pstr[nTags];
1373           for (j=0;j<nTags;j++)
1374             field[i][j] = NULL;
1375         }
1376         sprintf ( rS,"%.*g",prec,R[i] );
1377         CreateCopy ( field[i][k],rS );
1378       }
1379     }
1380 
PutIVector(ivector I,cpstr T,int i1,int i2)1381     void Loop::PutIVector ( ivector I, cpstr T,
1382                                   int i1, int i2 )  {
1383     int  l,j,k;
1384     char iS[100];
1385       PutInteger ( I[i2],T,i2 );
1386       if (iColumn==0)  k = nTags-1;
1387                  else  k = iColumn-1;
1388       for (l=i2-1;l>=i1;l--)  {
1389         if (!field[l])  {
1390           field[l] = new pstr[nTags];
1391           for (j=0;j<nTags;j++)
1392             field[l][j] = NULL;
1393         }
1394         sprintf ( iS,"%i",I[l] );
1395         CreateCopy ( field[l][k],iS );
1396       }
1397     }
1398 
WriteMMCIFLoop(cpstr FName,io::GZ_MODE gzipMode)1399     bool Loop::WriteMMCIFLoop ( cpstr FName, io::GZ_MODE gzipMode )  {
1400     io::File f;
1401       f.assign ( FName,true,false,gzipMode );
1402       if (f.rewrite())  {
1403         WriteMMCIF ( f );
1404         f.shut();
1405         return true;
1406       } else
1407         return false;
1408     }
1409 
WriteMMCIF(io::RFile f)1410     void Loop::WriteMMCIF ( io::RFile f )  {
1411     int     i,j,k,m,n;
1412     ivector l;
1413     pstr    F;
1414 
1415       // write loop keyword
1416       f.Write ( pstr("\nloop_\n") );
1417 
1418       GetVectorMemory ( l,nTags,0 );
1419       k = 0;
1420       for (i=0;i<nTags;i++)  {
1421         if (name[0]!=char(1))  {
1422           f.Write ( name      );
1423           f.Write ( pstr(".") );
1424         }
1425         F = FirstOccurence ( tag[i],'\1' );
1426         if (F)  {
1427           *F = char(0);
1428           f.WriteLine ( tag[i] );
1429           *F = '\1';
1430         } else
1431           f.WriteLine ( tag[i] );
1432         l[i] = 0;
1433         for (j=0;j<nRows;j++)
1434           if (field[j])  {
1435             if (field[j][i])  {
1436               F = field[j][i];
1437               if (FirstOccurence(F,'\n') || strstr(F,"\" "))
1438                                        l[i] = 10001;
1439               else if (F[0]==char(2))  l[i] = IMax(l[i],1);
1440               else if (((F[0]=='.') || (F[0]=='?')) &&
1441                        (!F[1]))        l[i] = IMax(l[i],3);
1442               else  {
1443                 if (FirstOccurence(F,' ') || FirstOccurence(F,'"') || FirstOccurence(F,'\''))
1444                       m = 2;
1445                 else  m = 0;
1446                 l[i] = IMax(l[i],strlen(F)+m);
1447               }
1448             }
1449           }
1450         l[i] = IMax(l[i],1);
1451         k += l[i]+1;
1452         if (k>_max_output_line_width)  {
1453           l[i] = -l[i];
1454           k = 0;
1455         }
1456       }
1457       for (i=0;i<nRows;i++)  {
1458         m = 0;  // counts symbols in the string
1459         k = 0;  // rest of left-aligned fields to fill with spaces
1460         for (j=0;j<nTags;j++)  {
1461           n = k;
1462           k = l[j];   // length of the field
1463           if (k<0)  k = -k;
1464           m += k+1;
1465           if (m>_max_output_line_width)  {
1466             f.LF();
1467             m = k+1;
1468           } else
1469             while (n>0) {
1470               f.Write ( pstr(" ") );
1471               n--;
1472             }
1473           if (field[i])  {
1474             if (field[i][j])  {
1475               F = field[i][j];
1476               if (k>10000)  {
1477                 if (F[0]==char(2))  {
1478                   f.Write     ( pstr(" ") );
1479                   f.WriteLine ( &(F[1])   );
1480                 } else if (!F[0])  {
1481                   f.Write     ( pstr(" ") );
1482                   f.WriteLine ( NODATA_P  );
1483                 } else  {
1484                   f.Write     ( pstr(";") );
1485                   f.WriteLine ( F         );
1486                   f.WriteLine ( pstr(";") );
1487                 }
1488                 m = 0;
1489                 k = 0;
1490               } else if ((((F[0]=='.') ||
1491                           (F[0]=='?')) && (!F[1])) ||
1492                          FirstOccurence(F,' ') ||
1493                          FirstOccurence(F,'"') ||
1494                          FirstOccurence(F,'\''))  {
1495                 f.Write ( pstr(" \"") );
1496                 f.Write ( F           );
1497                 f.Write ( pstr("\"")  );
1498                 k -= strlen(F)+2;
1499               } else if (F[0]==char(2))  {
1500                 f.Write ( pstr(" ") );
1501                 f.Write ( &(F[1])   );
1502                 k--;
1503               } else if (!F[0])  {
1504                 f.Write ( pstr(" ") );
1505                 f.Write ( NODATA_P  );
1506                 k--;
1507               } else  {
1508                 f.Write ( pstr(" ") );
1509                 f.Write ( F         );
1510                 k -= strlen(F);
1511               }
1512             } else  {
1513               f.Write ( pstr(" ") );
1514               f.Write ( NODATA_Q  );
1515               k--;
1516             }
1517           } else  {
1518             f.Write ( pstr(" ") );
1519             f.Write ( NODATA_Q  );
1520             k--;
1521           }
1522         }
1523         if (m) f.LF();
1524       }
1525       f.WriteLine ( pstr("#") );
1526 
1527     }
1528 
1529 
Copy(PCategory Loop)1530     void Loop::Copy ( PCategory Loop )  {
1531     int i,j;
1532       Category::Copy ( Loop );
1533       nRows      = PLoop(Loop)->nRows;
1534       nAllocRows = nRows;
1535       if ((nTags>0) && (nRows>0))  {
1536         field = new psvector[nRows];
1537         for (i=0;i<nRows;i++)  {
1538           if (PLoop(Loop)->field[i])  {
1539             field[i] = new pstr[nTags];
1540             for (j=0;j<nTags;j++)  {
1541               field[i][j] = NULL;
1542               CreateCopy ( field[i][j],PLoop(Loop)->field[i][j] );
1543             }
1544           } else
1545             field[i] = NULL;
1546         }
1547       }
1548       iColumn = PLoop(Loop)->iColumn;
1549     }
1550 
1551 
write(io::RFile f)1552     void Loop::write ( io::RFile f )  {
1553     int i,j;
1554       Category::write ( f );
1555       f.WriteInt ( &nRows );
1556       if ((nTags>0) && (nRows>0))  {
1557         for (i=0;i<nRows;i++)  {
1558           if (field[i])  {
1559             j = 1;
1560             f.WriteInt ( &j );
1561             for (j=0;j<nTags;j++)
1562               f.CreateWrite ( field[i][j] );
1563           } else  {
1564             j = 0;
1565             f.WriteInt ( &j );
1566           }
1567         }
1568       }
1569       f.WriteInt ( &iColumn );
1570     }
1571 
read(io::RFile f)1572     void Loop::read ( io::RFile f )  {
1573     int i,j;
1574       Category::read ( f );
1575       f.ReadInt ( &nRows );
1576       nAllocRows = nRows;
1577       if ((nTags>0) && (nRows>0))  {
1578         field = new psvector[nRows];
1579         for (i=0;i<nRows;i++)  {
1580           f.ReadInt ( &j );
1581           if (j)  {
1582             field[i] = new pstr[nTags];
1583             for (j=0;j<nTags;j++)  {
1584               field[i][j] = NULL;
1585               f.CreateRead ( field[i][j] );
1586             }
1587           } else
1588             field[i] = NULL;
1589         }
1590       }
1591       f.ReadInt ( &iColumn );
1592     }
1593 
1594 
MakeStreamFunctions(Loop)1595     MakeStreamFunctions(Loop)
1596 
1597 
1598 
1599     //  ======================  Data  =============================
1600 
1601 
1602     Data::Data() : io::Stream()  {
1603       InitData();
1604     }
1605 
Data(cpstr N)1606     Data::Data ( cpstr N ) : io::Stream()  {
1607       InitData();
1608       CreateCopy ( name,N );
1609     }
1610 
Data(io::RPStream Object)1611     Data::Data ( io::RPStream Object ) : io::Stream(Object)  {
1612       InitData();
1613     }
1614 
~Data()1615     Data::~Data() {
1616       FreeMemory(0);
1617     }
1618 
InitData()1619     void Data::InitData()  {
1620       name         = NULL;
1621       nCategories  = 0;
1622       Category     = NULL;
1623       index        = NULL;
1624       flags        = 0;
1625       Warning      = 0;
1626       loopNo       = 0;
1627       tagNo        = 0;
1628       WrongCat     = NULL;
1629       WrongTag     = NULL;
1630       nWrongFields = 0;
1631     }
1632 
FreeMemory(int key)1633     void Data::FreeMemory ( int key )  {
1634     int i;
1635       if (name)  delete[] name;
1636       name = NULL;
1637       if (Category)  {
1638         for (i=0;i<nCategories;i++)
1639           if (Category[i]) delete Category[i];
1640         delete[] Category;
1641         Category = NULL;
1642       }
1643       nCategories = 0;
1644       FreeVectorMemory ( index,0 );
1645       if (key==0)  FreeWrongFields();
1646     }
1647 
FreeWrongFields()1648     void Data::FreeWrongFields()  {
1649     int i;
1650       if (WrongCat)  {
1651         for (i=0;i<nWrongFields;i++)
1652           if (WrongCat[i])  delete[] WrongCat[i];
1653         delete[] WrongCat;
1654       }
1655       if (WrongTag)  {
1656         for (i=0;i<nWrongFields;i++)
1657           if (WrongTag[i])  delete[] WrongTag[i];
1658         delete[] WrongTag;
1659       }
1660       WrongCat     = NULL;
1661       WrongTag     = NULL;
1662       nWrongFields = 0;
1663     }
1664 
1665 
SetPrintWarnings(bool SPW)1666     void  Data::SetPrintWarnings ( bool SPW )  {
1667       if (SPW)  SetFlag    ( CIFFL_PrintWarnings );
1668           else  RemoveFlag ( CIFFL_PrintWarnings );
1669     }
1670 
SetStopOnWarning(bool SOW)1671     void  Data::SetStopOnWarning ( bool SOW )  {
1672       if (SOW)  SetFlag    ( CIFFL_StopOnWarnings );
1673           else  RemoveFlag ( CIFFL_StopOnWarnings );
1674     }
1675 
SetFlag(CIF_FLAG F)1676     void  Data::SetFlag ( CIF_FLAG F )  {
1677       flags |= F;
1678     }
1679 
RemoveFlag(CIF_FLAG F)1680     void  Data::RemoveFlag ( CIF_FLAG F )  {
1681       flags &= ~F;
1682     }
1683 
SetWrongFields(cpstr * cats,cpstr * tags)1684     void  Data::SetWrongFields ( cpstr *cats, cpstr *tags )  {
1685     int i,lc,lt;
1686       FreeWrongFields();
1687       if ((!cats) || (!tags))  return;
1688       lc = 0;
1689       while (cats[lc]) lc++;
1690       lt = 0;
1691       while (tags[lt]) lt++;
1692       nWrongFields = IMax(lc,lt);
1693       if (nWrongFields>0)  {
1694         WrongCat = new pstr[nWrongFields];
1695         WrongTag = new pstr[nWrongFields];
1696         for (i=0;i<nWrongFields;i++)  {
1697           WrongCat[i] = NULL;
1698           WrongTag[i] = NULL;
1699           if (cats[i])  {
1700             if (cats[i][0])  CreateCopy ( WrongCat[i],cats[i] );
1701           }
1702           if (!WrongCat[i])  {
1703             CreateCopy ( WrongCat[i],pstr(" ") );
1704             WrongCat[i][0] = char(1);
1705           }
1706           if (tags[i])  CreateCopy ( WrongTag[i],tags[i]  );
1707                   else  CreateCopy ( WrongTag[i],pstr("") );
1708         }
1709       }
1710     }
1711 
CheckWrongField(cpstr C,cpstr T)1712     bool Data::CheckWrongField ( cpstr C, cpstr T )  {
1713     int i;
1714       for (i=0;i<nWrongFields;i++)
1715         if ((!strcasecmp(C,WrongCat[i])) &&
1716             (!strcasecmp(T,WrongTag[i])))  return true;
1717       return false;
1718     }
1719 
1720     #define _max_buf_len   500
1721 
1722     static char  _err_string[_max_buf_len+1];
1723     static int   _err_line;
1724 
1725 
ReadMMCIFData(cpstr FName,io::GZ_MODE gzipMode)1726     int  Data::ReadMMCIFData ( cpstr FName, io::GZ_MODE gzipMode )  {
1727     io::File f;
1728     char     S[_max_buf_len+1];
1729     int      RC,lcount;
1730       f.assign ( FName,true,false,gzipMode );
1731       if (f.reset(true))  {
1732         S[0]   = char(0);
1733         lcount = 0;
1734         RC     = ReadMMCIFData ( f,S,lcount );
1735         f.shut();
1736         return RC;
1737       } else  {
1738         _err_string[0] = char(0);
1739         _err_line      = 0;
1740         Warning = CIFRC_CantOpenFile;
1741         return CIFRC_CantOpenFile;
1742       }
1743     }
1744 
1745 
1746     // ---------------  General I/O functions
1747 
ReadMMCIFData(io::RFile f,pstr S,int & lcount)1748     int  Data::ReadMMCIFData ( io::RFile f, pstr S, int & lcount )  {
1749     pstr p;
1750     int  i,llen;
1751     pstr L;
1752 
1753       FreeMemory(1);
1754       Warning = 0;
1755       loopNo  = 0;
1756       tagNo   = 0;
1757 
1758       // 1. Look for 'data_' tag
1759       do {
1760         p = &(S[0]);
1761         while ((*p==' ') || (*p==char(9)))  p++;
1762         if (strncmp(p,"data_",5))  {
1763           f.ReadLine ( S,_max_buf_len );
1764           lcount++;
1765           p = NULL;
1766         }
1767       } while ((!p) && (!f.FileEnd()));
1768 
1769       if (!p)  {
1770         strcpy ( _err_string,S );
1771         _err_line = lcount;
1772         if (flags & CIFFL_PrintWarnings)
1773           printf ( "\n **** mmCIF READ ERROR "
1774                    "<<line %i: no 'data_XXXX' tag found>>\n",lcount );
1775         return CIFRC_NoDataLine;
1776       }
1777 
1778       llen = _max_buf_len;
1779       L    = new char[llen];
1780       i    = 0;
1781       p   += 5;
1782       while ((*p) && (*p!=' ') && (*p!=char(9)))  {
1783         L[i++] = *p;
1784         p++;
1785       }
1786       L[i] = char(0);
1787       CreateCopy ( name,L );
1788 
1789 
1790       // 2. Loop over tags until next 'data_' or end of file
1791 
1792       while (p)  {
1793 
1794         // skip spaces
1795         while ((*p==' ') || (*p==char(9)))  p++;
1796 
1797         if ((*p) && (*p!='#'))  {  // this is not a comment, continue
1798           if (*p=='_')
1799             GetDataItem ( f,S,L,p,lcount,llen );
1800           else if (!strncmp(p,"loop_",5))
1801             GetLoop ( f,S,L,p,lcount,llen );
1802           else if (!strncmp(p,"data_",5))  {
1803             p = NULL;
1804             break;
1805           } else  {
1806             // if got to here, the file is corrupted
1807             strcpy ( _err_string,S );
1808             _err_line = lcount;
1809             Warning |= CIFW_UnrecognizedItems;
1810             if (flags & CIFFL_PrintWarnings)
1811               printf ( "\n **** mmCIF READ WARNING "
1812                        "<<line %i: unrecognized string>>\n%s\n",
1813                        lcount,S );
1814             while ((*p) && (*p!=' ') && (*p!=char(9)))
1815               if (*p=='#')  *p = char(0);
1816                       else  p++;
1817           }
1818         } else
1819           *p = char(0);
1820 
1821         if (Warning && (flags & CIFFL_StopOnWarnings))  {
1822           if (L)  delete[] L;
1823           return Warning;
1824         }
1825 
1826         if (!(*p))  {
1827           if (!f.FileEnd())  {
1828             f.ReadLine ( S,_max_buf_len );
1829             lcount++;
1830             p = &(S[0]);
1831           } else
1832             p = NULL;
1833         }
1834 
1835       }
1836 
1837       if (L)  delete[] L;
1838 
1839       Optimize();  // get rid of over-allocated fields.
1840 
1841       return Warning;
1842 
1843     }
1844 
1845 
GetDataItem(io::RFile f,pstr S,pstr & L,pstr & p,int & lcount,int & llen)1846     void Data::GetDataItem ( io::RFile f, pstr S, pstr & L,
1847                              pstr & p, int & lcount, int & llen )  {
1848     PStruct cifStruct;
1849     char    T[100];
1850     int     RC,i;
1851 
1852       i = 0;
1853       while ((*p) && (*p!=' ') && (*p!=char(9)) && (*p!='.'))  {
1854         if (i<(int)sizeof(T)-1)  T[i++] = *p;
1855         p++;
1856       }
1857       T[i] = char(0);
1858 
1859       if (*p!='.')  {    // category name missing
1860         strcpy ( L,T );  // item name
1861         T[0] = char(1);  // special
1862         T[1] = char(0);  //   category name
1863       }
1864 
1865       //  look for category
1866       i = AddCategory ( T );
1867       if (i<0)  {
1868         // negative value means that the category was not in the list,
1869         // but a place for it has been provided and index updated
1870         cifStruct = new Struct ( T );
1871         Category[nCategories-1] = cifStruct;
1872       } else  {
1873         cifStruct = PStruct(Category[i]);
1874         if (cifStruct->GetCategoryID()!=MMCIF_Struct)  {
1875           strcpy ( _err_string,S );
1876           _err_line = lcount;
1877           Warning |= CIFW_NotAStructure;
1878           if (flags & CIFFL_PrintWarnings)
1879             printf ( "\n **** mmCIF READ WARNING "
1880                      "<<line %i: %s was a loop -- replaced>>\n%s\n",
1881                      lcount,T,S );
1882           delete Category[i];
1883           cifStruct = new Struct ( T );
1884           Category[i] = cifStruct;
1885         }
1886       }
1887 
1888       if (*p=='.')  {  // get item name
1889         i = 0;
1890         p++;  // skip period
1891         while ((*p) && (*p!=' ') && (*p!=char(9)))  {
1892           T[i++] = *p;
1893           p++;
1894         }
1895         T[i] = char(0);
1896       } else
1897         strcpy ( T,L );
1898 
1899       if (nWrongFields>0)  {
1900         if (CheckWrongField(cifStruct->name,T))  {
1901           GetField ( f,S,L,p,lcount,llen );
1902           cifStruct->DeleteField ( T );
1903           return;
1904         }
1905       }
1906 
1907       RC = GetField ( f,S,L,p,lcount,llen );
1908 
1909       if (RC)  {
1910         strcpy ( _err_string,S );
1911         _err_line = lcount;
1912         Warning |= CIFW_MissingField;
1913         if (flags & CIFFL_PrintWarnings)
1914           printf ( "\n **** mmCIF READ WARNING "
1915                    "<<line %i: expected data field missing>>\n%s\n",
1916                    lcount,S );
1917       }
1918 
1919       while ((*p==' ') || (*p==char(9)))  p++;
1920       if (*p=='#')  *p = char(0);
1921 
1922       i = cifStruct->GetTagNo ( T );
1923       if (i>=0)  {
1924         if (flags & CIFFL_SuggestTags)  {
1925           tagNo++;
1926           ParamStr ( T,pstr("\1"),tagNo );
1927         } else  {
1928           strcpy ( _err_string,S );
1929           _err_line = lcount;
1930           Warning |= CIFW_DuplicateTag;
1931           if (flags & CIFFL_PrintWarnings)
1932             printf ( "\n **** mmCIF READ WARNING "
1933                      "<<line %i: duplicated tag>>\n%s\n",lcount,S );
1934         }
1935       }
1936       cifStruct->AddField ( L,T );
1937 
1938     }
1939 
GetLoop(io::RFile f,pstr S,pstr & L,pstr & p,int & lcount,int & llen)1940     void Data::GetLoop ( io::RFile f, pstr S, pstr & L,
1941                          pstr & p, int & lcount, int & llen )  {
1942     PLoop cifLoop;
1943     pstr  p1;
1944     char  T[100];
1945     bool  Repeat,WrongField;
1946     int   RC,i,nC;
1947 
1948       RC = 0;
1949 
1950       p += 5;  // skip 'loop_' tag
1951 
1952       loopNo++;
1953       cifLoop = NULL;
1954       nC      = -1; // undefined category number
1955       do {
1956 
1957         while ((*p==' ') || (*p==char(9)))  p++;
1958         p1 = p;
1959 
1960         if (*p=='_')  {
1961 
1962           // get category name
1963           i = 0;
1964           while ((*p) && (*p!=' ') && (*p!=char(9)) && (*p!='.'))  {
1965             if (i<(int)sizeof(T)-1)  T[i++] = *p;
1966             p++;
1967           }
1968           T[i] = char(0);
1969 
1970           if (*p!='.')  {    // category name missing
1971             strcpy ( L,T );  // item name
1972             if (flags & CIFFL_SuggestCategories)
1973                  sprintf ( T,"X%i",loopNo );
1974             else strcpy  ( T,"X" );
1975             T[0] = char(1);  // special category name
1976           }
1977 
1978           if (cifLoop)  {
1979             if (strcmp(cifLoop->GetCategoryName(),T))  {
1980               // loop ended, empty
1981               p    = p1;   // play back to last category
1982               cifLoop = NULL;
1983             }
1984           } else  {
1985             //  look for category
1986             i = AddCategory ( T );
1987             if ((i!=nC) && (nC>=0))  {  // empty loop; most probably
1988                                         // a corrupted file
1989               p = p1;   // play back to last category
1990               strcpy ( _err_string,S );
1991               _err_line = lcount;
1992               Warning |= CIFW_EmptyLoop;
1993               if (flags & CIFFL_PrintWarnings)
1994                 printf ( "\n **** mmCIF READ WARNING "
1995                          "<<line %i: empty loop>>\n%s\n",lcount,S );
1996               // AddCategory(..) has added a NULL-Category on the top of
1997               // category list; remove it now
1998               DeleteCategory ( nCategories-1 );
1999               cifLoop = NULL;
2000     //          return;
2001             }
2002             if (i<0)  {
2003               // negative value means that the category was not in the list,
2004               // but a place for it has been provided and index updated
2005               cifLoop = new Loop ( T );
2006               Category[nCategories-1] = cifLoop;
2007               nC = nCategories-1;
2008             }
2009           }
2010     /*
2011      else if (Loop)  {
2012             if (!strcmp(Loop->GetCategoryName(),
2013                         Category[i]->GetCategoryName()))  {
2014               if (Loop->GetCategoryID()!=MMCIF_Loop)  {
2015                 Warning |= CIFW_NotALoop;
2016                 if (flags & CIFFL_PrintWarnings)
2017                   printf ( "\n **** mmCIF READ WARNING "
2018                            "<<line %i: %s was a structure --"
2019                            " replaced>>\n%s\n",lcount,T,S );
2020                 delete Category[i];
2021                 Loop = new Loop ( T );
2022                 Category[i] = Loop;
2023               }
2024             } else
2025               Loop = NULL;
2026           }
2027     */
2028           if (cifLoop)  {
2029 
2030             if (*p=='.')  {  // get item name
2031               i = 0;
2032               p++;  // skip period
2033               while ((*p) && (*p!=' ') && (*p!=char(9)))  {
2034                 T[i++] = *p;
2035                 p++;
2036               }
2037               T[i] = char(0);
2038             } else
2039               strcpy ( T,L );
2040 
2041             if (nWrongFields>0)
2042                   WrongField = CheckWrongField ( cifLoop->name,T );
2043             else  WrongField = false;
2044 
2045             if (!WrongField)  {
2046               if (cifLoop->AddTag(T)>=0)  {
2047                 if (flags & CIFFL_SuggestTags)  {
2048                   tagNo++;
2049                   ParamStr ( T,pstr("\1"),tagNo );
2050                   cifLoop->AddTag(T);
2051                 } else  {
2052                   strcpy ( _err_string,S );
2053                   _err_line = lcount;
2054                   Warning |= CIFW_DuplicateTag;
2055                   if (flags & CIFFL_PrintWarnings)
2056                     printf ( "\n **** mmCIF READ WARNING "
2057                              "<<line %i: duplicate tag>>\n%s\n",lcount,S );
2058                 }
2059               }
2060             }
2061             Repeat = true;
2062 
2063           } else  {
2064 
2065             p = p1;
2066             Repeat = false;
2067 
2068           }
2069 
2070         } else if (!(*p) || (*p=='#'))  {
2071           Repeat = !f.FileEnd();
2072           if (Repeat)  {
2073             f.ReadLine ( S,_max_buf_len );
2074             lcount++;
2075             p = &(S[0]);
2076           } else  {
2077             strcpy ( _err_string,S );
2078             _err_line = lcount;
2079             Warning |= CIFW_UnexpectedEOF;
2080             if (flags & CIFFL_PrintWarnings)
2081               printf ( "\n **** mmCIF READ WARNING "
2082                        "<<line %i: unexpected end of file>>\n%s\n",
2083                        lcount,S );
2084           }
2085         } else
2086           Repeat = false;
2087 
2088       } while (Repeat);
2089 
2090       if (cifLoop)  {
2091         do  {
2092           while ((*p==' ') || (*p==char(9)))  p++;
2093           if (!(*p) || (*p=='#'))  {
2094             Repeat = !f.FileEnd();
2095             if (Repeat)  {
2096               f.ReadLine ( S,_max_buf_len );
2097               lcount++;
2098               p = &(S[0]);
2099             }
2100           } else if (*p=='_')  Repeat = false;
2101           else if (!strncmp(p,"loop_",5))  Repeat = false;
2102           else if (!strncmp(p,"data_",5))  Repeat = false;
2103           else if (!strncmp(p,"stop_",5))  {
2104             p += 5;
2105             Repeat = false;
2106           } else  {
2107             RC = GetField ( f,S,L,p,lcount,llen );
2108             if (!RC)  {
2109               cifLoop->AddString ( L );
2110               Repeat = true;
2111             } else
2112               Repeat = false;
2113           }
2114         } while (Repeat);
2115         if ((cifLoop->iColumn!=0) || (RC))  {
2116           strcpy ( _err_string,S );
2117           _err_line = lcount;
2118           Warning |= CIFW_LoopFieldMissing;
2119           if (flags & CIFFL_PrintWarnings)
2120             printf ( "\n **** mmCIF READ WARNING "
2121                      "<<line %i: expected loop field missing>>\n%s\n",
2122                      lcount,S );
2123         }
2124       }
2125 
2126     }
2127 
GetField(io::RFile f,pstr S,pstr & L,pstr & p,int & lcount,int & llen)2128     int  Data::GetField ( io::RFile f, pstr S, pstr & L,
2129                           pstr & p, int & lcount, int & llen )  {
2130     bool Repeat;
2131     pstr L1;
2132     int  i,flen;
2133     char c;
2134 
2135       flen    = 0;
2136       L[flen] = char(0);
2137 
2138       do {
2139 
2140         // skip all spaces before the field
2141         while ((*p==' ') || (*p==char(9)))  p++;
2142 
2143         if ((*p=='#') || (!(*p)))  {
2144           // comment or end of line met;  the field should be
2145           // found on the next line
2146           Repeat = !f.FileEnd();
2147           if (Repeat)  {
2148             // take the next line
2149             f.ReadLine ( S,_max_buf_len );
2150             lcount++;
2151             p = &(S[0]);
2152             Repeat = (*p!=';');
2153           } else  {
2154             // end of file and the field is not taken
2155             L[0] = char(0);
2156             return 1;
2157           }
2158         } else
2159           // first symbol of a field is found
2160           Repeat = false;
2161 
2162       } while (Repeat);
2163 
2164 
2165       if (*p==';')  {      // this is a multiline field
2166         p++;
2167         strcpy ( L,p );    // take first line of the field
2168         flen = strlen(L);
2169         while (!f.FileEnd())  {
2170           f.ReadLine ( S,_max_buf_len );
2171           lcount++;
2172           p = &(S[0]);
2173           if (*p==';')  {  // multiline field terminated
2174             p++;
2175             while ((*p==' ') || (*p==char(9)))  p++;
2176             return 0;
2177           } else  {        // multiline field continues -- take next line
2178             flen += strlen(S)+2;
2179             if (flen>=llen)  {
2180               llen = flen + IMin(2000,llen);
2181               L1   = new char[llen];
2182               strcpy ( L1,L );
2183               delete[] L;
2184               L = L1;
2185             }
2186             strcat ( L,"\n" );
2187             strcat ( L,S );
2188           }
2189         }
2190 
2191         // end of file -- take the last line of the multiline field
2192         p = &(S[strlen(S)]);
2193 
2194       } else  {
2195 
2196         i = 0;
2197         if (*p!='_')  {
2198           if ((*p=='\'') || (*p=='"'))  {
2199             c = *p;
2200             // field in quotation marks
2201             do  {
2202               p++;
2203               // stop taking characters either on end of line
2204               // or the quotation mark
2205               while ((*p) && (*p!=c))  {
2206                 L[i++] = *p;
2207                 p++;
2208               }
2209               while (*p==c)  {
2210                 // it was a quotation mark -- check that it is followed
2211                 // by end of line or space
2212                 p++;
2213                 if ((*p) && (*p!=' ') && (*p!=char(9)))  {
2214                   // the quotation mark is not a field terminator and
2215                   // belongs to the field.
2216                   L[i++] = c;    // take the quotation mark
2217                   if (*p!=c)     // take the non-space character
2218                     L[i++] = *p; // but check for field terminator
2219                 }
2220               }
2221               // terminate the loop only on space or end of line
2222             } while ((*p) && (*p!=' ') && (*p!=char(9)));
2223             if (*p)  p++;
2224             L[i] = char(0);
2225           } else  {
2226             // a simplest field without spaces
2227             while ((*p) && (*p!=' ') && (*p!=char(9)))  {
2228               L[i++] = *p;
2229               p++;
2230             }
2231             L[i] = char(0);
2232             if (((L[0]=='.') || (L[0]=='?')) && (!L[1]))  {
2233               //  "no data" tokens
2234               L[1] = L[0];
2235               L[0] = char(2);
2236               L[2] = char(0);
2237             }
2238           }
2239         }
2240 
2241       }
2242 
2243       return 0;
2244 
2245     }
2246 
Sort()2247     void  Data::Sort()  {
2248     int      i,k;
2249     psvector cnames;
2250 
2251       k = 0;
2252       for (i=0;i<nCategories;i++)
2253         if (Category[i])  {
2254           if (k<i)  Category[k] = Category[i];
2255           k++;
2256         }
2257       for (i=k;i<nCategories;i++)
2258         Category[i] = NULL;
2259       nCategories = k;
2260 
2261       FreeVectorMemory ( index ,0 );
2262       GetVectorMemory  ( cnames,nCategories,0 );
2263       GetVectorMemory  ( index ,nCategories,0 );
2264 
2265       for (i=0;i<nCategories;i++)  {
2266         Category[i]->Sort();
2267         cnames[i] = NULL;
2268         CreateCopy ( cnames[i],Category[i]->name );
2269       }
2270 
2271       SortTags ( cnames,nCategories,index );
2272 
2273       for (i=0;i<nCategories;i++)
2274         if (cnames[i])  delete[] cnames[i];
2275 
2276       if (cnames) delete[] cnames;
2277 
2278     }
2279 
GetCategoryNo(cpstr cname)2280     int  Data::GetCategoryNo ( cpstr cname )  {
2281     //   Binary search for index of category cname in Category[].
2282     // Return:
2283     //    >=0 : position of the category found
2284     //     <0 : the category was not found, it could be inserted before
2285     //          (-RC-1)th element, where RC is the return value
2286     int l1,l2,l,k;
2287 
2288       if ((!Category) || (nCategories<1)) return -1;
2289 
2290       if (!index)    Sort();
2291 
2292       if (cname[0])  {
2293         l  = 0;
2294         l1 = 0;
2295         l2 = nCategories-1;
2296         k  = 1;
2297         while (l1<l2-1)  {
2298           l = (l1+l2)/2;
2299           k = strcasecmp ( cname,Category[index[l]]->name );
2300           if (k<0)      l2 = l;
2301           else if (k>0) l1 = l;
2302           else  {
2303             l1 = l;
2304             break;
2305           }
2306         }
2307         if (k==0)  return index[l];    // is at RCth position
2308         k = strcasecmp(cname,Category[index[l1]]->name);
2309         if (k==0)  return index[l1];   // is at RCth position
2310         if (k<0)   return -1;          // would be at (-RC-1)th position
2311         if (l2!=l1)  {
2312           k = strcasecmp(cname,Category[index[l2]]->name);
2313           if (k==0)  return index[l2]; // is at RCth position
2314           if (k>0)   return -2-l2;   // would be at l2+1=(-RC-1)th position
2315         }
2316         return -2-l1;                // would be at l1+1=(-RC-1)th position
2317       } else
2318         // 'root' category should be always on top
2319         if (Category[index[0]]->name[0]==char(1))  return index[0];
2320 
2321       return -1;
2322 
2323     }
2324 
AddCategory(cpstr cname)2325     int  Data::AddCategory ( cpstr cname )  {
2326     //  return -1: a place for category has been added on the top of array;
2327     //             index is added and sorted automatically
2328     //        >=0: the category is already in the array -- its position
2329     //             is returned
2330     int              l1,l;
2331     PPCategory Category1;
2332     ivector          index1;
2333 
2334 
2335       if (!Category)  {
2336         Category     = new PCategory[1];
2337         Category[0]  = NULL;
2338         GetVectorMemory ( index,1,0 );
2339         index[0]     = 0;
2340         nCategories  = 1;
2341         return -nCategories;  // the category has been added on the top of array
2342       }
2343       l1 = GetCategoryNo ( cname );
2344       if (l1>=0)  return l1;  // non-negative returns mean that
2345                               // the category is already in the array
2346       l1 = -l1-1;  // otherwise the category has to be added and indexed at here
2347       // put new NULL-category on the top of array and update the index
2348       Category1 = new PCategory[nCategories+1];
2349       GetVectorMemory ( index1,nCategories+1,0 );
2350       for (l=0;l<nCategories;l++)
2351         Category1[l] = Category[l];
2352       Category1[nCategories] = NULL;
2353       for (l=0;l<l1;l++)
2354         index1[l] = index[l];
2355       index1[l1] = nCategories;
2356       for (l=l1+1;l<=nCategories;l++)
2357         index1[l] = index[l-1];
2358       delete[] Category;
2359       FreeVectorMemory ( index,0 );
2360       Category = Category1;
2361       index    = index1;
2362       nCategories++;
2363       return -nCategories; // the category has been added on
2364                            // the top of array
2365     }
2366 
WriteMMCIFData(cpstr FName,io::GZ_MODE gzipMode)2367     bool Data::WriteMMCIFData ( cpstr FName, io::GZ_MODE gzipMode )  {
2368     io::File f;
2369       f.assign ( FName,true,false,gzipMode );
2370       if (f.rewrite())  {
2371         WriteMMCIF ( f );
2372         f.shut();
2373         return true;
2374       } else
2375         return false;
2376     }
2377 
WriteMMCIF(io::RFile f)2378     void Data::WriteMMCIF ( io::RFile f )  {
2379     int  i;
2380 
2381       if (name)  {
2382         f.Write     ( pstr("\ndata_") );
2383         f.WriteLine ( name            );
2384       } else
2385         f.WriteLine ( pstr("\ndata_") );
2386 
2387       for (i=0;i<nCategories;i++)
2388         if (Category[i])
2389           Category[i]->WriteMMCIF ( f );
2390 
2391     }
2392 
2393 
2394     // ---------------  Retrieving data
2395 
DeleteCategory(cpstr CName)2396     int  Data::DeleteCategory ( cpstr CName )  {
2397     int k;
2398       k = GetCategoryNo ( CName );
2399       if (k<0)  return CIFRC_NoCategory;
2400       return DeleteCategory ( k );
2401     }
2402 
DeleteCategory(int CatNo)2403     int  Data::DeleteCategory ( int CatNo ) {
2404     int i;
2405       if (Category[CatNo])  delete Category[CatNo];
2406       for (i=CatNo+1;i<nCategories;i++)
2407         Category[i-1] = Category[i];
2408       i = 0;
2409       while ((i<nCategories) && (index[i]!=CatNo))  {
2410         if (index[i]>CatNo) index[i]--;
2411         i++;
2412       }
2413       i++;
2414       while (i<nCategories)  {
2415         if (index[i]>CatNo) index[i]--;
2416         index[i-1] = index[i];
2417         i++;
2418       }
2419       nCategories--;
2420       index   [nCategories] = 0;
2421       Category[nCategories] = NULL;
2422       return 0;
2423     }
2424 
DeleteStructure(cpstr CName)2425     int  Data::DeleteStructure ( cpstr CName )  {
2426     int k;
2427       k = GetCategoryNo ( CName );
2428       if (k<0)  return CIFRC_NoCategory;
2429       if (Category[k]->GetCategoryID()==MMCIF_Struct)
2430             return DeleteCategory ( k );
2431       else  return CIFRC_NotAStructure;
2432     }
2433 
DeleteLoop(cpstr CName)2434     int  Data::DeleteLoop ( cpstr CName )  {
2435     int k;
2436       k = GetCategoryNo ( CName );
2437       if (k<0)  return CIFRC_NoCategory;
2438       if (Category[k]->GetCategoryID()==MMCIF_Loop)
2439             return DeleteCategory ( k );
2440       else  return CIFRC_NotALoop;
2441     }
2442 
GetCategory(int categoryNo)2443     PCategory Data::GetCategory ( int categoryNo )  {
2444       if ((categoryNo>=0) && (categoryNo<nCategories))
2445         return Category[categoryNo];
2446       return NULL;
2447     }
2448 
GetStructure(cpstr CName)2449     PStruct Data::GetStructure ( cpstr CName )  {
2450     int i;
2451       i = GetCategoryNo ( CName );
2452       if (i<0)  return NULL;
2453       if (Category[i]->GetCategoryID()==MMCIF_Struct)
2454             return PStruct(Category[i]);
2455       else  return NULL;
2456     }
2457 
GetLoop(cpstr CName)2458     PLoop Data::GetLoop ( cpstr CName )  {
2459     int i;
2460       i = GetCategoryNo ( CName );
2461       if (i<0)  return NULL;
2462       if (Category[i]->GetCategoryID()==MMCIF_Loop)
2463             return PLoop(Category[i]);
2464       else  return NULL;
2465     }
2466 
FindLoop(cpstr * tagList)2467     PLoop Data::FindLoop ( cpstr * tagList )  {
2468     int i;
2469       for (i=0;i<nCategories;i++)
2470         if (Category[i])  {
2471           if (Category[i]->GetCategoryID()==MMCIF_Loop)  {
2472             if (Category[i]->CheckTags(tagList))
2473               return PLoop(Category[i]);
2474           }
2475         }
2476       return NULL;
2477     }
2478 
GetDataName(pstr & dname,bool Remove)2479     void Data::GetDataName ( pstr & dname, bool Remove )  {
2480       if (Remove)  {
2481         if (dname)  delete[] dname;
2482         dname = name;
2483         name  = NULL;
2484       } else
2485         CreateCopy ( dname,name );
2486     }
2487 
CheckData(cpstr CName,cpstr TName)2488     int  Data::CheckData ( cpstr CName, cpstr TName )  {
2489     //   CheckData(..) returns positive value if the field is in the
2490     // file:
2491     //   CIFRC_Structure  category CName is a structure
2492     //   CIFRC_Loop       category CName is a loop
2493     // Negative returns mean:
2494     //   CIFRC_StructureNoTag  category CName is present,
2495     //                        it is a structure, but it does not
2496     //                        have tag TName
2497     //   CIFRC_LoopNoTag       category CName is present,
2498     //                        it is a loop, but it does not have
2499     //                        tag TName
2500     //   CIFRC_NoCategory      category CName is not present.
2501     // If TName is set to NULL then only the CName is checked and
2502     // possible returns are CIFRC_Structure, CIFRC_Loop and
2503     // CIFRC_NoCategory.
2504     int i,k;
2505       i = GetCategoryNo ( CName );
2506       if (i<0)  return CIFRC_NoCategory;
2507       if (Category[i]->GetCategoryID()==MMCIF_Struct)
2508             k = CIFRC_Structure;
2509       else  k = CIFRC_Loop;
2510       if (TName)  {
2511         if (Category[i]->GetTagNo(TName)<0)  {
2512           if (k==CIFRC_Structure)
2513                 k = CIFRC_StructureNoTag;
2514           else  k = CIFRC_LoopNoTag;
2515         }
2516       }
2517       return k;
2518     }
2519 
Optimize()2520     void Data::Optimize()  {
2521     int              i,k;
2522     PPCategory C1;
2523       k = 0;
2524       for (i=0;i<nCategories;i++)
2525         if (Category[i])  {
2526           Category[i]->Optimize();
2527           if (Category[i]->nTags<=0)  {
2528             delete Category[i];
2529             Category[i] = NULL;
2530           } else
2531             k++;
2532         }
2533       if (k>0)  {
2534         if (k!=nCategories)  {
2535           C1 = new PCategory[k];
2536           k  = 0;
2537           for (i=0;i<nCategories;i++)
2538             if (Category[i])
2539               C1[k++] = Category[i];
2540           if (Category) delete[] Category;
2541           Category    = C1;
2542           nCategories = k;
2543           FreeVectorMemory ( index,0 );
2544           Sort();
2545         }
2546       } else  {
2547         if (Category)  delete[] Category;
2548         Category    = NULL;
2549         nCategories = 0;
2550       }
2551     }
2552 
GetString(pstr & Dest,cpstr CName,cpstr TName,bool Remove)2553     int  Data::GetString  ( pstr & Dest, cpstr CName,
2554                                   cpstr TName, bool Remove )  {
2555     //   GetString(..), GetReal(..) and GetInteger(..) return 0 if the
2556     // requested field was found and successfully converted. Negative
2557     // returns mean:
2558     //    CIFRC_WrongFormat    the field was found but failed to convert
2559     //                        due to improper numeric format
2560     //    CIFRC_NoTag          category CName was found, but it does not
2561     //                        have tag TName
2562     //    CIFRC_NoCategory     category CName was not found
2563     //    CIFRC_NotAStructure  category CName was found, but it is a loop
2564     //                        rather than a structure.
2565     //   GetString(..) will try to dispose Dest unless it is assugned
2566     // NULL value before the call. The string will be then dynamically
2567     // allocated and copied.
2568     int i = GetCategoryNo ( CName );
2569       if (i<0)  return CIFRC_NoCategory;
2570       if (Category[i]->GetCategoryID()!=MMCIF_Struct)
2571                 return CIFRC_NotAStructure;
2572       return PStruct(Category[i])->GetString ( Dest,TName,Remove );
2573     }
2574 
GetString(cpstr CName,cpstr TName,int & RC)2575     pstr Data::GetString ( cpstr CName, cpstr TName, int & RC )  {
2576     int i = GetCategoryNo ( CName );
2577       if (i<0)  {
2578         RC = CIFRC_NoCategory;
2579         return NULL;
2580       }
2581       if (Category[i]->GetCategoryID()!=MMCIF_Struct)  {
2582         RC = CIFRC_NotAStructure;
2583         return NULL;
2584       }
2585       return PStruct(Category[i])->GetString ( TName,RC );
2586     }
2587 
DeleteField(cpstr CName,cpstr TName)2588     int  Data::DeleteField ( cpstr CName, cpstr TName )  {
2589     int i = GetCategoryNo ( CName );
2590       if (i<0)  return CIFRC_NoCategory;
2591       if (Category[i]->GetCategoryID()!=MMCIF_Struct)
2592                 return CIFRC_NotAStructure;
2593       return PStruct(Category[i])->DeleteField ( TName );
2594     }
2595 
GetReal(realtype & R,cpstr CName,cpstr TName,bool Remove)2596     int  Data::GetReal ( realtype & R, cpstr CName,
2597                          cpstr TName, bool Remove )  {
2598     int i = GetCategoryNo ( CName );
2599       if (i<0)  return CIFRC_NoCategory;
2600       if (Category[i]->GetCategoryID()!=MMCIF_Struct)
2601                 return CIFRC_NotAStructure;
2602       return PStruct(Category[i])->GetReal ( R,TName,Remove );
2603     }
2604 
GetInteger(int & I,cpstr CName,cpstr TName,bool Remove)2605     int  Data::GetInteger ( int & I, cpstr CName,
2606                                   cpstr TName, bool Remove )  {
2607     int j = GetCategoryNo ( CName );
2608       if (j<0)  return CIFRC_NoCategory;
2609       if (Category[j]->GetCategoryID()!=MMCIF_Struct)
2610                 return CIFRC_NotAStructure;
2611       return PStruct(Category[j])->GetInteger ( I,TName,Remove );
2612     }
2613 
GetLoopLength(cpstr CName)2614     int  Data::GetLoopLength ( cpstr CName )  {
2615     //   GetLoopLength(..) returns CIFRC_NotALoop if the category CName
2616     // is not a loop, CIFRC_NoCategory if the category CName is not
2617     // found. Non-negative returns give the length of the loop (may be
2618     // 0 if the loop is empty).
2619     int i;
2620       i = GetCategoryNo ( CName );
2621       if (i<0)  return CIFRC_NoCategory;
2622       if (Category[i]->GetCategoryID()!=MMCIF_Loop)
2623                 return CIFRC_NotALoop;
2624       return PLoop(Category[i])->nRows;
2625     }
2626 
GetLoopString(pstr & Dest,cpstr CName,cpstr TName,int nrow,bool Remove)2627     int  Data::GetLoopString  ( pstr & Dest, cpstr CName,
2628                                       cpstr TName, int nrow,
2629                                       bool Remove )  {
2630     //   GetLoopString(..), GetLoopReal(..) and GetLoopInteger(..) act
2631     // like GetString(..), GetReal(..) and GetInteger(..) above for
2632     // nrow-th element of the 'loop_' (indexed like 0..N-1 where N
2633     // is obtained through GetLoopLength(..)). They will return
2634     // CIFRC_WrongIndex if nrow is out of range.
2635     int i = GetCategoryNo ( CName );
2636       if (i<0)  return CIFRC_NoCategory;
2637       if (Category[i]->GetCategoryID()!=MMCIF_Loop)
2638                 return CIFRC_NotALoop;
2639       return PLoop(Category[i])->GetString ( Dest,TName,nrow,Remove );
2640     }
2641 
GetLoopString(cpstr CName,cpstr TName,int nrow,int & RC)2642     pstr Data::GetLoopString  ( cpstr CName, cpstr TName,
2643                                       int nrow, int & RC )  {
2644     int i = GetCategoryNo ( CName );
2645       if (i<0)  {
2646         RC = CIFRC_NoCategory;
2647         return NULL;
2648       }
2649       if (Category[i]->GetCategoryID()!=MMCIF_Loop)  {
2650         RC = CIFRC_NotALoop;
2651         return NULL;
2652       }
2653       return  PLoop(Category[i])->GetString ( TName,nrow,RC );
2654     }
2655 
DeleteLoopField(cpstr CName,cpstr TName,int nrow)2656     int Data::DeleteLoopField ( cpstr CName, cpstr TName,
2657                                       int nrow )  {
2658     int i = GetCategoryNo ( CName );
2659       if (i<0)  return CIFRC_NoCategory;
2660       if (Category[i]->GetCategoryID()!=MMCIF_Loop)
2661                 return CIFRC_NotALoop;
2662       return PLoop(Category[i])->DeleteField ( TName,nrow );
2663     }
2664 
GetLoopReal(realtype & R,cpstr CName,cpstr TName,int nrow,bool Remove)2665     int  Data::GetLoopReal ( realtype & R, cpstr CName,
2666                                    cpstr TName, int nrow,
2667                                    bool Remove )  {
2668     int i = GetCategoryNo ( CName );
2669       if (i<0)  return CIFRC_NoCategory;
2670       if (Category[i]->GetCategoryID()!=MMCIF_Loop)
2671                 return CIFRC_NotALoop;
2672       return PLoop(Category[i])->GetReal ( R,TName,nrow,Remove );
2673     }
2674 
GetLoopInteger(int & I,cpstr CName,cpstr TName,int nrow,bool Remove)2675     int  Data::GetLoopInteger ( int & I, cpstr CName,
2676                                       cpstr TName, int nrow,
2677                                       bool Remove )  {
2678     int j = GetCategoryNo ( CName );
2679       if (j<0)  return CIFRC_NoCategory;
2680       if (Category[j]->GetCategoryID()!=MMCIF_Loop)
2681                 return CIFRC_NotALoop;
2682       return PLoop(Category[j])->GetInteger ( I,TName,nrow,Remove );
2683     }
2684 
2685 
GetLoopSVector(psvector & S,cpstr CName,cpstr TName,int i1,int i2,bool Remove)2686     int  Data::GetLoopSVector ( psvector & S, cpstr CName,
2687                                       cpstr TName, int i1, int i2,
2688                                       bool Remove )  {
2689     //   GetLoopSVector(..), GetLoopRVector(..) and GetLoopIVector(..)
2690     // read CIF 'loop_' data into allocated vectors of strings, reals and
2691     // integers, correspondingly. The vectors may be deallocated prior
2692     // to call and assigned NULL, in which case they will be allocated
2693     // with offsets of i1, which is also the lower index of the 'loop_'
2694     // data transferred into it. The upper vector index is given by i2 or
2695     // by the loop's length whichever is less. If vectors are not assigned
2696     // NULL prior the call, it is assumed that they are properly (i1-offset,
2697     // i2-i1+1 length) allocated.
2698     //   The return codes are same as those of GetLoopString(..),
2699     // GetLoopReal(..) and GetLoopInteger(..).
2700     int i;
2701       i = GetCategoryNo ( CName );
2702       if (i<0)    return CIFRC_NoCategory;
2703       if (Category[i]->GetCategoryID()!=MMCIF_Loop)
2704                   return CIFRC_NotALoop;
2705       return PLoop(Category[i])->GetSVector ( S,TName,i1,i2,Remove );
2706     }
2707 
GetLoopRVector(rvector & R,cpstr CName,cpstr TName,int i1,int i2,bool Remove)2708     int  Data::GetLoopRVector ( rvector & R, cpstr CName,
2709                                       cpstr TName, int i1, int i2,
2710                                       bool Remove )  {
2711     int i;
2712       i = GetCategoryNo ( CName );
2713       if (i<0)    return CIFRC_NoCategory;
2714       if (Category[i]->GetCategoryID()!=MMCIF_Loop)
2715                   return CIFRC_NotALoop;
2716       return PLoop(Category[i])->GetRVector ( R,TName,i1,i2,Remove );
2717     }
2718 
GetLoopIVector(ivector & I,cpstr CName,cpstr TName,int i1,int i2,bool Remove)2719     int  Data::GetLoopIVector ( ivector & I, cpstr CName,
2720                                       cpstr TName, int i1, int i2,
2721                                       bool Remove )  {
2722     int j;
2723       j = GetCategoryNo ( CName );
2724       if (j<0)    return CIFRC_NoCategory;
2725       if (Category[j]->GetCategoryID()!=MMCIF_Loop)
2726                   return CIFRC_NotALoop;
2727       return PLoop(Category[j])->GetIVector ( I,TName,i1,i2,Remove );
2728     }
2729 
2730 
2731     // ---------------  Storing data
2732 
PutDataName(cpstr dname)2733     void  Data::PutDataName ( cpstr dname )  {
2734     // stores name for 'data_' record
2735       CreateCopy ( name,dname );
2736     }
2737 
PutNoData(int NoDataType,cpstr CName,cpstr TName)2738     int  Data::PutNoData ( int NoDataType, cpstr CName, cpstr TName )  {
2739     PStruct cifStruct;
2740     int     i,RC;
2741 
2742       RC = CIFRC_Ok;
2743 
2744       //  look for category
2745       i = AddCategory ( CName );
2746       if (i<0)  {
2747         // negative value means that the category was not in the list,
2748         // but a place for it has been provided and index updated
2749         cifStruct = new Struct ( CName );
2750         Category[nCategories-1] = cifStruct;
2751       } else  {
2752         cifStruct = PStruct(Category[i]);
2753         if (cifStruct->GetCategoryID()!=MMCIF_Struct)  {
2754           RC = CIFRC_NotAStructure;
2755           delete Category[i];
2756           cifStruct = new Struct ( CName );
2757           Category[i] = cifStruct;
2758         }
2759       }
2760 
2761       cifStruct->PutNoData ( NoDataType,TName );
2762 
2763       return RC;
2764 
2765     }
2766 
PutString(cpstr S,cpstr CName,cpstr TName,bool Concatenate)2767     int  Data::PutString ( cpstr S, cpstr CName,
2768                            cpstr TName, bool Concatenate )  {
2769     //   PutString(..), PutReal(..) and PutInteger(..) will put the
2770     // values given into the specified category (CName) under the
2771     // specified tag (TName). The category, tag and field are created
2772     // automatically; the field will be replaced silently if identical
2773     // CName.TName is specified in two calls. Calls of these functions
2774     // may follow in random order; however CIF file will have all tags
2775     // grouped by categories and catgories will follow in the order
2776     // of first appearance in PutString(..), PutReal(..) or
2777     // PutInteger(..).
2778     //   Return code - one of CIFRC_Ok or CIFRC_NotAStruct
2779     PStruct cifStruct;
2780     int     i,RC;
2781 
2782       RC = CIFRC_Ok;
2783 
2784       //  look for category
2785       i = AddCategory ( CName );
2786       if (i<0)  {
2787         // negative value means that the category was not in the list,
2788         // but a place for it has been provided and index updated
2789         cifStruct = new Struct ( CName );
2790         Category[nCategories-1] = cifStruct;
2791       } else  {
2792         cifStruct = PStruct(Category[i]);
2793         if (cifStruct->GetCategoryID()!=MMCIF_Struct)  {
2794           RC = CIFRC_NotAStructure;
2795           delete Category[i];
2796           cifStruct = new Struct ( CName );
2797           Category[i] = cifStruct;
2798         }
2799       }
2800 
2801       cifStruct->AddField ( S,TName,Concatenate );
2802 
2803       return RC;
2804 
2805     }
2806 
PutDate(cpstr CName,cpstr TName)2807     int   Data::PutDate ( cpstr CName, cpstr TName )  {
2808     PStruct cifStruct;
2809     int     i,RC;
2810 
2811       RC = CIFRC_Ok;
2812 
2813       //  look for category
2814       i = AddCategory ( CName );
2815       if (i<0)  {
2816         // negative value means that the category was not in the list,
2817         // but a place for it has been provided and index updated
2818         cifStruct = new Struct ( CName );
2819         Category[nCategories-1] = cifStruct;
2820       } else  {
2821         cifStruct = PStruct(Category[i]);
2822         if (cifStruct->GetCategoryID()!=MMCIF_Struct)  {
2823           RC = CIFRC_NotAStructure;
2824           delete Category[i];
2825           cifStruct = new Struct ( CName );
2826           Category[i] = cifStruct;
2827         }
2828       }
2829 
2830       cifStruct->PutDate ( TName );
2831 
2832       return RC;
2833 
2834     }
2835 
PutReal(realtype R,cpstr CName,cpstr TName,int prec)2836     int  Data::PutReal ( realtype R, cpstr CName,
2837                          cpstr TName, int prec )  {
2838     char rS[100];
2839       sprintf ( rS,"%.*g",prec,R );
2840       return PutString ( rS,CName,TName,false );
2841     }
2842 
PutInteger(int I,cpstr CName,cpstr TName)2843     int  Data::PutInteger ( int I, cpstr CName,
2844                                          cpstr TName )  {
2845     char iS[100];
2846       if (I>MinInt4)  sprintf ( iS,"%i",I );
2847       else  {
2848         iS[0] = char(2);
2849         iS[1] = '.';
2850         iS[2] = char(0);
2851       }
2852       return PutString ( iS,CName,TName,false );
2853     }
2854 
2855 
AddLoop(cpstr CName,PLoop & cifLoop)2856     int  Data::AddLoop ( cpstr CName, PLoop & cifLoop )  {
2857     int  i,RC;
2858 
2859       RC = CIFRC_Ok;
2860 
2861       //  look for category
2862       i = AddCategory ( CName );
2863       if (i<0)  {
2864         // negative value means that the category was not in the list,
2865         // but a place for it has been provided and index updated
2866         cifLoop = new Loop ( CName );
2867         Category[nCategories-1] = cifLoop;
2868         RC = CIFRC_Created;
2869       } else  {
2870         cifLoop = PLoop(Category[i]);
2871         if (cifLoop->GetCategoryID()!=MMCIF_Loop)  {
2872           RC = CIFRC_NotALoop;
2873           delete Category[i];
2874           cifLoop = new Loop ( CName );
2875           Category[i] = cifLoop;
2876         }
2877       }
2878 
2879       return RC;
2880 
2881     }
2882 
AddStructure(cpstr CName,PStruct & cifStruct)2883     int  Data::AddStructure ( cpstr CName, PStruct & cifStruct )  {
2884     int  i,RC;
2885 
2886       RC = CIFRC_Ok;
2887 
2888       //  look for category
2889       i = AddCategory ( CName );
2890       if (i<0)  {
2891         // negative value means that the category was not in the list,
2892         // but a place for it has been provided and index updated
2893         cifStruct = new Struct ( CName );
2894         Category[nCategories-1] = cifStruct;
2895         RC = CIFRC_Created;
2896       } else  {
2897         cifStruct = PStruct(Category[i]);
2898         if (cifStruct->GetCategoryID()!=MMCIF_Struct)  {
2899           RC = CIFRC_NotAStructure;
2900           delete Category[i];
2901           cifStruct = new Struct ( CName );
2902           Category[i] = cifStruct;
2903         }
2904       }
2905 
2906       return RC;
2907 
2908     }
2909 
2910 
PutLoopNoData(int NoDataType,cpstr CName,cpstr TName,int nrow)2911     int  Data::PutLoopNoData ( int NoDataType, cpstr CName,
2912                                      cpstr TName, int nrow )  {
2913     PLoop cifLoop;
2914     int   i,RC;
2915 
2916       RC = CIFRC_Ok;
2917 
2918       //  look for category
2919       i = AddCategory ( CName );
2920       if (i<0)  {
2921         // negative value means that the category was not in the list,
2922         // but a place for it has been provided and index updated
2923         cifLoop = new Loop ( CName );
2924         Category[nCategories-1] = cifLoop;
2925       } else  {
2926         cifLoop = PLoop(Category[i]);
2927         if (cifLoop->GetCategoryID()!=MMCIF_Loop)  {
2928           RC = CIFRC_NotALoop;
2929           delete Category[i];
2930           cifLoop = new Loop ( CName );
2931           Category[i] = cifLoop;
2932         }
2933       }
2934 
2935       cifLoop->PutNoData ( NoDataType,TName,nrow );
2936 
2937       return RC;
2938 
2939     }
2940 
2941 
PutLoopString(cpstr S,cpstr CName,cpstr TName,int nrow)2942     int  Data::PutLoopString ( cpstr S, cpstr CName,
2943                                      cpstr TName, int nrow )  {
2944     //   PutLoopString(..), PutLoopReal(..) and PutLoopInteger(..) act
2945     // like PutString(..), PutReal(..) and PutInteger(..) above for
2946     // nrow-th element of the 'loop_' CName (indexed begining from 0).
2947     // In consequitive calls, given values of nrow does not have to be
2948     // ordered; the most efficient way is to start with HIGHEST value
2949     // for nrow in the loop and move down to 0. The least efficient way
2950     // is to start with nrow=0 and move up.
2951     PLoop cifLoop;
2952     int   i,RC;
2953 
2954       RC = CIFRC_Ok;
2955 
2956       //  look for category
2957       i = AddCategory ( CName );
2958       if (i<0)  {
2959         // negative value means that the category was not in the list,
2960         // but a place for it has been provided and index updated
2961         cifLoop = new Loop ( CName );
2962         Category[nCategories-1] = cifLoop;
2963       } else  {
2964         cifLoop = PLoop(Category[i]);
2965         if (cifLoop->GetCategoryID()!=MMCIF_Loop)  {
2966           RC = CIFRC_NotALoop;
2967           delete Category[i];
2968           cifLoop = new Loop ( CName );
2969           Category[i] = cifLoop;
2970         }
2971       }
2972 
2973       cifLoop->PutString ( S,TName,nrow );
2974 
2975       return RC;
2976 
2977     }
2978 
PutLoopReal(realtype R,cpstr CName,cpstr TName,int nrow,int prec)2979     int  Data::PutLoopReal ( realtype R, cpstr CName,
2980                                    cpstr TName, int nrow, int prec )  {
2981     char rS[100];
2982       sprintf ( rS,"%.*g",prec,R );
2983       return PutLoopString ( rS,CName,TName,nrow );
2984     }
2985 
PutLoopInteger(int I,cpstr CName,cpstr TName,int nrow)2986     int  Data::PutLoopInteger ( int I, cpstr CName,
2987                                       cpstr TName, int nrow )  {
2988     char iS[100];
2989       sprintf ( iS,"%i",I );
2990       return PutLoopString ( iS,CName,TName,nrow );
2991     }
2992 
2993 
PutLoopSVector(psvector S,cpstr CName,cpstr TName,int i1,int i2)2994     int  Data::PutLoopSVector ( psvector S, cpstr CName,
2995                                       cpstr TName, int i1, int i2 )  {
2996     //   PutLoopSVector(..), PutLoopRVector(..) and PutLoopIVector(..)
2997     // put vectors of values into specified loop fields. Parameters i1
2998     // and i2 give the range of indices of values which are to be
2999     // transfered. To transfer an entire vector allocated as [0..N-1]
3000     // i1 shoudl be set to 0 and i2 - to N-1. Note that the loop is
3001     // always indexed as starting form 0 on, therefore negative i1 and
3002     // i2 are not allowed, and specifying i1>0 will leave first i1
3003     // elements of the CIF loop for the corresponding tag undefined
3004     // (will be output like '?').
3005     PLoop cifLoop;
3006     int   i,RC;
3007 
3008       RC = CIFRC_Ok;
3009 
3010       //  look for category
3011       i = AddCategory ( CName );
3012       if (i<0)  {
3013         // negative value means that the category was not in the list,
3014         // but a place for it has been provided and index updated
3015         cifLoop = new Loop ( CName );
3016         Category[nCategories-1] = cifLoop;
3017       } else  {
3018         cifLoop = PLoop(Category[i]);
3019         if (cifLoop->GetCategoryID()!=MMCIF_Loop)  {
3020           RC = CIFRC_NotALoop;
3021           delete Category[i];
3022           cifLoop = new Loop ( CName );
3023           Category[i] = cifLoop;
3024         }
3025       }
3026 
3027       cifLoop->PutSVector ( S,TName,i1,i2 );
3028 
3029       return RC;
3030 
3031     }
3032 
PutLoopRVector(rvector R,cpstr CName,cpstr TName,int i1,int i2,int prec)3033     int  Data::PutLoopRVector ( rvector R, cpstr CName,
3034                                       cpstr TName,
3035                                       int i1, int i2, int prec )  {
3036     PLoop cifLoop;
3037     int   i,RC;
3038 
3039       RC = CIFRC_Ok;
3040 
3041       //  look for category
3042       i = AddCategory ( CName );
3043       if (i<0)  {
3044         // negative value means that the category was not in the list,
3045         // but a place for it has been provided and index updated
3046         cifLoop = new Loop ( CName );
3047         Category[nCategories-1] = cifLoop;
3048       } else  {
3049         cifLoop = PLoop(Category[i]);
3050         if (cifLoop->GetCategoryID()!=MMCIF_Loop)  {
3051           RC = CIFRC_NotALoop;
3052           delete Category[i];
3053           cifLoop = new Loop ( CName );
3054           Category[i] = cifLoop;
3055         }
3056       }
3057 
3058       cifLoop->PutRVector ( R,TName,i1,i2,prec );
3059 
3060       return RC;
3061 
3062     }
3063 
PutLoopIVector(ivector I,cpstr CName,cpstr TName,int i1,int i2)3064     int  Data::PutLoopIVector ( ivector I, cpstr CName,
3065                                       cpstr TName,
3066                                       int i1, int i2 )  {
3067     PLoop cifLoop;
3068     int   j,RC;
3069 
3070       RC = CIFRC_Ok;
3071 
3072       //  look for category
3073       j = AddCategory ( CName );
3074       if (j<0)  {
3075         // negative value means that the category was not in the list,
3076         // but a place for it has been provided and index updated
3077         cifLoop = new Loop ( CName );
3078         Category[nCategories-1] = cifLoop;
3079       } else  {
3080         cifLoop = PLoop(Category[j]);
3081         if (cifLoop->GetCategoryID()!=MMCIF_Loop)  {
3082           RC = CIFRC_NotALoop;
3083           delete Category[j];
3084           cifLoop = new Loop ( CName );
3085           Category[j] = cifLoop;
3086         }
3087       }
3088 
3089       cifLoop->PutIVector ( I,TName,i1,i2 );
3090 
3091       return RC;
3092 
3093     }
3094 
3095 
RenameCategory(cpstr CName,cpstr newCName)3096     int Data::RenameCategory ( cpstr CName,
3097                                      cpstr newCName )  {
3098     int i,RC;
3099       i = GetCategoryNo ( CName );
3100       if (i>=0)  {
3101         Category[i]->PutCategoryName ( newCName );
3102         Sort();
3103         RC = CIFRC_Ok;
3104       } else
3105         RC = CIFRC_NoCategory;
3106       return RC;
3107     }
3108 
3109 
Copy(PData Data)3110     void Data::Copy ( PData Data )  {
3111     int i;
3112       FreeMemory(0);
3113       CreateCopy ( name,Data->name );
3114       nCategories = Data->nCategories;
3115       if (nCategories>0)  {
3116         Category = new PCategory[nCategories];
3117         GetVectorMemory ( index,nCategories,0 );
3118         for (i=0;i<nCategories;i++)  {
3119           if (Data->Category[i])  {
3120             if (Data->Category[i]->GetCategoryID()==MMCIF_Struct)
3121                   Category[i] = new Struct();
3122             else  Category[i] = new Loop();
3123             Category[i]->Copy ( Data->Category[i] );
3124           } else
3125             Category[i] = NULL;
3126           index[i] = Data->index[i];
3127         }
3128       }
3129       flags   = Data->flags;
3130       Warning = Data->Warning;
3131     }
3132 
3133 
CopyCategory(PData Data,cpstr CName,cpstr newCName)3134     int  Data::CopyCategory ( PData Data, cpstr CName,
3135                                     cpstr newCName )  {
3136     PCategory Cat;
3137     int             i,di,dc,RC;
3138 
3139       di = Data->GetCategoryNo ( CName );
3140 
3141       if (di>=0)  {
3142 
3143         RC = CIFRC_Ok;
3144         dc = Data->Category[di]->GetCategoryID();
3145 
3146         //  look for category
3147         i = AddCategory ( CName );
3148         if (i<0)  {
3149           // negative value means that the category was not in the list,
3150           // but a place for it has been provided and index updated
3151           if (dc==MMCIF_Loop)  Cat = new Loop   ( CName );
3152                          else  Cat = new Struct ( CName );
3153           Category[nCategories-1] = Cat;
3154         } else  {
3155           Cat = Category[i];
3156           if (Cat->GetCategoryID()!=dc)  {
3157             RC = CIFRC_NotAStructure;
3158             delete Category[i];
3159             if (dc==MMCIF_Loop)  Cat = new Loop   ( CName );
3160                            else  Cat = new Struct ( CName );
3161             Category[i] = Cat;
3162           }
3163         }
3164 
3165         Cat->Copy ( Data->Category[di] );
3166         if (newCName)  {
3167           Cat->PutCategoryName ( newCName );
3168           Sort();
3169         }
3170 
3171       } else
3172         RC = CIFRC_NoCategory;
3173 
3174       return RC;
3175 
3176     }
3177 
PrintCategories()3178     void Data::PrintCategories()  {
3179     // for debuging only
3180     int i;
3181       printf ( " Total %i categories:\n",nCategories );
3182       for (i=0;i<nCategories;i++)
3183         if (Category[i])  {
3184           printf ( " %5i. ",i+1 );
3185           if (Category[i]->GetCategoryID()==MMCIF_Loop)
3186                 printf ( "Loop      %s\n",Category[i]->name );
3187           else  printf ( "Structure %s\n",Category[i]->name );
3188         }
3189     }
3190 
3191 
write(io::RFile f)3192     void Data::write ( io::RFile f )  {
3193     int i,k;
3194       if (!index)  Sort();
3195       f.CreateWrite ( name );
3196       f.WriteInt    ( &nCategories );
3197       for (i=0;i<nCategories;i++)  {
3198         if (Category[i])  {
3199           k = Category[i]->GetCategoryID();
3200           f.WriteInt ( &k );
3201           Category[i]->write ( f );
3202         } else  {
3203           k = -1;
3204           f.WriteInt ( &k );
3205         }
3206         f.WriteInt ( &(index[i]) );
3207       }
3208       f.WriteInt ( &flags   );
3209       f.WriteInt ( &Warning );
3210     }
3211 
3212 
read(io::RFile f)3213     void Data::read ( io::RFile f )  {
3214     int i,k;
3215       FreeMemory(0);
3216       f.CreateRead ( name );
3217       f.ReadInt    ( &nCategories );
3218       if (nCategories>0)  {
3219         Category = new PCategory[nCategories];
3220         GetVectorMemory ( index,nCategories,0 );
3221         for (i=0;i<nCategories;i++)  {
3222           f.ReadInt ( &k );
3223           if (k>=0)  {
3224             if (k==MMCIF_Struct)  Category[i] = new Struct();
3225                             else  Category[i] = new Loop();
3226             Category[i]->read ( f );
3227           } else
3228             Category[i] = NULL;
3229           f.ReadInt ( &(index[i]) );
3230         }
3231       }
3232       f.ReadInt ( &flags   );
3233       f.ReadInt ( &Warning );
3234     }
3235 
3236 
MakeStreamFunctions(Data)3237     MakeStreamFunctions(Data)
3238 
3239 
3240 
3241     //  ======================  File  =============================
3242 
3243 
3244     File::File() : io::Stream()  {
3245       InitFile();
3246     }
3247 
File(cpstr FName,io::GZ_MODE gzipMode)3248     File::File ( cpstr FName, io::GZ_MODE gzipMode ) : io::Stream()  {
3249       InitFile ();
3250       ReadMMCIFFile ( FName,gzipMode );
3251     }
3252 
File(io::RPStream Object)3253     File::File ( io::RPStream Object ) : io::Stream(Object)  {
3254       InitFile();
3255     }
3256 
~File()3257     File::~File()  {
3258       FreeMemory();
3259     }
3260 
InitFile()3261     void File::InitFile()  {
3262       nData         = 0;
3263       nAllocData    = 0;
3264       data          = NULL;
3265       index         = NULL;
3266       PrintWarnings = false;
3267       StopOnWarning = false;
3268     }
3269 
FreeMemory()3270     void File::FreeMemory()  {
3271     int i;
3272       for (i=0;i<nData;i++)
3273         if (data[i])  delete data[i];
3274       if (data)  delete[] data;
3275       data       = NULL;
3276       FreeVectorMemory ( index,0 );
3277       nData      = 0;
3278       nAllocData = 0;
3279     }
3280 
3281 
GetMMCIFInputBuffer(int & LineNo)3282     pstr GetMMCIFInputBuffer ( int & LineNo )  {
3283       LineNo = _err_line;
3284       _err_string[sizeof(_err_string)-1] = char(0);
3285       return _err_string;
3286     }
3287 
ReadMMCIFFile(cpstr FName,io::GZ_MODE gzipMode)3288     int  File::ReadMMCIFFile ( cpstr FName, io::GZ_MODE gzipMode )  {
3289     io::File  f;
3290     PData     CIF;
3291     char      S[500];
3292     int       RC,lcount;
3293 
3294       FreeMemory();
3295 
3296       CIF = NULL;
3297       f.assign ( FName,true,false,gzipMode );
3298       if (f.reset(true))  {
3299         S[0]   = char(0);
3300         lcount = 0;
3301         RC     = 0;
3302         while ((!RC) && (!f.FileEnd()))  {
3303           if (!CIF)  CIF = new Data();
3304           CIF->SetPrintWarnings ( PrintWarnings );
3305           CIF->SetStopOnWarning ( StopOnWarning );
3306           RC = CIF->ReadMMCIFData ( f,S,lcount );
3307           if (!RC)  {
3308             ExpandData ( nData+1 );
3309             data[nData] = CIF;
3310             nData++;
3311             CIF = NULL;
3312           }
3313         }
3314         if (CIF)  delete CIF;
3315         f.shut();
3316         if (RC==CIFRC_NoDataLine)  {
3317           if (nData>0)  RC = 0;
3318         }
3319         Sort();
3320         return RC;
3321       } else
3322         return CIFRC_CantOpenFile;
3323 
3324     }
3325 
WriteMMCIFFile(cpstr FName,io::GZ_MODE gzipMode)3326     int File::WriteMMCIFFile ( cpstr FName, io::GZ_MODE gzipMode )  {
3327     io::File f;
3328       f.assign ( FName,true,false,gzipMode );
3329       if (f.rewrite())  {
3330         WriteMMCIF ( f );
3331         f.shut();
3332         return 0;
3333       } else
3334         return CIFRC_CantOpenFile;
3335     }
3336 
WriteMMCIF(io::RFile f)3337     void File::WriteMMCIF ( io::RFile f )  {
3338     int i;
3339       for (i=0;i<nData;i++)
3340         if (data[i])
3341           data[i]->WriteMMCIF ( f );
3342     }
3343 
3344 
Sort()3345     void File::Sort()  {
3346     psvector tag;
3347     int      i;
3348       if (nData>0)  {
3349         FreeVectorMemory ( index,0 );
3350         GetVectorMemory  ( index,nData,0 );
3351         GetVectorMemory  ( tag  ,nData,0 );
3352         for (i=0;i<nData;i++)  {
3353           tag[i] = NULL;
3354           CreateCopy ( tag[i],data[i]->name );
3355         }
3356         SortTags ( tag,nData,index );
3357         for (i=0;i<nData;i++)
3358           if (tag[i])  {
3359             delete[] tag[i];
3360             tag[i] = NULL;
3361           }
3362         FreeVectorMemory ( tag,0 );
3363       }
3364     }
3365 
GetCIFDataNo(cpstr DName)3366     int  File::GetCIFDataNo ( cpstr DName )  {
3367     //   Binary search for index of DName ttag in data[].
3368     // Return:
3369     //    >=0 : position of the DName found
3370     //     <0 : the DName was not found, it could be inserted before
3371     //          (-RC-1)th element, where RC is the return value
3372     int l1,l2,l,k;
3373 
3374       if (!data)   return -1;
3375       if (!index)  Sort();
3376 
3377       l  = 0;
3378       l1 = 0;
3379       l2 = nData-1;
3380       k  = 1;
3381       while (l1<l2-1)  {
3382         l = (l1+l2)/2;
3383         k = strcasecmp ( DName,data[index[l]]->name );
3384         if (k<0)      l2 = l;
3385         else if (k>0) l1 = l;
3386         else  {
3387           l1 = l;
3388           break;
3389         }
3390       }
3391 
3392       if (k==0)  return index[l];    // is at RCth position
3393       k = strcasecmp ( DName,data[index[l1]]->name );
3394       if (k==0)  return index[l1];   // is at RCth position
3395       if (k<0)   return -1;          // would be at (-RC-1)th position
3396       if (l2!=l1)  {
3397         k = strcasecmp ( DName,data[index[l2]]->name );
3398         if (k==0)  return index[l2]; // is at RCth position
3399         if (k>0)   return -2-l2;     // would be at l2+1=(-RC-1)th position
3400       }
3401 
3402       return -2-l1;                  // would be at l1+1=(-RC-1)th position
3403 
3404     }
3405 
GetCIFData(int dataNo)3406     PData  File::GetCIFData ( int dataNo )  {
3407       if ((dataNo>=0) && (dataNo<nData))  return data[dataNo];
3408                                     else  return NULL;
3409     }
3410 
GetCIFData(cpstr DName)3411     PData  File::GetCIFData ( cpstr DName )  {
3412     int l;
3413       l = GetCIFDataNo ( DName );
3414       if (l>=0)  return data[l];
3415            else  return NULL;
3416     }
3417 
ExpandData(int nDataNew)3418     void File::ExpandData ( int nDataNew )  {
3419     int          i,nAD;
3420     PPData data1;
3421     ivector      index1;
3422       if (nDataNew>nAllocData)  {
3423         nAD   = nDataNew + IMin(nAllocData/2+1,100);
3424         data1 = new PData[nAD];
3425         GetVectorMemory ( index1,nAD,0 );
3426         for (i=0;i<nAllocData;i++)  {
3427           data1 [i] = data [i];
3428           index1[i] = index[i];
3429         }
3430         for (i=nAllocData;i<nAD;i++)  {
3431           data1 [i] = NULL;
3432           index1[i] = i;
3433         }
3434         if (data)  delete[] data;
3435         FreeVectorMemory ( index,0 );
3436         data       = data1;
3437         index      = index1;
3438         nAllocData = nAD;
3439       }
3440     }
3441 
AddCIFData(cpstr DName)3442     int  File::AddCIFData ( cpstr DName )  {
3443     //  return -1: the CIF data structure has been added on the
3444     //             top of data array; the index is added and sorted
3445     //             automatically
3446     //        >=0: the CIF data structure is already in the array
3447     //             -- its position is returned
3448     int  i1,i;
3449       if (!data)  {
3450         ExpandData ( 3 );  // get space for first 3 CIF data structures
3451         data[0] = new Data ( DName );
3452         nData   = 1;
3453         return -nData;     // the CIF data structure has been added
3454                            // "on the top" of array
3455       }
3456       i1 = GetCIFDataNo ( DName );
3457       if (i1>=0)  return i1;  // non-negative returns mean that the CIF
3458                               // data structure is already in the array
3459       i1 = -i1-1;  // otherwise the data has to be added and indexed at here
3460       // put new CIF data structure on the top of array and update index
3461       ExpandData ( nData+1 );
3462       data[nData] = new Data ( DName );
3463       for (i=nData;i>i1;i--)
3464         index[i] = index[i-1];
3465       index[i1] = nData;
3466       nData++;
3467       return -nData; // the tag has been added on the top of array
3468     }
3469 
3470 
DeleteCIFData(cpstr DName)3471     int File::DeleteCIFData ( cpstr DName )  {
3472     int dataNo = GetCIFDataNo ( DName );
3473 
3474       if (dataNo>=0)  return DeleteCIFData ( dataNo );
3475       return dataNo;
3476 
3477     }
3478 
DeleteCIFData(int dataNo)3479     int File::DeleteCIFData ( int dataNo )  {
3480     int i;
3481 
3482       if ((0<=dataNo) && (dataNo<nData))  {
3483 
3484         if (data[dataNo])  delete data[dataNo];
3485         for (i=dataNo+1;i<nData;i++)
3486           data[i-1] = data[i];
3487         nData--;
3488 
3489         Sort();
3490 
3491         return 0;
3492 
3493       }
3494 
3495       return -nData;
3496 
3497     }
3498 
Copy(PFile File)3499     void File::Copy  ( PFile File )  {
3500     int i;
3501       FreeMemory();
3502       nData      = File->nData;
3503       nAllocData = nData;
3504       if (nData>0)  {
3505         data = new PData[nData];
3506         for (i=0;i<nData;i++)  {
3507           if (File->data[i])  {
3508             data[i] = new Data();
3509             data[i]->Copy ( File->data[i] );
3510           } else
3511             data[i] = NULL;
3512         }
3513       }
3514     }
3515 
3516 
write(io::RFile f)3517     void File::write ( io::RFile f )  {
3518     int i,k;
3519       f.WriteInt ( &nData );
3520       for (i=0;i<nData;i++)
3521         if (data[i])  {
3522           k = 1;
3523           f.WriteInt ( &k );
3524           data[i]->write ( f );
3525         } else  {
3526           k = 0;
3527           f.WriteInt ( &k );
3528         }
3529     }
3530 
read(io::RFile f)3531     void File::read  ( io::RFile f )  {
3532     int i,k;
3533       FreeMemory();
3534       f.ReadInt ( &nData );
3535       nAllocData = nData;
3536       if (nData>0)  {
3537         data = new PData[nData];
3538         for (i=0;i<nData;i++)  {
3539           f.ReadInt ( &k );
3540           if (k)  {
3541             data[i] = new Data();
3542             data[i]->read ( f );
3543           } else
3544             data[i] = NULL;
3545         }
3546       }
3547     }
3548 
3549 
MakeStreamFunctions(File)3550     MakeStreamFunctions(File)
3551 
3552 
3553     int  isCIF ( cpstr FName, io::GZ_MODE gzipMode )  {
3554     io::File f;
3555     int      rc;
3556 
3557       f.assign ( FName,true,false,gzipMode );
3558       if (f.reset(true))  {
3559         rc = isCIF ( f );
3560         f.shut();
3561       } else
3562         rc = -1;
3563 
3564       return rc;
3565 
3566     }
3567 
isCIF(io::RFile f)3568     int  isCIF ( io::RFile f )  {
3569     char    S[_max_buf_len+1];
3570     bool Done;
3571     pstr    p;
3572 
3573       f.ReadLine ( S,_max_buf_len );
3574       S[_max_buf_len] = char(0);
3575       Done = false;
3576       while (!Done)  {
3577         p = &(S[0]);
3578         while ((*p==' ') || (*p==char(9)))  p++;
3579         Done = !strncmp(p,"data_",5);
3580         if (!Done)  {
3581           if (f.FileEnd())  {
3582             Done = true;
3583             p    = NULL;
3584           } else  {
3585             f.ReadLine ( S,_max_buf_len );
3586             S[_max_buf_len] = char(0);
3587           }
3588         }
3589       }
3590 
3591       if (!p)  return 1;
3592       if (!strncmp(p,"data_",5))  return 0;
3593                             else  return 1;
3594 
3595     }
3596 
3597 
GetCIFMessage(pstr M,int RC)3598     pstr GetCIFMessage ( pstr M, int RC )  {
3599     int  LineNo;
3600     pstr InputLine;
3601 
3602       InputLine = GetMMCIFInputBuffer ( LineNo );
3603 
3604       if (RC>10)  {
3605         if (RC & CIFW_UnrecognizedItems)
3606           sprintf ( M,"unrecognized items found on %ith line\n%s",
3607                     LineNo,InputLine );
3608         else if (RC & CIFW_MissingField)
3609           sprintf ( M,"expected data field not found; line %i reads\n%s",
3610                     LineNo,InputLine );
3611         else if (RC & CIFW_EmptyLoop)
3612           sprintf ( M,"empty loop ('loop_') on %ith line\n%s",
3613                     LineNo,InputLine );
3614         else if (RC & CIFW_UnexpectedEOF)
3615           sprintf ( M,"unexpected end of file; line %i reads\n%s",
3616                     LineNo,InputLine );
3617         else if (RC & CIFW_LoopFieldMissing)
3618           sprintf ( M,"expected data field in a loop not found; "
3619                       "line %i reads\n%s", LineNo,InputLine );
3620         else if (RC & CIFW_LoopFieldMissing)
3621           sprintf ( M,"expected data field in a loop not found; "
3622                       "line %i reads\n%s", LineNo,InputLine );
3623         else if (RC & CIFW_NotAStructure)
3624           sprintf ( M,"a loop is used as a structure on line %i\n%s",
3625                     LineNo,InputLine );
3626         else if (RC & CIFW_NotALoop)
3627           sprintf ( M,"a structure is used as a loop on line %i\n%s",
3628                     LineNo,InputLine );
3629         else if (RC & CIFW_DuplicateTag)
3630           sprintf ( M,"duplicate tag was found on line %i\n%s",
3631                     LineNo,InputLine );
3632         else
3633           sprintf ( M,"undocumented warning issued for line %i\n%s",
3634                     LineNo,InputLine );
3635       } else if (RC<0)
3636         switch (RC)  {
3637           case CIFRC_StructureNoTag : strcpy(M,"tag of a structure not "
3638                                                "found");
3639                                  break;
3640           case CIFRC_LoopNoTag      : strcpy(M,"tag of a loop not found");
3641                                  break;
3642           case CIFRC_NoCategory     : strcpy(M,"category not found");
3643                                  break;
3644           case CIFRC_WrongFormat    : strcpy(M,"wrong format of a number");
3645                                  break;
3646           case CIFRC_NoTag          : strcpy(M,"tag not found");
3647                                  break;
3648           case CIFRC_NotAStructure  : strcpy(M,"category is not a "
3649                                                "structure");
3650                                  break;
3651           case CIFRC_NotALoop       : strcpy(M,"category is not a loop");
3652                                  break;
3653           case CIFRC_WrongIndex     : strcpy(M,"index outside the loop's "
3654                                                "limits");
3655                                  break;
3656           case CIFRC_NoField        : strcpy(M,"data is absent");
3657                                  break;
3658           case CIFRC_Created        : strcpy(M,"category created");
3659                                  break;
3660           case CIFRC_CantOpenFile   : strcpy(M,"can't open CIF file");
3661                                  break;
3662           case CIFRC_NoDataLine     : strcpy(M,"'data_' tag not found." );
3663                                  break;
3664           default                   : strcpy(M,"undocumented return code");
3665         }
3666 
3667       return M;
3668 
3669     }
3670 
3671   }  // namespace mmcif
3672 
3673 }  // namespace mmdb
3674