home *** CD-ROM | disk | FTP | other *** search
/ C/C++ User's Journal & Wi…eveloper's Journal Tools / C-C__Users_Journal_and_Windows_Developers_Journal_Tools_1997.iso / stingray / dbfile.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-10-27  |  6.5 KB  |  333 lines

  1. // This is a part of the Objective Grid C++ Library.
  2. // Copyright (C) 1995,1996 ClassWorks, Stefan Hoenig.
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to
  6. // the Objective Grid Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding
  9. // the Objective Grid product.
  10. //
  11.  
  12. // dbfile.cpp : implemematation of the CDBaseFile class
  13. //
  14.  
  15. #include "stdafx.h"
  16. #include "dbfile.h"
  17.  
  18. static CString& StripTrailingChars (CString& sEdit, TCHAR cChar)
  19. {
  20.     ASSERT(cChar != _T('\0'));
  21.  
  22.     // find beginning of trailing spaces by starting at beginning (DBCS aware)
  23.     int nIndex = 0,
  24.         nLast = -1;
  25.     while (nIndex < sEdit.GetLength())
  26.     {
  27.         if (sEdit[nIndex] == cChar)
  28.         {
  29.             if (nLast == -1)
  30.                 nLast = nIndex;
  31.         }
  32.         else
  33.             nLast = -1;
  34.         nIndex += _tclen((const TCHAR *) sEdit + nIndex);
  35.     }
  36.  
  37.     if (nLast != -1)
  38.     {
  39.         // truncate at trailing space start
  40.         sEdit = sEdit.Left(nLast);
  41.     }
  42.  
  43.     return sEdit;
  44. }
  45.  
  46. static void StripLeadingBlanks(CString& sEdit)
  47. {
  48.     // find first non-space character
  49.     LPCTSTR lpsz = sEdit;
  50.     int n = 0;
  51.     while (*lpsz == _T(' '))
  52.     {
  53.         lpsz = _tcsinc(lpsz);
  54.         n++;
  55.     }
  56.  
  57.     // fix up data
  58.     if (n >= sEdit.GetLength())
  59.         sEdit.Empty();
  60.     else if (n > 0)
  61.         sEdit = sEdit.Mid(n);
  62. }
  63.  
  64. CDBaseFile::CDBaseFile()
  65. {
  66.     recordBuf = NULL;
  67.     fd = NULL;
  68.     nRecordCount = 0;
  69.     nCurrentRecord = -1;
  70.     nFieldCount = 0;
  71.     bWriteFlag = FALSE;
  72. }
  73.  
  74. CDBaseFile::~CDBaseFile()
  75. {
  76.     Close();
  77. }
  78.  
  79. void CDBaseFile::Close()
  80. {
  81.     if (fd)
  82.     {
  83.         Flush();
  84.         delete recordBuf;
  85.         for (int i = 0; i < nFieldCount; i++)
  86.             delete fieldArray[i];
  87.         fieldArray.RemoveAll();
  88.         fclose(fd);
  89.     }
  90.     fd = NULL;
  91. }
  92.  
  93. BOOL CDBaseFile::Open(LPCTSTR szFileName, BOOL readOnly)
  94. {
  95.     fd = _tfopen (sFileName = szFileName, (bReadOnly = readOnly) != FALSE ? _T("rb") : _T("rb+"));
  96.  
  97.     if (fd == 0)
  98.         return FALSE;
  99.  
  100.     int nBufSize = InitFields ();
  101.  
  102.     recordBuf = new char[nBufSize+1];
  103.  
  104.     return Seek(1);
  105. }
  106.  
  107. BOOL CDBaseFile::Seek(long nRecord)
  108. {
  109.     // move to new record and flush changes of previous record
  110.     if (nRecord < 0 || nRecord > nRecordCount)
  111.         return FALSE;
  112.     else if (nRecord == nCurrentRecord)
  113.         return TRUE;
  114.  
  115.     // new record
  116.     if (bWriteFlag)
  117.         Flush();
  118.  
  119.     nCurrentRecord = nRecord;
  120.     long pos = (nRecord * size) + offset;
  121.  
  122.     if (nRecord == nRecordCount)
  123.     {
  124.         // New record
  125.         memset(recordBuf, ' ', size);
  126.     }
  127.     else
  128.     {
  129.         fseek (fd, pos, 0);
  130.         fread (recordBuf, size, 1, fd);
  131.         recordBuf[size] = 0;
  132.     }
  133.  
  134.     return TRUE;
  135. }
  136.  
  137. void CDBaseFile::AddNew()
  138. {
  139.     // determine structure of dbase file
  140.     // and determine field types, offsets and lengths
  141.  
  142.     // new record
  143.     if (bWriteFlag)
  144.         Flush();
  145.  
  146.     nCurrentRecord = nRecordCount;
  147.     memset(recordBuf, ' ', size);
  148. }
  149.  
  150. void CDBaseFile::Flush()
  151. {
  152.     // write all changes of the current record to the dbase file
  153.  
  154.     if (!bWriteFlag || bReadOnly)
  155.         return;
  156.  
  157.     bWriteFlag = FALSE;
  158.  
  159.     long pos = (nCurrentRecord * size) + offset;
  160.  
  161.     fseek (fd, pos, 0);
  162.     fwrite (recordBuf, size, 1, fd);
  163.  
  164.     if (nCurrentRecord == nRecordCount)
  165.     {
  166.         fseek (fd, 4L, 0);
  167.         nRecordCount++;
  168.         fwrite(&nRecordCount, 4, 1, fd);
  169.     }
  170. }
  171.  
  172. CField* CDBaseFile::GetField(int n) const
  173. {
  174.     // returns field info
  175.  
  176.     ASSERT(n >= 0 && n <= nFieldCount);
  177.     return (CField*) fieldArray.GetAt(n);
  178. }
  179.  
  180. BOOL CDBaseFile::IsDeleted() const
  181. {
  182.     // if record is deleted, a '*' is at the first position
  183.  
  184.     return *recordBuf != _T(' ');
  185. }
  186.  
  187. BOOL CDBaseFile::GetValue(int n, CString& result) const
  188. {
  189.     // read value from buffer
  190.  
  191.     ASSERT(n >= 0 && n <= nFieldCount);
  192.  
  193.     if (!(n >= 0 && n <= nFieldCount))
  194.         return FALSE;
  195.  
  196.     result.Empty();
  197.  
  198.     CField* fld = GetField(n);
  199.  
  200.     // A note on DBCS/Unicode support.
  201.     //
  202.     // I assume that data in the dbase file are in ANSI/DBCS format.
  203.     // If this application was compiled with _UNICODE switch,
  204.     // data will be converted from single byte chars to wide chars.
  205.  
  206. #ifdef _UNICODE
  207.  
  208.     // Convert ansi chars to wide chars
  209.     char* psz = new char[fld->len+1];
  210.  
  211.     // read ansi chars
  212.     strncpy(psz, recordBuf+fld->offset, fld->len);
  213.     psz[fld->len] = _T('\0');
  214.  
  215.     // convert to wide char
  216.     result = psz;
  217.  
  218.     delete psz;
  219.  
  220. #else
  221.  
  222.     // simply copy chars to result string
  223.     LPTSTR p = result.GetBuffer(fld->len+1);
  224.  
  225.     _tcsncpy(p, recordBuf+fld->offset, fld->len);
  226.     p[fld->len] = _T('\0');
  227.  
  228.     result.ReleaseBuffer();
  229.  
  230. #endif
  231.  
  232.     StripTrailingChars(result, _T(' '));
  233.     StripLeadingBlanks(result);
  234.  
  235.     return TRUE;
  236. }
  237.  
  238. BOOL CDBaseFile::SetValue(int n, LPCTSTR pszValue)
  239. {
  240.     // write value to buffer
  241.  
  242.     ASSERT(n >= 0 && n <= nFieldCount);
  243.  
  244.     if (bReadOnly)
  245.         return FALSE;
  246.  
  247.     if (!(n >= 0 && n <= nFieldCount))
  248.         return FALSE;
  249.  
  250.     CField* fld = GetField(n);
  251.  
  252.     // A note on DBCS/Unicode support.
  253.     //
  254.     // I assume that data in the dbase file are in ANSI/DBCS format.
  255.     // If this application was compiled with _UNICODE switch,
  256.     // data will be converted from single byte chars to wide chars.
  257.  
  258.     int len;
  259.  
  260. #ifdef _UNICODE
  261.  
  262.     // Convert wide chars to ansi chars
  263.     char* pastring = new char[fld->len+1];
  264.     WideCharToMultiByte (CP_ACP, 0, pszValue, -1,
  265.                         pastring, fld->len, NULL, NULL);
  266.  
  267.  
  268.     strncpy(recordBuf+fld->offset, pastring, len = strlen(pastring));
  269.  
  270.     delete pastring;
  271. #else
  272.  
  273.     // simply copy chars to buffer
  274.     _tcsncpy(recordBuf+fld->offset, pszValue, len = _tcslen(pszValue));
  275. #endif
  276.  
  277.     // fill up with blanks
  278.     while (len < fld->len)
  279.         recordBuf[fld->offset+len++] = _T(' ');
  280.  
  281.     bWriteFlag = TRUE;
  282.  
  283.     return TRUE;
  284. }
  285.  
  286. int CDBaseFile::InitFields ()
  287. {
  288.     // determine structure of dbase file
  289.     // and determine field types, offsets and lengths
  290.  
  291.     fseek (fd, 4L, 0);
  292.     fread (&nRecordCount, 4, 1, fd);
  293.  
  294.     if (nRecordCount == 0L)
  295.         AfxThrowFileException (CFileException::endOfFile);
  296.  
  297.     fread (&offset, 2, 1, fd);
  298.     fread (&size, 2, 1, fd);
  299.  
  300.     nFieldCount = (offset-33)/32;
  301.  
  302.     int off = 1;  // field offset
  303.  
  304.     long pos = 32;
  305.     for (int i = 0; i < nFieldCount; i++, pos += 32)
  306.     {
  307.         CField* fld = new CField;
  308.         fieldArray.SetAtGrow(i, fld);
  309.         fseek (fd, pos, 0);
  310.         fread (fld->name, 10, 1, fd);
  311.         fld->name[10] = 0;
  312.  
  313.         fseek (fd, pos+11, 0);
  314.         fread (&fld->type, 1, 1, fd);
  315.  
  316.         fseek (fd, pos+16, 0);
  317.         unsigned char width, decimals;
  318.         fread (&width, 1, 1, fd);
  319.         fread (&decimals, 1, 1, fd);
  320.         fld->len = width;
  321.         fld->display_width = fld->width = width;
  322.         fld->decimals = decimals;
  323.         fld->offset = off;
  324.         off += width;
  325.  
  326.         int l = strlen(fld->name);
  327.         if (fld->width < l)
  328.             fld->width = (short)l;
  329.     }
  330.  
  331.     return off;
  332. }
  333.