2 Copyright (c) 2012, Michel Van den Bergh <michel.vandenbergh@uhasselt.be>
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:
12 The above copyright notice and this permission notice shall be
13 included in all copies or substantial portions of the Software.
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.
26 #include <sys/types.h>
34 const char * pgheader_version="1.0";
35 const char * pgheader_magic="@PG@";
37 static char * errmsg[]={
40 "Badly formatted input file.",
42 "Input and output file are the same.",
52 typedef unsigned __int64 uint64_t;
54 typedef unsigned long long int uint64_t;
57 static int int_from_file(FILE *f, int l, uint64_t *r){
70 int pgheader_detect(const char *infile){
75 fin=fopen(infile,"rb");
77 return PGHEADER_OS_ERROR;
79 fseek(fin,0L,SEEK_END);
80 if(ftell(fin)%16 !=0){
82 return PGHEADER_BAD_FORMAT;
84 fseek(fin,0L,SEEK_SET);
89 if(int_from_file(fin,8,&r1)){
92 if(int_from_file(fin,8,&r2)){
94 return PGHEADER_BAD_FORMAT;
98 return PGHEADER_BAD_FORMAT;
103 return PGHEADER_NO_ERROR;
106 void pgheader_error(const char *prompt, int ret){
108 case PGHEADER_NO_ERROR:
110 case PGHEADER_OS_ERROR:
114 fprintf(stderr,"%s: %s\n",prompt,errmsg[ret]);
120 int pgheader_create_raw(char **raw_header, const char *header, unsigned int *size){
121 unsigned int b,i,j,k;
124 *size=2*(8*(b/8)+(b%8?8:0));
125 *raw_header=malloc(*size);
127 return PGHEADER_OS_ERROR;
133 (*raw_header)[j++]=0;
136 (*raw_header)[j++]=header[i];
138 for(k=j;k<(*size);k++){
141 return PGHEADER_NO_ERROR;
144 int pgheader_parse(const char *header, char **variants, char **comment){
151 header_dup=strdup(header);
152 *variants=malloc(strlen(header)+1);
153 *comment=malloc(strlen(header)+1);
157 token=strtok(header_dup,"\x0a");
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 */
170 /* we allow a zero number of variants */
172 for(j=0;j<count;j++){
173 variant=strtok(NULL,"\x0a");
175 if((*variants)[0]!=0){
176 strcat(*variants,"\x0a");
178 strcat(*variants,variant);
196 /* skip unknown fields */
197 if(ret==0 && cf<=nbpredef){
198 while((cf++)<nbpredef){
199 token=strtok(NULL,"\x0a");
205 /* parse comment fields */
207 token=strtok(NULL,"\x0a");
209 if((*comment)[0]!=0){
210 strcat(*comment,"\x0a");
212 strcat(*comment,token);
213 token=strtok(NULL,"\x0a");
221 return PGHEADER_BAD_HEADER;
225 return PGHEADER_NO_ERROR;
229 int pgheader_create(char **header, const char *variants, const char *comment){
231 unsigned int nbvariants;
232 unsigned int nbheader;
236 /* Step 0: Validate variants: only lowercase, no spaces */
238 for(i=0;i<strlen(variants);i++){
240 if(c==' ' || isupper(c)){
241 return PGHEADER_BAD_PARAMETER;
245 /* Step 1: the number of variants is one more than the number of linefeeds */
248 for(i=0;i<strlen(variants);i++){
249 if(variants[i]==0x0a){
252 /* Quick hack: at most 998 variants */
254 return PGHEADER_BAD_PARAMETER;
259 /* Step 2: estimate length */
262 strlen(pgheader_magic)+1
263 +strlen(pgheader_version)+1
269 /* Step 3: allocate memory */
271 *header=malloc(nbheader);
273 return PGHEADER_OS_ERROR;
276 /* Step 4: fill header */
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);
290 return PGHEADER_NO_ERROR;
295 int pgheader_read(char **header, const char *infile){
298 unsigned int nbheader;
299 unsigned int read_bytes;
304 *header=malloc(nbheader);
306 return PGHEADER_OS_ERROR;
310 /* open input file */
312 fin=open(infile,O_RDONLY|O_BINARY);
315 return PGHEADER_OS_ERROR;
319 /* read bytes in input file */
325 /* enlarge buffer if necessary */
326 if(read_bytes>=nbheader){
328 *header=realloc(*header,nbheader);
336 return PGHEADER_BAD_FORMAT;
345 /* if we encounter a non null record here it means
346 we have not bailed out earlier */
350 return PGHEADER_NO_HEADER;
354 (*header)[read_bytes+j-8]=buf[j];
366 return PGHEADER_NO_ERROR;
370 int pgheader_write(const char *header, const char *infile, const char *outfile){
377 /* make sure we are dealing with a polyglot book */
379 if((ret=pgheader_detect(infile))){
386 if(!strcmp(infile,outfile)){
387 return PGHEADER_NAME_COLLISION;
391 fin=open(infile,O_RDONLY|O_BINARY);
393 return PGHEADER_OS_ERROR;
395 fout=open(outfile,O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,S_IRUSR|S_IWUSR);
398 return PGHEADER_OS_ERROR;
401 /* skip null records in input file */
410 return PGHEADER_BAD_FORMAT;
423 /* write header to output file */
425 if((ret=pgheader_create_raw(&raw_header,header,&size))){
431 if(write(fout,&c,1)!=1){
434 return PGHEADER_OS_ERROR;
440 /* copy remaining records from input to output */
442 if(write(fout,buf,16)!=16){
445 return PGHEADER_OS_ERROR;
447 while((ret=read(fin,buf,16))==16){
448 if(write(fout,buf,ret)!=16){
451 return PGHEADER_OS_ERROR;
458 return PGHEADER_BAD_FORMAT;
460 return PGHEADER_OS_ERROR;
463 return PGHEADER_NO_ERROR;
467 int pgheader_delete(const char *infile, const char *outfile){
473 /* make sure we are dealing with a polyglot book */
475 if((ret=pgheader_detect(infile))){
481 if(!strcmp(infile,outfile)){
482 return PGHEADER_NAME_COLLISION;
487 fin=open(infile,O_RDONLY|O_BINARY);
489 return PGHEADER_OS_ERROR;
491 fout=open(outfile,O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,S_IRUSR|S_IWUSR);
494 return PGHEADER_OS_ERROR;
497 /* skip null records in input file */
506 return PGHEADER_BAD_FORMAT;
519 /* copy remaining records from input to output */
521 if(write(fout,buf,16)!=16){
524 return PGHEADER_OS_ERROR;
527 while((ret=read(fin,buf,16))==16){
528 if(write(fout,buf,ret)!=16){
531 return PGHEADER_OS_ERROR;
538 return PGHEADER_BAD_FORMAT;
540 return PGHEADER_OS_ERROR;
543 return PGHEADER_NO_ERROR;