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