EnglishРусский  

   ..

   base.dbf

   dbase.txt

   dbf.g

   dbf.txt

   dbt.txt

   field.g

   testdbf.g

   TI838D.txt

The project is closed! You can look at a new scripting language. It is available on GitHub.
Also, try our open source cross-platform automation software.

Ads

Installer and installation software
Commercial and Freeware installers.

source\lib\Dbf\dbf.g
  1 /******************************************************************************
  2 *
  3 * Copyright (C) 2004-2008, The Gentee Group. All rights reserved. 
  4 * This file is part of the Gentee open source project - http://www.gentee.com. 
  5 * 
  6 * THIS FILE IS PROVIDED UNDER THE TERMS OF THE GENTEE LICENSE ("AGREEMENT"). 
  7 * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE CONSTITUTES RECIPIENTS 
  8 * ACCEPTANCE OF THE AGREEMENT.
  9 *
 10 * Author: Alexey Krivonogov ( gentee )
 11 *
 12 ******************************************************************************/
 13 
 14 /*-----------------------------------------------------------------------------
 15 * Id: dbf L "Dbf"
 16 * 
 17 * Summary: This library is used to work with #b(dbf) files. The formats 
 18            #b(dBase III) and #b(dBase IV) are supported. To be able to work, 
 19            you should describe a variable of the #b(dbf) type. For using this
 20            library, it is required to specify the file dbf.g (from Lib
 21            subfolder) with include command. #srcg[
 22 |include : $"...\gentee\lib\dbf\dbf.g"]    
 23 *
 24 * List: *#lng/opers#,dbf_oplen,dbf_opfor,
 25         *#lng/methods#,dbf_append,dbf_bof,dbf_bottom,dbf_close,dbf_create,
 26         dbf_del,dbf_empty,dbf_eof,dbf_geterror,dbf_go,dbf_isdel,dbf_open,
 27         dbf_pack,dbf_recno,dbf_skip,dbf_top, 
 28         *Field methods,dbf_f_count,dbf_f_date,dbf_f_decimal,dbf_f_double,
 29         dbf_f_find,dbf_f_int,dbf_f_logic,dbf_f_memo,
 30         dbf_f_name,dbf_f_offset,dbf_f_ptr,dbf_f_str,dbf_f_type,dbf_f_width,
 31         dbf_fw_date,dbf_fw_double,dbf_fw_int,dbf_fw_logic,dbf_fw_memo,
 32         dbf_fw_str 
 33 * 
 34 -----------------------------------------------------------------------------*/
 35 
 36 define <export>
 37 {
 38    DBF_PAGE       = 0x40000  // Page size 262144
 39    
 40 /*-----------------------------------------------------------------------------
 41 * Id: dbflogic D
 42 * 
 43 * Summary: Logic value.
 44 *
 45 -----------------------------------------------------------------------------*/
 46    DBF_LFALSE     = 0   // The value of the logical field is FALSE.
 47    DBF_LTRUE      = 1   // The value of the logical field is TRUE.
 48    DBF_LUNKNOWN   = 2   // The value of the logical field is undefined. 
 49 
 50 /*-----------------------------------------------------------------------------
 51 * Id: dbftypes D
 52 * 
 53 * Summary: Types of fields.
 54 *
 55 -----------------------------------------------------------------------------*/
 56    DBFF_CHAR      = 'C'   // String.
 57    DBFF_DATE      = 'D'   // Date.
 58    DBFF_LOGIC     = 'L'   // Logical.
 59    DBFF_NUMERIC   = 'N'   // Integer.
 60    DBFF_FLOAT     = 'F'   // Fraction.
 61    DBFF_MEMO      = 'M'   // Memo field.
 62 
 63 /*-----------------------------------------------------------------------------
 64 * Id: dbferrs D
 65 * 
 66 * Summary: Flags for dbf.geterror.
 67 *
 68 -----------------------------------------------------------------------------*/
 69    ERRDBF_OPEN    = 1   // Cannot open dbf file.
 70    ERRDBF_READ          // Cannot read dbf file.
 71    ERRDBF_POS           // File position error.
 72    ERRDBF_EOF           // There is not the current record.
 73    ERRDBF_WRITE         // Cannot write dbf file.
 74    ERRDBF_FOVER         // The length of the string being written is greater /
 75                         // than the size of the field.
 76    ERRDBF_TYPE          // Incompatible field type.
 77    ERRDBT_OPEN          // Cannot open dbt file.
 78    ERRDBT_READ          // Cannot read dbt file.
 79    ERRDBT_POS           // An error of positioning in the dbt file.
 80    ERRDBT_WRITE         // Cannot write dbt file.
 81 //-----------------------------------------------------------------------------
 82 
 83 }
 84 
 85 type dbfhead {
 86     byte     version        
 87     byte     yy
 88     byte     mm
 89     byte     dd
 90     uint     num_recs
 91     ushort   header_len
 92     ushort   record_width
 93     reserved filler[20]
 94 }
 95 
 96 type dbfhfield {
 97     reserved  name[11]
 98     byte      ftype
 99     uint      reserve
100     byte      width
101     byte      decimals
102     reserved  filler[14]
103 }
104 
105 type dbffield {
106     str       name
107     uint      ftype
108     uint      width
109     uint      decimals
110     uint      offset
111 }
112 
113 type dbf
114 {
115    dbfhead    head
116    str        name
117    uint       error
118    file       dbffile
119    file       dbtfile
120    buf        page          // Страница загруженных данных
121    uint       pageoff       // Номер первого загруженного с 0
122    uint       pagecount     // Количество загруженных
123    uint       pagelimit     // Максимально возможное количество 
124                             // элементов на странице
125    uint       cur           // Номер текущего элемента с 0
126    uint       curfor        // Номер текущего элемента с 1 для foreach
127    uint       ptr           // Указатель на текущую запись
128    buf        eofbuf        // Пустой буфер       
129    uint       mblock        // Размер Memo поля
130    arr        fields of dbffield
131 }
132 
133 extern {
134    method uint dbf.top()
135 }
136 
137 method dbf.delete
138 {
139    if this.dbffile.fopen 
140    {
141       this.dbffile.close( )
142       //this.dbffile = 0
143    }
144    if this.dbtfile.fopen 
145    {
146       this.dbtfile.close( )
147       //this.dbtfile = 0
148    }
149    this.page.clear()
150    this.fields.clear()
151 }
152 
153 method uint dbf.error( uint code )
154 {
155    this.error = code
156    return 0
157 }
158 
159 /*-----------------------------------------------------------------------------
160 * Id: dbf_oplen F4
161 * 
162 * Summary: Get the number of records in the database.
163 *  
164 * Return: The number of records.
165 *
166 -----------------------------------------------------------------------------*/
167 
168 operator uint *( dbf dbase )
169 {
170    return dbase.head.num_recs
171 }
172 
173 /*-----------------------------------------------------------------------------
174 * Id: dbf_opfor F5
175 *
176 * Summary: Foreach operator. You can use #b(foreach) operator to look over all 
177            records of the database. #b(Variable) is a number of the current
178            record.
179 *  
180 * Title: foreach var,dbf
181 *
182 * Define: foreach variable,dbf {...}
183 * 
184 -----------------------------------------------------------------------------*/
185 
186 /*-----------------------------------------------------------------------------
187 * Id: dbf_eof F2
188 * 
189 * Summary: Determine is the current record is in the database.
190 *  
191 * Params: fd - This parameter is used in forech operator. /
192           Specify 0->fordata.
193 *
194 * Return: Returns 1 if the current record is not defined/found and 0 otherwise. 
195 *
196 -----------------------------------------------------------------------------*/
197 
198 method  uint dbf.eof( fordata fd )
199 {
200    return this.cur >= *this   
201 }
202 
203 method uint dbf.getdate()
204 {
205    datetime dt
206    
207    dt.gettime()
208    dt.year %= 100
209    if dt.year != this.head.yy || dt.month != this.head.mm ||
210       dt.day != this.head.dd
211    {
212       this.head.yy = dt.year
213       this.head.mm = dt.month
214       this.head.dd = dt.day
215       return 1
216    }
217    return 0
218 }
219 
220 include : "field.g"
221 
222 /*-----------------------------------------------------------------------------
223 * Id: dbf_geterror F3
224 * 
225 * Summary: Getting an error code. Get the error code in case some method is
226            finished unsuccessfully. 
227 *  
228 * Return: The code of the last error is returned.$$[dbferrs] 
229 *
230 -----------------------------------------------------------------------------*/
231 
232 method uint dbf.geterror()
233 {
234    return this.error
235 }
236 
237 /*-----------------------------------------------------------------------------
238 * Id: dbf_open F2
239 * 
240 * Summary: Open a database (a dbf file). 
241 *  
242 * Params: name - The name of the dbf file being opened.
243 *
244 * Return: #lng/retf# 
245 *
246 -----------------------------------------------------------------------------*/
247 
248 method uint dbf.open( str name )
249 {
250    buf   btemp
251    str   dbtname
252    uint  size
253    uint  cur
254    uint  i
255    
256    this.delete()
257    
258    this.name = name
259    if !( this.dbffile.open( name, 0 )) : return this.error( $ERRDBF_OPEN )
260    if this.dbffile.read( btemp, sizeof( dbfhead )) != sizeof( dbfhead )
261    {
262       return this.error( $ERRDBF_READ )
263    }   
264    mcopy( &this.head, btemp.ptr(), sizeof( dbfhead ))
265    if this.head.version == 0x83 || this.head.version == 0x8B
266    {
267       dbtname.fsetext( name, "dbt" )
268       if !( this.dbtfile.open( dbtname, 0 )) 
269       {
270          this.delete()
271          return this.error( $ERRDBT_OPEN )
272       }
273       if this.head.version == 0x8B
274       {
275          btemp.use = 0
276          if this.dbtfile.read( btemp, 32 ) != 32
277          {
278             this.delete()
279             return this.error( $ERRDBT_READ )
280          }
281          this.mblock = ( btemp.ptr() + 20 )->ushort
282          if !this.mblock : this.mblock = 512
283       }
284       else : this.mblock = 512
285    }   
286    size = this.dbffile.getsize( )
287    //this.page.alloc( ?( $DBF_PAGE < size, $DBF_PAGE, size ))
288    this.page.reserve( ?( $DBF_PAGE < size, $DBF_PAGE, size ) )   
289    this.pagelimit = this.page.size / this.head.record_width
290    this.pagecount = 0
291    this.pageoff = 0
292    this.cur = 0
293 
294    btemp.use = 0
295    size = this.head.header_len - sizeof( dbfhead )
296    
297    if this.dbffile.read( btemp, size ) != size
298    {
299       this.delete()
300       return this.error( $ERRDBF_READ )
301    }
302    
303    cur = btemp.ptr()
304    size = 1
305    while cur->byte != 0x0D
306    {
307       cur as dbfhfield
308       i = this.fields.expand( 1 );
309       this.fields[ i ].name.copy( &cur.name )
310       this.fields[ i ].ftype = cur.ftype
311       this.fields[ i ].width = cur.width
312       this.fields[ i ].decimals = cur.decimals
313       this.fields[ i ].offset = size
314       size += cur.width
315       cur as uint
316       cur += sizeof( dbfhfield )
317    }
318 //   print("Size = \(size) Rec_width=\(this.head.record_width)\n")
319    // Зануляем буфер для записи eof
320    this.eofbuf.clear()
321    this.eofbuf.reserve( this.head.record_width + 1 )
322    this.eofbuf.use = this.head.record_width
323    fornum i=0, this.head.record_width : this.eofbuf[i] = ' '
324 //   mzero( this.eofbuf.ptr(), this.eofbuf.size )
325    this.ptr = this.eofbuf.ptr()
326    
327    this.top()
328    return 1
329 }
330 
331 /*-----------------------------------------------------------------------------
332 * Id: dbf_close F3
333 * 
334 * Summary: Close a database. 
335 *  
336 -----------------------------------------------------------------------------*/
337 
338 method dbf.close()
339 {
340    this.delete()
341 }
342 
343 /*-----------------------------------------------------------------------------
344 * Id: dbf_go F2
345 * 
346 * Summary: Move to the record with the specified number.
347 *  
348 * Params: num - The required record number starting from 1.
349 *
350 * Return: #lng/retf# 
351 *
352 -----------------------------------------------------------------------------*/
353 
354 method  uint dbf.go( uint num )
355 {
356    uint  prevcur = this.cur
357    uint  count = this.head.num_recs
358    
359    this.cur = --num
360    
361    if num < *this
362    {
363       if this.pageoff > num || this.pageoff + this.pagecount <= num
364       {
365          if count > this.pagelimit
366          {
367             if prevcur <= num
368             {
369                this.pageoff = num
370                this.pagecount = ?( count - num > this.pagelimit, 
371                                    this.pagelimit, count - num )
372             }
373             else
374             {
375                this.pagecount = ?( num + 1 > this.pagelimit, 
376                                    this.pagelimit, num + 1 )
377                this.pageoff = num + 1 - this.pagecount
378             }
379          }
380          else
381          {
382             this.pageoff = 0
383             this.pagecount = count
384          }
385 //         print("Pagecount=\(this.pagecount)\n")
386          uint  pos = this.head.header_len + 
387                      this.pageoff * this.head.record_width
388          // Загружаем данные
389          if this.dbffile.setpos( pos, $FILE_BEGIN ) != pos
390          {
391              return this.error( $ERRDBF_POS )
392          }
393          uint size = this.pagecount * this.head.record_width
394          this.page.use = 0         
395          if this.dbffile.read( this.page, size ) != size
396          {
397             return this.error( $ERRDBF_READ )
398          }
399 //         print("Read=\(size)\n")
400       }
401       this.ptr = this.page.ptr() + ( this.cur - this.pageoff ) * 
402                  this.head.record_width
403    }
404    else 
405    { 
406       this.cur = *this
407       this.ptr = this.eofbuf.ptr()
408    }
409    
410    return  1
411 }
412 
413 /*-----------------------------------------------------------------------------
414 * Id: dbf_top F3
415 * 
416 * Summary: Move to the first record.
417 *  
418 * Return: #lng/retf# 
419 *
420 -----------------------------------------------------------------------------*/
421 
422 method  uint dbf.top()
423 {
424    return this.go( 1 )
425 }
426 
427 /*-----------------------------------------------------------------------------
428 * Id: dbf_bottom F3
429 * 
430 * Summary: Move to the last record.
431 *  
432 * Return: #lng/retf# 
433 *
434 -----------------------------------------------------------------------------*/
435 
436 method  uint dbf.bottom()
437 {
438    return this.go( *this )
439 }
440 
441 /*-----------------------------------------------------------------------------
442 * Id: dbf_skip F2
443 * 
444 * Summary: Moving to another record. Move forward or backward for the 
445            specified number of records.
446 *  
447 * Params: step - The step of moving. If it is less than zero, the move will /
448                  be toward the beginning of the database. 
449 *
450 * Return: #lng/retf# 
451 *
452 -----------------------------------------------------------------------------*/
453 
454 method  uint dbf.skip( int step )
455 {
456    return this.go( this.cur + 1 + step )
457 }
458 /*
459 method  uint dbf.skip( uint step )
460 {
461    return this.go( this.cur + 1 + step )
462 }*/
463 
464 /*-----------------------------------------------------------------------------
465 * Id: dbf_recno F3
466 * 
467 * Summary: Getting the number of the current record.
468 *  
469 * Return: The number of the current record or 0 if the record is not defined. 
470 *
471 -----------------------------------------------------------------------------*/
472 
473 method  uint dbf.recno()
474 {
475    fordata fd 
476    if this.eof( fd ) : return 0
477    return this.cur + 1
478 }
479 
480 /*-----------------------------------------------------------------------------
481 * Id: dbf_bof F3
482 * 
483 * Summary: Determine is the current record is the first one.
484 *  
485 * Return: 1 is returned if the current record is the first one. 
486 *
487 -----------------------------------------------------------------------------*/
488 
489 method  uint dbf.bof()
490 {
491    return !this.cur && *this
492 }
493 
494 /*-----------------------------------------------------------------------------
495 * Id: dbf_isdel F3
496 * 
497 * Summary: Getting the record deletion mark. Determine if the current record 
498            is marked as deleted. 
499 *  
500 * Return: 1 is returned if the current record is marked as deleted.  
501 *
502 -----------------------------------------------------------------------------*/
503 
504 method  uint dbf.isdel()
505 {
506    return ( this.f_ptr( 1 ) - 1 )->byte == 0x2A
507 }
508 
509 /*-----------------------------------------------------------------------------
510 * Id: dbf_del F3
511 * 
512 * Summary: Set/clear the deletion mark for the current record. 
513 *  
514 * Return: #lng/retf#  
515 *
516 -----------------------------------------------------------------------------*/
517 
518 method  uint dbf.del
519 {
520    fordata fd
521    if this.eof( fd ) : return this.error( $ERRDBF_EOF )
522    
523    uint  pos = this.head.header_len + 
524                      this.cur * this.head.record_width   
525 
526    this.ptr->byte = ?( this.ptr->byte == '*', ' ', '*' )
527    //!this.page.write( this.dbffile, pos, this.ptr - this.page.data, 1 )
528    if !this.dbffile.writepos( pos, this.page.ptr() + this.ptr - this.page.data, 1 )
529    {
530       return this.error( $ERRDBF_WRITE )
531    }
532    return 1
533 }
534 
535 /*-----------------------------------------------------------------------------
536 * Id: dbf_append F3
537 * 
538 * Summary: Adding a record. The method adds a record to a database.  
539 *  
540 * Return: #lng/retf#  
541 *
542 -----------------------------------------------------------------------------*/
543 
544 method uint dbf.append()
545 {
546    str   val
547    uint  size 
548    
549    val.fillspacer( this.head.record_width )
550    val.appendch( 0x1A )
551    
552    uint  pos = this.dbffile.getsize( ) - 1 
553         //this.head.header_len + this.head.num_recs * this.head.record_width
554    
555 //   if !val->buf.write( this.dbffile, pos, 0, *val )
556    if !this.dbffile.writepos( pos, val->buf.ptr(), *val )
557    {
558       return this.error( $ERRDBF_WRITE )
559    }
560    this.dbffile.setpos( 4, $FILE_BEGIN )
561    //ReadFile( this.dbffile, &this.head.num_recs, 4, &size, 0 )
562    size = this.dbffile.read( &this.head.num_recs, 4 )
563    this.head.num_recs++
564    this.getdate()
565    this.dbffile.setpos( 1, $FILE_BEGIN )
566    // Записываем дату и количество записей
567    //WriteFile( this.dbffile, &this.head.yy, 7, &size, 0 )
568    size = this.dbffile.write( &this.head.yy, 7 ) 
569    this.pagecount = 1
570    this.cur = this.head.num_recs - 1
571    this.pageoff = this.cur
572    mcopy( this.page.ptr(), val.ptr(), *val )
573    this.page.use = *val
574    return 1
575 }
576 
577 func  uint  creatememo( str filename, uint sblock )
578 {
579    buf  btemp
580    str  fname 
581    
582    btemp.reserve( 512 )
583    mzero( btemp.ptr(), 512 )
584    btemp.use = 512
585    fname.fnameext( filename )
586    mcopy( btemp.ptr() + 8, fname.ptr(), *fname )
587    ( btemp.ptr() + 20 )->ushort = sblock
588    return btemp.write( fname.fsetext( filename, "dbt" ))   
589 }
590 
591 /*-----------------------------------------------------------------------------
592 * Id: dbf_empty F2
593 * 
594 * Summary: Creating an empty copy. The method creates the same, but empty
595            database.
596 *  
597 * Params: filename - The full name of the dbf file being created.  
598 *
599 * Return: #lng/retf# 
600 *
601 -----------------------------------------------------------------------------*/
602 
603 method uint dbf.empty( str outfile )
604 {
605    buf  btemp
606    dbf  dbftemp
607    
608    this.dbffile.setpos( 0, $FILE_BEGIN )
609    this.dbffile.read( btemp, this.head.header_len )
610    btemp += byte( 0x1A )
611    dbftemp.getdate()
612    mcopy( btemp.ptr() + 1, &dbftemp.head.yy, 3 )
613    ( btemp.ptr() + 4 )->uint = 0
614 
615    if !btemp.write( outfile )
616    {
617       return this.error( $ERRDBF_WRITE )
618    }
619    if this.head.version == 0x83 || this.head.version == 0x8B
620    {
621       if !creatememo( outfile, this.mblock )
622       {
623          return this.error( $ERRDBT_WRITE )
624       }
625    }
626    return 1
627 }
628 
629 method uint dbf.first( fordata fd )
630 {
631    this.top()
632    this.curfor = this.cur + 1
633    return &this.curfor
634 }
635 
636 method uint dbf.next( fordata fd )
637 {
638    this.skip( 1 )
639    this.curfor = this.cur + 1
640    return &this.curfor
641 }
642 
643 define {
644    DBF_PACKSIZE = 1002000
645    DBF_PACKUSE = 1000000
646 }
647 
648 /*-----------------------------------------------------------------------------
649 * Id: dbf_pack F2
650 * 
651 * Summary: Pack a database. The database is copied into a new file excluding
652            records marked as deleted.
653 *  
654 * Params: outfile - The name of the new dbf file.  
655 *
656 * Return: #lng/retf# 
657 *
658 -----------------------------------------------------------------------------*/
659 
660 method uint dbf.pack( str outfile )
661 {
662    if !this.empty( outfile ) : return 0
663 
664    dbf  newb
665    buf  dbfout dbtout
666    str  filler
667    uint nextmemo rw
668    
669    if !newb.open( outfile ) 
670    {
671       return this.error( newb.error )
672    }
673    newb.dbffile.setpos( newb.head.header_len, $FILE_BEGIN )
674    if newb.dbtfile 
675    {
676       newb.dbtfile.setpos( 0, $FILE_END )
677       dbtout.reserve( $DBF_PACKSIZE )
678       filler.fill( "\01A", this.mblock, $FILL_LEN )
679       nextmemo = 1
680    }
681    dbfout.reserve( $DBF_PACKSIZE )
682    foreach cur, this
683    {
684       if !this.isdel()
685       {
686          if newb.dbtfile 
687          {
688             uint i
689             fornum i = 0, *this.fields
690             {
691                if this.fields[ i ].ftype == $DBFF_MEMO &&
692                   this.f_int( i + 1 )                  
693                {
694                   str  val vmemo
695                   uint rsize
696                   uint bmust
697                   
698                   this.f_memo( val, i + 1 )
699                   
700                   rsize = *val + ?( this.head.version == 0x83, 2, 8 )
701                   rsize = rsize / this.mblock + ?( rsize % this.mblock, 1, 0 )
702                   bmust = rsize * this.mblock + *dbtout
703                   
704                   if this.head.version == 0x8B
705                   {
706                      dbtout += 'ff ff 08 00 \i4 \( *val + 8 )'
707                   }
708                   vmemo += nextmemo
709                   vmemo.fillspacel( this.fields[ i ].width )
710                   mcopy( this.f_ptr( i + 1 ), vmemo.ptr(), 
711                          this.fields[ i ].width )
712                          
713                   dbtout.append( val.ptr(), *val )
714                   dbtout.append( filler.ptr(), bmust - *dbtout )
715                   nextmemo += rsize
716                   
717                   if dbtout.use > $DBF_PACKUSE
718                   {
719                      if !newb.dbtfile.write( dbtout )
720                      {
721                         newb.close()
722                         return this.error( $ERRDBT_WRITE )
723                      }
724                      dbtout.use = 0
725                   }
726                }
727             }
728          }
729          dbfout.append( this.ptr, this.head.record_width )
730          newb.head.num_recs++
731          if dbfout.use > $DBF_PACKUSE
732          {
733             if !newb.dbffile.write( dbfout )
734             {
735                newb.close()
736                return this.error( $ERRDBF_WRITE )
737             }
738             dbfout.use = 0
739          }
740       }            
741    }
742    dbfout += byte( 0x1A )
743    if !newb.dbffile.write( dbfout )
744    {
745       newb.close()
746       return this.error( $ERRDBF_WRITE )
747    }
748    newb.dbffile.setpos( 4, $FILE_BEGIN )
749    //WriteFile( newb.dbffile, &newb.head.num_recs, 4, &rw, 0 )
750    newb.dbffile.write( &newb.head.num_recs, 4 ) 
751    
752    if newb.dbtfile 
753    {
754       if !newb.dbtfile.write( dbtout )
755       {
756          newb.close()
757          return this.error( $ERRDBT_WRITE )
758       }
759       newb.dbtfile.setpos( 0, $FILE_BEGIN )
760       //WriteFile( newb.dbtfile, &nextmemo, 4, &rw, 0 )
761       newb.dbtfile.write( &nextmemo, 4 )
762    }
763    newb.close()
764    return 1
765 }
766 
767 /*-----------------------------------------------------------------------------
768 ** Id: dbf_create F2
769 * 
770 * Summary: Create a dbf file and open it.
771 *  
772 * Params: filename - The name of the dbf file being created. 
773           fields - The description of database fields. The line containing /
774           the description of fields separated by a line break or ';' /
775           Field name,Field type,Width,Fractional part length for numbers /
776           The name of a field cannot be longer than 10 characters. /
777           Possible type fields: $$[dbftypes]
778           ver - Version. 0 for dBase III or 1 for dBase IV. 
779 *
780 * Return: #lng/retf# 
781 *
782 -----------------------------------------------------------------------------*/
783 
784 method  uint dbf.create( str filename, str fields, uint ver )
785 {
786    arrstr  field //of str
787    str  nfield
788    buf  out outfield
789    uint ismemo
790    dbf  dbftemp   
791    
792    fields.lines( field, 1, 0->arr )
793    
794    foreach cur, field
795    {
796       nfield += cur
797       nfield.appendch( ';' )
798    }   
799    field.clear()
800    nfield.split( field, ';', $SPLIT_NOSYS )
801    dbftemp.head.record_width = 1
802    foreach sf, field
803    {
804       arrstr     items //of str
805       dbfhfield  dfield
806       
807       sf.split( items, ',', $SPLIT_NOSYS )
808       if *items[0] > 10 : items[0].setlen( 10 )
809       mcopy( &dfield.name, items[0].ptr(), *items[0] )
810       
811       dfield.ftype = ((items[1])->str)[0]
812       dfield.width = uint( items[2] )
813       if !dfield.width : dfield.width = 1
814       switch dfield.ftype
815       {
816          case 'N','F'
817          {
818             if dfield.ftype == 'F' && !ver
819             {
820                dfield.ftype == 'N'            
821             }
822             if dfield.width > 20 : dfield.width = 20                        
823             dfield.decimals = uint( items[3] )
824             
825             if dfield.decimals >= dfield.width - 1
826             {
827                dfield.decimals = dfield.width - 1
828             } 
829          }
830          case 'M'
831          {
832             dfield.width = 10
833             ismemo = 1
834          }
835          case 'L'
836          {
837             dfield.width = 1
838          }
839          case 'D'
840          {
841             dfield.width = 8
842          }
843          default
844          {
845             dfield.ftype = $DBFF_CHAR
846          }
847       }
848       dbftemp.head.record_width += dfield.width
849       outfield.append( &dfield, sizeof( dbfhfield ))
850    }
851    dbftemp.getdate()
852    if ismemo
853    {
854       dbftemp.head.version = ?( ver, 0x8B, 0x83 )
855    }
856    else : dbftemp.head.version = 0x3
857    
858    dbftemp.head.header_len = sizeof( dbfhead ) + *outfield + 2
859   
860    out.copy( &dbftemp.head, sizeof( dbfhead ))
861    out += outfield
862    out += '0d 00 1a'   
863    if !out.write( filename )
864    {
865       return this.error( $ERRDBF_WRITE )
866    }
867    if ismemo && !creatememo( filename, 512 )
868    {
869       return this.error( $ERRDBT_WRITE )
870    }
871    return this.open( filename )
872 }