emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/Inflate.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Thu, 07 Feb 2013 12:58:12 +0100
branchemul
changeset 694 0d277415ed02
permissions -rw-r--r--
Rebasing the Inflater support on jzlib which, unlike GNU ClassPath, has correct implementation of Huffman code. Making the implementation more easily testable by turning Inflater and ZipInputStream into pure delegates. Current implementation is going to need proper long support.
     1 /* -*-mode:java; c-basic-offset:2; -*- */
     2 /*
     3 Copyright (c) 2000-2011 ymnk, JCraft,Inc. All rights reserved.
     4 
     5 Redistribution and use in source and binary forms, with or without
     6 modification, are permitted provided that the following conditions are met:
     7 
     8   1. Redistributions of source code must retain the above copyright notice,
     9      this list of conditions and the following disclaimer.
    10 
    11   2. Redistributions in binary form must reproduce the above copyright 
    12      notice, this list of conditions and the following disclaimer in 
    13      the documentation and/or other materials provided with the distribution.
    14 
    15   3. The names of the authors may not be used to endorse or promote products
    16      derived from this software without specific prior written permission.
    17 
    18 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
    19 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    20 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
    21 INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
    22 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    23 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
    24 OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
    25 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    26 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
    27 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    28  */
    29 /*
    30  * This program is based on zlib-1.1.3, so all credit should go authors
    31  * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
    32  * and contributors of zlib.
    33  */
    34 
    35 package org.apidesign.bck2brwsr.emul.zip;
    36 
    37 import org.apidesign.bck2brwsr.emul.lang.System;
    38 
    39 final class Inflate{
    40   
    41   static final private int MAX_WBITS=15; // 32K LZ77 window
    42 
    43   // preset dictionary flag in zlib header
    44   static final private int PRESET_DICT=0x20;
    45 
    46   static final int Z_NO_FLUSH=0;
    47   static final int Z_PARTIAL_FLUSH=1;
    48   static final int Z_SYNC_FLUSH=2;
    49   static final int Z_FULL_FLUSH=3;
    50   static final int Z_FINISH=4;
    51 
    52   static final private int Z_DEFLATED=8;
    53 
    54   static final private int Z_OK=0;
    55   static final private int Z_STREAM_END=1;
    56   static final private int Z_NEED_DICT=2;
    57   static final private int Z_ERRNO=-1;
    58   static final private int Z_STREAM_ERROR=-2;
    59   static final private int Z_DATA_ERROR=-3;
    60   static final private int Z_MEM_ERROR=-4;
    61   static final private int Z_BUF_ERROR=-5;
    62   static final private int Z_VERSION_ERROR=-6;
    63 
    64   static final private int METHOD=0;   // waiting for method byte
    65   static final private int FLAG=1;     // waiting for flag byte
    66   static final private int DICT4=2;    // four dictionary check bytes to go
    67   static final private int DICT3=3;    // three dictionary check bytes to go
    68   static final private int DICT2=4;    // two dictionary check bytes to go
    69   static final private int DICT1=5;    // one dictionary check byte to go
    70   static final int DICT0=6;    // waiting for inflateSetDictionary
    71   static final private int BLOCKS=7;   // decompressing blocks
    72   static final private int CHECK4=8;   // four check bytes to go
    73   static final private int CHECK3=9;   // three check bytes to go
    74   static final private int CHECK2=10;  // two check bytes to go
    75   static final private int CHECK1=11;  // one check byte to go
    76   static final private int DONE=12;    // finished check, done
    77   static final private int BAD=13;     // got an error--stay here
    78 
    79   static final private int HEAD=14;
    80   static final private int LENGTH=15;
    81   static final private int TIME=16;
    82   static final private int OS=17;
    83   static final private int EXLEN=18;
    84   static final private int EXTRA=19;
    85   static final private int NAME=20;
    86   static final private int COMMENT=21;
    87   static final private int HCRC=22;
    88   static final private int FLAGS=23;
    89 
    90   int mode;                            // current inflate mode
    91 
    92   // mode dependent information
    93   int method;        // if FLAGS, method byte
    94 
    95   // if CHECK, check values to compare
    96   long was = -1;           // computed check value
    97   long need;               // stream check value
    98 
    99   // if BAD, inflateSync's marker bytes count
   100   int marker;
   101 
   102   // mode independent information
   103   int  wrap;          // flag for no wrapper
   104   int wbits;            // log2(window size)  (8..15, defaults to 15)
   105 
   106   InfBlocks blocks;     // current inflate_blocks state
   107 
   108   private final ZStream z;
   109 
   110   private int flags; 
   111 
   112   private int need_bytes = -1;
   113   private byte[] crcbuf=new byte[4];
   114 
   115   GZIPHeader gheader = null;
   116 
   117   int inflateReset(){
   118     if(z == null) return Z_STREAM_ERROR;
   119     
   120     z.total_in = z.total_out = 0;
   121     z.msg = null;
   122     this.mode = HEAD;
   123     this.need_bytes = -1;
   124     this.blocks.reset();
   125     return Z_OK;
   126   }
   127 
   128   int inflateEnd(){
   129     if(blocks != null){
   130       blocks.free();
   131     }
   132     return Z_OK;
   133   }
   134 
   135   Inflate(ZStream z){
   136     this.z=z;
   137   }
   138 
   139   int inflateInit(int w){
   140     z.msg = null;
   141     blocks = null;
   142 
   143     // handle undocumented wrap option (no zlib header or check)
   144     wrap = 0;
   145     if(w < 0){
   146       w = - w;
   147     }
   148     else {
   149       wrap = (w >> 4) + 1;
   150       if(w < 48)
   151         w &= 15;
   152     }
   153 
   154     if(w<8 ||w>15){
   155       inflateEnd();
   156       return Z_STREAM_ERROR;
   157     }
   158     if(blocks != null && wbits != w){
   159       blocks.free();
   160       blocks=null;
   161     }
   162 
   163     // set window size
   164     wbits=w;
   165 
   166     this.blocks=new InfBlocks(z, 1<<w);
   167 
   168     // reset state
   169     inflateReset();
   170 
   171     return Z_OK;
   172   }
   173 
   174   int inflate(int f){
   175     int hold = 0;
   176 
   177     int r;
   178     int b;
   179 
   180     if(z == null || z.next_in == null){
   181       if(f == Z_FINISH && this.mode==HEAD)
   182         return Z_OK; 
   183       return Z_STREAM_ERROR;
   184     }
   185 
   186     f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK;
   187     r = Z_BUF_ERROR;
   188     while (true){
   189 
   190       switch (this.mode){
   191       case HEAD:
   192         if(wrap==0){
   193 	  this.mode = BLOCKS;
   194           break;
   195         } 
   196 
   197         try { r=readBytes(2, r, f); }
   198         catch(Return e){ return e.r; }
   199 
   200         if((wrap&2)!=0 && this.need == 0x8b1fL) {   // gzip header
   201 	  z.adler=new CRC32();
   202           checksum(2, this.need);
   203 
   204           if(gheader==null) 
   205             gheader=new GZIPHeader();
   206 
   207           this.mode = FLAGS;
   208           break;
   209         }
   210 
   211         flags = 0;
   212 
   213         this.method = ((int)this.need)&0xff;
   214         b=((int)(this.need>>8))&0xff;
   215 
   216         if((wrap&1)==0 ||  // check if zlib header allowed
   217            (((this.method << 8)+b) % 31)!=0){
   218           this.mode = BAD;
   219           z.msg = "incorrect header check";
   220           // since zlib 1.2, it is allowted to inflateSync for this case.
   221           /*
   222           this.marker = 5;       // can't try inflateSync
   223           */
   224           break;
   225         }
   226 
   227         if((this.method&0xf)!=Z_DEFLATED){
   228           this.mode = BAD;
   229           z.msg="unknown compression method";
   230           // since zlib 1.2, it is allowted to inflateSync for this case.
   231 	  /*
   232           this.marker = 5;       // can't try inflateSync
   233 	  */
   234           break;
   235         }
   236 
   237         if((this.method>>4)+8>this.wbits){
   238           this.mode = BAD;
   239           z.msg="invalid window size";
   240           // since zlib 1.2, it is allowted to inflateSync for this case.
   241 	  /*
   242           this.marker = 5;       // can't try inflateSync
   243 	  */
   244           break;
   245         }
   246 
   247         z.adler=new Adler32();
   248 
   249         if((b&PRESET_DICT)==0){
   250           this.mode = BLOCKS;
   251           break;
   252         }
   253         this.mode = DICT4;
   254       case DICT4:
   255 
   256         if(z.avail_in==0)return r;r=f;
   257 
   258         z.avail_in--; z.total_in++;
   259         this.need=((z.next_in[z.next_in_index++]&0xff)<<24)&0xff000000L;
   260         this.mode=DICT3;
   261       case DICT3:
   262 
   263         if(z.avail_in==0)return r;r=f;
   264 
   265         z.avail_in--; z.total_in++;
   266         this.need+=((z.next_in[z.next_in_index++]&0xff)<<16)&0xff0000L;
   267         this.mode=DICT2;
   268       case DICT2:
   269 
   270         if(z.avail_in==0)return r;r=f;
   271 
   272         z.avail_in--; z.total_in++;
   273         this.need+=((z.next_in[z.next_in_index++]&0xff)<<8)&0xff00L;
   274         this.mode=DICT1;
   275       case DICT1:
   276 
   277         if(z.avail_in==0)return r;r=f;
   278 
   279         z.avail_in--; z.total_in++;
   280         this.need += (z.next_in[z.next_in_index++]&0xffL);
   281         z.adler.reset(this.need);
   282         this.mode = DICT0;
   283         return Z_NEED_DICT;
   284       case DICT0:
   285         this.mode = BAD;
   286         z.msg = "need dictionary";
   287         this.marker = 0;       // can try inflateSync
   288         return Z_STREAM_ERROR;
   289       case BLOCKS:
   290         r = this.blocks.proc(r);
   291         if(r == Z_DATA_ERROR){
   292           this.mode = BAD;
   293           this.marker = 0;     // can try inflateSync
   294           break;
   295         }
   296         if(r == Z_OK){
   297           r = f;
   298         }
   299         if(r != Z_STREAM_END){
   300           return r;
   301         }
   302         r = f;
   303         this.was=z.adler.getValue();
   304         this.blocks.reset();
   305         if(this.wrap==0){
   306           this.mode=DONE;
   307           break;
   308         }
   309         this.mode=CHECK4;
   310       case CHECK4:
   311 
   312         if(z.avail_in==0)return r;r=f;
   313 
   314         z.avail_in--; z.total_in++;
   315         this.need=((z.next_in[z.next_in_index++]&0xff)<<24)&0xff000000L;
   316         this.mode=CHECK3;
   317       case CHECK3:
   318 
   319         if(z.avail_in==0)return r;r=f;
   320 
   321         z.avail_in--; z.total_in++;
   322         this.need+=((z.next_in[z.next_in_index++]&0xff)<<16)&0xff0000L;
   323         this.mode = CHECK2;
   324       case CHECK2:
   325 
   326         if(z.avail_in==0)return r;r=f;
   327 
   328         z.avail_in--; z.total_in++;
   329         this.need+=((z.next_in[z.next_in_index++]&0xff)<<8)&0xff00L;
   330         this.mode = CHECK1;
   331       case CHECK1:
   332 
   333         if(z.avail_in==0)return r;r=f;
   334 
   335         z.avail_in--; z.total_in++;
   336         this.need+=(z.next_in[z.next_in_index++]&0xffL);
   337 
   338         if(flags!=0){  // gzip
   339           this.need = ((this.need&0xff000000)>>24 | 
   340                           (this.need&0x00ff0000)>>8 | 
   341                           (this.need&0x0000ff00)<<8 | 
   342                           (this.need&0x0000ffff)<<24)&0xffffffffL;
   343         }
   344 
   345         if(((int)(this.was)) != ((int)(this.need))){
   346           z.msg = "incorrect data check";
   347           // chack is delayed
   348           /*
   349           this.mode = BAD;
   350           this.marker = 5;       // can't try inflateSync
   351           break;
   352 	  */
   353         }
   354         else if(flags!=0 && gheader!=null){
   355           gheader.crc = this.need; 
   356         }
   357 
   358         this.mode = LENGTH;
   359       case LENGTH:
   360         if (wrap!=0 && flags!=0) {
   361 
   362           try { r=readBytes(4, r, f); }
   363           catch(Return e){ return e.r; }
   364 
   365           if(z.msg!=null && z.msg.equals("incorrect data check")){
   366             this.mode = BAD;
   367             this.marker = 5;       // can't try inflateSync
   368             break;
   369           }
   370 
   371           if (this.need != (z.total_out & 0xffffffffL)) {
   372             z.msg = "incorrect length check";
   373             this.mode = BAD;
   374             break;
   375           }
   376           z.msg = null;
   377         }
   378         else {
   379           if(z.msg!=null && z.msg.equals("incorrect data check")){
   380             this.mode = BAD;
   381             this.marker = 5;       // can't try inflateSync
   382             break;
   383           }
   384         }
   385 
   386         this.mode = DONE;
   387       case DONE:
   388         return Z_STREAM_END;
   389       case BAD:
   390         return Z_DATA_ERROR;
   391 
   392       case FLAGS:
   393 
   394         try { r=readBytes(2, r, f); }
   395         catch(Return e){ return e.r; }
   396 
   397         flags = ((int)this.need)&0xffff;
   398 
   399         if ((flags & 0xff) != Z_DEFLATED) {
   400           z.msg = "unknown compression method";
   401           this.mode = BAD; 
   402           break;
   403         }
   404         if ((flags & 0xe000)!=0) {
   405           z.msg = "unknown header flags set";
   406           this.mode = BAD; 
   407           break;
   408         }
   409 
   410         if ((flags & 0x0200)!=0){
   411           checksum(2, this.need);
   412         } 
   413 
   414         this.mode = TIME;
   415 
   416       case TIME:
   417         try { r=readBytes(4, r, f); }
   418         catch(Return e){ return e.r; }
   419         if(gheader!=null)
   420           gheader.time = this.need;
   421         if ((flags & 0x0200)!=0){
   422           checksum(4, this.need);
   423         }
   424         this.mode = OS;
   425       case OS:
   426         try { r=readBytes(2, r, f); }
   427         catch(Return e){ return e.r; }
   428         if(gheader!=null){
   429           gheader.xflags = ((int)this.need)&0xff;
   430           gheader.os = (((int)this.need)>>8)&0xff;
   431         }
   432         if ((flags & 0x0200)!=0){
   433           checksum(2, this.need);
   434         }
   435         this.mode = EXLEN;
   436       case EXLEN:
   437         if ((flags & 0x0400)!=0) {
   438           try { r=readBytes(2, r, f); }
   439           catch(Return e){ return e.r; }
   440           if(gheader!=null){
   441             gheader.extra = new byte[((int)this.need)&0xffff];
   442           }
   443           if ((flags & 0x0200)!=0){
   444             checksum(2, this.need);
   445           }
   446         }
   447         else if(gheader!=null){
   448           gheader.extra=null;
   449         }
   450         this.mode = EXTRA;
   451 
   452       case EXTRA:
   453         if ((flags & 0x0400)!=0) {
   454           try { 
   455             r=readBytes(r, f);
   456             if(gheader!=null){
   457               byte[] foo = tmp_array;
   458               tmp_array=null;
   459               if(foo.length == gheader.extra.length){
   460                 System.arraycopy(foo, 0, gheader.extra, 0, foo.length);
   461 	      }
   462               else{
   463                 z.msg = "bad extra field length";
   464                 this.mode = BAD; 
   465                 break;
   466 	      }
   467             }
   468           }
   469           catch(Return e){ return e.r; }
   470         }
   471         else if(gheader!=null){
   472           gheader.extra=null;
   473 	}
   474 	this.mode = NAME;
   475       case NAME:
   476 	if ((flags & 0x0800)!=0) {
   477           try { 
   478             r=readString(r, f);
   479             if(gheader!=null){
   480               gheader.name=tmp_array;
   481             }
   482             tmp_array=null;
   483           }
   484           catch(Return e){ return e.r; }
   485         }
   486         else if(gheader!=null){
   487           gheader.name=null;
   488 	}
   489         this.mode = COMMENT;
   490       case COMMENT:
   491         if ((flags & 0x1000)!=0) {
   492           try { 
   493             r=readString(r, f);
   494             if(gheader!=null){
   495               gheader.comment=tmp_array;
   496             }
   497             tmp_array=null;
   498           }
   499           catch(Return e){ return e.r; }
   500         }
   501         else if(gheader!=null){
   502           gheader.comment=null;
   503 	}
   504         this.mode = HCRC;
   505       case HCRC:
   506 	if ((flags & 0x0200)!=0) {
   507           try { r=readBytes(2, r, f); }
   508           catch(Return e){ return e.r; }
   509           if(gheader!=null){
   510             gheader.hcrc=(int)(this.need&0xffff);
   511           }
   512           if(this.need != (z.adler.getValue()&0xffffL)){
   513             this.mode = BAD;
   514             z.msg = "header crc mismatch";
   515             this.marker = 5;       // can't try inflateSync
   516             break;
   517           }
   518         }
   519         z.adler = new CRC32();
   520 
   521         this.mode = BLOCKS;
   522         break;
   523       default:
   524         return Z_STREAM_ERROR;
   525       }
   526     }
   527   }
   528 
   529   int inflateSetDictionary(byte[] dictionary, int dictLength){
   530     if(z==null || (this.mode != DICT0 && this.wrap != 0)){
   531       return Z_STREAM_ERROR;
   532     }
   533 
   534     int index=0;
   535     int length = dictLength;
   536 
   537     if(this.mode==DICT0){
   538       long adler_need=z.adler.getValue();
   539       z.adler.reset();
   540       z.adler.update(dictionary, 0, dictLength);
   541       if(z.adler.getValue()!=adler_need){
   542         return Z_DATA_ERROR;
   543       }
   544     }
   545 
   546     z.adler.reset();
   547 
   548     if(length >= (1<<this.wbits)){
   549       length = (1<<this.wbits)-1;
   550       index=dictLength - length;
   551     }
   552     this.blocks.set_dictionary(dictionary, index, length);
   553     this.mode = BLOCKS;
   554     return Z_OK;
   555   }
   556 
   557   static private byte[] mark = {(byte)0, (byte)0, (byte)0xff, (byte)0xff};
   558 
   559   int inflateSync(){
   560     int n;       // number of bytes to look at
   561     int p;       // pointer to bytes
   562     int m;       // number of marker bytes found in a row
   563     long r, w;   // temporaries to save total_in and total_out
   564 
   565     // set up
   566     if(z == null)
   567       return Z_STREAM_ERROR;
   568     if(this.mode != BAD){
   569       this.mode = BAD;
   570       this.marker = 0;
   571     }
   572     if((n=z.avail_in)==0)
   573       return Z_BUF_ERROR;
   574 
   575     p=z.next_in_index;
   576     m=this.marker;
   577     // search
   578     while (n!=0 && m < 4){
   579       if(z.next_in[p] == mark[m]){
   580         m++;
   581       }
   582       else if(z.next_in[p]!=0){
   583         m = 0;
   584       }
   585       else{
   586         m = 4 - m;
   587       }
   588       p++; n--;
   589     }
   590 
   591     // restore
   592     z.total_in += p-z.next_in_index;
   593     z.next_in_index = p;
   594     z.avail_in = n;
   595     this.marker = m;
   596 
   597     // return no joy or set up to restart on a new block
   598     if(m != 4){
   599       return Z_DATA_ERROR;
   600     }
   601     r=z.total_in;  w=z.total_out;
   602     inflateReset();
   603     z.total_in=r;  z.total_out = w;
   604     this.mode = BLOCKS;
   605 
   606     return Z_OK;
   607   }
   608 
   609   // Returns true if inflate is currently at the end of a block generated
   610   // by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
   611   // implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH
   612   // but removes the length bytes of the resulting empty stored block. When
   613   // decompressing, PPP checks that at the end of input packet, inflate is
   614   // waiting for these length bytes.
   615   int inflateSyncPoint(){
   616     if(z == null || this.blocks == null)
   617       return Z_STREAM_ERROR;
   618     return this.blocks.sync_point();
   619   }
   620 
   621   private int readBytes(int n, int r, int f) throws Return{
   622     if(need_bytes == -1){
   623       need_bytes=n;
   624       this.need=0;
   625     }
   626     while(need_bytes>0){
   627       if(z.avail_in==0){ throw new Return(r); }; r=f;
   628       z.avail_in--; z.total_in++;
   629       this.need = this.need | 
   630 	((z.next_in[z.next_in_index++]&0xff)<<((n-need_bytes)*8));
   631       need_bytes--;
   632     }
   633     if(n==2){
   634       this.need&=0xffffL;
   635     }
   636     else if(n==4) {
   637       this.need&=0xffffffffL;
   638     }
   639     need_bytes=-1;
   640     return r;
   641   }
   642   class Return extends Exception{
   643     int r;
   644     Return(int r){this.r=r; }
   645   }
   646 
   647   private byte[] tmp_array;
   648   private int readString(int r, int f) throws Return{
   649     int b=0; 
   650     byte[] arr = new byte[4092];
   651     int at = 0;
   652     do {
   653       if(z.avail_in==0){ throw new Return(r); }; r=f;
   654       z.avail_in--; z.total_in++;
   655       b = z.next_in[z.next_in_index];
   656       if(b!=0) arr = append(arr, z.next_in[z.next_in_index], at++);
   657       z.adler.update(z.next_in, z.next_in_index, 1);
   658       z.next_in_index++;
   659     }while(b!=0);
   660     
   661     tmp_array = copy(arr, at);
   662     
   663     return r;
   664   }
   665 
   666   private int readBytes(int r, int f) throws Return{
   667     int b=0; 
   668     byte[] arr = new byte[4092];
   669     int at = 0;
   670     while(this.need>0){
   671       if(z.avail_in==0){ throw new Return(r); }; r=f;
   672       z.avail_in--; z.total_in++;
   673       b = z.next_in[z.next_in_index];
   674       arr = append(arr, z.next_in[z.next_in_index], at++);
   675       z.adler.update(z.next_in, z.next_in_index, 1);
   676       z.next_in_index++;
   677       this.need--;
   678     }
   679     
   680     tmp_array = copy(arr, at);
   681     
   682     return r;
   683   }
   684   
   685   private static byte[] copy(byte[] arr, int len) {
   686       byte[] ret = new byte[len];
   687       org.apidesign.bck2brwsr.emul.lang.System.arraycopy(arr, 0, ret, 0, len);
   688       return ret;
   689   }
   690   private static byte[] append(byte[] arr, byte b, int index) {
   691       arr[index] = b;
   692       return arr;
   693   }
   694 
   695   private void checksum(int n, long v){
   696     for(int i=0; i<n; i++){
   697       crcbuf[i]=(byte)(v&0xff);
   698       v>>=8;
   699     }
   700     z.adler.update(crcbuf, 0, n);
   701   }
   702 
   703   public GZIPHeader getGZIPHeader(){
   704     return gheader;
   705   }
   706 
   707   boolean inParsingHeader(){
   708     switch(mode){
   709       case HEAD:
   710       case DICT4:
   711       case DICT3:
   712       case DICT2:
   713       case DICT1:
   714       case FLAGS:
   715       case TIME:
   716       case OS:
   717       case EXLEN:
   718       case EXTRA:
   719       case NAME:
   720       case COMMENT:
   721       case HCRC:
   722 	return true;
   723       default:
   724 	return false;
   725     }
   726   }
   727 }