Add forgotten files 1.4.70b
[polyglot.git] / pgheader.c
1 /*
2 Copyright (c) 2012, Michel Van den Bergh <michel.vandenbergh@uhasselt.be>
3
4 Permission is hereby granted, free of charge, to any person obtaining
5 a copy of this software and associated documentation files (the
6 "Software"), to deal in the Software without restriction, including
7 without limitation the rights to use, copy, modify, merge, publish,
8 distribute, sublicense, and/or sell copies of the Software, and to
9 permit persons to whom the Software is furnished to do so, subject to
10 the following conditions:
11
12 The above copyright notice and this permission notice shall be
13 included in all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 #include "pgheader.h"
25 #include <stdlib.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <stdio.h>
32 #include <ctype.h>
33
34 const char * pgheader_version="1.0";
35 const char * pgheader_magic="@PG@";
36
37 static char * errmsg[]={
38     "No error.",
39     "OS error.",
40     "Badly formatted input file.",
41     "No header.",
42     "Input and output file are the same.",
43     "Bad parameter.",
44     "Bad header."
45 };
46
47 #ifndef WIN32
48 #define O_BINARY 0x0
49 #endif
50
51 #ifdef _MSC_VER
52   typedef unsigned __int64 uint64_t;
53 #else
54   typedef unsigned long long int uint64_t;
55 #endif
56
57 static int int_from_file(FILE *f, int l, uint64_t *r){
58     int i,c;
59     for(i=0;i<l;i++){
60         c=fgetc(f);
61         if(c==EOF){
62             return 1;
63         }
64         (*r)=((*r)<<8)+c;
65     }
66     return 0;
67 }
68
69
70 int pgheader_detect(const char *infile){
71     FILE *fin;
72     uint64_t r0,r1,r2;
73     int i;
74
75     fin=fopen(infile,"rb");
76     if(!fin){
77         return PGHEADER_OS_ERROR;
78     }
79     fseek(fin,0L,SEEK_END);
80     if(ftell(fin)%16 !=0){
81         fclose(fin);
82         return PGHEADER_BAD_FORMAT;
83     }
84     fseek(fin,0L,SEEK_SET);
85     r0=0;
86     r1=0;
87     r2=0;
88     for(i=0;i<10;i++){
89         if(int_from_file(fin,8,&r1)){
90             break;
91         }
92         if(int_from_file(fin,8,&r2)){
93             fclose(fin);
94             return PGHEADER_BAD_FORMAT;
95         }
96         if(r1<r0){
97             fclose(fin);
98             return PGHEADER_BAD_FORMAT;
99         }
100         r0=r1;
101     }
102     fclose(fin);
103     return PGHEADER_NO_ERROR;
104 }
105
106 void pgheader_error(const char *prompt, int ret){
107     switch(ret){
108     case PGHEADER_NO_ERROR:
109         break;
110     case PGHEADER_OS_ERROR:
111         perror(prompt);
112         break;
113     default:
114         fprintf(stderr,"%s: %s\n",prompt,errmsg[ret]);
115         break;
116         
117     }
118 }
119
120 int pgheader_create_raw(char **raw_header, const char *header, unsigned int *size){
121     unsigned int b,i,j,k;
122
123     b=strlen(header)+1;
124     *size=2*(8*(b/8)+(b%8?8:0));
125     *raw_header=malloc(*size);
126     if(!(*raw_header)){
127         return PGHEADER_OS_ERROR;
128     }
129     j=0;
130     for(i=0;i<b;i++){
131         if(i%8==0){
132             for(k=0;k<8;k++){
133                 (*raw_header)[j++]=0;
134             }
135         }
136         (*raw_header)[j++]=header[i];
137     }
138     for(k=j;k<(*size);k++){
139         (*raw_header)[k]=0;
140     }
141     return PGHEADER_NO_ERROR;
142 }
143
144 int pgheader_parse(const char *header, char **variants, char **comment){
145     int ret,count,j;
146     char *header_dup;
147     char *token;
148     char *variant;
149     int nbpredef;
150     int cf;
151     header_dup=strdup(header);
152     *variants=malloc(strlen(header)+1);
153     *comment=malloc(strlen(header)+1);
154     ret=0;
155     (*variants)[0]='\0';
156     (*comment)[0]='\0';
157     token=strtok(header_dup,"\x0a");
158     cf=0;
159     if(token){  /* MAGIC */
160         token=strtok(NULL,"\x0a");
161         if(token){  /* VERSION */
162             token=strtok(NULL,"\x0a");
163             if(token){ /* PREDEF */
164                 nbpredef=atoi(token);
165                 /* parse variant fields */
166                 token=strtok(NULL,"\x0a");
167                 if(token){ /* NBVARIANTS */
168                     count=atoi(token);
169                     cf++;
170                     /* we allow a zero number of variants */
171                     if(count>=0){
172                         for(j=0;j<count;j++){
173                             variant=strtok(NULL,"\x0a");
174                             cf++;
175                             if((*variants)[0]!=0){
176                                 strcat(*variants,"\x0a");
177                             }
178                             strcat(*variants,variant);
179                         }
180                     }else{
181                         ret=1;
182                     }
183                     
184                 }else{
185                     ret=1;
186                 }
187             }else{
188                 ret=1;
189             }
190         }else{
191             ret=1;
192         }
193     }else{
194         ret=1;
195     }
196     /* skip unknown fields */
197     if(ret==0 && cf<=nbpredef){
198         while((cf++)<nbpredef){
199             token=strtok(NULL,"\x0a");
200             if(!token){
201                 ret=1;
202             }
203         }
204     }
205     /* parse comment fields */
206     if(ret==0){
207         token=strtok(NULL,"\x0a");
208         while(token){
209             if((*comment)[0]!=0){
210                 strcat(*comment,"\x0a");
211             }
212             strcat(*comment,token);
213             token=strtok(NULL,"\x0a");
214         }
215     }else{
216         free(*variants);
217         free(*comment);
218         free(header_dup);
219         *variants=NULL;
220         *comment=NULL;
221         return PGHEADER_BAD_HEADER;
222     }
223
224     free(header_dup);
225     return PGHEADER_NO_ERROR;
226
227 }
228
229 int pgheader_create(char **header, const char *variants, const char *comment){
230     int i;
231     unsigned int nbvariants;
232     unsigned int nbheader;
233     char c;
234
235
236     /* Step 0: Validate variants: only lowercase, no spaces */
237
238     for(i=0;i<strlen(variants);i++){
239         c=variants[i];
240         if(c==' ' || isupper(c)){
241             return PGHEADER_BAD_PARAMETER;
242         }
243     }
244
245     /* Step 1: the number of variants is one more than the number of linefeeds */
246
247     nbvariants=1;
248     for(i=0;i<strlen(variants);i++){
249         if(variants[i]==0x0a){
250             nbvariants++;
251         }
252         /* Quick hack: at most 998 variants */
253         if(nbvariants>998){
254             return PGHEADER_BAD_PARAMETER;
255         }
256     }
257
258
259     /* Step 2: estimate length */
260
261     nbheader=
262         strlen(pgheader_magic)+1
263         +strlen(pgheader_version)+1
264         +3/*nbpredef*/+1
265         +3/*nbvariants*/+1
266         +strlen(variants)+1
267         +strlen(comment)+1;
268
269     /* Step 3: allocate memory */
270
271     *header=malloc(nbheader);
272     if(!(*header)){
273         return PGHEADER_OS_ERROR;
274     }
275
276     /* Step 4: fill header */
277
278     strcpy(*header,pgheader_magic);
279     strcat(*header,"\x0a");
280     strcat(*header,pgheader_version); 
281     strcat(*header,"\x0a");
282     sprintf(*header+strlen(*header),"%d",nbvariants+1); /* predef */
283     strcat(*header,"\x0a");
284     sprintf(*header+strlen(*header),"%d",nbvariants);
285     strcat(*header,"\x0a");
286     strcat(*header,variants);
287     strcat(*header,"\x0a");
288     strcat(*header,comment);
289
290     return PGHEADER_NO_ERROR;
291
292 }
293
294
295 int pgheader_read(char **header, const char *infile){
296     int fin;
297     char buf[16];
298     unsigned int nbheader;
299     unsigned int read_bytes;
300
301     /* initial malloc */
302
303     nbheader=2048;
304     *header=malloc(nbheader);
305     if(!(*header)){
306         return PGHEADER_OS_ERROR;
307     }
308     read_bytes=0;
309
310     /* open input file */
311
312     fin=open(infile,O_RDONLY|O_BINARY);
313     if(fin==-1){
314         *header=NULL;
315         return PGHEADER_OS_ERROR;
316     }
317
318
319     /* read bytes in input file */
320
321     while(1){
322         int j,r;
323         int zero;
324         int last;
325         /* enlarge buffer if necessary */
326         if(read_bytes>=nbheader){
327             nbheader*=2;
328             *header=realloc(*header,nbheader);
329         }
330         
331         r=read(fin,buf,16);
332         if(r<16){
333             free(*header);
334             *header=NULL;
335             close(fin);
336             return PGHEADER_BAD_FORMAT;
337         }
338         zero=1;
339         for(j=0;j<8;j++){
340             if(buf[j]!=0){
341                 zero=0;
342             }
343         }
344         if(!zero){
345             /* if we encounter a non null record here it means 
346                we have not bailed out earlier */
347             free(*header);
348             *header=NULL;
349             close(fin);
350             return PGHEADER_NO_HEADER;
351         }
352         last=0;
353         for(j=8;j<16;j++){
354             (*header)[read_bytes+j-8]=buf[j];
355             if(buf[j]==0){
356                 last=1;
357             }
358
359         }
360         if(last){
361             break;
362         }
363         read_bytes+=8;
364     }
365     close(fin);
366     return PGHEADER_NO_ERROR;
367 }
368
369
370 int pgheader_write(const char *header, const char *infile, const char *outfile){
371     int fin,fout,i,ret;
372     char c;
373     char buf[16];
374     char *raw_header;
375     unsigned int size;
376
377     /* make sure we are dealing with a polyglot book */
378
379     if((ret=pgheader_detect(infile))){
380         return ret;
381     }
382
383
384
385     /* safety check! */
386     if(!strcmp(infile,outfile)){
387         return PGHEADER_NAME_COLLISION;
388     }
389
390     /* open files */
391     fin=open(infile,O_RDONLY|O_BINARY);
392     if(fin==-1){
393         return PGHEADER_OS_ERROR;
394     }
395     fout=open(outfile,O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,S_IRUSR|S_IWUSR);
396     if(fout==-1){
397         close(fin);
398         return PGHEADER_OS_ERROR;
399     }
400
401     /* skip null records in input file */
402
403     while(1){
404         int j,r;
405         int zero;
406         r=read(fin,buf,16);
407         if(r<16){
408             close(fin);
409             close(fout);
410             return PGHEADER_BAD_FORMAT;
411         }
412         zero=1;
413         for(j=0;j<8;j++){
414             if(buf[j]!=0){
415                 zero=0;
416             }
417         }
418         if(!zero){
419             break;
420         }
421     }
422
423     /* write header to output file */
424
425     if((ret=pgheader_create_raw(&raw_header,header,&size))){
426         return ret;
427     }
428
429     for(i=0;i<size;i++){
430         c=raw_header[i];
431         if(write(fout,&c,1)!=1){
432             close(fin);
433             close(fout);
434             return PGHEADER_OS_ERROR;
435         }
436     }
437
438     free(raw_header);
439
440     /* copy remaining records from input to output */
441
442     if(write(fout,buf,16)!=16){
443         close(fin);
444         close(fout);
445         return PGHEADER_OS_ERROR;
446     }
447         while((ret=read(fin,buf,16))==16){
448         if(write(fout,buf,ret)!=16){
449             close(fin);
450             close(fout);
451             return PGHEADER_OS_ERROR;
452         }
453     };
454     close(fin);
455     close(fout);
456
457     if(0<ret && ret<16){
458         return PGHEADER_BAD_FORMAT;
459     }else if(ret==-1){
460         return PGHEADER_OS_ERROR;
461     } 
462
463     return PGHEADER_NO_ERROR;
464 }
465
466
467 int pgheader_delete(const char *infile, const char *outfile){
468     int fin, fout;
469     char buf[16];
470     int ret;
471
472
473     /* make sure we are dealing with a polyglot book */
474
475     if((ret=pgheader_detect(infile))){
476         return ret;
477     }
478
479
480     /* safety check! */
481     if(!strcmp(infile,outfile)){
482         return PGHEADER_NAME_COLLISION;
483     }
484
485
486     /* open files */
487     fin=open(infile,O_RDONLY|O_BINARY);
488     if(fin==-1){
489         return PGHEADER_OS_ERROR;
490     }
491     fout=open(outfile,O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,S_IRUSR|S_IWUSR);
492     if(fout==-1){
493         close(fin);
494         return PGHEADER_OS_ERROR;
495     }
496
497     /* skip null records in input file */
498
499     while(1){
500         int j,r;
501         int zero;
502         r=read(fin,buf,16);
503         if(r<16){
504             close(fin);
505             close(fout);
506             return PGHEADER_BAD_FORMAT;
507         }
508         zero=1;
509         for(j=0;j<8;j++){
510             if(buf[j]!=0){
511                 zero=0;
512             }
513         }
514         if(!zero){
515             break;
516         }
517     }
518
519     /* copy remaining records from input to output */
520
521     if(write(fout,buf,16)!=16){
522         close(fin);
523         close(fout);
524         return PGHEADER_OS_ERROR;
525     }
526     
527     while((ret=read(fin,buf,16))==16){
528         if(write(fout,buf,ret)!=16){
529             close(fin);
530             close(fout);
531             return PGHEADER_OS_ERROR;
532         }
533     };
534     close(fin);
535     close(fout);
536
537     if(0<ret && ret<16){
538         return PGHEADER_BAD_FORMAT;
539     }else if(ret==-1){
540         return PGHEADER_OS_ERROR;
541     } 
542
543     return PGHEADER_NO_ERROR;
544 }