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 }