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 */
int_from_file(FILE * f,int l,uint64_t * r)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 
pgheader_detect(const char * infile)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 
pgheader_error(const char * prompt,int ret)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 
pgheader_create_raw(char ** raw_header,const char * header,unsigned int * size)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 
pgheader_parse(const char * header,char ** variants,char ** comment)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 
pgheader_create(char ** header,const char * variants,const char * comment)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 
pgheader_read(char ** header,const char * infile)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 
pgheader_write(const char * header,const char * infile,const char * outfile)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 
pgheader_delete(const char * infile,const char * outfile)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 }
545