a99  V32.6
allegro Windows Hauptprogramm
 Alle Klassen Dateien Funktionen Variablen Typdefinitionen Aufzählungen Aufzählungswerte Makrodefinitionen
abase.cpp
gehe zur Dokumentation dieser Datei
1 // abase.cpp Saetze lesen, schreiben, nachladen, sperren, freigeben
2 // 1995-08-26
3 // Copyright 2011 Universitätsbibliothek Braunschweig, more see bottom
4 
5 // Klasse ABASE Db. Grundfunktionen (c) UB Braunschweig 1995
6 // ACHTUNG: Schreibfunktionen in abasew.cpp
7 
8 // Copyright 2011 Universitätsbibliothek Braunschweig, more see bottom
9 
10 #ifdef WIN32
11 
12 #include <windows.h> // nur fuer Sleep(1000)
13 #define _handle _file
14 #include <sys\locking.h> // ---- MICROSOFT ----
15 #endif
16 
17 #include "abase.hpp"
18 
19 // FUNKTIONEN (public)
20 
21 // AbGet() : Datensatz in ein RECORD holen
22 // AbExpV14() : Datensatz mit V14-Ersetzungen exportieren
23 // AbKeys() : Indexeintraege eines Satzes ermitteln (gem. Indexparam., z.B. CAT.API
24 // AbRecChk() : Satznummer pruefen (zu gross, <0, unbesetzt?)
25 // AbRecAdr() : Satzadresse ermitteln
26 // AbIsLocked() : Is record locked?
27 
28 
29 // $$ii : NEU wg Aufbohrung, Indexparam. ii $$991116
30 
31 // Konstruktor:
32 // directory APIname CFGname
33 //z.B. ABASE("C:\\ALLEGRO\\DEMO\\","CAT", kfg, 3, "z\\save\\cat.log")
34 
35 // wobei zuerst KONFIG kfg=new KONFIG("A"); gemacht wurde
36 
37 // filmax=16000000L; /* max size of .ALD file */ /*$$951025 NEU */
38 
39 // inputFi=1; // file# for new records
40 
41 // opt_N=2; /* $$951027 -N 0, 1, 2 */
42 
43 // acc =0,1,2,3 access right, -1: don't open index file
44 
45 
46 ABASE::ABASE(char *dbdir, char *dbn, KONFIG *cfg, int acc,
47 
48  char *logName,int opt_n,long lFilMax,
49 
50  int inputFiNr, char *inxName, char *inxParam)
51 
52  : INDEX(dbdir,dbn,cfg,acc, 3, inxName, inxParam) // Ableitung von Klasse INDEX
53 {
54 
55  if (*Aerror!=0) return; // the INDEX constructor had trouble!
56 
57  Filename(dbdir,dbn);
58 
59  logflg=1; // enable logging;
60  accf=acc;
61  inputFi=inputFiNr;
62  opt_N=opt_n;
63 
64  sct=1L; // session counter: nr of rec's saved $$120116
65 
66  if (accf>0)
67  {
68  if (logName==NULL)
69  sprintf(dbLog,"%s%s.log%c",dbDir,dbN,0); // $$970203 log
70  else strncpy(dbLog,logName,PATH_SIZE-1);
71  }
72 
73 // Adn=activeNr(); done in INDEX()
74  if (Adn<0 || Adn>5) Adn=0; // zur Sicherheit
75 
76  if (lFilMax<2000L) filmax=16000000L*(long)ii;
77  else filmax=lFilMax;
78 
79 
80 // Now we're ready to open the database files:
81 
82 // Index file was opened in INDEX() already
83 
84 
85  if (acc>=0)
86  {
87  dbTb=Tblopen(".tbl"); // record address table
88 
89  if (dbTb==EOF) return; // error msg is in Aerror!
90 
91  if (InMaxRecNr()<1L) // NEW $$000612
92  {
93  char h[3];
94  sprintf(h,"%c%c",0,ii+2);
95  write(dbTb,h,2);
96  }
97 
98  { // $$ii
99  char h[3];
100  while (read(dbTb,h,2)==-1) lseek(dbTb,0L,0); // $$000417 Neu while()
101  ii=h[1]-2;
102  if (ii<1) ii=1; // $$000413 NEU Absicherung
103  }
104  }
105  fwr=(FILE *)NULL; // prevent disaster on first use
106 
107  Abase[Adn]=this; // pointer to this object
108 
109  s_fctn(); // externe Routine fuer Programmierer
110 }
111 
112 
113 // Hilfsfunktion zum Erstellen eines Dateinamens
114 
115 void ABASE::Filename(char *dbdir,char *dbn) // make .ALD file name
116 {
117 
118  sprintf(dbFn,"%s%s_%c",dbdir,dbn,0);
119  // example: c:\allegro\demo2\cat_
121  // points to the number part of the name
122  return;
123 }
124 
125 // Open a database file (.xld) for read access
126 
127 FILE *ABASE::Openr(int fnr) // open file_fnr.ALD f. reading
128 {
129  sprintf(dbFNum,"%d.%cld%c",fnr,kfg->schema,0);
130  return(fopen(dbFn,"rb"));
131 }
132 
133 
134 // Einen Datensatz holen und in RECORD-Objekt speichern
135 
136 int ABASE::AbGet(RECNR recn, RECORD *rec, int md)
137 // recn = 0L : re-load latest record (rec->rNr)
138 // md = 0 : new rec
139 // 1 : stack it up in record space
140 // 2 : get file# + addr ONLY %% getrecadd()
141 
142 // return: 0 rec# was <1
143 // -1 rec# not in use (unbesetzte nr.)
144 // -2 rec# was too large
145 // -3 TBL not accessible
146 // -4 TBL not readable
147 // -5 file# <1 or >255
148 // -6 wrong recn : Satznr im Satz stimmt nicht ueberein (4 erste Bytes)
149 // gri number of fields of this rec (always >0)
150 {
151  static int rfi, rAdn;
152  static long rof;
153  static CHAR crad[4];
154  FILE *frd;
155  int crix;
156  if (!recn) recn=rec->rNr; // load latest rec again
157 
158  if (recn<1L) return 0;
159 
160  rof=(lseek(dbTb,0L,2)-2L)/4L;
161  if (recn>rof) return -2; // recn too large
162  if (lseek(dbTb,(recn-1L)*4L+2L,0)==-1) return -3;
163 
164  if (read(dbTb,crad,4)==0) // TBL nicht lesbar
165  {
166  sprintf(Aerror,uif[170],recn); // Nummer %ld
167  return -4;
168  }
169 
170  if (crad[0]==0) return -1; // file# 0: rec# not in use
171  rfi=(int)crad[0];
172 
173  rof=(RECNR)crad[1]*65536L+(RECNR)crad[2]*256L+(RECNR)crad[3];
174  if (ii>1 && rof>1L) rof=rof*(long)ii; // $$ii
175  else rof-=1L;
176  rAdn=Adn;
177 
178  if (md!=1)
179 
180  {
181  if (md==0) // $$980813 NEU, d.h. nicht fuer md==2
182 
183  if (rec->rSt==2) rec->Release(); // Satz war in Bearbeitung, aber
184 
185  // nicht abgespeichert: erst freigeben
186  rec->rFi=rfi;
187  rec->rOf=rof;
188  rec->rNr=recn;
189  rec->rNa=0L; // $$981109
190  rec->Adn=Adn;
191  }
192 
193  else rec->rNa=recn;
194  if (md==2) return 1;
195  if (md) rec->cri=rec->gri;
196  else
197  {
198  rec->cri=0;
199  rec->gri=0;
200  rec->gend=rec->ga[0];
201  }
202  crix=rec->cri; // base point; reset to this after reading is finished
203  int rlg=0; // length of record on disk
204  *Aerror=0;
205  frd = Openr(rfi);
206  if (frd == (FILE *)0) // Statt frd.
207  {
208  sprintf(Aerror,uif[171],dbFn); // Datei nicht zugaenglich
209  return -1;
210  }
211 
212  fseek(frd,rof,0);
213 
214  { // %% DOS: this was rec_in()
215  int ci; // char input / kateg position
216  RECNR recnf;
217  CHAR *h;
218  char pg;
219  ci=getc(frd); // get 1st char, it may be 1, 8, or 9
220  if (ci==EOF)
221  {
222  fclose(frd);
223  sprintf(Aerror,"file %s not usable or too short,%c",dbFn,0);
224  return -1;
225  } // it was no address at all
226  pg=Apgnb;
227  Apgnb='4'; // Property pruefung ausschalten
228  if (!md)
229  if (ci!=8) rec->rSt=ci; // rec status : 1:normal 9:cleared
230  else rec->rSt=1; // locked
231 // next 4 characters contain rec# :
232 
233  recnf = (RECNR)getc( frd )*65536L*256L
234  + (RECNR)getc( frd )*65536L
235  + (RECNR)getc( frd )*256L
236  + (RECNR)getc( frd );
237 
238  ci=getc(frd); // 1st real character of our rec
239  while (1) // read loop
240  {
241  h=Aspace; // initialize position for rec
242  switch (ci)
243  {
244  case 0:
245  case 26:
246  case '#':
247  ++rlg;
248  ci=0;
249  break;
250  case '\n': // ignore
251  case '\r':
252  ++rlg;
253  ci=0;
254  break;
255  case EOF:
256  case 9: // nxt is clear
257  case 8: // nxt is locked
258  case 1:
259  if (!md) rec->rLg=rlg;
260  fclose(frd);
261  rec->cri=crix; // reset this counter
262  Apgnb=pg; // and this indicator
263 
264 // compare recn with the number that was found in the rec:
265 
266  if (recn!=recnf) // fuer Diagnose bei Adressfehler
267  {
268  sprintf(Aerror,"recn=%ld, wrong recn=%ld, ci=%d, file=%d rad=%ld%c",recn,recnf,ci,rec->rFi,rec->rOf,0);
269  return -6;
270  }
271  return rec->gri; // Normalfall, wenn Satz ok und geladen
272 
273  case 2:
274  case 3:
275  case 4: // hierarchies (subrecords)
276  case 5:
277  case 6:
278  case 7:
279  ++rlg;
280  rec->cri=rec->gri;
281  ci=0;
282  break;
283 
284  default: // normal input: kat#+text
285  if (ci==kfg->FIL) // filler field
286  {
287  while ((ci=getc(frd))==kfg->FIL) ++rlg; // ends with \0
288  rlg+=2;
289  if (ci>9) ci=0;
290  break;
291  }
292 
293  // now comes a real data field:
294 
295  *h++=kfg->KC;
296  if (ci=='U') ci='u'; // for #U
297  *h++=ci;
298  ++rlg;
299  while ((ci=getc(frd))>9) *h++=ci, ++rlg;
300  *h++=NL;
301  ++rlg;
302  *Aerror=8; // flag to prevent testing of multi indicator
303  rec->Ins(Aspace);
304  *Aerror=0;
305  break;
306  }
307  if (!ci) ci=getc(frd);
308  }
309  }
310 }
311 
312 // ************************* Nachladungen **********************************
313 // load another record,
314 // find ax in index fct/10-1
315 // ix_load(rec,"goethe",12); means: find key beginning with "goethe" in index 0
316 // (0 is the internal number of index 1 )
317 
318 int ABASE::ix_load(RECORD *R, CHAR *ax, int fct, int ui4)
319 // from aexp1 : manip command |if
320 // i = 1..11 = Index
321 // fct = i0/i4 : key must be exactly equal
322 // i2/i6 : only first part must be equal
323 // i1/i5 : same as 0/2, but next key
324 // i3/i7 : same as 2/6, but next key
325 // (i = index number)
326 // 20k : db_switch to database #k
327 // k='0' : back to initial db
328 // 0/1 : load short / full rec
329 // -1 : unload sec rec, i.e., reset stack
330 { // return 1 if positive, 0 if no result
331  char kkk[259], kky[262];
332  RECNR recnr;
333  static int IXS=0,fct_no;
334  static char *kkx;
335  int i;
336  int modus; // ==0: ax must match ==><: ax><key
337  kkx=kky+kfg->skt;
338  strcpy(kky,"#ux1 ");
339 
340  if (fct<0) return ix_stack(R,fct); // unload etc.
341 
342  if (fct==0) // short list entry |00 -> #ux0
343  {
344  kky[3]='0';
345  if (!stll) return 0;
346  lseek(dbSt,atol((char *)ax)*(long)stll,0); // get shortline of nxt rec
347  i=read(dbSt,kkx,stll);
348  if (i==EOF) return 0;
349  kkx[stll]=0;
350  UtFinst((CHAR *)kky,2072,kfg->tgl);
351  return 1;
352  }
353 
354  if (ax) if (strlen(ax)>250) ax[250]=0;
355 
356  if (fct==1) // ax=rec#, load this rec
357  {// get record whose number is ax
358  recnr=atol((char *)ax);
359  goto RECN;
360  }
361 
362  if (*ax!='<' && *ax!='>')
363  {
364  modus=0;
365  strcpy(kkk,ax);
366  }
367  else
368  {
369  modus=*ax;
370  strcpy(kkk,ax+1);
371  }
372 
373  pad((CHAR *)kkk,1); // $$020725 ,1 statt ,0 wg. Leerz. am Ende
374  IXS=fct/10-1;
375 
376  fct_no=fct%10;
377  if (fct_no>7)
378  {
379  if ((i=UtFindX(2072,IXS+'1',tgl))!=EOF) UtFdel(i); // delete #uxi first
380  }
381 
382  InSwix('d'); // vorsichtshalber
383 
384  switch (fct_no & 3) // ignore 4/6 = 3rd bit
385  {
386  case 0:
387  case 2: // aiEntLe wg modus
388  if (modus=='<')
389  {
390  kkk[strlen(kkk)]=1;
391  if ((recnr=aiEntLe(IXS+Adn*15,(CHAR*)kkk,(CHAR*)kkx))==0L) return(0);
392  else ;
393  }
394  else if ((recnr=aiEntGe(IXS+Adn*15,(CHAR*)kkk,(CHAR*)kkx))==0L) return(0);
395  break;
396 
397  case 1:
398  case 3:
399  if ((recnr=aiEntNx(IXS+Adn*15,(CHAR*)kkx))==0L) return(0);
400  // ix_stack(R,0); // remove last rec if there was any
401  break;
402  }
403 
404  if (!modus) // $$950624 NEUe Zeile
405  if ((fct_no & 11)<2)
406  if (strcmp(kkk,kkx)!=0) return(0);
407  else ;
408  else if (strncmp(kkk,kkx,strlen(kkk))!=0) return(0);
409  else ;
410 
411  kky[3]=IXS+'1';
412  UtFinst((CHAR *)kky,2072,kfg->tgl); // put the string found into backgr
413 
414  if (fct_no>7) return 1;
415 
416  if (!R->gri) return 0; // Sicherung, Arb.speicher leer
417 
418  if (fct_no<4)
419  if (!ix_stack(R,1)) return 0;
420  else ;
421 
422  // stack the current rec because
423  // we're ready now to retrieve the sec rec
424 
425  else ix_stack(R,2); // 4/6 : push, but pop first (repetition!)
426 
427 RECN: // $$950602 NEU
428 
429 
430  if (AbGet(recnr,R,1)<1) return 0; // $$020802 if NEU
431 
432 // wenn aus exet.cpp gerufen: ui4=i4 aus den Exp.param.
433 
434  if (ui4==-2) ui4=xi4;
435 
436  InV14Repl(R,ui4); // V14 : Referenzen in nachgel. Satz aufloesen, index.cpp
437 
438 
439 // replace(2,0); // do global replacements on this rec
440 
441 // printf("done .%ld\n",recnr); getch(); //test
442 
443 // ix_stack(R,3); // register it on stack
444 
445  return(1);
446 }
447 
448 
449 // Nachladung aufheben
450 
451 int ABASE::ix_stack(RECORD *R, int fct)
452 /* fct<0 pop
453  =1 push
454  =2 push, but pop first
455  =3 push and pop (to be prepared for #<^ !!!)
456  =4 push without cri=gri
457 */
458 {
459  if (fct<0) // pop -1: simple pop
460  { // -2: back to first rec #<<
461  // -3: one up again #<^
462  // -4: clear stack + -2 #</
463  // -5: reset stack obsolete
464  // -6 und -7 : hierarchische Saetze
465 
466  if (fct==-6) // #<d forward: nxt subrec of same or lower level
467  {
468  int crit=R->cri;
469  int crix=0; // XXX crix aus EXET !
470  while ((++crit)<R->gri && *R->ga[crit]!=RS) ;
471  if (crit<R->gri && R->ga[crit][1]>1 && R->ga[crit][1]>=R->ga[crix][1])
472  {
473  R->cri=crit;
474  return 1;
475  }
476  else return 0;
477  }
478 
479  if (fct==-7) // #<u backward: previous rec in the hierarchy
480  {
481  int crit=R->cri;
482  int crix=0; // XXX crix aus EXET !
483  if (!R->cri) return 0;
484  while ((--crit)>crix && *R->ga[crit]!=RS) ;
485  R->cri=crit;
486  return 1;
487  }
488 
489  if (fct==-4)
490  {
491  int i=1;
492  R->cri=0;
493  while (R->ga[i][1]!=1 && i<R->gri) ++i;
494  if (i==R->gri) return 0; // stack was empty
495  R->gend=R->ga[R->gri=i];
496  R->gi[R->gri]=0;
497  R->rNa=0L; // $$981109 neu, Nr. des nachgelad. Satzes
498  *(R->gend)=EOD; // Satzende markieren
499  return 1;
500 
501  }
502 
503  if (fct==-3)
504  {
505  int i=R->cri+1;
506  while (R->ga[i][1]!=1 && i<R->gri) ++i;
507  if (i==R->gri) return 0; // this was the last
508  else R->cri=i;
509  return 1;
510  }
511 
512  if (fct==-2) // unload whole stack
513  {
514  int i=1;
515  R->cri=0;
516  return 1; // $$040415
517  }
518 
519 // -1 : simple pop
520  if (R->cri==0)
521  {
522  R->rNa=0L; // rNa $$070405 // #< : this was the first
523  return 0;
524  }
525  {
526  int i=R->cri-1;
527  while (R->ga[i][1]!=1 && i) --i;
528  R->cri=i;
529  }
530 
531  return(1);
532  }
533 
534  else // push
535  {
536  if (fct==1) // $$990706 verbessert: Nachladung wird an akt. Teil angehaengt
537  { // vorher ganz hinten
538  int i=R->cri+1;
539  while (i<R->gri && R->ga[i][1]!=1) ++i;
540  R->cri=R->gri=i;
541  R->gend=R->ga[i];
542  }
543  else if (fct==2) // pop $$000201 verbessert
544  {
545  if (R->cri==0) return 0; // this was the first
546  int i=R->cri;
547  R->gri=i;
548  R->gend=R->ga[i--];
549  while (R->ga[i][1]!=1 && i) --i;
550  R->cri=i;
551  }
552  else if (fct==3)
553  {
554  ;
555  }
556  return(1);
557  }
558 }
559 
560 
561 
562 // wohl obsolet (bei PRESTO)
563 void ABASE::ix_reset(void)
564 // reset index pointer to the position where it was
565 // before the loading of other records began;
566 // otherwise browsing would be disturbed
567 {
568  return;
569 }
570 
571 
572 
573 
574 // Export eines Satzes mit V14-Ersetzungen
575 
576 int ABASE::AbExpV14(RECORD *R, EXET *exp, int mn, char *labl, RECORD *Bac)
577 
578 // means: export the RECORD using the param set exp
579 // with possible V14 replacements
580 {
581  int rc;
582  RECORD *Reserve;
583  if (!Bac) Reserve = new RECORD(kfg,(unsigned int)(R->gend - R->ga[0])+10,(R->gri)+5,100);
584  else Reserve=Bac;
585  if (!Reserve)
586  {
587  sprintf(Aerror,"Not enough space for exporting\n");
588  return 0;
589  }
590 
591  if (mn!=-1)
592  { // bei mn==-1, dann Sonderfall globale Manipulation
593  Reserve->Copy(R);
594  if (exp->xi4 > -1) InV14Repl(R,exp->xi4);
595  }
596 
597  rc=exp->Exp(R,(mn < 0)? 0 : mn,(CHAR *)labl);
598 
599  if (mn!=-1) R->Copy(Reserve);
600  if (!Bac) delete Reserve;
601 
602  if (!rc)
603  {
604  sprintf(Aerror,"export param trouble");
605  return 0;
606  }
607 
608 // if(i5) { Get(R->rNr, R, 0); } // V14 : Satz neu einlesen
609 
610  return 1;
611 }
612 
613 
614 // Schluessel eines Satzes ermitteln
615 
616 int ABASE::AbKeys(RECORD *R, int store) // see INDEX.CPP
617 { // return # of keys or -1
618  int rc;
619  RECORD *Reserve;
620  Reserve = new RECORD(kfg,(unsigned int)(R->gend - R->ga[0])+10,(R->gri)+15,100);
621 // Reserve = new RECORD(kfg);
622  if (!Reserve)
623  {
624  sprintf(Aerror,"Not enough space for Key production");
625  return -1;
626  }
627 
628  Reserve->Copy(R); // Original des Satzes R in Reserve kopieren
629 
630 // Indexparameter abarbeiten (index.cpp)
631  rc=InKeyCalc(R, store);
632  R->Copy(Reserve); // kopierten Satz zurueck
633  delete Reserve;
634  return rc;
635 }
636 
637 
638 // Satznummer auf Gueltigkeit pruefen
639 // validate rec nr
640 
641 int ABASE::AbRecChk(RECNR recn) // check rec#
642 // return: 0 : recn too large or no ALD file
643 // 1 : OK
644 // -2 : recn <0
645 // -1 : not in use (unbesetzt)
646 {
647  if (recn<1L) return 0;
648 
649  long rof;
650  CHAR crad[4];
651 
652  rof=(lseek(dbTb,0L,2)-2L)/4L;
653  if (recn>rof) return -2; // recn too large
654  if (lseek(dbTb,(recn-1L)*4L+2L,0)==-1) return -3;
655 
656  if (read(dbTb,crad,4)==0) // TBL nicht lesbar
657  {
658  sprintf(Aerror,uif[170],recn); // Nummer %ld problematisch
659  return -4;
660  }
661 
662  if (crad[0]==0) return -1; // recn not in use
663  return 1;
664 }
665 
666 
667 // Dateinummer und Satzadresse ermitteln
668 // record address
669 // return file# , offset is returned in of
670 
671 int ABASE::AbRecAdr(RECNR recn, long &of)
672 
673 {
674 
675  long rof;
676  CHAR crad[4];
677 
678  if (recn<1L) return -2;
679 
680  rof=(lseek(dbTb,0L,2)-2L)/4L; // how long is .TBL?
681 
682  if (recn>rof) return 0; // recn too large
683 
684  if (lseek(dbTb,(recn-1L)*4L+2L,0)==-1) return -5; // trouble with .TBL
685 
686  if (read(dbTb,crad,4)==0)
687  {
688  sprintf(Aerror,uif[170],recn); // Nummer %ld problematisch
689  return 0;
690  }
691  if (crad[0]==0) return -1; // Satznr unbesetzt
692 
693  rof=(long)crad[1]*65536L+(long)crad[2]*256L+(long)crad[3];
694  if (ii>1 && rof>1L) rof=rof*(long)ii; // $$ii
695  else rof-=1L;
696  of=rof;
697  return((int)crad[0]);
698 }
699 
700 // ********** is rec R locked? Return 1st byte of record (1, 8, or 9)
702 {
703  int num;
704  FILE *rdf;
705  if(!R->rNr) return 0;
706 
707 
708  sprintf(dbFNum,"%d.%cld%c",R->rFi,R->schema,0);
709 
710 
711  rdf=fopen(dbFn,"rb");
712  if(!rdf) return 0;
713 
714  fseek(rdf,R->rOf,0);
715  num=fgetc(rdf);
716  fclose(rdf);
717  return num;
718 }
719 
720 // *************
721 
722 ABASE::~ABASE() // Destruktor Rest wird in INDEX gemacht
723 {
724  close(dbTb); // $$970129 NEU 2 Zeilen
725  return;
726 
727 }
728 
729 
730 /*
731  Copyright 2011 Universitätsbibliothek Braunschweig
732 
733  Licensed under the Apache License, Version 2.0 (the "License");
734  you may not use this file except in compliance with the License.
735  You may obtain a copy of the License at
736 
737  http://www.apache.org/licenses/LICENSE-2.0
738 
739  Unless required by applicable law or agreed to in writing, software
740  distributed under the License is distributed on an "AS IS" BASIS,
741  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
742  See the License for the specific language governing permissions and
743  limitations under the License.
744 */
745 
746 /*
747  Copyright 2011 Universitätsbibliothek Braunschweig
748 
749  Licensed under the Apache License, Version 2.0 (the "License");
750  you may not use this file except in compliance with the License.
751  You may obtain a copy of the License at
752 
753  http://www.apache.org/licenses/LICENSE-2.0
754 
755  Unless required by applicable law or agreed to in writing, software
756  distributed under the License is distributed on an "AS IS" BASIS,
757  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
758  See the License for the specific language governing permissions and
759  limitations under the License.
760 */
761