| 1 | /*********** File AM Map C++ Program Source Code File (.CPP) ***********/ | 
| 2 | /* PROGRAM NAME: FILAMAP                                               */ | 
| 3 | /* -------------                                                       */ | 
| 4 | /*  Version 1.6                                                        */ | 
| 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 MAP 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 | #if defined(__BORLANDC__) | 
| 22 | #define __MFC_COMPAT__                   // To define min/max as macro | 
| 23 | #endif   // __BORLANDC__ | 
| 24 | //#include <windows.h> | 
| 25 | #else    // !__WIN__ | 
| 26 | #if defined(UNIX) | 
| 27 | #include <errno.h> | 
| 28 | #include <unistd.h> | 
| 29 | #else    // !UNIX | 
| 30 | #include <io.h> | 
| 31 | #endif  // !UNIX | 
| 32 | #include <fcntl.h> | 
| 33 | #endif  // !__WIN__ | 
| 34 |  | 
| 35 | /***********************************************************************/ | 
| 36 | /*  Include application header files:                                  */ | 
| 37 | /*  global.h    is header containing all global declarations.          */ | 
| 38 | /*  plgdbsem.h  is header containing the DB application declarations.  */ | 
| 39 | /*  filamtxt.h  is header containing the file AM classes declarations. */ | 
| 40 | /*  Note: these files are included inside the include files below.     */ | 
| 41 | /***********************************************************************/ | 
| 42 | #include "global.h" | 
| 43 | #include "plgdbsem.h" | 
| 44 | #include "osutil.h" | 
| 45 | #include "maputil.h" | 
| 46 | #include "filamap.h" | 
| 47 | #include "tabdos.h" | 
| 48 | #include "tabfmt.h" | 
| 49 |  | 
| 50 | /* --------------------------- Class MAPFAM -------------------------- */ | 
| 51 |  | 
| 52 | /***********************************************************************/ | 
| 53 | /*  Constructors.                                                      */ | 
| 54 | /***********************************************************************/ | 
| 55 | MAPFAM::MAPFAM(PDOSDEF tdp) : TXTFAM(tdp) | 
| 56 |   { | 
| 57 |   Memory = NULL; | 
| 58 |   Mempos = NULL; | 
| 59 |   Tpos = NULL; | 
| 60 |   Fpos = NULL; | 
| 61 |   Spos = NULL; | 
| 62 |   Top = NULL; | 
| 63 |   } // end of MAPFAM standard constructor | 
| 64 |  | 
| 65 | MAPFAM::MAPFAM(PMAPFAM tmfp) : TXTFAM(tmfp) | 
| 66 |   { | 
| 67 |   Memory = tmfp->Memory; | 
| 68 |   Mempos = tmfp->Mempos; | 
| 69 |   Fpos = tmfp->Fpos; | 
| 70 |   Spos = tmfp->Spos; | 
| 71 |   Tpos = tmfp->Tpos; | 
| 72 |   Top = tmfp->Top; | 
| 73 |   } // end of MAPFAM copy constructor | 
| 74 |  | 
| 75 | /***********************************************************************/ | 
| 76 | /*  Reset: reset position values at the beginning of file.             */ | 
| 77 | /***********************************************************************/ | 
| 78 | void MAPFAM::Reset(void) | 
| 79 |   { | 
| 80 |   TXTFAM::Reset(); | 
| 81 |   Fpos = Tpos = Spos = NULL; | 
| 82 |   }  // end of Reset | 
| 83 |  | 
| 84 | /***********************************************************************/ | 
| 85 | /*  MAP GetFileLength: returns file size in number of bytes.           */ | 
| 86 | /***********************************************************************/ | 
| 87 | int MAPFAM::GetFileLength(PGLOBAL g) | 
| 88 |   { | 
| 89 |   int len; | 
| 90 |  | 
| 91 |   len = (To_Fb && To_Fb->Count) ? To_Fb->Length : TXTFAM::GetFileLength(g); | 
| 92 |  | 
| 93 |   if (trace(1)) | 
| 94 |     htrc("Mapped file length=%d\n" , len); | 
| 95 |  | 
| 96 |   return len; | 
| 97 |   } // end of GetFileLength | 
| 98 |  | 
| 99 | /***********************************************************************/ | 
| 100 | /*  OpenTableFile: Open a DOS/UNIX table file as a mapped file.        */ | 
| 101 | /***********************************************************************/ | 
| 102 | bool MAPFAM::OpenTableFile(PGLOBAL g) | 
| 103 |   { | 
| 104 |   char    filename[_MAX_PATH]; | 
| 105 |   int     len; | 
| 106 |   MODE    mode = Tdbp->GetMode(); | 
| 107 |   PFBLOCK fp; | 
| 108 |   PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr; | 
| 109 |  | 
| 110 | #if defined(_DEBUG) | 
| 111 |   // Insert mode is no more handled using file mapping | 
| 112 |   assert(mode != MODE_INSERT); | 
| 113 | #endif   // _DEBUG | 
| 114 |  | 
| 115 |   /*********************************************************************/ | 
| 116 |   /*  We used the file name relative to recorded datapath.             */ | 
| 117 |   /*********************************************************************/ | 
| 118 |   PlugSetPath(filename, To_File, Tdbp->GetPath()); | 
| 119 |  | 
| 120 |   /*********************************************************************/ | 
| 121 |   /*  Under Win32 the whole file will be mapped so we can use it as    */ | 
| 122 |   /*  if it were entirely read into virtual memory.                    */ | 
| 123 |   /*  Firstly we check whether this file have been already mapped.     */ | 
| 124 |   /*********************************************************************/ | 
| 125 |   if (mode == MODE_READ) { | 
| 126 |     for (fp = dbuserp->Openlist; fp; fp = fp->Next) | 
| 127 |       if (fp->Type == TYPE_FB_MAP && !stricmp(fp->Fname, filename) | 
| 128 |                      && fp->Count && fp->Mode == mode) | 
| 129 |         break; | 
| 130 |  | 
| 131 | 		if (trace(1)) | 
| 132 |       htrc("Mapping file, fp=%p\n" , fp); | 
| 133 |  | 
| 134 |   } else | 
| 135 |     fp = NULL; | 
| 136 |  | 
| 137 |   if (fp) { | 
| 138 |     /*******************************************************************/ | 
| 139 |     /*  File already mapped. Just increment use count and get pointer. */ | 
| 140 |     /*******************************************************************/ | 
| 141 |     fp->Count++; | 
| 142 |     Memory = fp->Memory; | 
| 143 |     len = fp->Length; | 
| 144 |   } else { | 
| 145 |     /*******************************************************************/ | 
| 146 |     /*  If required, delete the whole file if no filtering is implied. */ | 
| 147 |     /*******************************************************************/ | 
| 148 |     bool   del; | 
| 149 |     HANDLE hFile; | 
| 150 |     MEMMAP mm; | 
| 151 |  | 
| 152 |     del = mode == MODE_DELETE && !Tdbp->GetNext(); | 
| 153 |  | 
| 154 |     if (del) | 
| 155 |       DelRows = Cardinality(g); | 
| 156 |  | 
| 157 |     /*******************************************************************/ | 
| 158 |     /*  Create the mapping file object.                                */ | 
| 159 |     /*******************************************************************/ | 
| 160 |     hFile = CreateFileMap(g, filename, &mm, mode, del); | 
| 161 |  | 
| 162 |     if (hFile == INVALID_HANDLE_VALUE) { | 
| 163 |       DWORD rc = GetLastError(); | 
| 164 |  | 
| 165 |       if (!(*g->Message)) | 
| 166 |         sprintf(g->Message, MSG(OPEN_MODE_ERROR), | 
| 167 |                 "map" , (int) rc, filename); | 
| 168 |  | 
| 169 |       if (trace(1)) | 
| 170 |         htrc("CreateFileMap: %s\n" , g->Message); | 
| 171 |  | 
| 172 |       return (mode == MODE_READ && rc == ENOENT) | 
| 173 |               ? PushWarning(g, Tdbp) : true; | 
| 174 |       } // endif hFile | 
| 175 |  | 
| 176 |     /*******************************************************************/ | 
| 177 |     /*  Get the file size (assuming file is smaller than 4 GB)         */ | 
| 178 |     /*******************************************************************/ | 
| 179 |     len = mm.lenL; | 
| 180 |     Memory = (char *)mm.memory; | 
| 181 |  | 
| 182 |     if (!len) {              // Empty or deleted file | 
| 183 |       CloseFileHandle(hFile); | 
| 184 |       Tdbp->ResetSize(); | 
| 185 |       return false; | 
| 186 |       } // endif len | 
| 187 |  | 
| 188 |     if (!Memory) { | 
| 189 |       CloseFileHandle(hFile); | 
| 190 |       sprintf(g->Message, MSG(MAP_VIEW_ERROR), | 
| 191 |                           filename, GetLastError()); | 
| 192 |       return true; | 
| 193 |       } // endif Memory | 
| 194 |  | 
| 195 | #if defined(__WIN__) | 
| 196 |     if (mode != MODE_DELETE) { | 
| 197 | #else   // !__WIN__ | 
| 198 |     if (mode == MODE_READ) { | 
| 199 | #endif  // !__WIN__ | 
| 200 |       CloseFileHandle(hFile);                    // Not used anymore | 
| 201 |       hFile = INVALID_HANDLE_VALUE;              // For Fblock | 
| 202 |       } // endif Mode | 
| 203 |  | 
| 204 |     /*******************************************************************/ | 
| 205 |     /*  Link a Fblock. This make possible to reuse already opened maps */ | 
| 206 |     /*  and also to automatically unmap them in case of error g->jump. */ | 
| 207 |     /*  Note: block can already exist for previously closed file.      */ | 
| 208 |     /*******************************************************************/ | 
| 209 |     fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); | 
| 210 |     fp->Type = TYPE_FB_MAP; | 
| 211 |     fp->Fname = PlugDup(g, filename); | 
| 212 |     fp->Next = dbuserp->Openlist; | 
| 213 |     dbuserp->Openlist = fp; | 
| 214 |     fp->Count = 1; | 
| 215 |     fp->Length = len; | 
| 216 |     fp->Memory = Memory; | 
| 217 |     fp->Mode = mode; | 
| 218 |     fp->File = NULL; | 
| 219 |     fp->Handle = hFile;                // Used for Delete | 
| 220 |   } // endif fp | 
| 221 |  | 
| 222 |   To_Fb = fp;                               // Useful when closing | 
| 223 |  | 
| 224 |   /*********************************************************************/ | 
| 225 |   /*  The pseudo "buffer" is here the entire file mapping view.        */ | 
| 226 |   /*********************************************************************/ | 
| 227 |   Fpos = Mempos = Memory; | 
| 228 |   Top = Memory + len; | 
| 229 |  | 
| 230 |   if (trace(1)) | 
| 231 |     htrc("fp=%p count=%d MapView=%p len=%d Top=%p\n" , | 
| 232 |           fp, fp->Count, Memory, len, Top); | 
| 233 |  | 
| 234 |   return AllocateBuffer(g);          // Useful for DBF files | 
| 235 |   } // end of OpenTableFile | 
| 236 |  | 
| 237 | /***********************************************************************/ | 
| 238 | /*  GetRowID: return the RowID of last read record.                    */ | 
| 239 | /***********************************************************************/ | 
| 240 | int MAPFAM::GetRowID(void) | 
| 241 |   { | 
| 242 |   return Rows; | 
| 243 |   } // end of GetRowID | 
| 244 |  | 
| 245 | /***********************************************************************/ | 
| 246 | /*  GetPos: return the position of last read record.                   */ | 
| 247 | /***********************************************************************/ | 
| 248 | int MAPFAM::GetPos(void) | 
| 249 |   { | 
| 250 |   return (int)(Fpos - Memory); | 
| 251 |   } // end of GetPos | 
| 252 |  | 
| 253 | /***********************************************************************/ | 
| 254 | /*  GetNextPos: return the position of next record.                    */ | 
| 255 | /***********************************************************************/ | 
| 256 | int MAPFAM::GetNextPos(void) | 
| 257 |   { | 
| 258 |   return (int)(Mempos - Memory); | 
| 259 |   } // end of GetNextPos | 
| 260 |  | 
| 261 | /***********************************************************************/ | 
| 262 | /*  SetPos: Replace the table at the specified position.               */ | 
| 263 | /***********************************************************************/ | 
| 264 | bool MAPFAM::SetPos(PGLOBAL g, int pos) | 
| 265 |   { | 
| 266 |   Fpos = Mempos = Memory + pos; | 
| 267 |  | 
| 268 |   if (Mempos >= Top || Mempos < Memory) { | 
| 269 |     strcpy(g->Message, MSG(INV_MAP_POS)); | 
| 270 |     return true; | 
| 271 |     } // endif Mempos | 
| 272 |  | 
| 273 |   Placed = true; | 
| 274 |   return false; | 
| 275 |   } // end of SetPos | 
| 276 |  | 
| 277 | /***********************************************************************/ | 
| 278 | /*  Record file position in case of UPDATE or DELETE.                  */ | 
| 279 | /***********************************************************************/ | 
| 280 | bool MAPFAM::RecordPos(PGLOBAL) | 
| 281 |   { | 
| 282 |   Fpos = Mempos; | 
| 283 |   return false; | 
| 284 |   } // end of RecordPos | 
| 285 |  | 
| 286 | /***********************************************************************/ | 
| 287 | /*  Initialize Fpos and Mempos for indexed DELETE.                     */ | 
| 288 | /***********************************************************************/ | 
| 289 | int MAPFAM::InitDelete(PGLOBAL, int fpos, int spos) | 
| 290 |   { | 
| 291 |   Fpos = Memory + (ptrdiff_t)fpos; | 
| 292 |   Mempos = Memory + (ptrdiff_t)spos; | 
| 293 |   return RC_OK; | 
| 294 |   } // end of InitDelete | 
| 295 |  | 
| 296 | /***********************************************************************/ | 
| 297 | /*  Skip one record in file.                                           */ | 
| 298 | /***********************************************************************/ | 
| 299 | int MAPFAM::SkipRecord(PGLOBAL g, bool ) | 
| 300 |   { | 
| 301 |   PDBUSER dup = (PDBUSER)g->Activityp->Aptr; | 
| 302 |  | 
| 303 |   // Skip this record | 
| 304 |   while (*Mempos++ != '\n')					 		// What about Unix ??? | 
| 305 | 		if (Mempos == Top) | 
| 306 |       return RC_EF; | 
| 307 |  | 
| 308 |   // Update progress information | 
| 309 |   dup->ProgCur = GetPos(); | 
| 310 |  | 
| 311 |   if (header) | 
| 312 |     Fpos = Tpos = Spos = Mempos;      // For Delete | 
| 313 |  | 
| 314 |   return RC_OK; | 
| 315 |   } // end of SkipRecord | 
| 316 |  | 
| 317 | /***********************************************************************/ | 
| 318 | /*  ReadBuffer: Read one line for a mapped text file.                  */ | 
| 319 | /***********************************************************************/ | 
| 320 | int MAPFAM::ReadBuffer(PGLOBAL g) | 
| 321 |   { | 
| 322 |   int rc, len, n = 1; | 
| 323 |  | 
| 324 |   // Are we at the end of the memory | 
| 325 | 	if (Mempos >= Top) { | 
| 326 | 		if ((rc = GetNext(g)) != RC_OK) | 
| 327 | 			return rc; | 
| 328 | 		else if (Tdbp->GetAmType() == TYPE_AM_CSV && ((PTDBCSV)Tdbp)->Header) | 
| 329 | 			if ((rc = SkipRecord(g, true)) != RC_OK) | 
| 330 | 				return rc; | 
| 331 |  | 
| 332 | 	}	// endif Mempos | 
| 333 |  | 
| 334 |  | 
| 335 |   if (!Placed) { | 
| 336 |     /*******************************************************************/ | 
| 337 |     /*  Record file position in case of UPDATE or DELETE.              */ | 
| 338 |     /*******************************************************************/ | 
| 339 |    next: | 
| 340 |     Fpos = Mempos; | 
| 341 |     CurBlk = (int)Rows++; | 
| 342 |  | 
| 343 |     /*******************************************************************/ | 
| 344 |     /*  Check whether optimization on ROWID                            */ | 
| 345 |     /*  can be done, as well as for join as for local filtering.       */ | 
| 346 |     /*******************************************************************/ | 
| 347 |     switch (Tdbp->TestBlock(g)) { | 
| 348 |       case RC_EF: | 
| 349 | 				if ((rc = GetNext(g)) != RC_OK) | 
| 350 | 					return rc; | 
| 351 |  | 
| 352 | 			case RC_NF: | 
| 353 |         // Skip this record | 
| 354 |         if ((rc = SkipRecord(g, false)) != RC_OK) | 
| 355 |           return rc; | 
| 356 |  | 
| 357 |         goto next; | 
| 358 |       } // endswitch rc | 
| 359 |  | 
| 360 |   } else | 
| 361 |     Placed = false; | 
| 362 |  | 
| 363 |   // Immediately calculate next position (Used by DeleteDB) | 
| 364 |   while (*Mempos++ != '\n')          // What about Unix ??? | 
| 365 | 		if (Mempos == Top) { | 
| 366 | 			n = 0; | 
| 367 | 			break; | 
| 368 | 		}	// endif Mempos | 
| 369 |  | 
| 370 |   // Set caller line buffer | 
| 371 |   len = (int)(Mempos - Fpos) - n; | 
| 372 |  | 
| 373 |   // Don't rely on ENDING setting | 
| 374 |   if (len > 0 && *(Mempos - 2) == '\r') | 
| 375 |     len--;             // Line ends by CRLF | 
| 376 |  | 
| 377 |   memcpy(Tdbp->GetLine(), Fpos, len); | 
| 378 |   Tdbp->GetLine()[len] = '\0'; | 
| 379 |   return RC_OK; | 
| 380 |   } // end of ReadBuffer | 
| 381 |  | 
| 382 | /***********************************************************************/ | 
| 383 | /*  WriteBuffer: File write routine for MAP access method.             */ | 
| 384 | /***********************************************************************/ | 
| 385 | int MAPFAM::WriteBuffer(PGLOBAL g __attribute__((unused))) | 
| 386 |   { | 
| 387 | #if defined(_DEBUG) | 
| 388 |   // Insert mode is no more handled using file mapping | 
| 389 |   if (Tdbp->GetMode() == MODE_INSERT) { | 
| 390 |     strcpy(g->Message, MSG(NO_MAP_INSERT)); | 
| 391 |     return RC_FX; | 
| 392 |     } // endif | 
| 393 | #endif   // _DEBUG | 
| 394 |  | 
| 395 |   /*********************************************************************/ | 
| 396 |   /*  Copy the updated record back into the memory mapped file.        */ | 
| 397 |   /*********************************************************************/ | 
| 398 |   memcpy(Fpos, Tdbp->GetLine(), strlen(Tdbp->GetLine())); | 
| 399 |   return RC_OK; | 
| 400 |   } // end of WriteBuffer | 
| 401 |  | 
| 402 | /***********************************************************************/ | 
| 403 | /*  Data Base delete line routine for MAP (and FIX?) access methods.   */ | 
| 404 | /*  Lines between deleted lines are moved in the mapfile view.         */ | 
| 405 | /***********************************************************************/ | 
| 406 | int MAPFAM::DeleteRecords(PGLOBAL g, int irc) | 
| 407 |   { | 
| 408 |   int    n; | 
| 409 |  | 
| 410 |   if (trace(1)) | 
| 411 |     htrc("MAP DeleteDB: irc=%d mempos=%p tobuf=%p Tpos=%p Spos=%p\n" , | 
| 412 |          irc, Mempos, To_Buf, Tpos, Spos); | 
| 413 |  | 
| 414 |   if (irc != RC_OK) { | 
| 415 |     /*******************************************************************/ | 
| 416 |     /*  EOF: position Fpos at the top of map position.                 */ | 
| 417 |     /*******************************************************************/ | 
| 418 |     Fpos = Top; | 
| 419 |  | 
| 420 |     if (trace(1)) | 
| 421 |       htrc("Fpos placed at file top=%p\n" , Fpos); | 
| 422 |  | 
| 423 |     } // endif irc | 
| 424 |  | 
| 425 |   if (Tpos == Spos) { | 
| 426 |     /*******************************************************************/ | 
| 427 |     /*  First line to delete. Move of eventual preceding lines is     */ | 
| 428 |     /*  not required here, just setting of future Spos and Tpos.       */ | 
| 429 |     /*******************************************************************/ | 
| 430 |     Tpos = Spos = Fpos; | 
| 431 |   } else if ((n = (int)(Fpos - Spos)) > 0) { | 
| 432 |     /*******************************************************************/ | 
| 433 |     /*  Non consecutive line to delete. Move intermediate lines.       */ | 
| 434 |     /*******************************************************************/ | 
| 435 |     memmove(Tpos, Spos, n); | 
| 436 |     Tpos += n; | 
| 437 |  | 
| 438 |     if (trace(1)) | 
| 439 |       htrc("move %d bytes\n" , n); | 
| 440 |  | 
| 441 |   } // endif n | 
| 442 |  | 
| 443 |   if (irc == RC_OK) { | 
| 444 |     Spos = Mempos;                               // New start position | 
| 445 |  | 
| 446 |     if (trace(1)) | 
| 447 |       htrc("after: Tpos=%p Spos=%p\n" , Tpos, Spos); | 
| 448 |  | 
| 449 |   } else if (To_Fb) {                 // Can be NULL for deleted files | 
| 450 |     /*******************************************************************/ | 
| 451 |     /*  Last call after EOF has been reached.                          */ | 
| 452 |     /*  We must firstly Unmap the view and use the saved file handle   */ | 
| 453 |     /*  to put an EOF at the end of the copied part of the file.       */ | 
| 454 |     /*******************************************************************/ | 
| 455 |     PFBLOCK fp = To_Fb; | 
| 456 |  | 
| 457 |     CloseMemMap(fp->Memory, (size_t)fp->Length); | 
| 458 |     fp->Count = 0;                             // Avoid doing it twice | 
| 459 |  | 
| 460 |     if (!Abort) { | 
| 461 |       /*****************************************************************/ | 
| 462 |       /*  Remove extra records.                                        */ | 
| 463 |       /*****************************************************************/ | 
| 464 |       n = (int)(Tpos - Memory); | 
| 465 |  | 
| 466 | #if defined(__WIN__) | 
| 467 |       DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN); | 
| 468 |  | 
| 469 |       if (drc == 0xFFFFFFFF) { | 
| 470 |         sprintf(g->Message, MSG(FUNCTION_ERROR), | 
| 471 |                             "SetFilePointer" , GetLastError()); | 
| 472 |         CloseHandle(fp->Handle); | 
| 473 |         return RC_FX; | 
| 474 |         } // endif | 
| 475 |  | 
| 476 |       if (trace(1)) | 
| 477 |        htrc("done, Tpos=%p newsize=%d drc=%d\n" , Tpos, n, drc); | 
| 478 |  | 
| 479 |       if (!SetEndOfFile(fp->Handle)) { | 
| 480 |         sprintf(g->Message, MSG(FUNCTION_ERROR), | 
| 481 |                             "SetEndOfFile" , GetLastError()); | 
| 482 |         CloseHandle(fp->Handle); | 
| 483 |         return RC_FX; | 
| 484 |         } // endif | 
| 485 |  | 
| 486 | #else    // UNIX | 
| 487 |       if (ftruncate(fp->Handle, (off_t)n)) { | 
| 488 |         sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); | 
| 489 |         close(fp->Handle); | 
| 490 |         return RC_FX; | 
| 491 |         } // endif | 
| 492 |  | 
| 493 | #endif   // UNIX | 
| 494 |     } // endif Abort | 
| 495 |  | 
| 496 | #if defined(__WIN__) | 
| 497 |   CloseHandle(fp->Handle); | 
| 498 | #else    // UNIX | 
| 499 |   close(fp->Handle); | 
| 500 | #endif   // UNIX | 
| 501 |   } // endif irc | 
| 502 |  | 
| 503 |   return RC_OK;                                      // All is correct | 
| 504 |   } // end of DeleteRecords | 
| 505 |  | 
| 506 | /***********************************************************************/ | 
| 507 | /*  Table file close routine for MAP access method.                    */ | 
| 508 | /***********************************************************************/ | 
| 509 | void MAPFAM::CloseTableFile(PGLOBAL g, bool) | 
| 510 |   { | 
| 511 |   PlugCloseFile(g, To_Fb); | 
| 512 | //To_Fb = NULL;              // To get correct file size in Cardinality | 
| 513 |  | 
| 514 |   if (trace(1)) | 
| 515 |     htrc("MAP Close: closing %s count=%d\n" , | 
| 516 |          To_File, (To_Fb) ? To_Fb->Count : 0); | 
| 517 |  | 
| 518 |   } // end of CloseTableFile | 
| 519 |  | 
| 520 | /***********************************************************************/ | 
| 521 | /*  Rewind routine for MAP access method.                              */ | 
| 522 | /***********************************************************************/ | 
| 523 | void MAPFAM::Rewind(void) | 
| 524 |   { | 
| 525 |   Mempos = Memory; | 
| 526 |   } // end of Rewind | 
| 527 |  | 
| 528 | /* --------------------------- Class MBKFAM -------------------------- */ | 
| 529 |  | 
| 530 | /***********************************************************************/ | 
| 531 | /*  Constructors.                                                      */ | 
| 532 | /***********************************************************************/ | 
| 533 | MBKFAM::MBKFAM(PDOSDEF tdp) : MAPFAM(tdp) | 
| 534 |   { | 
| 535 |   Blocked = true; | 
| 536 |   Block = tdp->GetBlock(); | 
| 537 |   Last = tdp->GetLast(); | 
| 538 |   Nrec = tdp->GetElemt(); | 
| 539 |   BlkPos = tdp->GetTo_Pos(); | 
| 540 |   CurNum = Nrec; | 
| 541 |   } // end of MBKFAM standard constructor | 
| 542 |  | 
| 543 | /***********************************************************************/ | 
| 544 | /*  Reset: reset position values at the beginning of file.             */ | 
| 545 | /***********************************************************************/ | 
| 546 | void MBKFAM::Reset(void) | 
| 547 |   { | 
| 548 |   MAPFAM::Reset(); | 
| 549 |   CurNum = Nrec;              // To start by a new block | 
| 550 |   }  // end of Reset | 
| 551 |  | 
| 552 | /***********************************************************************/ | 
| 553 | /*  Cardinality: returns table cardinality in number of rows.          */ | 
| 554 | /*  This function can be called with a null argument to test the       */ | 
| 555 | /*  availability of Cardinality implementation (1 yes, 0 no).          */ | 
| 556 | /***********************************************************************/ | 
| 557 | int MBKFAM::Cardinality(PGLOBAL g) | 
| 558 |   { | 
| 559 |   return (g) ? (int)((Block - 1) * Nrec + Last) : 1; | 
| 560 |   } // end of Cardinality | 
| 561 |  | 
| 562 | /***********************************************************************/ | 
| 563 | /*  Skip one record in file.                                           */ | 
| 564 | /***********************************************************************/ | 
| 565 | int MBKFAM::SkipRecord(PGLOBAL, bool) | 
| 566 |   { | 
| 567 |   return RC_OK; | 
| 568 |   } // end of SkipRecord | 
| 569 |  | 
| 570 | /***********************************************************************/ | 
| 571 | /*  GetRowID: return the RowID of last read record.                    */ | 
| 572 | /***********************************************************************/ | 
| 573 | int MBKFAM::GetRowID(void) | 
| 574 |   { | 
| 575 |   return CurNum + Nrec * CurBlk + 1; | 
| 576 |   } // end of GetRowID | 
| 577 |  | 
| 578 | /***********************************************************************/ | 
| 579 | /*  ReadBuffer: Read one line for a mapped Fix file.                   */ | 
| 580 | /***********************************************************************/ | 
| 581 | int MBKFAM::ReadBuffer(PGLOBAL g) | 
| 582 |   { | 
| 583 |   int rc, len; | 
| 584 |  | 
| 585 |   /*********************************************************************/ | 
| 586 |   /*  Sequential block reading when Placed is not true.                */ | 
| 587 |   /*********************************************************************/ | 
| 588 |   if (Placed) { | 
| 589 |     Placed = false; | 
| 590 |   } else if (Mempos >= Top) {        // Are we at the end of the memory | 
| 591 | 		if ((rc = GetNext(g)) != RC_OK) | 
| 592 | 			return rc; | 
| 593 |  | 
| 594 | 	} else if (++CurNum < Nrec) { | 
| 595 |     Fpos = Mempos; | 
| 596 |   } else { | 
| 597 |     /*******************************************************************/ | 
| 598 |     /*  New block.                                                     */ | 
| 599 |     /*******************************************************************/ | 
| 600 |     CurNum = 0; | 
| 601 |  | 
| 602 |    next: | 
| 603 |     if (++CurBlk >= Block) | 
| 604 | 			if ((rc = GetNext(g)) != RC_OK) | 
| 605 | 				return rc; | 
| 606 |  | 
| 607 |     /*******************************************************************/ | 
| 608 |     /*  Before reading a new block, check whether block optimization   */ | 
| 609 |     /*  can be done, as well as for join as for local filtering.       */ | 
| 610 |     /*******************************************************************/ | 
| 611 |     switch (Tdbp->TestBlock(g)) { | 
| 612 |       case RC_EF: | 
| 613 | 				if ((rc = GetNext(g)) != RC_OK) | 
| 614 | 					return rc; | 
| 615 |  | 
| 616 | 				break; | 
| 617 | 			case RC_NF: | 
| 618 |         goto next; | 
| 619 |       } // endswitch rc | 
| 620 |  | 
| 621 |     Fpos = Mempos = Memory + BlkPos[CurBlk]; | 
| 622 |   } // endif's | 
| 623 |  | 
| 624 |   // Immediately calculate next position (Used by DeleteDB) | 
| 625 | 	while (*Mempos++ != '\n')          // What about Unix ??? | 
| 626 | 		if (Mempos == Top) | 
| 627 | 			break; | 
| 628 |  | 
| 629 |   // Set caller line buffer | 
| 630 |   len = (int)(Mempos - Fpos) - Ending; | 
| 631 |   memcpy(Tdbp->GetLine(), Fpos, len); | 
| 632 |   Tdbp->GetLine()[len] = '\0'; | 
| 633 |   return RC_OK; | 
| 634 |   } // end of ReadBuffer | 
| 635 |  | 
| 636 | /***********************************************************************/ | 
| 637 | /*  Rewind routine for FIX MAP access method.                          */ | 
| 638 | /***********************************************************************/ | 
| 639 | void MBKFAM::Rewind(void) | 
| 640 |   { | 
| 641 |   Mempos = Memory + Headlen; | 
| 642 |   CurBlk = -1; | 
| 643 |   CurNum = Nrec; | 
| 644 |   } // end of Rewind | 
| 645 |  | 
| 646 | /* --------------------------- Class MPXFAM -------------------------- */ | 
| 647 |  | 
| 648 | /***********************************************************************/ | 
| 649 | /*  Constructors.                                                      */ | 
| 650 | /***********************************************************************/ | 
| 651 | MPXFAM::MPXFAM(PDOSDEF tdp) : MBKFAM(tdp) | 
| 652 |   { | 
| 653 |   Blksize = tdp->GetBlksize(); | 
| 654 |   Padded = tdp->GetPadded(); | 
| 655 |  | 
| 656 |   if (Padded && Blksize) | 
| 657 |     Nrec = Blksize / Lrecl; | 
| 658 |   else { | 
| 659 |     Nrec = (tdp->GetElemt()) ? tdp->GetElemt() : DOS_BUFF_LEN; | 
| 660 |     Blksize = Nrec * Lrecl; | 
| 661 |     Padded = false; | 
| 662 |     } // endelse | 
| 663 |  | 
| 664 |   CurNum = Nrec; | 
| 665 |   } // end of MPXFAM standard constructor | 
| 666 |  | 
| 667 | #if 0                 // MBKFAM routine is correct | 
| 668 | /***********************************************************************/ | 
| 669 | /*  GetRowID: return the RowID of last read record.                    */ | 
| 670 | /***********************************************************************/ | 
| 671 | int MPXFAM::GetRowID(void) | 
| 672 |   { | 
| 673 |   return (Mempos - Memory - Headlen) / Lrecl; | 
| 674 |   } // end of GetRowID | 
| 675 | #endif | 
| 676 |  | 
| 677 | /***********************************************************************/ | 
| 678 | /*  GetPos: return the position of last read record.                   */ | 
| 679 | /***********************************************************************/ | 
| 680 | int MPXFAM::GetPos(void) | 
| 681 |   { | 
| 682 |   return (CurNum + Nrec * CurBlk);          // Computed file index | 
| 683 |   } // end of GetPos | 
| 684 |  | 
| 685 | /***********************************************************************/ | 
| 686 | /*  SetPos: Replace the table at the specified position.               */ | 
| 687 | /***********************************************************************/ | 
| 688 | bool MPXFAM::SetPos(PGLOBAL g, int pos) | 
| 689 |   { | 
| 690 |   if (pos < 0) { | 
| 691 |     strcpy(g->Message, MSG(INV_REC_POS)); | 
| 692 |     return true; | 
| 693 |     } // endif recpos | 
| 694 |  | 
| 695 |   CurBlk = pos / Nrec; | 
| 696 |   CurNum = pos % Nrec; | 
| 697 |   Fpos = Mempos = Memory + Headlen + pos * Lrecl; | 
| 698 |  | 
| 699 |   // Indicate the table position was externally set | 
| 700 |   Placed = true; | 
| 701 |   return false; | 
| 702 |   } // end of SetPos | 
| 703 |  | 
| 704 | /***********************************************************************/ | 
| 705 | /*  Initialize CurBlk, CurNum, Mempos and Fpos for indexed DELETE.     */ | 
| 706 | /***********************************************************************/ | 
| 707 | int MPXFAM::InitDelete(PGLOBAL, int fpos, int) | 
| 708 |   { | 
| 709 |   Fpos = Memory + Headlen + (ptrdiff_t)fpos * Lrecl; | 
| 710 |   Mempos = Fpos + Lrecl; | 
| 711 |   return RC_OK; | 
| 712 |   } // end of InitDelete | 
| 713 |  | 
| 714 | /***********************************************************************/ | 
| 715 | /*  ReadBuffer: Read one line for a mapped Fix file.                   */ | 
| 716 | /***********************************************************************/ | 
| 717 | int MPXFAM::ReadBuffer(PGLOBAL g) | 
| 718 |   { | 
| 719 | 	int rc; | 
| 720 |  | 
| 721 |   /*********************************************************************/ | 
| 722 |   /*  Sequential block reading when Placed is not true.                */ | 
| 723 |   /*********************************************************************/ | 
| 724 |   if (Placed) { | 
| 725 |     Placed = false; | 
| 726 |   } else if (Mempos >= Top) {        // Are we at the end of the memory | 
| 727 | 		if ((rc = GetNext(g)) != RC_OK) | 
| 728 | 			return rc; | 
| 729 |  | 
| 730 | 	} else if (++CurNum < Nrec) { | 
| 731 |     Fpos = Mempos; | 
| 732 |   } else { | 
| 733 |     /*******************************************************************/ | 
| 734 |     /*  New block.                                                     */ | 
| 735 |     /*******************************************************************/ | 
| 736 |     CurNum = 0; | 
| 737 |  | 
| 738 |    next: | 
| 739 |     if (++CurBlk >= Block) | 
| 740 | 			return GetNext(g); | 
| 741 |  | 
| 742 |     /*******************************************************************/ | 
| 743 |     /*  Before reading a new block, check whether block optimization   */ | 
| 744 |     /*  can be done, as well as for join as for local filtering.       */ | 
| 745 |     /*******************************************************************/ | 
| 746 |     switch (Tdbp->TestBlock(g)) { | 
| 747 |       case RC_EF: | 
| 748 | 				if ((rc = GetNext(g)) != RC_OK) | 
| 749 | 					return rc; | 
| 750 |  | 
| 751 | 				break; | 
| 752 | 			case RC_NF: | 
| 753 |         goto next; | 
| 754 |       } // endswitch rc | 
| 755 |  | 
| 756 |     Fpos = Mempos = Headlen + Memory + CurBlk * Blksize; | 
| 757 |   } // endif's | 
| 758 |  | 
| 759 |   Tdbp->SetLine(Mempos); | 
| 760 |  | 
| 761 |   // Immediately calculate next position (Used by DeleteDB) | 
| 762 |   Mempos += Lrecl; | 
| 763 |   return RC_OK; | 
| 764 |   } // end of ReadBuffer | 
| 765 |  | 
| 766 | /***********************************************************************/ | 
| 767 | /*  WriteBuffer: File write routine for MAP access method.             */ | 
| 768 | /***********************************************************************/ | 
| 769 | int MPXFAM::WriteBuffer(PGLOBAL g __attribute__((unused))) | 
| 770 |   { | 
| 771 | #if defined(_DEBUG) | 
| 772 |   // Insert mode is no more handled using file mapping | 
| 773 |   if (Tdbp->GetMode() == MODE_INSERT) { | 
| 774 |     strcpy(g->Message, MSG(NO_MAP_INSERT)); | 
| 775 |     return RC_FX; | 
| 776 |     } // endif | 
| 777 | #endif   // _DEBUG | 
| 778 |  | 
| 779 |   // In Update mode, file was modified in memory | 
| 780 |   return RC_OK; | 
| 781 |   } // end of WriteBuffer | 
| 782 |  | 
| 783 |  |