/[cvs]/dary/db.cpp
ViewVC logotype

Contents of /dary/db.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.2 - (show annotations)
Sun Mar 13 21:57:14 2005 UTC (13 years, 7 months ago) by riso
Branch: MAIN
CVS Tags: HEAD
Changes since 1.1: +3 -2 lines
Stitky

1 #include "db.h"
2 #include "db_view.h"
3 #include "util.h"
4 #include "config.h"
5 #include <stdlib.h>
6 #include <stdio.h>
7
8 #include <wx/wx.h>
9 #include <wx/utils.h>
10
11 #define J_NEW_PERSON 1
12 #define J_UPD_PERSON 2
13 #define J_DEL_PERSON 3
14 #define J_NEW_ENTRY 4
15 #define J_UPD_ENTRY 5
16 #define J_DEL_ENTRY 6
17
18 tAllocator *persons, *entries;
19 static char *db_filename;
20 static FILE *journal;
21 static int err=0;
22
23 static void journal_add_op(int op) {
24 journal = fopen(db_filename,"ab");
25 fwrite(&op, sizeof(int), 1, journal);
26 }
27
28 static void journal_close() {
29 fclose(journal);
30 }
31
32 static void journal_add_string(char *s) {
33 int l;
34 l=strlen(s)+1;
35 fwrite(&l, sizeof(int), 1, journal);
36 fwrite(s, 1, l, journal);
37 }
38
39 static void journal_add_int(int i) {
40 fwrite(&i, sizeof(int), 1, journal);
41 }
42
43 static void journal_add_long(long i) {
44 fwrite(&i, sizeof(long), 1, journal);
45 }
46
47 static void journal_add_address(tAddress addr) {
48 journal_add_string(addr.title);
49 journal_add_string(addr.name);
50 journal_add_string(addr.street);
51 journal_add_string(addr.city);
52 journal_add_string(addr.zip);
53 }
54
55 static int journal_get_int() {
56 int i;
57 if (!fread(&i, sizeof(int), 1, journal)) err=-1;
58 return i;
59 }
60
61 static long journal_get_long() {
62 long i;
63 if (!fread(&i, sizeof(long), 1, journal)) err=-1;
64 return i;
65 }
66
67 static char *journal_get_string() {
68 int i;
69 char *s;
70 i=0;
71 if (!fread(&i, sizeof(int), 1, journal)) {
72 err=-1;
73 return NULL;
74 }
75 if (i==0) {
76 err=-1;
77 return NULL;
78 }
79 s=ALLOC(char, i);
80 // wxMessageBox(wxString::Format("XX1: %d",i),"journal_get_string", wxOK | wxCENTRE | wxICON_ERROR);
81 if (fread(s, 1, i, journal)<i) {
82 // wxMessageBox("YYY1","journal_get_string", wxOK | wxCENTRE | wxICON_ERROR);
83 err=-1;
84 free(s);
85 // wxMessageBox("YYY2","journal_get_string", wxOK | wxCENTRE | wxICON_ERROR);
86 return NULL;
87 }
88 // wxMessageBox("XX2","journal_get_string", wxOK | wxCENTRE | wxICON_ERROR);
89 return s;
90 }
91
92 static void journal_get_address(tAddress *out) {
93 // wxMessageBox("start","journal_get_address", wxOK | wxCENTRE | wxICON_ERROR);
94 out->title=out->name=out->street=out->city=out->zip = NULL;
95 if ( (out->title = journal_get_string()) == NULL) goto fail;
96 // wxMessageBox(out->title,"journal_get_address - title", wxOK | wxCENTRE | wxICON_ERROR);
97 if ( (out->name = journal_get_string()) == NULL) goto fail;
98 // wxMessageBox(out->name,"journal_get_address - name", wxOK | wxCENTRE | wxICON_ERROR);
99 if ( (out->street = journal_get_string()) == NULL) goto fail;
100 // wxMessageBox(out->street,"journal_get_address - street", wxOK | wxCENTRE | wxICON_ERROR);
101 if ( (out->city = journal_get_string()) == NULL) goto fail;
102 // wxMessageBox(out->city,"journal_get_address - city", wxOK | wxCENTRE | wxICON_ERROR);
103 if ( (out->zip = journal_get_string()) == NULL) goto fail;
104 // wxMessageBox(out->zip,"journal_get_address - zip", wxOK | wxCENTRE | wxICON_ERROR);
105 return;
106 fail:
107 FreeAddress(out);
108 err = -1;
109 }
110
111 static void replay_journal() {
112 int op,i,j, date;
113 long sum;
114 tPersonId p;
115 tEntryId e;
116 tPersonId *pa=(tPersonId *)(persons->data), id;
117 tEntryId *ea=(tEntryId *)(entries->data);
118 char *a, *b;
119 tAddress c;
120
121 err = 0;
122 while (!feof(journal)) {
123 op = journal_get_int();
124 if (err) goto end;
125 switch (op) {
126 case J_NEW_PERSON:
127 a = journal_get_string(); if (err) goto end;
128 b = journal_get_string(); if (err) { free(a); goto end; }
129 journal_get_address(&c); if (err) { free(a); free(b); goto end; }
130 p = ALLOC(tPerson, 1);
131 p->name = a;
132 p->surname = b;
133 memcpy(&(p->address), &c, sizeof(c));
134 IncAlloc(persons);
135 pa=(tPersonId *)(persons->data); ea=(tEntryId *)(entries->data);
136 pa[persons->n-1] = p;
137 p->priv.i = persons->n-1;
138 UpdateViewPerson(p, UPD_INSERT);
139 break;
140 case J_UPD_PERSON:
141 i = journal_get_int(); if (err) goto end;
142 if (i<0) goto end;
143 if (i>=persons->n) goto end;
144 a = journal_get_string(); if (err) goto end;
145 b = journal_get_string(); if (err) { free(a); goto end; }
146 journal_get_address(&c); if (err) { free(a); free(b); goto end; }
147
148 pa[i]->name = a;
149 pa[i]->surname = b;
150 memcpy(&(pa[i]->address), &c, sizeof(c));
151 UpdateViewPerson(pa[i], UPD_INSERT);
152 break;
153 case J_DEL_PERSON:
154 i = journal_get_int(); if (err) goto end;
155 if (i<0) goto end;
156 if (i>=persons->n) goto end;
157 id = pa[i];
158 pa[i]=pa[persons->n-1];
159 pa[i]->priv.i = i;
160 DecAlloc(persons);
161 pa=(tPersonId *)(persons->data); ea=(tEntryId *)(entries->data);
162 for (i=0; i<entries->n; i++) {
163 if (ea[i]->person == id) {
164 UpdateViewEntry(ea[i], UPD_DELETE);
165 free(ea[i]);
166 ea[i]=ea[entries->n-1];
167 ea[i]->priv.i = i;
168 DecAlloc(entries);
169 pa=(tPersonId *)(persons->data); ea=(tEntryId *)(entries->data);
170 i--;
171 }
172 }
173
174 UpdateViewPerson(id, UPD_DELETE);
175 free(id->name);
176 free(id->surname);
177 FreeAddress(&(id->address));
178 free(id);
179
180 break;
181 case J_NEW_ENTRY:
182 i = journal_get_int(); if (err) goto end;
183 if (i<0) goto end;
184 if (i>=persons->n) goto end;
185 date = journal_get_int(); if (err) goto end;
186 sum = journal_get_long(); if (err) goto end;
187
188 e = ALLOC(tEntry, 1);
189 e->person = pa[i];
190 e->date = date;
191 e->sum = sum;
192
193 IncAlloc(entries);
194 pa=(tPersonId *)(persons->data); ea=(tEntryId *)(entries->data);
195 ea[entries->n-1] = e;
196 e->priv.i = entries->n-1;
197 e->person->sum += sum;
198 UpdateViewEntry(e, UPD_INSERT);
199 UpdateViewPerson(e->person, UPD_UPDATE);
200
201 break;
202 case J_UPD_ENTRY:
203 i = journal_get_int(); if (err) goto end;
204 if (i<0) goto end;
205 if (i>=entries->n) goto end;
206 j = journal_get_int(); if (err) goto end;
207 if (j<0) goto end;
208 if (j>=persons->n) goto end;
209 date = journal_get_int(); if (err) goto end;
210 sum = journal_get_long(); if (err) goto end;
211 ea[i]->person->sum -= ea[i]->sum;
212 UpdateViewPerson(ea[i]->person, UPD_UPDATE);
213 ea[i]->person = pa[j];
214 ea[i]->date = date;
215 ea[i]->sum = sum;
216 ea[i]->person->sum += ea[i]->sum;
217 UpdateViewPerson(ea[i]->person, UPD_UPDATE);
218 UpdateViewEntry(ea[i], UPD_INSERT);
219 break;
220 case J_DEL_ENTRY:
221 i = journal_get_int(); if (err) goto end;
222 if (i<0) goto end;
223 if (i>=entries->n) goto end;
224 ea[i]->person->sum -= ea[i]->sum;
225 UpdateViewPerson(ea[i]->person, UPD_UPDATE);
226 UpdateViewEntry(ea[i], UPD_DELETE);
227 free(ea[i]);
228 ea[i]=ea[entries->n-1];
229 ea[i]->priv.i = i;
230 DecAlloc(entries);
231 pa=(tPersonId *)(persons->data); ea=(tEntryId *)(entries->data);
232 break;
233 default: // error in journal
234 goto end;
235 break;
236 }
237 }
238 end:
239 ResetView();
240 }
241
242 static void Save(char *filename) {
243 FILE *f;
244 int i;
245 tPersonId *pa = (tPersonId *)(persons->data);
246 tEntryId *ea = (tEntryId *)(entries->data);
247
248 journal = f = fopen(filename,"wb");
249 fwrite(&(persons->n), sizeof(int), 1, f);
250 fwrite(&(entries->n), sizeof(int), 1, f);
251 for (i=0; i<persons->n; i++) {
252 journal_add_string(pa[i]->name);
253 journal_add_string(pa[i]->surname);
254 journal_add_address(pa[i]->address);
255 }
256 for (i=0; i<entries->n; i++) {
257 fwrite(&(ea[i]->person->priv.i), sizeof(int), 1, f);
258 fwrite(&(ea[i]->date), sizeof(int), 1, f);
259 fwrite(&(ea[i]->sum), sizeof(long), 1, f);
260 }
261 fclose(f);
262 }
263
264 static int Init() {
265 FILE *f;
266 int i;
267
268 f = fopen(db_filename,"wb");
269 if (f==NULL) return 0;
270 InitView();
271 persons = ALLOC(tAllocator, 1);
272 entries = ALLOC(tAllocator, 1);
273 InitAlloc(persons,0);
274 InitAlloc(entries,0);
275
276 i=0;
277 fwrite(&i, sizeof(int), 1, f);
278 fwrite(&i, sizeof(int), 1, f);
279 fclose(f);
280 return 1;
281 }
282
283 static int Load(char *filename) {
284 FILE *f;
285 int i, j, date;
286 long sum;
287 char *name, *surname;
288 tAddress address;
289 tPersonId *pa;
290 tEntryId *ea;
291
292 // wxMessageBox(filename,"Load - start", wxOK | wxCENTRE | wxICON_ERROR);
293
294 f = fopen(filename,"rb");
295 if (f==NULL) return 0;
296 journal = f;
297
298 persons = ALLOC(tAllocator, 1);
299 entries = ALLOC(tAllocator, 1);
300 err = 0;
301 i = journal_get_int(); if (err) return 0;
302 // wxMessageBox("001","Load", wxOK | wxCENTRE | wxICON_ERROR);
303 InitAlloc(persons,i);
304 // wxMessageBox("002","Load", wxOK | wxCENTRE | wxICON_ERROR);
305 i = journal_get_int(); if (err) { FreeAlloc(persons); return 0; }
306 // wxMessageBox("003","Load", wxOK | wxCENTRE | wxICON_ERROR);
307 InitAlloc(entries,i);
308 // wxMessageBox("004","Load", wxOK | wxCENTRE | wxICON_ERROR);
309
310 InitView();
311 // wxMessageBox("005","Load", wxOK | wxCENTRE | wxICON_ERROR);
312
313 pa = (tPersonId *)(persons->data); ea = (tEntryId *)(entries->data);
314
315 for (i=0; i<persons->n; i++) pa[i]=NULL;
316 for (i=0; i<entries->n; i++) ea[i]=NULL;
317
318 // wxMessageBox("006","Load", wxOK | wxCENTRE | wxICON_ERROR);
319 for (i=0; i<persons->n; i++) {
320 // wxMessageBox("006x","Load", wxOK | wxCENTRE | wxICON_ERROR);
321 name = journal_get_string();
322 // wxMessageBox(name,"Load - 006x", wxOK | wxCENTRE | wxICON_ERROR);
323 if (err) goto fail;
324 surname = journal_get_string();
325 // wxMessageBox(surname,"Load - 006x", wxOK | wxCENTRE | wxICON_ERROR);
326 if (err) { free(name); goto fail; }
327 journal_get_address(&address);
328 if (err) { free(name); free(surname); goto fail; }
329 // wxMessageBox("address","Load - 006x", wxOK | wxCENTRE | wxICON_ERROR);
330
331 pa[i] = ALLOC(tPerson, 1);
332 pa[i]->priv.i = i;
333 pa[i]->sum = 0;
334 pa[i]->name = name;
335 pa[i]->surname = surname;
336 memcpy(&(pa[i]->address),&address, sizeof(address));
337
338 UpdateViewPerson(pa[i], UPD_INSERT);
339 }
340 // wxMessageBox("007","Load", wxOK | wxCENTRE | wxICON_ERROR);
341 for (i=0; i<entries->n; i++) {
342 // wxMessageBox("007x","Load", wxOK | wxCENTRE | wxICON_ERROR);
343
344 j = journal_get_int(); if (err) goto fail;
345 if (j<0) goto fail;
346 if (j>=persons->n) goto fail;
347 date = journal_get_int(); if (err) goto fail;
348 sum = journal_get_long(); if (err) goto fail;
349
350 ea[i] = ALLOC(tEntry, 1);
351 ea[i]->priv.i = i;
352 ea[i]->sum = sum;
353 ea[i]->date = date;
354 ea[i]->person = pa[j];
355 pa[j]->sum += sum;
356 UpdateViewEntry(ea[i], UPD_INSERT);
357 UpdateViewPerson(pa[j], UPD_UPDATE);
358 }
359
360 ResetView();
361
362 replay_journal();
363 fclose(f);
364 return 1;
365
366 fail:
367 for (i=0; i<persons->n; i++)
368 if (pa[i]!=NULL) {
369 free(pa[i]->name);
370 free(pa[i]->surname);
371 FreeAddress(&(pa[i]->address));
372 free(pa[i]);
373 }
374 for (i=0; i<entries->n; i++)
375 if (ea[i]!=NULL) {
376 free(ea[i]);
377 }
378 FreeAlloc(persons);
379 FreeAlloc(entries);
380
381 fclose(f);
382 return 0;
383 }
384
385 tPersonId GetPerson(const char *name, const char *surname) {
386 int i;
387 int n=persons->n;
388 tPersonId *a=(tPersonId *)(persons->data);
389
390 for (i=0; i<n; i++) {
391 if (mystrcmp(surname, a[i]->surname)==0)
392 if (mystrcmp(name, a[i]->name)==0)
393 return a[i];
394 }
395 return NULL;
396 }
397
398 static char* GuessTitle(const char *surname) {
399 static char mr[] = "Váž. pán";
400 static char ms[] = "Váž. pani";
401
402 int l;
403 l = strlen(surname);
404 if (l<3) return mr;
405 if (strcmp(surname+l-3, "ová")==0) return ms;
406 if (strcmp(surname+l-3, "ova")==0) return ms;
407 if (strcmp(surname+l-1, "á")==0) return ms;
408
409 return mr;
410 }
411
412 tPersonId NewPerson(const char *name, const char *surname) {
413 tPersonId p,*q;
414
415 p = ALLOC(tPerson, 1);
416 p->name = ALLOC(char, strlen(name)+1);
417 strcpy(p->name, name);
418 p->surname = ALLOC(char, strlen(surname)+1);
419 strcpy(p->surname, surname);
420
421 p->address.title = ALLOC(char, 20);
422 sprintf(p->address.title, GuessTitle(surname));
423
424 p->address.name = ALLOC(char, 20+strlen(name)+strlen(surname));
425 sprintf(p->address.name, "%s %s",name,surname);
426
427 p->address.street = ALLOC(char, 20);
428 strcpy(p->address.street, "");
429
430 p->address.city = ALLOC(char, 20);
431 strcpy(p->address.city, "");
432
433 p->address.zip = ALLOC(char, 20);
434 strcpy(p->address.zip, "");
435
436 IncAlloc(persons);
437 q = (tPersonId *)(persons->data + persons->n-1);
438 *q = p;
439 p->priv.i = persons->n-1;
440 p->sum = 0;
441 UpdateViewPerson(p, UPD_INSERT);
442 ResetView();
443
444 journal_add_op(J_NEW_PERSON);
445 journal_add_string(p->name);
446 journal_add_string(p->surname);
447 journal_add_address(p->address);
448 journal_close();
449
450 return p;
451 }
452
453 void UpdatePerson(tPersonId id, const char *name, const char *surname, const tAddress *address) {
454 if (name!=NULL) {
455 free(id->name);
456 }
457 id->name = ALLOC(char, strlen(name)+1);
458 strcpy(id->name, name);
459
460 if (surname!=NULL) {
461 free(id->surname);
462 }
463 id->surname = ALLOC(char, strlen(surname)+1);
464 strcpy(id->surname, surname);
465
466 if (address!=NULL) {
467 FreeAddress(&(id->address));
468 }
469 AddressCpy(&(id->address), address);
470
471 UpdateViewPerson(id, UPD_UPDATE);
472 ResetView();
473
474 journal_add_op(J_UPD_PERSON);
475 journal_add_int(id->priv.i);
476 journal_add_string(id->name);
477 journal_add_string(id->surname);
478 journal_add_address(id->address);
479 journal_close();
480 }
481
482 void DelPerson(tPersonId id) {
483 int i, pn=persons->n, en=entries->n;
484 tPersonId *pa=(tPersonId *)(persons->data);
485 tEntryId *ea=(tEntryId *)(entries->data);
486
487 journal_add_op(J_DEL_PERSON);
488 journal_add_int(id->priv.i);
489 journal_close();
490
491 for (i=0; i<pn; i++) {
492 if (pa[i] == id) {
493 pa[i]=pa[--pn];
494 pa[i]->priv.i = i;
495 DecAlloc(persons);
496 pa=(tPersonId *)(persons->data); ea=(tEntryId *)(entries->data);
497 i--;
498 }
499 }
500 for (i=0; i<en; i++) {
501 if (ea[i]->person == id) {
502 UpdateViewEntry(ea[i], UPD_DELETE);
503 free(ea[i]);
504 ea[i]=ea[--en];
505 ea[i]->priv.i = i;
506 DecAlloc(entries);
507 pa=(tPersonId *)(persons->data); ea=(tEntryId *)(entries->data);
508 i--;
509 }
510 }
511
512 UpdateViewPerson(id, UPD_DELETE);
513 free(id->name);
514 free(id->surname);
515 FreeAddress(&(id->address));
516 free(id);
517 ResetView();
518 }
519
520 tEntryId NewEntry(tPersonId person, int date, long sum) {
521 tEntryId e;
522
523 e = ALLOC(tEntry, 1);
524 e->person = person;
525 e->date = date;
526 e->sum = sum;
527
528 IncAlloc(entries);
529 entries->data[entries->n-1] = e;
530 e->priv.i = entries->n-1;
531 e->person->sum += e->sum;
532 UpdateViewPerson(e->person, UPD_UPDATE);
533 UpdateViewEntry(e, UPD_INSERT);
534 ResetView();
535
536 journal_add_op(J_NEW_ENTRY);
537 journal_add_int(e->person->priv.i);
538 journal_add_int(e->date);
539 journal_add_long(e->sum);
540 journal_close();
541
542
543 return e;
544 }
545
546 void UpdateEntry(tEntryId id, tPersonId person, int date, long sum) {
547 id->person->sum -= id->sum;
548 UpdateViewPerson(id->person, UPD_UPDATE);
549 id->person = person;
550 id->date = date;
551 id->sum = sum;
552 id->person->sum += id->sum;
553 UpdateViewPerson(id->person, UPD_UPDATE);
554 UpdateViewEntry(id, UPD_UPDATE);
555 ResetView();
556
557 journal_add_op(J_UPD_ENTRY);
558 journal_add_int(id->priv.i);
559 journal_add_int(id->person->priv.i);
560 journal_add_int(id->date);
561 journal_add_long(id->sum);
562 journal_close();
563 }
564
565 void DelEntry(tEntryId id) {
566 int i, n=entries->n;
567 tEntryId *a=(tEntryId *)(entries->data);
568
569 id->person->sum -= id->sum;
570 UpdateViewPerson(id->person, UPD_UPDATE);
571
572 journal_add_op(J_DEL_ENTRY);
573 journal_add_int(id->priv.i);
574 journal_close();
575
576 for (i=0; i<n; i++) {
577 if (a[i] == id) {
578 a[i]=a[--n];
579 a[i]->priv.i = i;
580 DecAlloc(entries);
581 a=(tEntryId *)(entries->data);
582 i--;
583 }
584 }
585 UpdateViewEntry(id, UPD_DELETE);
586 free(id);
587 ResetView();
588 }
589
590 int OpenDb(const char *dir) {
591 int i;
592 char *s;
593
594 // wxMessageBox(dir,"OPEN DB - start", wxOK | wxCENTRE | wxICON_ERROR);
595 i = strlen(dir);
596 db_filename = ALLOC(char, i+10);
597 if (dir[i-1]==DIR_SEP) sprintf(db_filename, "%sdb", dir);
598 else sprintf(db_filename, "%s%cdb", dir, DIR_SEP);
599
600 // wxMessageBox("001","OPEN DB", wxOK | wxCENTRE | wxICON_ERROR);
601 if (Load(db_filename)) return 1;
602 // wxMessageBox("002","OPEN DB", wxOK | wxCENTRE | wxICON_ERROR);
603
604 s = ALLOC(char, i+10);
605 strcpy(s, db_filename);
606 strcat(s, ".bak");
607
608 if (Load(s)) {
609 // wxMessageBox("003","OPEN DB", wxOK | wxCENTRE | wxICON_ERROR);
610
611 wxRemoveFile(db_filename);
612 wxCopyFile(s,db_filename);
613 free(s);
614 // wxMessageBox("004","OPEN DB", wxOK | wxCENTRE | wxICON_ERROR);
615 return 1;
616 }
617
618 // wxMessageBox("005","OPEN DB", wxOK | wxCENTRE | wxICON_ERROR);
619 wxRemoveFile(db_filename);
620 return Init();
621 }
622
623 void CloseDb() {
624 char *s;
625 int i;
626
627 i = strlen(db_filename);
628 s = ALLOC(char, i+10);
629 strcpy(s, db_filename);
630 strcat(s, ".bak");
631 wxRemoveFile(s);
632 wxCopyFile(db_filename,s);
633 wxRemoveFile(db_filename);
634 Save(db_filename);
635
636 DeleteView();
637 for (i=0; i<persons->n; i++) {
638 free(((tPersonId)(persons->data[i]))->name);
639 free(((tPersonId)(persons->data[i]))->surname);
640 FreeAddress(&(((tPersonId)(persons->data[i]))->address));
641 free(persons->data[i]);
642 }
643 FreeAlloc(persons);
644 for (i=0; i<entries->n; i++) {
645 free(entries->data[i]);
646 }
647 FreeAlloc(entries);
648 }
649
650 void FreeAddress(tAddress *addr) {
651 if (addr->title!=NULL) free(addr->title);
652 if (addr->name!=NULL) free(addr->name);
653 if (addr->street!=NULL) free(addr->street);
654 if (addr->city!=NULL) free(addr->city);
655 if (addr->zip!=NULL) free(addr->zip);
656 addr->title=NULL;
657 addr->name=NULL;
658 addr->street=NULL;
659 addr->city=NULL;
660 addr->zip=NULL;
661 }
662
663 void AddressCpy(tAddress *dst, const tAddress *src) {
664 dst->title = ALLOC(char, strlen(src->title)+1); strcpy(dst->title, src->title);
665 dst->name = ALLOC(char, strlen(src->name)+1); strcpy(dst->name, src->name);
666 dst->street = ALLOC(char, strlen(src->street)+1); strcpy(dst->street, src->street);
667 dst->city = ALLOC(char, strlen(src->city)+1); strcpy(dst->city, src->city);
668 dst->zip = ALLOC(char, strlen(src->zip)+1); strcpy(dst->zip, src->zip);
669 }
670

CVS Admin">CVS Admin
ViewVC Help
Powered by ViewVC 1.1.26