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