version 1.4.68b
[polyglot.git] / book_merge.c
1
2 // book_merge.c
3
4 // includes
5
6 #include <errno.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10
11 #include "book_merge.h"
12 #include "util.h"
13 #include "pgheader.h"
14
15 // macros
16
17 #define MAXVARIANTS 50
18
19 // types
20
21 typedef struct {
22    FILE * file;
23    int size;
24 } book_t;
25
26 typedef struct {
27    uint64 key;
28    uint16 move;
29    uint16 count;
30    uint16 n;
31    uint16 sum;
32 } entry_t;
33
34 // variables
35
36 static book_t In1[1];
37 static book_t In2[1];
38 static book_t Out[1];
39
40 static const char *default_header="@PG@\n1.0\n1\nnormal\n";
41
42 // prototypes
43
44 static void   book_clear    (book_t * book);
45
46 static void   book_open     (book_t * book, const char file_name[], const char mode[]);
47 static void   book_close    (book_t * book);
48
49 static bool   read_entry    (book_t * book, entry_t * entry, int n);
50 static void   write_entry   (book_t * book, const entry_t * entry);
51
52 static uint64 read_integer  (FILE * file, int size);
53 static void   write_integer (FILE * file, int size, uint64 n);
54
55 // functions
56
57 // variants_merge()
58
59 static void variants_merge(char ** variants, char *header1, char *header2){
60
61     char *token;
62     int ret,i,j;
63     int count;
64     char *header1_dup;
65     char *header2_dup;
66     char *header;
67     char *variant;
68
69
70     // Step 1: Initial malloc
71
72     *variants=malloc(strlen(header1)+strlen(header2)+1);
73     (*variants)[0]='\0';
74
75     // Step 2: Extract variant names.
76
77     header1_dup=strdup(header1);
78     header2_dup=strdup(header2);
79
80     for(i=0;i<2;i++){
81         header=(i==0)?header1_dup:header2_dup;
82         ret=0;
83         token=strtok(header,"\n");
84         if(token){  // MAGIC
85             token=strtok(NULL,"\n");
86             if(token){  // VERSION
87                 token=strtok(NULL,"\n");
88                 if(token){ // NBVARIANTS
89                     count=atoi(token);
90                     if(count>0){
91                         for(j=0;j<count;j++){
92                             variant=strtok(NULL,"\n");
93                             if(!strstr(*variants,variant)){
94                                 if((*variants)[0]!=0){
95                                     strcat(*variants,",");
96                                 }
97                                 strcat(*variants,variant);
98                             }
99                         }
100                     }else{
101                         ret=1;
102                     }
103                     
104                 }else{
105                     ret=2;
106                 }
107             }else{
108                 ret=3;
109             }
110         }else{
111             ret=4;
112         }
113         if(ret){
114             my_fatal("header_merge(): bad header %d. Error %d\n",i,ret);
115         }
116
117     }
118     free(header1_dup);
119     free(header2_dup);
120
121 }
122
123
124 // book_merge()
125
126 void book_merge(int argc, char * argv[]) {
127
128    int i;
129    const char * in_file_1;
130    const char * in_file_2;
131    const char * out_file;
132    char *header1;
133    char *header2;
134    char *header;
135    char *variants;
136    char *raw_header;
137    int size;
138    char ret;
139    int i1, i2;
140    bool b1, b2;
141    entry_t e1[1], e2[1];
142    int skip;
143
144    in_file_1 = NULL;
145    my_string_clear(&in_file_1);
146
147    in_file_2 = NULL;
148    my_string_clear(&in_file_2);
149
150    out_file = NULL;
151    my_string_set(&out_file,"out.bin");
152
153    for (i = 1; i < argc; i++) {
154
155       if (FALSE) {
156
157       } else if (my_string_equal(argv[i],"merge-book")) {
158
159          // skip
160
161       } else if (my_string_equal(argv[i],"-in1")) {
162
163          i++;
164          if (argv[i] == NULL) my_fatal("book_merge(): missing argument\n");
165
166          my_string_set(&in_file_1,argv[i]);
167
168       } else if (my_string_equal(argv[i],"-in2")) {
169
170          i++;
171          if (argv[i] == NULL) my_fatal("book_merge(): missing argument\n");
172
173          my_string_set(&in_file_2,argv[i]);
174
175       } else if (my_string_equal(argv[i],"-out")) {
176
177          i++;
178          if (argv[i] == NULL) my_fatal("book_merge(): missing argument\n");
179
180          my_string_set(&out_file,argv[i]);
181
182       } else {
183
184          my_fatal("book_merge(): unknown option \"%s\"\n",argv[i]);
185       }
186    }
187
188
189    ret=pgheader_read(&header1,in_file_1);
190    if(ret){
191        switch(ret){
192        case PGHEADER_NO_HEADER:
193            header1=malloc(strlen(default_header)+1);
194            strcpy(header1,default_header);
195            break;
196        case PGHEADER_OS_ERROR:
197            my_fatal("book_merge(): %s: %s\n",in_file_1,strerror(errno));
198        default:
199            my_fatal("book_merge(): Could not read header of %s\n",in_file_1);
200        }
201    }
202    ret=pgheader_read(&header2,in_file_2);
203    if(ret){
204        switch(ret){
205        case PGHEADER_NO_HEADER:
206            header2=malloc(strlen(default_header)+1);
207            strcpy(header2,default_header);
208            break;
209        case PGHEADER_OS_ERROR:
210            my_fatal("book_merge(): %s: %s\n",in_file_2,strerror(errno));
211        default:
212            my_fatal("book_merge(): Could not read header of %s\n",in_file_2);
213        }
214    }
215
216    variants_merge(&variants,header1,header2);
217    free(header1);
218    free(header2);
219
220    pgheader_create(&header,variants,"Created by Polyglot.");
221    free(variants);
222    pgheader_create_raw(&raw_header,header,&size);
223    free(header);
224
225    book_clear(In1);
226    book_clear(In2);
227    book_clear(Out);
228
229    book_open(In1,in_file_1,"rb");
230    book_open(In2,in_file_2,"rb");
231    book_open(Out,out_file,"wb");
232
233    // write header
234    
235    for(i=0;i<size;i++){
236        fputc(raw_header[i],Out->file);
237    }
238    free(raw_header);
239
240    skip = 0;
241
242    i1 = 0;
243    i2 = 0;
244
245    
246
247    while (TRUE) {
248
249       do{
250            b1 = read_entry(In1,e1,i1);
251       }while(b1 && e1->key==U64(0x0) && (++i1));
252        
253       do{
254           b2 = read_entry(In2,e2,i2);
255       }while(b2 && e2->key==U64(0x0) && (++i2));
256           
257       if (FALSE) {
258
259       } else if (!b1 && !b2) {
260
261          break;
262
263       } else if (b1 && !b2) {
264          write_entry(Out,e1);
265          i1++;
266
267       } else if (b2 && !b1) {
268
269          write_entry(Out,e2);
270          i2++;
271
272       } else {
273
274          ASSERT(b1);
275          ASSERT(b2);
276
277          if (FALSE) {
278          } else if (e1->key < e2->key) {
279             write_entry(Out,e1);
280             i1++;
281          } else if (e1->key > e2->key) {
282             write_entry(Out,e2);
283             i2++;
284          } else {
285             ASSERT(e1->key==e2->key);
286             skip++;
287             i2++;
288          }
289       }
290    }
291
292    book_close(In1);
293    book_close(In2);
294    book_close(Out);
295
296    if (skip != 0) {
297       printf("skipped %d entr%s.\n",skip,(skip>1)?"ies":"y");
298    }
299
300    printf("done!\n");
301 }
302
303 // book_clear()
304
305 static void book_clear(book_t * book) {
306
307    ASSERT(book!=NULL);
308
309    book->file = NULL;
310    book->size = 0;
311 }
312
313 // book_open()
314
315 static void book_open(book_t * book, const char file_name[], const char mode[]) {
316
317    ASSERT(book!=NULL);
318    ASSERT(file_name!=NULL);
319    ASSERT(mode!=NULL);
320
321    book->file = fopen(file_name,mode);
322    if (book->file == NULL) my_fatal("book_open(): can't open file \"%s\": %s\n",file_name,strerror(errno));
323
324    if (fseek(book->file,0,SEEK_END) == -1) {
325       my_fatal("book_open(): fseek(): %s\n",strerror(errno));
326    }
327
328    book->size = ftell(book->file) / 16;
329 }
330
331 // book_close()
332
333 static void book_close(book_t * book) {
334
335    ASSERT(book!=NULL);
336
337    if (fclose(book->file) == EOF) {
338       my_fatal("book_close(): fclose(): %s\n",strerror(errno));
339    }
340 }
341
342 // read_entry()
343
344 static bool read_entry(book_t * book, entry_t * entry, int n) {
345
346    ASSERT(book!=NULL);
347    ASSERT(entry!=NULL);
348
349    if (n < 0 || n >= book->size) return FALSE;
350
351    ASSERT(n>=0&&n<book->size);
352
353    if (fseek(book->file,n*16,SEEK_SET) == -1) {
354       my_fatal("read_entry(): fseek(): %s\n",strerror(errno));
355    }
356
357    entry->key   = read_integer(book->file,8);
358    entry->move  = read_integer(book->file,2);
359    entry->count = read_integer(book->file,2);
360    entry->n     = read_integer(book->file,2);
361    entry->sum   = read_integer(book->file,2);
362
363    return TRUE;
364 }
365
366 // write_entry()
367
368 static void write_entry(book_t * book, const entry_t * entry) {
369
370    ASSERT(book!=NULL);
371    ASSERT(entry!=NULL);
372
373    write_integer(book->file,8,entry->key);
374    write_integer(book->file,2,entry->move);
375    write_integer(book->file,2,entry->count);
376    write_integer(book->file,2,entry->n);
377    write_integer(book->file,2,entry->sum);
378 }
379
380
381
382 // read_integer()
383
384 static uint64 read_integer(FILE * file, int size) {
385
386    uint64 n;
387    int i;
388    int b;
389
390    ASSERT(file!=NULL);
391    ASSERT(size>0&&size<=8);
392
393    n = 0;
394
395    for (i = 0; i < size; i++) {
396
397       b = fgetc(file);
398
399       if (b == EOF) {
400          if (feof(file)) {
401             my_fatal("read_integer(): fgetc(): EOF reached\n");
402          } else { // error
403             my_fatal("read_integer(): fgetc(): %s\n",strerror(errno));
404          }
405       }
406
407       ASSERT(b>=0&&b<256);
408       n = (n << 8) | b;
409    }
410
411    return n;
412 }
413
414 // write_integer()
415
416 static void write_integer(FILE * file, int size, uint64 n) {
417
418    int i;
419    int b;
420
421    ASSERT(file!=NULL);
422    ASSERT(size>0&&size<=8);
423    ASSERT(size==8||n>>(size*8)==0);
424
425    for (i = size-1; i >= 0; i--) {
426
427       b = (n >> (i*8)) & 0xFF;
428       ASSERT(b>=0&&b<256);
429
430       if (fputc(b,file) == EOF) {
431          my_fatal("write_integer(): fputc(): %s\n",strerror(errno));
432       }
433    }
434 }
435
436 // end of book_merge.cpp
437