1 /*********** File AM Txt C++ Program Source Code File (.CPP) ***********/
2 /* PROGRAM NAME: FILAMTXT */
3 /* ------------- */
4 /* Version 1.8 */
5 /* */
6 /* COPYRIGHT: */
7 /* ---------- */
8 /* (C) Copyright to the author Olivier BERTRAND 2005-2020 */
9 /* */
10 /* WHAT THIS PROGRAM DOES: */
11 /* ----------------------- */
12 /* This program are the Text file access method classes. */
13 /* */
14 /***********************************************************************/
15
16 /***********************************************************************/
17 /* Include relevant sections of the System header files. */
18 /***********************************************************************/
19 #include "my_global.h"
20 #if defined(_WIN32)
21 #include <io.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 #if defined(__BORLANDC__)
25 #define __MFC_COMPAT__ // To define min/max as macro
26 #endif // __BORLANDC__
27 //#include <windows.h>
28 #else // !_WIN32
29 #if defined(UNIX) || defined(UNIV_LINUX)
30 #include <errno.h>
31 #include <unistd.h>
32 //#if !defined(sun) // Sun has the ftruncate fnc.
33 //#define USETEMP // Force copy mode for DELETE
34 //#endif // !sun
35 #else // !UNIX
36 #include <io.h>
37 #endif // !UNIX
38 #include <fcntl.h>
39 #endif // !_WIN32
40
41 /***********************************************************************/
42 /* Include application header files: */
43 /* global.h is header containing all global declarations. */
44 /* plgdbsem.h is header containing the DB application declarations. */
45 /* filamtxt.h is header containing the file AM classes declarations. */
46 /***********************************************************************/
47 #include "global.h"
48 #include "plgdbsem.h"
49 #include "filamtxt.h"
50 #include "tabdos.h"
51 #include "tabjson.h"
52
53 #if defined(UNIX) || defined(UNIV_LINUX)
54 #include "osutil.h"
55 #define _fileno fileno
56 #define _O_RDONLY O_RDONLY
57 #endif
58
59 extern int num_read, num_there, num_eq[2]; // Statistics
60
61 /***********************************************************************/
62 /* Routine called externally by TXTFAM SortedRows functions. */
63 /***********************************************************************/
64 PARRAY MakeValueArray(PGLOBAL g, PPARM pp);
65
66 /* --------------------------- Class TXTFAM -------------------------- */
67
68 /***********************************************************************/
69 /* Constructors. */
70 /***********************************************************************/
TXTFAM(PDOSDEF tdp)71 TXTFAM::TXTFAM(PDOSDEF tdp)
72 {
73 Tdbp = NULL;
74 To_Fb = NULL;
75
76 if (tdp) {
77 To_File = tdp->Fn;
78 Lrecl = tdp->Lrecl;
79 Eof = tdp->Eof;
80 Ending = tdp->Ending;
81 } else {
82 To_File = NULL;
83 Lrecl = 0;
84 Eof = false;
85 #if defined(_WIN32)
86 Ending = 2;
87 #else
88 Ending = 1;
89 #endif
90 } // endif tdp
91
92 Placed = false;
93 IsRead = true;
94 Blocked = false;
95 To_Buf = NULL;
96 DelBuf = NULL;
97 BlkPos = NULL;
98 To_Pos = NULL;
99 To_Sos = NULL;
100 To_Upd = NULL;
101 Posar = NULL;
102 Sosar = NULL;
103 Updar = NULL;
104 BlkLen = 0;
105 Buflen = 0;
106 Dbflen = 0;
107 Rows = 0;
108 DelRows = 0;
109 Headlen = 0;
110 Block = 0;
111 Last = 0;
112 Nrec = 1;
113 OldBlk = -1;
114 CurBlk = -1;
115 ReadBlks = 0;
116 CurNum = 0;
117 Rbuf = 0;
118 Modif = 0;
119 Blksize = 0;
120 Fpos = Spos = Tpos = 0;
121 Padded = false;
122 Abort = false;
123 CrLf = (char*)(Ending == 1 ? "\n" : "\r\n");
124 } // end of TXTFAM standard constructor
125
TXTFAM(PTXF txfp)126 TXTFAM::TXTFAM(PTXF txfp)
127 {
128 Tdbp = txfp->Tdbp;
129 To_Fb = txfp->To_Fb;
130 To_File = txfp->To_File;
131 Lrecl = txfp->Lrecl;
132 Placed = txfp->Placed;
133 IsRead = txfp->IsRead;
134 Blocked = txfp->Blocked;
135 To_Buf = txfp->To_Buf;
136 DelBuf = txfp->DelBuf;
137 BlkPos = txfp->BlkPos;
138 To_Pos = txfp->To_Pos;
139 To_Sos = txfp->To_Sos;
140 To_Upd = txfp->To_Upd;
141 Posar = txfp->Posar;
142 Sosar = txfp->Sosar;
143 Updar = txfp->Updar;
144 BlkLen = txfp->BlkLen;
145 Buflen = txfp->Buflen;
146 Dbflen = txfp->Dbflen;
147 Rows = txfp->Rows;
148 DelRows = txfp->DelRows;
149 Headlen = txfp->Headlen;
150 Block = txfp->Block;
151 Last = txfp->Last;
152 Nrec = txfp->Nrec;
153 OldBlk = txfp->OldBlk;
154 CurBlk = txfp->CurBlk;
155 ReadBlks = txfp->ReadBlks;
156 CurNum = txfp->CurNum;
157 Rbuf = txfp->Rbuf;
158 Modif = txfp->Modif;
159 Blksize = txfp->Blksize;
160 Fpos = txfp->Fpos;
161 Spos = txfp->Spos;
162 Tpos = txfp->Tpos;
163 Padded = txfp->Padded;
164 Eof = txfp->Eof;
165 Ending = txfp->Ending;
166 Abort = txfp->Abort;
167 CrLf = txfp->CrLf;
168 } // end of TXTFAM copy constructor
169
170 /***********************************************************************/
171 /* Reset: reset position values at the beginning of file. */
172 /***********************************************************************/
Reset(void)173 void TXTFAM::Reset(void)
174 {
175 Rows = 0;
176 DelRows = 0;
177 OldBlk = -1;
178 CurBlk = -1;
179 ReadBlks = 0;
180 CurNum = 0;
181 Rbuf = 0;
182 Modif = 0;
183 Placed = false;
184 } // end of Reset
185
186 /***********************************************************************/
187 /* TXT GetFileLength: returns file size in number of bytes. */
188 /***********************************************************************/
GetFileLength(PGLOBAL g)189 int TXTFAM::GetFileLength(PGLOBAL g)
190 {
191 char filename[_MAX_PATH];
192 int h;
193 int len;
194
195 PlugSetPath(filename, To_File, Tdbp->GetPath());
196 h= global_open(g, MSGID_OPEN_MODE_STRERROR, filename, _O_RDONLY);
197
198 if (trace(1))
199 htrc("GetFileLength: fn=%s h=%d\n", filename, h);
200
201 if (h == -1) {
202 if (errno != ENOENT) {
203 if (trace(1))
204 htrc("%s\n", g->Message);
205
206 len = -1;
207 } else {
208 len = 0; // File does not exist yet
209 g->Message[0]= '\0';
210 } // endif errno
211
212 } else {
213 if ((len = _filelength(h)) < 0)
214 sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", filename);
215
216 if (Eof && len)
217 len--; // Do not count the EOF character
218
219 close(h);
220 } // endif h
221
222 return len;
223 } // end of GetFileLength
224
225 /***********************************************************************/
226 /* Cardinality: returns table cardinality in number of rows. */
227 /* This function can be called with a null argument to test the */
228 /* availability of Cardinality implementation (1 yes, 0 no). */
229 /* Note: This function is meant only for fixed length files but is */
230 /* placed here to be available to FIXFAM and MPXFAM classes. */
231 /***********************************************************************/
Cardinality(PGLOBAL g)232 int TXTFAM::Cardinality(PGLOBAL g)
233 {
234 if (g) {
235 int card = -1;
236 int len = GetFileLength(g);
237
238 if (len >= 0) {
239 if (Padded && Blksize) {
240 if (!(len % Blksize))
241 card = (len / Blksize) * Nrec;
242 else
243 sprintf(g->Message, MSG(NOT_FIXED_LEN), To_File, len, Lrecl);
244
245 } else {
246 if (!(len % Lrecl))
247 card = len / (int)Lrecl; // Fixed length file
248 else
249 sprintf(g->Message, MSG(NOT_FIXED_LEN), To_File, len, Lrecl);
250
251 } // endif Padded
252
253 if (trace(1))
254 htrc(" Computed max_K=%d Filen=%d lrecl=%d\n",
255 card, len, Lrecl);
256
257 } else
258 card = 0;
259
260 // Set number of blocks for later use
261 Block = (card > 0) ? (card + Nrec - 1) / Nrec : 0;
262 return card;
263 } else
264 return 1;
265
266 } // end of Cardinality
267
268 /***********************************************************************/
269 /* Use BlockTest to reduce the table estimated size. */
270 /* Note: This function is meant only for fixed length files but is */
271 /* placed here to be available to FIXFAM and MPXFAM classes. */
272 /***********************************************************************/
MaxBlkSize(PGLOBAL g,int s)273 int TXTFAM::MaxBlkSize(PGLOBAL g, int s)
274 {
275 int rc = RC_OK, savcur = CurBlk, blm1 = Block - 1;
276 int size, last = s - blm1 * Nrec;
277
278 // Roughly estimate the table size as the sum of blocks
279 // that can contain good rows
280 for (size = 0, CurBlk = 0; CurBlk < Block; CurBlk++)
281 if ((rc = Tdbp->TestBlock(g)) == RC_OK)
282 size += (CurBlk == blm1) ? last : Nrec;
283 else if (rc == RC_EF)
284 break;
285
286 CurBlk = savcur;
287 return size;
288 } // end of MaxBlkSize
289
290 /***********************************************************************/
291 /* AddListValue: Used when doing indexed update or delete. */
292 /***********************************************************************/
AddListValue(PGLOBAL g,int type,void * val,PPARM * top)293 bool TXTFAM::AddListValue(PGLOBAL g, int type, void *val, PPARM *top)
294 {
295 PPARM pp = (PPARM)PlugSubAlloc(g, NULL, sizeof(PARM));
296
297 switch (type) {
298 // case TYPE_INT:
299 // pp->Value = PlugSubAlloc(g, NULL, sizeof(int));
300 // *((int*)pp->Value) = *((int*)val);
301 // break;
302 case TYPE_VOID:
303 pp->Intval = *(int*)val;
304 break;
305 // case TYPE_STRING:
306 // pp->Value = PlugDup(g, (char*)val);
307 // break;
308 case TYPE_PCHAR:
309 pp->Value = val;
310 break;
311 default:
312 return true;
313 } // endswitch type
314
315 pp->Type = type;
316 pp->Domain = 0;
317 pp->Next = *top;
318 *top = pp;
319 return false;
320 } // end of AddListValue
321
322 /***********************************************************************/
323 /* Store needed values for indexed UPDATE or DELETE. */
324 /***********************************************************************/
StoreValues(PGLOBAL g,bool upd)325 int TXTFAM::StoreValues(PGLOBAL g, bool upd)
326 {
327 int pos = GetPos();
328 bool rc = AddListValue(g, TYPE_VOID, &pos, &To_Pos);
329
330 if (!rc) {
331 pos = GetNextPos();
332 rc = AddListValue(g, TYPE_VOID, &pos, &To_Sos);
333 } // endif rc
334
335 if (upd && !rc) {
336 char *buf;
337
338 if (Tdbp->PrepareWriting(g))
339 return RC_FX;
340
341 buf = PlugDup(g, Tdbp->GetLine());
342 rc = AddListValue(g, TYPE_PCHAR, buf, &To_Upd);
343 } // endif upd
344
345 return rc ? RC_FX : RC_OK;
346 } // end of StoreValues
347
348 /***********************************************************************/
349 /* UpdateSortedRows. When updating using indexing, the issue is that */
350 /* record are not necessarily updated in sequential order. */
351 /* Moving intermediate lines cannot be done while making them because */
352 /* this can cause extra wrong records to be included in the new file. */
353 /* What we do here is to reorder the updated records and do all the */
354 /* updates ordered by record position. */
355 /***********************************************************************/
UpdateSortedRows(PGLOBAL g)356 int TXTFAM::UpdateSortedRows(PGLOBAL g)
357 {
358 int *ix, i;
359
360 /*********************************************************************/
361 /* Get the stored update values and sort them. */
362 /*********************************************************************/
363 if (!(Posar = MakeValueArray(g, To_Pos))) {
364 // strcpy(g->Message, "Position array is null");
365 // return RC_INFO;
366 return RC_OK; // Nothing to do
367 } else if (!(Sosar = MakeValueArray(g, To_Sos))) {
368 strcpy(g->Message, "Start position array is null");
369 goto err;
370 } else if (!(Updar = MakeValueArray(g, To_Upd))) {
371 strcpy(g->Message, "Updated line array is null");
372 goto err;
373 } else if (!(ix = (int*)Posar->GetSortIndex(g))) {
374 strcpy(g->Message, "Error getting array sort index");
375 goto err;
376 } // endif's
377
378 Rewind();
379
380 for (i = 0; i < Posar->GetNval(); i++) {
381 SetPos(g, Sosar->GetIntValue(ix[i]));
382 Fpos = Posar->GetIntValue(ix[i]);
383 strcpy(Tdbp->To_Line, Updar->GetStringValue(ix[i]));
384
385 // Now write the updated line.
386 if (WriteBuffer(g))
387 goto err;
388
389 } // endfor i
390
391 return RC_OK;
392
393 err:
394 if (trace(1))
395 htrc("%s\n", g->Message);
396
397 return RC_FX;
398 } // end of UpdateSortedRows
399
400 /***********************************************************************/
401 /* DeleteSortedRows. When deleting using indexing, the issue is that */
402 /* record are not necessarily deleted in sequential order. Moving */
403 /* intermediate lines cannot be done while deleing them because */
404 /* this can cause extra wrong records to be included in the new file. */
405 /* What we do here is to reorder the deleted record and delete from */
406 /* the file from the ordered deleted records. */
407 /***********************************************************************/
DeleteSortedRows(PGLOBAL g)408 int TXTFAM::DeleteSortedRows(PGLOBAL g)
409 {
410 int *ix, i, irc;
411
412 /*********************************************************************/
413 /* Get the stored delete values and sort them. */
414 /*********************************************************************/
415 if (!(Posar = MakeValueArray(g, To_Pos))) {
416 // strcpy(g->Message, "Position array is null");
417 // return RC_INFO;
418 return RC_OK; // Nothing to do
419 } else if (!(Sosar = MakeValueArray(g, To_Sos))) {
420 strcpy(g->Message, "Start position array is null");
421 goto err;
422 } else if (!(ix = (int*)Posar->GetSortIndex(g))) {
423 strcpy(g->Message, "Error getting array sort index");
424 goto err;
425 } // endif's
426
427 Tpos = Spos = 0;
428
429 for (i = 0; i < Posar->GetNval(); i++) {
430 if ((irc = InitDelete(g, Posar->GetIntValue(ix[i]),
431 Sosar->GetIntValue(ix[i]))) == RC_FX)
432 goto err;
433
434 // Now delete the sorted rows
435 if (DeleteRecords(g, irc))
436 goto err;
437
438 } // endfor i
439
440 return RC_OK;
441
442 err:
443 if (trace(1))
444 htrc("%s\n", g->Message);
445
446 return RC_FX;
447 } // end of DeleteSortedRows
448
449 /***********************************************************************/
450 /* The purpose of this function is to deal with access methods that */
451 /* are not coherent regarding the use of SetPos and GetPos. */
452 /***********************************************************************/
InitDelete(PGLOBAL g,int,int)453 int TXTFAM::InitDelete(PGLOBAL g, int, int)
454 {
455 strcpy(g->Message, "InitDelete should not be used by this table type");
456 return RC_FX;
457 } // end of InitDelete
458
459 /* --------------------------- Class DOSFAM -------------------------- */
460
461 /***********************************************************************/
462 /* Constructors. */
463 /***********************************************************************/
DOSFAM(PDOSDEF tdp)464 DOSFAM::DOSFAM(PDOSDEF tdp) : TXTFAM(tdp)
465 {
466 To_Fbt = NULL;
467 Stream = NULL;
468 T_Stream = NULL;
469 UseTemp = false;
470 Bin = false;
471 } // end of DOSFAM standard constructor
472
DOSFAM(PDOSFAM tdfp)473 DOSFAM::DOSFAM(PDOSFAM tdfp) : TXTFAM(tdfp)
474 {
475 To_Fbt = tdfp->To_Fbt;
476 Stream = tdfp->Stream;
477 T_Stream = tdfp->T_Stream;
478 UseTemp = tdfp->UseTemp;
479 Bin = tdfp->Bin;
480 } // end of DOSFAM copy constructor
481
DOSFAM(PBLKFAM tdfp,PDOSDEF tdp)482 DOSFAM::DOSFAM(PBLKFAM tdfp, PDOSDEF tdp) : TXTFAM(tdp)
483 {
484 Tdbp = tdfp->Tdbp;
485 To_Fb = tdfp->To_Fb;
486 To_Fbt = tdfp->To_Fbt;
487 Stream = tdfp->Stream;
488 T_Stream = tdfp->T_Stream;
489 UseTemp = tdfp->UseTemp;
490 Bin = tdfp->Bin;
491 } // end of DOSFAM constructor from BLKFAM
492
493 /***********************************************************************/
494 /* Reset: reset position values at the beginning of file. */
495 /***********************************************************************/
Reset(void)496 void DOSFAM::Reset(void)
497 {
498 TXTFAM::Reset();
499 Bin = false;
500 Fpos = Tpos = Spos = 0;
501 } // end of Reset
502
503 /***********************************************************************/
504 /* DOS GetFileLength: returns file size in number of bytes. */
505 /***********************************************************************/
GetFileLength(PGLOBAL g)506 int DOSFAM::GetFileLength(PGLOBAL g)
507 {
508 int len;
509
510 if (!Stream)
511 len = TXTFAM::GetFileLength(g);
512 else
513 if ((len = _filelength(_fileno(Stream))) < 0)
514 sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", To_File);
515
516 if (trace(1))
517 htrc("File length=%d\n", len);
518
519 return len;
520 } // end of GetFileLength
521
522 /***********************************************************************/
523 /* Cardinality: returns table cardinality in number of rows. */
524 /* This function can be called with a null argument to test the */
525 /* availability of Cardinality implementation (1 yes, 0 no). */
526 /***********************************************************************/
Cardinality(PGLOBAL g)527 int DOSFAM::Cardinality(PGLOBAL g)
528 {
529 return (g) ? -1 : 0;
530 } // end of Cardinality
531
532 /***********************************************************************/
533 /* Use BlockTest to reduce the table estimated size. */
534 /* Note: This function is not really implemented yet. */
535 /***********************************************************************/
MaxBlkSize(PGLOBAL,int s)536 int DOSFAM::MaxBlkSize(PGLOBAL, int s)
537 {
538 return s;
539 } // end of MaxBlkSize
540
541 /***********************************************************************/
542 /* OpenTableFile: Open a DOS/UNIX table file using C standard I/Os. */
543 /***********************************************************************/
OpenTableFile(PGLOBAL g)544 bool DOSFAM::OpenTableFile(PGLOBAL g)
545 {
546 char opmode[4], filename[_MAX_PATH];
547 //int ftype = Tdbp->GetFtype();
548 MODE mode = Tdbp->Mode;
549 PDBUSER dbuserp = PlgGetUser(g);
550
551 // This is required when using Unix files under Windows and vice versa
552 //Bin = (Blocked || Ending != CRLF);
553 Bin = true; // To avoid ftell problems
554
555 switch (mode) {
556 case MODE_READ:
557 strcpy(opmode, "r");
558 break;
559 case MODE_DELETE:
560 if (!Tdbp->Next) {
561 // Store the number of deleted lines
562 DelRows = Cardinality(g);
563
564 if (Blocked) {
565 // Cardinality must return 0
566 Block = 0;
567 Last = Nrec;
568 } // endif blocked
569
570 // This will erase the entire file
571 strcpy(opmode, "w");
572 Tdbp->ResetSize();
573 break;
574 } // endif
575
576 // Selective delete, pass thru
577 Bin = true;
578 /* fall through */
579 case MODE_UPDATE:
580 if ((UseTemp = Tdbp->IsUsingTemp(g))) {
581 strcpy(opmode, "r");
582 Bin = true;
583 } else
584 strcpy(opmode, "r+");
585
586 break;
587 case MODE_INSERT:
588 strcpy(opmode, "a+");
589 break;
590 default:
591 sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
592 return true;
593 } // endswitch Mode
594
595 // For blocked I/O or for moving lines, open the table in binary
596 strcat(opmode, (Bin) ? "b" : "t");
597
598 // Now open the file stream
599 PlugSetPath(filename, To_File, Tdbp->GetPath());
600
601 if (!(Stream = PlugOpenFile(g, filename, opmode))) {
602 if (trace(1))
603 htrc("%s\n", g->Message);
604
605 return (mode == MODE_READ && errno == ENOENT)
606 ? PushWarning(g, Tdbp) : true;
607 } // endif Stream
608
609 if (trace(1))
610 htrc("File %s open Stream=%p mode=%s\n", filename, Stream, opmode);
611
612 To_Fb = dbuserp->Openlist; // Keep track of File block
613
614 /*********************************************************************/
615 /* Allocate the line buffer. For mode Delete a bigger buffer has to */
616 /* be allocated because is it also used to move lines into the file.*/
617 /*********************************************************************/
618 return AllocateBuffer(g);
619 } // end of OpenTableFile
620
621 /***********************************************************************/
622 /* Allocate the line buffer. For mode Delete a bigger buffer has to */
623 /* be allocated because is it also used to move lines into the file. */
624 /***********************************************************************/
AllocateBuffer(PGLOBAL g)625 bool DOSFAM::AllocateBuffer(PGLOBAL g)
626 {
627 MODE mode = Tdbp->Mode;
628
629 // Lrecl does not include line ending
630 Buflen = Lrecl + Ending + ((Bin) ? 1 : 0) + 1; // Sergei
631
632 if (trace(1))
633 htrc("SubAllocating a buffer of %d bytes\n", Buflen);
634
635 To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
636
637 if (UseTemp || mode == MODE_DELETE) {
638 // Have a big buffer to move lines
639 Dbflen = Buflen * DOS_BUFF_LEN;
640 DelBuf = PlugSubAlloc(g, NULL, Dbflen);
641 } else if (mode == MODE_INSERT) {
642 /*******************************************************************/
643 /* Prepare the buffer so eventual gaps are filled with blanks. */
644 /*******************************************************************/
645 memset(To_Buf, ' ', Buflen);
646 To_Buf[Buflen - 2] = '\n';
647 To_Buf[Buflen - 1] = '\0';
648 } // endif's mode
649
650 return false;
651 } // end of AllocateBuffer
652
653 /***********************************************************************/
654 /* GetRowID: return the RowID of last read record. */
655 /***********************************************************************/
GetRowID(void)656 int DOSFAM::GetRowID(void)
657 {
658 return Rows;
659 } // end of GetRowID
660
661 /***********************************************************************/
662 /* GetPos: return the position of last read record. */
663 /***********************************************************************/
GetPos(void)664 int DOSFAM::GetPos(void)
665 {
666 return Fpos;
667 } // end of GetPos
668
669 /***********************************************************************/
670 /* GetNextPos: return the position of next record. */
671 /***********************************************************************/
GetNextPos(void)672 int DOSFAM::GetNextPos(void)
673 {
674 return ftell(Stream);
675 } // end of GetNextPos
676
677 /***********************************************************************/
678 /* SetPos: Replace the table at the specified position. */
679 /***********************************************************************/
SetPos(PGLOBAL g,int pos)680 bool DOSFAM::SetPos(PGLOBAL g, int pos)
681 {
682 Fpos = pos;
683
684 if (fseek(Stream, Fpos, SEEK_SET)) {
685 sprintf(g->Message, MSG(FSETPOS_ERROR), Fpos);
686 return true;
687 } // endif
688
689 Placed = true;
690 return false;
691 } // end of SetPos
692
693 /***********************************************************************/
694 /* Record file position in case of UPDATE or DELETE. */
695 /***********************************************************************/
RecordPos(PGLOBAL g)696 bool DOSFAM::RecordPos(PGLOBAL g)
697 {
698 if ((Fpos = ftell(Stream)) < 0) {
699 sprintf(g->Message, MSG(FTELL_ERROR), 0, strerror(errno));
700 // strcat(g->Message, " (possible wrong ENDING option value)");
701 return true;
702 } // endif Fpos
703
704 return false;
705 } // end of RecordPos
706
707 /***********************************************************************/
708 /* Initialize Fpos and the current position for indexed DELETE. */
709 /***********************************************************************/
InitDelete(PGLOBAL g,int fpos,int spos)710 int DOSFAM::InitDelete(PGLOBAL g, int fpos, int spos)
711 {
712 Fpos = fpos;
713
714 if (fseek(Stream, spos, SEEK_SET)) {
715 sprintf(g->Message, MSG(FSETPOS_ERROR), Fpos);
716 return RC_FX;
717 } // endif
718
719 return RC_OK;
720 } // end of InitDelete
721
722 /***********************************************************************/
723 /* Skip one record in file. */
724 /***********************************************************************/
SkipRecord(PGLOBAL g,bool header)725 int DOSFAM::SkipRecord(PGLOBAL g, bool header)
726 {
727 PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
728
729 // Skip this record
730 if (!fgets(To_Buf, Buflen, Stream)) {
731 if (feof(Stream))
732 return RC_EF;
733
734 #if defined(_WIN32)
735 sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
736 #else
737 sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(0));
738 #endif
739 return RC_FX;
740 } // endif fgets
741
742 // Update progress information
743 dup->ProgCur = GetPos();
744
745 if (header) {
746 // For Delete
747 Fpos = ftell(Stream);
748
749 if (!UseTemp)
750 Tpos = Spos = Fpos; // No need to move header
751
752 } // endif header
753
754 #if defined(THREAD)
755 return RC_NF; // To have progress info
756 #else
757 return RC_OK; // To loop locally
758 #endif
759 } // end of SkipRecord
760
761 /***********************************************************************/
762 /* ReadBuffer: Read one line for a text file. */
763 /***********************************************************************/
ReadBuffer(PGLOBAL g)764 int DOSFAM::ReadBuffer(PGLOBAL g)
765 {
766 char *p;
767 int rc;
768
769 if (!Stream)
770 return RC_EF;
771
772 if (trace(2))
773 htrc("ReadBuffer: Tdbp=%p To_Line=%p Placed=%d\n",
774 Tdbp, Tdbp->To_Line, Placed);
775
776 if (!Placed) {
777 /*******************************************************************/
778 /* Record file position in case of UPDATE or DELETE. */
779 /*******************************************************************/
780 next:
781 if (RecordPos(g))
782 return RC_FX;
783
784 CurBlk = (int)Rows++;
785
786 if (trace(2))
787 htrc("ReadBuffer: CurBlk=%d\n", CurBlk);
788
789 /********************************************************************/
790 /* Check whether optimization on ROWID */
791 /* can be done, as well as for join as for local filtering. */
792 /*******************************************************************/
793 switch (Tdbp->TestBlock(g)) {
794 case RC_EF:
795 return RC_EF;
796 case RC_NF:
797 // Skip this record
798 if ((rc = SkipRecord(g, FALSE)) != RC_OK)
799 return rc;
800
801 goto next;
802 } // endswitch rc
803
804 } else
805 Placed = false;
806
807 if (trace(2))
808 htrc(" About to read: stream=%p To_Buf=%p Buflen=%d Fpos=%d\n",
809 Stream, To_Buf, Buflen, Fpos);
810
811 if (fgets(To_Buf, Buflen, Stream)) {
812 p = To_Buf + strlen(To_Buf) - 1;
813
814 if (trace(2))
815 htrc(" Read: To_Buf=%p p=%c\n", To_Buf, p);
816
817 #if defined(_WIN32)
818 if (Bin) {
819 // Data file is read in binary so CRLF remains
820 #else
821 if (true) {
822 // Data files can be imported from Windows (having CRLF)
823 #endif
824 if (*p == '\n' || *p == '\r') {
825 // is this enough for Unix ???
826 *p = '\0'; // Eliminate ending CR or LF character
827
828 if (p > To_Buf) {
829 // is this enough for Unix ???
830 p--;
831
832 if (*p == '\n' || *p == '\r')
833 *p = '\0'; // Eliminate ending CR or LF character
834
835 } // endif To_Buf
836
837 } // endif p
838
839 } else if (*p == '\n')
840 *p = '\0'; // Eliminate ending new-line character
841
842 if (trace(2))
843 htrc(" To_Buf='%s'\n", To_Buf);
844
845 strcpy(Tdbp->To_Line, To_Buf);
846 num_read++;
847 rc = RC_OK;
848 } else if (feof(Stream)) {
849 rc = RC_EF;
850 } else {
851 #if defined(_WIN32)
852 sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
853 #else
854 sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(0));
855 #endif
856
857 if (trace(1))
858 htrc("%s\n", g->Message);
859
860 rc = RC_FX;
861 } // endif's fgets
862
863 if (trace(2))
864 htrc("ReadBuffer: rc=%d\n", rc);
865
866 IsRead = true;
867 return rc;
868 } // end of ReadBuffer
869
870 /***********************************************************************/
871 /* WriteBuffer: File write routine for DOS access method. */
872 /***********************************************************************/
873 int DOSFAM::WriteBuffer(PGLOBAL g)
874 {
875 int curpos = 0;
876 bool moved = true;
877
878 // T_Stream is the temporary stream or the table file stream itself
879 if (!T_Stream) {
880 if (UseTemp && Tdbp->Mode == MODE_UPDATE) {
881 if (OpenTempFile(g))
882 return RC_FX;
883
884 } else
885 T_Stream = Stream;
886
887 } // endif T_Stream
888
889 if (Tdbp->Mode == MODE_UPDATE) {
890 /*******************************************************************/
891 /* Here we simply rewrite a record on itself. There are two cases */
892 /* were another method should be used, a/ when Update apply to */
893 /* the whole file, b/ when updating the last field of a variable */
894 /* length file. The method could be to rewrite a new file, then */
895 /* to erase the old one and rename the new updated file. */
896 /*******************************************************************/
897 curpos = ftell(Stream);
898
899 if (trace(1))
900 htrc("Last : %d cur: %d\n", Fpos, curpos);
901
902 if (UseTemp) {
903 /*****************************************************************/
904 /* We are using a temporary file. */
905 /* Before writing the updated record, we must eventually copy */
906 /* all the intermediate records that have not been updated. */
907 /*****************************************************************/
908 if (MoveIntermediateLines(g, &moved))
909 return RC_FX;
910
911 Spos = curpos; // New start position
912 } else
913 // Update is directly written back into the file,
914 // with this (fast) method, record size cannot change.
915 if (fseek(Stream, Fpos, SEEK_SET)) {
916 sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
917 return RC_FX;
918 } // endif
919
920 } // endif mode
921
922 /*********************************************************************/
923 /* Prepare the write the updated line. */
924 /*********************************************************************/
925 strcat(strcpy(To_Buf, Tdbp->To_Line), (Bin) ? CrLf : "\n");
926
927 /*********************************************************************/
928 /* Now start the writing process. */
929 /*********************************************************************/
930 if ((fputs(To_Buf, T_Stream)) == EOF) {
931 sprintf(g->Message, MSG(FPUTS_ERROR), strerror(errno));
932 return RC_FX;
933 } // endif EOF
934
935 if (Tdbp->Mode == MODE_UPDATE && moved)
936 if (fseek(Stream, curpos, SEEK_SET)) {
937 sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
938 return RC_FX;
939 } // endif
940
941 if (trace(1))
942 htrc("write done\n");
943
944 return RC_OK;
945 } // end of WriteBuffer
946
947 /***********************************************************************/
948 /* Data Base delete line routine for DOS and BLK access methods. */
949 /***********************************************************************/
950 int DOSFAM::DeleteRecords(PGLOBAL g, int irc)
951 {
952 bool moved;
953 int curpos = ftell(Stream);
954
955 /*********************************************************************/
956 /* There is an alternative here: */
957 /* 1 - use a temporary file in which are copied all not deleted */
958 /* lines, at the end the original file will be deleted and */
959 /* the temporary file renamed to the original file name. */
960 /* 2 - directly move the not deleted lines inside the original */
961 /* file, and at the end erase all trailing records. */
962 /* This will be experimented. */
963 /*********************************************************************/
964 if (trace(1))
965 htrc(
966 "DOS DeleteDB: rc=%d UseTemp=%d curpos=%d Fpos=%d Tpos=%d Spos=%d\n",
967 irc, UseTemp, curpos, Fpos, Tpos, Spos);
968
969 if (irc != RC_OK) {
970 /*******************************************************************/
971 /* EOF: position Fpos at the end-of-file position. */
972 /*******************************************************************/
973 fseek(Stream, 0, SEEK_END);
974 Fpos = ftell(Stream);
975
976 if (trace(1))
977 htrc("Fpos placed at file end=%d\n", Fpos);
978
979 } // endif irc
980
981 if (Tpos == Spos) {
982 /*******************************************************************/
983 /* First line to delete, Open temporary file. */
984 /*******************************************************************/
985 if (UseTemp) {
986 if (OpenTempFile(g))
987 return RC_FX;
988
989 } else {
990 /*****************************************************************/
991 /* Move of eventual preceding lines is not required here. */
992 /* Set the target file as being the source file itself. */
993 /* Set the future Tpos, and give Spos a value to block copying. */
994 /*****************************************************************/
995 T_Stream = Stream;
996 Spos = Tpos = Fpos;
997 } // endif UseTemp
998
999 } // endif Tpos == Spos
1000
1001 /*********************************************************************/
1002 /* Move any intermediate lines. */
1003 /*********************************************************************/
1004 if (MoveIntermediateLines(g, &moved))
1005 return RC_FX;
1006
1007 if (irc == RC_OK) {
1008 /*******************************************************************/
1009 /* Reposition the file pointer and set Spos. */
1010 /*******************************************************************/
1011 if (!UseTemp || moved)
1012 if (fseek(Stream, curpos, SEEK_SET)) {
1013 sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
1014 return RC_FX;
1015 } // endif
1016
1017 Spos = GetNextPos(); // New start position
1018
1019 if (trace(1))
1020 htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
1021
1022 } else {
1023 /*******************************************************************/
1024 /* Last call after EOF has been reached. */
1025 /* The UseTemp case is treated in CloseTableFile. */
1026 /*******************************************************************/
1027 if (!UseTemp & !Abort) {
1028 /*****************************************************************/
1029 /* Because the chsize functionality is only accessible with a */
1030 /* system call we must close the file and reopen it with the */
1031 /* open function (_fopen for MS ??) this is still to be checked */
1032 /* for compatibility with Text files and other OS's. */
1033 /*****************************************************************/
1034 char filename[_MAX_PATH];
1035 int h; // File handle, return code
1036
1037 PlugSetPath(filename, To_File, Tdbp->GetPath());
1038 /*rc=*/ PlugCloseFile(g, To_Fb);
1039
1040 if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0)
1041 return RC_FX;
1042
1043 /*****************************************************************/
1044 /* Remove extra records. */
1045 /*****************************************************************/
1046 #if defined(_WIN32)
1047 if (chsize(h, Tpos)) {
1048 sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno));
1049 close(h);
1050 return RC_FX;
1051 } // endif
1052 #else
1053 if (ftruncate(h, (off_t)Tpos)) {
1054 sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
1055 close(h);
1056 return RC_FX;
1057 } // endif
1058 #endif
1059
1060 close(h);
1061
1062 if (trace(1))
1063 htrc("done, h=%d irc=%d\n", h, irc);
1064
1065 } // endif !UseTemp
1066
1067 } // endif irc
1068
1069 return RC_OK; // All is correct
1070 } // end of DeleteRecords
1071
1072 /***********************************************************************/
1073 /* Open a temporary file used while updating or deleting. */
1074 /***********************************************************************/
1075 bool DOSFAM::OpenTempFile(PGLOBAL g)
1076 {
1077 char tempname[_MAX_PATH];
1078 bool rc = false;
1079
1080 /*********************************************************************/
1081 /* Open the temporary file, Spos is at the beginning of file. */
1082 /*********************************************************************/
1083 PlugSetPath(tempname, To_File, Tdbp->GetPath());
1084 strcat(PlugRemoveType(tempname, tempname), ".t");
1085
1086 if (!(T_Stream = PlugOpenFile(g, tempname, "wb"))) {
1087 if (trace(1))
1088 htrc("%s\n", g->Message);
1089
1090 rc = true;
1091 } else
1092 To_Fbt = PlgGetUser(g)->Openlist;
1093
1094 return rc;
1095 } // end of OpenTempFile
1096
1097 /***********************************************************************/
1098 /* Move intermediate deleted or updated lines. */
1099 /* This works only for file open in binary mode. */
1100 /***********************************************************************/
1101 bool DOSFAM::MoveIntermediateLines(PGLOBAL g, bool *b)
1102 {
1103 int n;
1104 size_t req, len;
1105
1106 for (*b = false, n = Fpos - Spos; n > 0; n -= req) {
1107 if (!UseTemp || !*b)
1108 if (fseek(Stream, Spos, SEEK_SET)) {
1109 sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno));
1110 return true;
1111 } // endif
1112
1113 req = (size_t)MY_MIN(n, Dbflen);
1114 len = fread(DelBuf, 1, req, Stream);
1115
1116 if (trace(1))
1117 htrc("after read req=%d len=%d\n", req, len);
1118
1119 if (len != req) {
1120 sprintf(g->Message, MSG(DEL_READ_ERROR), (int) req, (int) len);
1121 return true;
1122 } // endif len
1123
1124 if (!UseTemp)
1125 if (fseek(T_Stream, Tpos, SEEK_SET)) {
1126 sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
1127 return true;
1128 } // endif
1129
1130 if ((len = fwrite(DelBuf, 1, req, T_Stream)) != req) {
1131 sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
1132 return true;
1133 } // endif
1134
1135 if (trace(1))
1136 htrc("after write pos=%d\n", ftell(Stream));
1137
1138 Tpos += (int)req;
1139 Spos += (int)req;
1140
1141 if (trace(1))
1142 htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos);
1143
1144 *b = true;
1145 } // endfor n
1146
1147 return false;
1148 } // end of MoveIntermediate Lines
1149
1150 /***********************************************************************/
1151 /* Delete the old file and rename the new temp file. */
1152 /* If aborting just delete the new temp file. */
1153 /* If indexed, make the temp file from the arrays. */
1154 /***********************************************************************/
1155 int DOSFAM::RenameTempFile(PGLOBAL g)
1156 {
1157 char *tempname, filetemp[_MAX_PATH], filename[_MAX_PATH];
1158 int rc = RC_OK;
1159
1160 if (To_Fbt)
1161 tempname = (char*)To_Fbt->Fname;
1162 else
1163 return RC_INFO; // Nothing to do ???
1164
1165 // This loop is necessary because, in case of join,
1166 // To_File can have been open several times.
1167 for (PFBLOCK fb = PlgGetUser(g)->Openlist; fb; fb = fb->Next)
1168 if (fb == To_Fb || (fb == To_Fbt))
1169 rc = PlugCloseFile(g, fb);
1170
1171 if (!Abort) {
1172 PlugSetPath(filename, To_File, Tdbp->GetPath());
1173 strcat(PlugRemoveType(filetemp, filename), ".ttt");
1174 remove(filetemp); // May still be there from previous error
1175
1176 if (rename(filename, filetemp)) { // Save file for security
1177 snprintf(g->Message, MAX_STR, MSG(RENAME_ERROR),
1178 filename, filetemp, strerror(errno));
1179 throw 51;
1180 } else if (rename(tempname, filename)) {
1181 snprintf(g->Message, MAX_STR, MSG(RENAME_ERROR),
1182 tempname, filename, strerror(errno));
1183 rc = rename(filetemp, filename); // Restore saved file
1184 throw 52;
1185 } else if (remove(filetemp)) {
1186 sprintf(g->Message, MSG(REMOVE_ERROR),
1187 filetemp, strerror(errno));
1188 rc = RC_INFO; // Acceptable
1189 } // endif's
1190
1191 } else
1192 remove(tempname);
1193
1194 return rc;
1195 } // end of RenameTempFile
1196
1197 /***********************************************************************/
1198 /* Table file close routine for DOS access method. */
1199 /***********************************************************************/
1200 void DOSFAM::CloseTableFile(PGLOBAL g, bool abort)
1201 {
1202 int rc;
1203
1204 Abort = abort;
1205
1206 if (UseTemp && T_Stream) {
1207 if (Tdbp->Mode == MODE_UPDATE && !Abort) {
1208 // Copy eventually remaining lines
1209 bool b;
1210
1211 fseek(Stream, 0, SEEK_END);
1212 Fpos = ftell(Stream);
1213 Abort = MoveIntermediateLines(g, &b) != RC_OK;
1214 } // endif Abort
1215
1216 // Delete the old file and rename the new temp file.
1217 rc = RenameTempFile(g); // Also close all files
1218 } else {
1219 rc = PlugCloseFile(g, To_Fb);
1220
1221 if (trace(1))
1222 htrc("DOS Close: closing %s rc=%d\n", To_File, rc);
1223
1224 } // endif UseTemp
1225
1226 Stream = NULL; // So we can know whether table is open
1227 T_Stream = NULL;
1228 } // end of CloseTableFile
1229
1230 /***********************************************************************/
1231 /* Rewind routine for DOS access method. */
1232 /***********************************************************************/
1233 void DOSFAM::Rewind(void)
1234 {
1235 if (Stream) // Can be NULL when making index on void table
1236 rewind(Stream);
1237
1238 Rows = 0;
1239 OldBlk = CurBlk = -1;
1240 } // end of Rewind
1241
1242 /* --------------------------- Class BLKFAM -------------------------- */
1243
1244 /***********************************************************************/
1245 /* Constructors. */
1246 /***********************************************************************/
1247 BLKFAM::BLKFAM(PDOSDEF tdp) : DOSFAM(tdp)
1248 {
1249 Blocked = true;
1250 Block = tdp->GetBlock();
1251 Last = tdp->GetLast();
1252 Nrec = tdp->GetElemt();
1253 Closing = false;
1254 BlkPos = tdp->GetTo_Pos();
1255 CurLine = NULL;
1256 NxtLine = NULL;
1257 OutBuf = NULL;
1258 } // end of BLKFAM standard constructor
1259
1260 BLKFAM::BLKFAM(PBLKFAM txfp) : DOSFAM(txfp)
1261 {
1262 Closing = txfp->Closing;
1263 CurLine = txfp->CurLine;
1264 NxtLine = txfp->NxtLine;
1265 OutBuf = txfp->OutBuf;
1266 } // end of BLKFAM copy constructor
1267
1268 /***********************************************************************/
1269 /* Reset: reset position values at the beginning of file. */
1270 /***********************************************************************/
1271 void BLKFAM::Reset(void)
1272 {
1273 DOSFAM::Reset();
1274 Closing = false;
1275 } // end of Reset
1276
1277 /***********************************************************************/
1278 /* Cardinality: returns table cardinality in number of rows. */
1279 /* This function can be called with a null argument to test the */
1280 /* availability of Cardinality implementation (1 yes, 0 no). */
1281 /***********************************************************************/
1282 int BLKFAM::Cardinality(PGLOBAL g)
1283 {
1284 return (g) ? ((Block > 0) ? (int)((Block - 1) * Nrec + Last) : 0) : 1;
1285 } // end of Cardinality
1286
1287 /***********************************************************************/
1288 /* Use BlockTest to reduce the table estimated size. */
1289 /***********************************************************************/
1290 int BLKFAM::MaxBlkSize(PGLOBAL g, int)
1291 {
1292 int rc = RC_OK, savcur = CurBlk;
1293 int size;
1294
1295 // Roughly estimate the table size as the sum of blocks
1296 // that can contain good rows
1297 for (size = 0, CurBlk = 0; CurBlk < Block; CurBlk++)
1298 if ((rc = Tdbp->TestBlock(g)) == RC_OK)
1299 size += (CurBlk == Block - 1) ? Last : Nrec;
1300 else if (rc == RC_EF)
1301 break;
1302
1303 CurBlk = savcur;
1304 return size;
1305 } // end of MaxBlkSize
1306
1307 /***********************************************************************/
1308 /* Allocate the line buffer. For mode Delete or when a temp file is */
1309 /* used another big buffer has to be allocated because is it used */
1310 /* to move or update the lines into the (temp) file. */
1311 /***********************************************************************/
1312 bool BLKFAM::AllocateBuffer(PGLOBAL g)
1313 {
1314 int len;
1315 MODE mode = Tdbp->GetMode();
1316
1317 // For variable length files, Lrecl does not include CRLF
1318 len = Lrecl + ((Tdbp->GetFtype()) ? 0 : Ending);
1319 Buflen = len * Nrec;
1320 CurLine = To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
1321
1322 if (UseTemp || mode == MODE_DELETE) {
1323 if (mode == MODE_UPDATE)
1324 OutBuf = (char*)PlugSubAlloc(g, NULL, len + 1);
1325
1326 Dbflen = Buflen;
1327 DelBuf = PlugSubAlloc(g, NULL, Dbflen);
1328 } else if (mode == MODE_INSERT)
1329 Rbuf = Nrec; // To be used by WriteDB
1330
1331 return false;
1332 } // end of AllocateBuffer
1333
1334 /***********************************************************************/
1335 /* GetRowID: return the RowID of last read record. */
1336 /***********************************************************************/
1337 int BLKFAM::GetRowID(void)
1338 {
1339 return CurNum + Nrec * CurBlk + 1;
1340 } // end of GetRowID
1341
1342 /***********************************************************************/
1343 /* GetPos: return the position of last read record. */
1344 /***********************************************************************/
1345 int BLKFAM::GetPos(void)
1346 {
1347 return (CurNum + Nrec * CurBlk); // Computed file index
1348 } // end of GetPos
1349
1350 /***********************************************************************/
1351 /* GetNextPos: called by DeleteRecords. */
1352 /***********************************************************************/
1353 int BLKFAM::GetNextPos(void)
1354 {
1355 return (int)(Fpos + NxtLine - CurLine);
1356 } // end of GetNextPos
1357
1358 /***********************************************************************/
1359 /* SetPos: Replace the table at the specified position. */
1360 /***********************************************************************/
1361 bool BLKFAM::SetPos(PGLOBAL g, int)
1362 {
1363 strcpy(g->Message, "Blocked variable tables cannot be used indexed");
1364 return true;
1365 } // end of SetPos
1366
1367 /***********************************************************************/
1368 /* Record file position in case of UPDATE or DELETE. */
1369 /* Not used yet for blocked tables. */
1370 /***********************************************************************/
1371 bool BLKFAM::RecordPos(PGLOBAL)
1372 {
1373 Fpos = (CurNum + Nrec * CurBlk); // Computed file index
1374 return false;
1375 } // end of RecordPos
1376
1377 /***********************************************************************/
1378 /* Skip one record in file. */
1379 /***********************************************************************/
1380 int BLKFAM::SkipRecord(PGLOBAL, bool header)
1381 {
1382 if (header) {
1383 // For Delete
1384 Fpos = BlkPos[0]; // First block starts after the header
1385
1386 if (!UseTemp)
1387 Tpos = Spos = Fpos; // No need to move header
1388
1389 } // endif header
1390
1391 OldBlk = -2; // To force fseek on first block
1392 return RC_OK;
1393 } // end of SkipRecord
1394
1395 /***********************************************************************/
1396 /* ReadBuffer: Read one line for a text file. */
1397 /***********************************************************************/
1398 int BLKFAM::ReadBuffer(PGLOBAL g)
1399 {
1400 int i, rc = RC_OK;
1401 size_t n;
1402
1403 /*********************************************************************/
1404 /* Sequential reading when Placed is not true. */
1405 /*********************************************************************/
1406 if (Placed) {
1407 Placed = false;
1408 } else if (++CurNum < Rbuf) {
1409 CurLine = NxtLine;
1410
1411 // Get the position of the next line in the buffer
1412 while (*NxtLine++ != '\n') ;
1413
1414 // Set caller line buffer
1415 n = NxtLine - CurLine - Ending;
1416 memcpy(Tdbp->GetLine(), CurLine, n);
1417 Tdbp->GetLine()[n] = '\0';
1418 goto fin;
1419 } else if (Rbuf < Nrec && CurBlk != -1) {
1420 return RC_EF;
1421 } else {
1422 /*******************************************************************/
1423 /* New block. */
1424 /*******************************************************************/
1425 CurNum = 0;
1426
1427 next:
1428 if (++CurBlk >= Block)
1429 return RC_EF;
1430
1431 /*******************************************************************/
1432 /* Before reading a new block, check whether block optimization */
1433 /* can be done, as well as for join as for local filtering. */
1434 /*******************************************************************/
1435 switch (Tdbp->TestBlock(g)) {
1436 case RC_EF:
1437 return RC_EF;
1438 case RC_NF:
1439 goto next;
1440 } // endswitch rc
1441
1442 } // endif's
1443
1444 if (OldBlk == CurBlk)
1445 goto ok; // Block is already there
1446
1447 // fseek is required only in non sequential reading
1448 if (CurBlk != OldBlk + 1)
1449 if (fseek(Stream, BlkPos[CurBlk], SEEK_SET)) {
1450 sprintf(g->Message, MSG(FSETPOS_ERROR), BlkPos[CurBlk]);
1451 return RC_FX;
1452 } // endif fseek
1453
1454 // Calculate the length of block to read
1455 BlkLen = BlkPos[CurBlk + 1] - BlkPos[CurBlk];
1456
1457 if (trace(1))
1458 htrc("File position is now %d\n", ftell(Stream));
1459
1460 // Read the entire next block
1461 n = fread(To_Buf, 1, (size_t)BlkLen, Stream);
1462
1463 if ((size_t) n == (size_t) BlkLen) {
1464 // ReadBlks++;
1465 num_read++;
1466 Rbuf = (CurBlk == Block - 1) ? Last : Nrec;
1467
1468 ok:
1469 rc = RC_OK;
1470
1471 // Get the position of the current line
1472 for (i = 0, CurLine = To_Buf; i < CurNum; i++)
1473 while (*CurLine++ != '\n') ; // What about Unix ???
1474
1475 // Now get the position of the next line
1476 for (NxtLine = CurLine; *NxtLine++ != '\n';) ;
1477
1478 // Set caller line buffer
1479 n = NxtLine - CurLine - Ending;
1480 memcpy(Tdbp->GetLine(), CurLine, n);
1481 Tdbp->GetLine()[n] = '\0';
1482 } else if (feof(Stream)) {
1483 rc = RC_EF;
1484 } else {
1485 #if defined(_WIN32)
1486 sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
1487 #else
1488 sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(errno));
1489 #endif
1490
1491 if (trace(1))
1492 htrc("%s\n", g->Message);
1493
1494 return RC_FX;
1495 } // endelse
1496
1497 OldBlk = CurBlk; // Last block actually read
1498 IsRead = true; // Is read indeed
1499
1500 fin:
1501 // Store the current record file position for Delete and Update
1502 Fpos = (int)(BlkPos[CurBlk] + CurLine - To_Buf);
1503 return rc;
1504 } // end of ReadBuffer
1505
1506 /***********************************************************************/
1507 /* WriteBuffer: File write routine for the blocked DOS access method. */
1508 /* Update is directly written back into the file, */
1509 /* with this (fast) method, record size cannot change. */
1510 /***********************************************************************/
1511 int BLKFAM::WriteBuffer(PGLOBAL g)
1512 {
1513 if (Tdbp->GetMode() == MODE_INSERT) {
1514 /*******************************************************************/
1515 /* In Insert mode, blocks are added sequentially to the file end. */
1516 /*******************************************************************/
1517 if (!Closing) { // Add line to the write buffer
1518 strcat(strcpy(CurLine, Tdbp->GetLine()), CrLf);
1519
1520 if (++CurNum != Rbuf) {
1521 CurLine += strlen(CurLine);
1522 return RC_OK; // We write only full blocks
1523 } // endif CurNum
1524
1525 } // endif Closing
1526
1527 // Now start the writing process.
1528 NxtLine = CurLine + strlen(CurLine);
1529 BlkLen = (int)(NxtLine - To_Buf);
1530
1531 if (fwrite(To_Buf, 1, BlkLen, Stream) != (size_t)BlkLen) {
1532 sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno));
1533 Closing = true; // To tell CloseDB about a Write error
1534 return RC_FX;
1535 } // endif size
1536
1537 CurBlk++;
1538 CurNum = 0;
1539 CurLine = To_Buf;
1540 } else {
1541 /*******************************************************************/
1542 /* Mode == MODE_UPDATE. */
1543 /*******************************************************************/
1544 const char *crlf;
1545 size_t len;
1546 int curpos = ftell(Stream);
1547 bool moved = true;
1548
1549 // T_Stream is the temporary stream or the table file stream itself
1550 if (!T_Stream)
1551 {
1552 if (UseTemp /*&& Tdbp->GetMode() == MODE_UPDATE*/) {
1553 if (OpenTempFile(g))
1554 return RC_FX;
1555
1556 } else
1557 T_Stream = Stream;
1558 }
1559 if (UseTemp) {
1560 /*****************************************************************/
1561 /* We are using a temporary file. Before writing the updated */
1562 /* record, we must eventually copy all the intermediate records */
1563 /* that have not been updated. */
1564 /*****************************************************************/
1565 if (MoveIntermediateLines(g, &moved))
1566 return RC_FX;
1567
1568 Spos = GetNextPos(); // New start position
1569
1570 // Prepare the output buffer
1571 #if defined(_WIN32)
1572 crlf = "\r\n";
1573 #else
1574 crlf = "\n";
1575 #endif // _WIN32
1576 strcat(strcpy(OutBuf, Tdbp->GetLine()), crlf);
1577 len = strlen(OutBuf);
1578 } else {
1579 if (fseek(Stream, Fpos, SEEK_SET)) { // Fpos is last position
1580 sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
1581 return RC_FX;
1582 } // endif fseek
1583
1584 // Replace the line inside read buffer (length has not changed)
1585 memcpy(CurLine, Tdbp->GetLine(), strlen(Tdbp->GetLine()));
1586 OutBuf = CurLine;
1587 len = (size_t)(NxtLine - CurLine);
1588 } // endif UseTemp
1589
1590 if (fwrite(OutBuf, 1, len, T_Stream) != (size_t)len) {
1591 sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno));
1592 return RC_FX;
1593 } // endif fwrite
1594
1595 if (moved)
1596 if (fseek(Stream, curpos, SEEK_SET)) {
1597 sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
1598 return RC_FX;
1599 } // endif
1600
1601 } // endif Mode
1602
1603 return RC_OK;
1604 } // end of WriteBuffer
1605
1606 /***********************************************************************/
1607 /* Table file close routine for DOS access method. */
1608 /***********************************************************************/
1609 void BLKFAM::CloseTableFile(PGLOBAL g, bool abort)
1610 {
1611 int rc, wrc = RC_OK;
1612
1613 Abort = abort;
1614
1615 if (UseTemp && T_Stream) {
1616 if (Tdbp->GetMode() == MODE_UPDATE && !Abort) {
1617 // Copy eventually remaining lines
1618 bool b;
1619
1620 fseek(Stream, 0, SEEK_END);
1621 Fpos = ftell(Stream);
1622 Abort = MoveIntermediateLines(g, &b) != RC_OK;
1623 } // endif Abort
1624
1625 // Delete the old file and rename the new temp file.
1626 rc = RenameTempFile(g); // Also close all files
1627 } else {
1628 // Closing is True if last Write was in error
1629 if (Tdbp->GetMode() == MODE_INSERT && CurNum && !Closing) {
1630 // Some more inserted lines remain to be written
1631 Rbuf = CurNum--;
1632 Closing = true;
1633 wrc = WriteBuffer(g);
1634 } else if (Modif && !Closing) {
1635 // Last updated block remains to be written
1636 Closing = true;
1637 wrc = ReadBuffer(g);
1638 } // endif's
1639
1640 rc = PlugCloseFile(g, To_Fb);
1641
1642 if (trace(1))
1643 htrc("BLK CloseTableFile: closing %s mode=%d wrc=%d rc=%d\n",
1644 To_File, Tdbp->GetMode(), wrc, rc);
1645
1646 } // endif UseTemp
1647
1648 Stream = NULL; // So we can know whether table is open
1649 } // end of CloseTableFile
1650
1651 /***********************************************************************/
1652 /* Rewind routine for DOS access method. */
1653 /* Note: commenting out OldBlk = -1 has two advantages: */
1654 /* 1 - It forces fseek on first block, thus suppressing the need to */
1655 /* rewind the file, anyway unuseful when second pass if indexed. */
1656 /* 2 - It permit to avoid re-reading small tables having only 1 block.*/
1657 /***********************************************************************/
1658 void BLKFAM::Rewind(void)
1659 {
1660 //rewind(Stream); will be placed by fseek
1661 CurBlk = -1;
1662 CurNum = Rbuf;
1663 //OldBlk = -1; commented out in case we reuse last read block
1664 //Rbuf = 0; commented out in case we reuse last read block
1665 } // end of Rewind
1666
1667 /* --------------------------- Class BINFAM -------------------------- */
1668
1669 #if 0
1670 /***********************************************************************/
1671 /* BIN GetFileLength: returns file size in number of bytes. */
1672 /***********************************************************************/
1673 int BINFAM::GetFileLength(PGLOBAL g)
1674 {
1675 int len;
1676
1677 if (!Stream)
1678 len = TXTFAM::GetFileLength(g);
1679 else
1680 if ((len = _filelength(_fileno(Stream))) < 0)
1681 sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", To_File);
1682
1683 xtrc(1, "File length=%d\n", len);
1684 return len;
1685 } // end of GetFileLength
1686
1687 /***********************************************************************/
1688 /* Cardinality: returns table cardinality in number of rows. */
1689 /* This function can be called with a null argument to test the */
1690 /* availability of Cardinality implementation (1 yes, 0 no). */
1691 /***********************************************************************/
1692 int BINFAM::Cardinality(PGLOBAL g)
1693 {
1694 return (g) ? -1 : 0;
1695 } // end of Cardinality
1696
1697 /***********************************************************************/
1698 /* OpenTableFile: Open a DOS/UNIX table file using C standard I/Os. */
1699 /***********************************************************************/
1700 bool BINFAM::OpenTableFile(PGLOBAL g) {
1701 char opmode[4], filename[_MAX_PATH];
1702 MODE mode = Tdbp->GetMode();
1703 PDBUSER dbuserp = PlgGetUser(g);
1704
1705 switch (mode) {
1706 case MODE_READ:
1707 strcpy(opmode, "rb");
1708 break;
1709 case MODE_WRITE:
1710 strcpy(opmode, "wb");
1711 break;
1712 default:
1713 sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
1714 return true;
1715 } // endswitch Mode
1716
1717 // Now open the file stream
1718 PlugSetPath(filename, To_File, Tdbp->GetPath());
1719
1720 if (!(Stream = PlugOpenFile(g, filename, opmode))) {
1721 if (trace(1))
1722 htrc("%s\n", g->Message);
1723
1724 return (mode == MODE_READ && errno == ENOENT)
1725 ? PushWarning(g, Tdbp) : true;
1726 } // endif Stream
1727
1728 if (trace(1))
1729 htrc("File %s open Stream=%p mode=%s\n", filename, Stream, opmode);
1730
1731 To_Fb = dbuserp->Openlist; // Keep track of File block
1732
1733 /*********************************************************************/
1734 /* Allocate the line buffer. */
1735 /*********************************************************************/
1736 return AllocateBuffer(g);
1737 } // end of OpenTableFile
1738 #endif // 0
1739
1740 /***********************************************************************/
1741 /* Allocate the line buffer. For mode Delete a bigger buffer has to */
1742 /* be allocated because is it also used to move lines into the file. */
1743 /***********************************************************************/
1744 bool BINFAM::AllocateBuffer(PGLOBAL g)
1745 {
1746 MODE mode = Tdbp->GetMode();
1747
1748 // Lrecl is Ok
1749 Buflen = Lrecl;
1750
1751 // Buffer will be allocated separately
1752 if (mode == MODE_ANY) {
1753 xtrc(1, "SubAllocating a buffer of %d bytes\n", Buflen);
1754 To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
1755 } else if (UseTemp || mode == MODE_DELETE) {
1756 // Have a big buffer to move lines
1757 Dbflen = Buflen * DOS_BUFF_LEN;
1758 DelBuf = PlugSubAlloc(g, NULL, Dbflen);
1759 } // endif mode
1760
1761 return false;
1762 #if 0
1763 MODE mode = Tdbp->GetMode();
1764
1765 // Lrecl is Ok
1766 Dbflen = Buflen = Lrecl;
1767
1768 if (trace(1))
1769 htrc("SubAllocating a buffer of %d bytes\n", Buflen);
1770
1771 DelBuf = To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
1772 return false;
1773 #endif // 0
1774 } // end of AllocateBuffer
1775
1776 #if 0
1777 /***********************************************************************/
1778 /* GetRowID: return the RowID of last read record. */
1779 /***********************************************************************/
1780 int BINFAM::GetRowID(void) {
1781 return Rows;
1782 } // end of GetRowID
1783
1784 /***********************************************************************/
1785 /* GetPos: return the position of last read record. */
1786 /***********************************************************************/
1787 int BINFAM::GetPos(void) {
1788 return Fpos;
1789 } // end of GetPos
1790
1791 /***********************************************************************/
1792 /* GetNextPos: return the position of next record. */
1793 /***********************************************************************/
1794 int BINFAM::GetNextPos(void) {
1795 return ftell(Stream);
1796 } // end of GetNextPos
1797
1798 /***********************************************************************/
1799 /* SetPos: Replace the table at the specified position. */
1800 /***********************************************************************/
1801 bool BINFAM::SetPos(PGLOBAL g, int pos) {
1802 Fpos = pos;
1803
1804 if (fseek(Stream, Fpos, SEEK_SET)) {
1805 sprintf(g->Message, MSG(FSETPOS_ERROR), Fpos);
1806 return true;
1807 } // endif
1808
1809 Placed = true;
1810 return false;
1811 } // end of SetPos
1812
1813 /***********************************************************************/
1814 /* Record file position in case of UPDATE or DELETE. */
1815 /***********************************************************************/
1816 bool BINFAM::RecordPos(PGLOBAL g) {
1817 if ((Fpos = ftell(Stream)) < 0) {
1818 sprintf(g->Message, MSG(FTELL_ERROR), 0, strerror(errno));
1819 // strcat(g->Message, " (possible wrong ENDING option value)");
1820 return true;
1821 } // endif Fpos
1822
1823 return false;
1824 } // end of RecordPos
1825 #endif // 0
1826
1827 /***********************************************************************/
1828 /* ReadBuffer: Read one line for a text file. */
1829 /***********************************************************************/
1830 int BINFAM::ReadBuffer(PGLOBAL g)
1831 {
1832 int rc;
1833
1834 if (!Stream)
1835 return RC_EF;
1836
1837 xtrc(2, "ReadBuffer: Tdbp=%p To_Line=%p Placed=%d\n",
1838 Tdbp, Tdbp->GetLine(), Placed);
1839
1840 if (!Placed) {
1841 /*******************************************************************/
1842 /* Record file position in case of UPDATE or DELETE. */
1843 /*******************************************************************/
1844 if (RecordPos(g))
1845 return RC_FX;
1846
1847 CurBlk = (int)Rows++;
1848 xtrc(2, "ReadBuffer: CurBlk=%d\n", CurBlk);
1849 } else
1850 Placed = false;
1851
1852 xtrc(2, " About to read: bstream=%p To_Buf=%p Buflen=%d Fpos=%d\n",
1853 Stream, To_Buf, Buflen, Fpos);
1854
1855 // Read the prefix giving the row length
1856 if (!fread(&Recsize, sizeof(size_t), 1, Stream)) {
1857 if (!feof(Stream)) {
1858 strcpy(g->Message, "Error reading line prefix\n");
1859 return RC_FX;
1860 } else
1861 return RC_EF;
1862
1863 } else if (Recsize > (unsigned)Buflen) {
1864 sprintf(g->Message, "Record too big (Recsize=%zd Buflen=%d)\n", Recsize, Buflen);
1865 return RC_FX;
1866 } // endif Recsize
1867
1868 if (fread(To_Buf, Recsize, 1, Stream)) {
1869 xtrc(2, " Read: To_Buf=%p Recsize=%zd\n", To_Buf, Recsize);
1870 num_read++;
1871 rc = RC_OK;
1872 } else if (feof(Stream)) {
1873 rc = RC_EF;
1874 } else {
1875 #if defined(_WIN32)
1876 sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
1877 #else
1878 sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(0));
1879 #endif
1880 xtrc(2, "%s\n", g->Message);
1881 rc = RC_FX;
1882 } // endif's fread
1883
1884 xtrc(2, "ReadBuffer: rc=%d\n", rc);
1885 IsRead = true;
1886 return rc;
1887 } // end of ReadBuffer
1888
1889 /***********************************************************************/
1890 /* WriteBuffer: File write routine for BIN access method. */
1891 /***********************************************************************/
1892 int BINFAM::WriteBuffer(PGLOBAL g)
1893 {
1894 int curpos = 0;
1895 bool moved = true;
1896
1897 // T_Stream is the temporary stream or the table file stream itself
1898 if (!T_Stream) {
1899 if (UseTemp && Tdbp->GetMode() == MODE_UPDATE) {
1900 if (OpenTempFile(g))
1901 return RC_FX;
1902
1903 } else
1904 T_Stream = Stream;
1905
1906 } // endif T_Stream
1907
1908 if (Tdbp->GetMode() == MODE_UPDATE) {
1909 /*******************************************************************/
1910 /* Here we simply rewrite a record on itself. There are two cases */
1911 /* were another method should be used, a/ when Update apply to */
1912 /* the whole file, b/ when updating the last field of a variable */
1913 /* length file. The method could be to rewrite a new file, then */
1914 /* to erase the old one and rename the new updated file. */
1915 /*******************************************************************/
1916 curpos = ftell(Stream);
1917
1918 if (trace(1))
1919 htrc("Last : %d cur: %d\n", Fpos, curpos);
1920
1921 if (UseTemp) {
1922 /*****************************************************************/
1923 /* We are using a temporary file. */
1924 /* Before writing the updated record, we must eventually copy */
1925 /* all the intermediate records that have not been updated. */
1926 /*****************************************************************/
1927 if (MoveIntermediateLines(g, &moved))
1928 return RC_FX;
1929
1930 Spos = curpos; // New start position
1931 } else
1932 // Update is directly written back into the file,
1933 // with this (fast) method, record size cannot change.
1934 if (fseek(Stream, Fpos, SEEK_SET)) {
1935 sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
1936 return RC_FX;
1937 } // endif
1938
1939 } // endif mode
1940
1941 /*********************************************************************/
1942 /* Prepare writing the line. */
1943 /*********************************************************************/
1944 //memcpy(To_Buf, Tdbp->GetLine(), Recsize);
1945
1946 /*********************************************************************/
1947 /* Now start the writing process. */
1948 /*********************************************************************/
1949 if (fwrite(&Recsize, sizeof(size_t), 1, T_Stream) != 1) {
1950 sprintf(g->Message, "Error %d writing prefix to %s",
1951 errno, To_File);
1952 return RC_FX;
1953 } else if (fwrite(To_Buf, Recsize, 1, T_Stream) != 1) {
1954 sprintf(g->Message, "Error %d writing %zd bytes to %s",
1955 errno, Recsize, To_File);
1956 return RC_FX;
1957 } // endif fwrite
1958
1959 if (Tdbp->GetMode() == MODE_UPDATE && moved)
1960 if (fseek(Stream, curpos, SEEK_SET)) {
1961 sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
1962 return RC_FX;
1963 } // endif
1964
1965 xtrc(1, "Binary write done\n");
1966 return RC_OK;
1967 } // end of WriteBuffer
1968
1969 #if 0
1970 /***********************************************************************/
1971 /* Data Base delete line routine for DOS and BLK access methods. */
1972 /***********************************************************************/
1973 int DOSFAM::DeleteRecords(PGLOBAL g, int irc)
1974 {
1975 bool moved;
1976 int curpos = ftell(Stream);
1977
1978 /*********************************************************************/
1979 /* There is an alternative here: */
1980 /* 1 - use a temporary file in which are copied all not deleted */
1981 /* lines, at the end the original file will be deleted and */
1982 /* the temporary file renamed to the original file name. */
1983 /* 2 - directly move the not deleted lines inside the original */
1984 /* file, and at the end erase all trailing records. */
1985 /* This will be experimented. */
1986 /*********************************************************************/
1987 if (trace(1))
1988 htrc(
1989 "DOS DeleteDB: rc=%d UseTemp=%d curpos=%d Fpos=%d Tpos=%d Spos=%d\n",
1990 irc, UseTemp, curpos, Fpos, Tpos, Spos);
1991
1992 if (irc != RC_OK) {
1993 /*******************************************************************/
1994 /* EOF: position Fpos at the end-of-file position. */
1995 /*******************************************************************/
1996 fseek(Stream, 0, SEEK_END);
1997 Fpos = ftell(Stream);
1998
1999 if (trace(1))
2000 htrc("Fpos placed at file end=%d\n", Fpos);
2001
2002 } // endif irc
2003
2004 if (Tpos == Spos) {
2005 /*******************************************************************/
2006 /* First line to delete, Open temporary file. */
2007 /*******************************************************************/
2008 if (UseTemp) {
2009 if (OpenTempFile(g))
2010 return RC_FX;
2011
2012 } else {
2013 /*****************************************************************/
2014 /* Move of eventual preceding lines is not required here. */
2015 /* Set the target file as being the source file itself. */
2016 /* Set the future Tpos, and give Spos a value to block copying. */
2017 /*****************************************************************/
2018 T_Stream = Stream;
2019 Spos = Tpos = Fpos;
2020 } // endif UseTemp
2021
2022 } // endif Tpos == Spos
2023
2024 /*********************************************************************/
2025 /* Move any intermediate lines. */
2026 /*********************************************************************/
2027 if (MoveIntermediateLines(g, &moved))
2028 return RC_FX;
2029
2030 if (irc == RC_OK) {
2031 /*******************************************************************/
2032 /* Reposition the file pointer and set Spos. */
2033 /*******************************************************************/
2034 if (!UseTemp || moved)
2035 if (fseek(Stream, curpos, SEEK_SET)) {
2036 sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
2037 return RC_FX;
2038 } // endif
2039
2040 Spos = GetNextPos(); // New start position
2041
2042 if (trace(1))
2043 htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
2044
2045 } else {
2046 /*******************************************************************/
2047 /* Last call after EOF has been reached. */
2048 /* The UseTemp case is treated in CloseTableFile. */
2049 /*******************************************************************/
2050 if (!UseTemp & !Abort) {
2051 /*****************************************************************/
2052 /* Because the chsize functionality is only accessible with a */
2053 /* system call we must close the file and reopen it with the */
2054 /* open function (_fopen for MS ??) this is still to be checked */
2055 /* for compatibility with Text files and other OS's. */
2056 /*****************************************************************/
2057 char filename[_MAX_PATH];
2058 int h; // File handle, return code
2059
2060 PlugSetPath(filename, To_File, Tdbp->GetPath());
2061 /*rc=*/ PlugCloseFile(g, To_Fb);
2062
2063 if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0)
2064 return RC_FX;
2065
2066 /*****************************************************************/
2067 /* Remove extra records. */
2068 /*****************************************************************/
2069 #if defined(_WIN32)
2070 if (chsize(h, Tpos)) {
2071 sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno));
2072 close(h);
2073 return RC_FX;
2074 } // endif
2075 #else
2076 if (ftruncate(h, (off_t)Tpos)) {
2077 sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
2078 close(h);
2079 return RC_FX;
2080 } // endif
2081 #endif
2082
2083 close(h);
2084
2085 if (trace(1))
2086 htrc("done, h=%d irc=%d\n", h, irc);
2087
2088 } // endif !UseTemp
2089
2090 } // endif irc
2091
2092 return RC_OK; // All is correct
2093 } // end of DeleteRecords
2094
2095 /***********************************************************************/
2096 /* Table file close routine for DOS access method. */
2097 /***********************************************************************/
2098 void BINFAM::CloseTableFile(PGLOBAL g, bool abort)
2099 {
2100 int rc;
2101
2102 Abort = abort;
2103 rc = PlugCloseFile(g, To_Fb);
2104 xtrc(1, "BIN Close: closing %s rc=%d\n", To_File, rc);
2105 Stream = NULL; // So we can know whether table is open
2106 } // end of CloseTableFile
2107
2108 /***********************************************************************/
2109 /* Rewind routine for BIN access method. */
2110 /***********************************************************************/
2111 void BINFAM::Rewind(void)
2112 {
2113 if (Stream) // Can be NULL when making index on void table
2114 rewind(Stream);
2115
2116 Rows = 0;
2117 OldBlk = CurBlk = -1;
2118 } // end of Rewind
2119 #endif // 0
2120