aboutsummaryrefslogtreecommitdiff
path: root/noao/twodspec/multispec/dbio/dbio.x
blob: faa21cef38408a523591007193fdceb5dfda1393 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564

include	<fset.h>
include	<error.h>
include "dbio.h"

.help dbio 2 "Subset Database I/O Procedures"
.sh
1. Introduction

     These DBIO procedures are a subset of the general
DBIO design described in "Specifications of the IRAF DBIO Interface" by
Doug Tody (Oct 1983).  It is designed to allow programs written using
the subset DBIO to be easily converted to the full DBIO.  It's features
are:
.ls 4 1.
Database open and close.
.le
.ls 4 2.
Reference to entries by a (possibly) subscripted record name string.
.le
.ls 4 3.
Ability to add new record types as desired.
.le
.ls 4 4.
Error recovery procedure to cleanup after an uncaught error.
.le

The primary limitations are:
.ls 4 1.
No aliases.
.le
.ls 4 2.
No datatyping and no self-describing structures.
.le
.ls 4 3.
No deletions of entries.
.le
.sh
2. Procedures

.nf
		db = dbopen (file_name, mode, max_entries)
		db = dbreopen (db)
		     dbclose (db)
		     dbenter (db, record_name, sz_elem, nreserve)
	       y/n = dbaccess (db, record_name)
	     nread = dbread (db, reference, buf, maxelems)
	  	     dbwrite (db, reference, buf, nelems)
.fi

     A new, empty database is created by opening with access modes NEW_FILE
or TEMP_FILE.  The dictionary will be intialized to allow max_entries
number of dictionary entries.  The other legal access modes are READ_ONLY and
READ_WRITE.  The max_entries argument is not used with these modes.  To create
a new entry in the database the name of the record, the size of a record
element, and the maximum number of such records to be stored are specified.
This differs from the full DBIO specification in that a record is described
only by a size instead of a datatype.  Also it is not possible to increase
the number of elements once it has been entered.  The database read and
write procedures do no type conversion.  They read procedure returns
the number of elements read.  If a reference is not found in the
dictionary in either reading or writing an error condition occurs.
Also an attempt to read or write an element exceeding the dimension
entered in the dictionary will create an error condition.
.endhelp


# DBOPEN, DBREOPEN -- Open a database file.

pointer procedure dbopen (file_name, ac_mode, db_nentries)

# Procedure dbopen parameters:
char	file_name[SZ_FNAME]		# Database filename
int	ac_mode				# Access mode (new,temp,r,rw)
int	db_nentries			# Creation dictionary size

# Entry dbreopen parameters:
pointer	dbreopen			# Function type
pointer	db_old				# Old database descriptor

int	mode
pointer	fd, db				# FIO descriptor and DBIO descriptor
pointer	dic
int	nelem, nentries

bool	strne()
int	open(), dbread(), reopen()
errchk	db_getdes, calloc, dbenter, dbread, db_init

begin
	# Check for valid access mode.  Valid modes require read permission.
	# If a valid access mode open database with FIO.
	mode = ac_mode
	if ((mode == WRITE_ONLY) || (mode == APPEND))
	    call error (DB_ERRCODE + 0, "Invalid database access mode")
	fd = open (file_name, mode, BINARY_FILE)
	goto 10

entry dbreopen (db_old) 

	fd = reopen (DB_FD(db_old), mode)

	# Get DBIO descriptor
10	call db_getdes (db)
	DB_FD(db) = fd

	# If the database is being created enter the dictionary in the file.
	# If the database already exists read the current dictionary and
	# check to see if the file is a database.
	switch (mode) {
	case NEW_FILE, TEMP_FILE:
	    # Allocate dictionary space and enter it in the database.
	    # The request entries is increased by one for the dictionary
	    # database entry itself.
	    nentries = db_nentries + 1
	    call calloc (dic, nentries * LEN_DB_ENTRY, TY_STRUCT)
	    DB_DIC(db) = dic
	    call dbenter (db, "db_dictionary", LEN_DB_ENTRY * SZ_STRUCT,
		nentries)
	case READ_ONLY, READ_WRITE:
	    # Read dictionary.
	    call db_init (db, 1)
	    dic = DB_DIC(db)
	    nelem = dbread (db, "db_dictionary", Memi[dic], 1)
	    if (nelem != 1)
		call error (DB_ERRCODE + 1, "Error reading database dictionary")
	    if (strne (DB_KEY(dic, 1), "db_dictionary"))
		call error (DB_ERRCODE + 2, "File is not a database")

	    nentries = DB_DIM(dic, 1)
	    call db_init (db, nentries)
	    dic = DB_DIC(db)
	    nelem = dbread (db, "db_dictionary", Memi[dic], nentries)
	    if (nelem != nentries)
		call error (DB_ERRCODE + 3, "Error reading database dictionary")
	}

	return (db)
end


# DB_INIT -- Initialize the program dictionary space

procedure db_init (db, db_nentries)

pointer	db
int	db_nentries

pointer	dic

long	note()
errchk	mfree, calloc, seek

begin
	# Allocate dictionary memory
	dic = DB_DIC(db)
	if (dic != NULL)
	    call mfree (dic, TY_STRUCT)
	call calloc (dic, db_nentries * LEN_DB_ENTRY, TY_STRUCT)
	DB_DIC(db) = dic

	# Fill in dictionary entry
	call strcpy ("db_dictionary", DB_KEY(dic, 1), SZ_DB_KEY)
	DB_SZ_ELEM(dic, 1) = LEN_DB_ENTRY * SZ_STRUCT
	DB_DIM(dic, 1) = db_nentries
	call seek (DB_FD(db), BOF)
	DB_OFFSET(dic, 1) = note (DB_FD(db))
end

# DBENTER -- Make a new entry in the database dictionary and reserve
# file space in the database.

procedure dbenter (db, record_name, sz_elem, nreserve)

pointer	db				# DBIO descriptor
char	record_name[SZ_DB_KEY]		# Record name string
int	sz_elem				# Size of record element in CHARS
int	nreserve			# Number of record elements to reserve

int	i
int	sz_reserve, sz_buf
pointer	dic, buf

bool	streq()
int	fstati()
long	note()

errchk	calloc, dbclose, write, seek

begin
	# Check access mode
	if (fstati(DB_FD(db), F_WRITE) == NO)
	    call error (DB_ERRCODE + 4, "Database is read only")

	# Find the last entry.  Check for attempts to redefine an
	# entry and to overflow the dictionary.
	dic = DB_DIC(db)
	for (i = 1; i <= DB_DIM(dic, 1); i = i + 1) {
	    if (DB_DIM(dic, i) == 0)
		break
	    if (streq (record_name, DB_KEY(dic, i)))
		call error (DB_ERRCODE + 5, "Attempt to redefine dictionary entry")
	}
	if ((i > 1) && (i > DB_DIM(dic, 1)))
	    call error (DB_ERRCODE + 6, "Database dictionary is full")

	# Make dictionary entry
	call strcpy (record_name, DB_KEY(dic, i), SZ_DB_KEY)
	DB_SZ_ELEM(dic, i) = sz_elem
	DB_DIM(dic, i) = nreserve
	call seek (DB_FD(db), EOF)
	DB_OFFSET(dic, i) = note (DB_FD(db))
	DB_UPDATE(db) = YES

	# Initialize file space to zero.  Zero file blocks for efficiency.
	sz_reserve = sz_elem * nreserve
	sz_buf = min (fstati (DB_FD(db), F_BLKSIZE), sz_reserve)
	call calloc (buf, sz_buf, TY_CHAR)

	while (sz_reserve > 0) {
	    call write (DB_FD(db), Memc[buf], sz_buf)
	    sz_reserve = sz_reserve - sz_buf
	    sz_buf = min (sz_buf, sz_reserve)
	}
	call mfree (buf, TY_CHAR)
end

# DBACCESS -- Is data reference in the database?

bool procedure dbaccess (db, record_name)

pointer	db				# DBIO descriptor
char	record_name[SZ_DB_KEY]		# Record name string

int	i
pointer	dic

bool	streq()

begin
	dic = DB_DIC(db)
	for (i = 1; i <= DB_DIM(dic, 1); i = i + 1) {
	    if (DB_DIM(dic, i) == 0)
		return (FALSE)
	    if (streq (record_name, DB_KEY(dic, i)))
		return (TRUE)
	}
	return (FALSE)
end


# DBNEXTNAME -- Return name of the next dictionary entry.

int procedure dbnextname (db, previous, outstr, maxch)

pointer	db				# DBIO descriptor
char	previous[ARB]
char	outstr[ARB]
int	maxch

int	i
pointer	dic

bool	streq(), strne()

begin
	dic = DB_DIC(db)
	i = 1
	if (strne (previous, "")) {
	    for (; i <= DB_DIM(dic, 1); i = i + 1) {
	        if (DB_DIM(dic, i) == 0)
		    return (EOF)
	        if (streq (previous, DB_KEY(dic, i)))
		    break
	    }
	}
	i = i + 1
	if ((i > DB_DIM(dic, 1)) || (DB_DIM(dic, i) == 0))
	    return (EOF)
	else
	    call strcpy (DB_KEY(dic, i), outstr, maxch)

	return (OK)
end


#DBREAD - Read data from the database.
# The number of data elements read is returned.

int procedure dbread (db, ref, buf, maxelems)

pointer	db			# Database file descriptor
char	ref[ARB]		# Data reference
char	buf[ARB]		# Data buffer
int	maxelems		# Number of elements to be read

int	i, j
int	stat, sz_elem, index, nread
long	offset
pointer	dic

int	strncmp(), strlen(), stridxs(), ctoi()
bool	streq()
int	read()
errchk	read, dbclose

begin
	dic = DB_DIC(db)

	# Decode the data reference and set the file offset and the size
	# of the data element.  If a valid data reference is not found
	# then a read status of 0 is returned.

	j = stridxs ("[", ref)
	for (i = 1; i <= DB_DIM(dic, 1); i = i + 1) {
	    if (DB_DIM(dic, i) == 0)
		call error (DB_ERRCODE + 7, "Database request not found")
	    if (j == 0) {
	        if (streq (ref, DB_KEY(dic, i)))
		    break
	    } else {
		if (strlen (DB_KEY(dic, i)) == j - 1)
		    if (strncmp (ref, DB_KEY(dic, i), j - 1) == 0)
		        break
	    }
	}

	offset = DB_OFFSET(dic, i)
	sz_elem = DB_SZ_ELEM(dic, i)
	nread = maxelems
	if (j > 0) {
	    j = ctoi (ref, j + 1, index)
	    if (j > 0) {
		if (maxelems > DB_DIM(dic, i) - index + 1) {
		    call error (DB_ERRCODE + 8, "Database request out of bounds")
		}
		offset = offset + (index - 1) * sz_elem
	    }
	}

	# Seek and read the data
	call seek (DB_FD(db), offset)
	stat = read (DB_FD(db), buf, sz_elem * nread) / sz_elem
	return (stat)
end


# DBWRITE - Write data to the database.

procedure dbwrite (db, ref, buf, nelems)

pointer	db			# DBIO descriptor
char	ref[ARB]		# Data reference
char	buf[ARB]		# Data buffer
int	nelems			# Number of elements to written

int	i, j
int	sz_elem, index, nwritten
long	offset
pointer	dic

int	strncmp(), strlen(), stridxs(), ctoi()
bool	streq()
errchk	write, dbclose

begin
	dic = DB_DIC(db)

	# Decode the data reference and set the file offset and the size
	# of the data element.  If a valid data reference is not found
	# then the data is not written and a write status of 0 is returned.

	j = stridxs ("[", ref)
	for (i = 1; i <= DB_DIM(dic, 1); i = i + 1) {
	    if (DB_DIM(dic, i) == 0)
		call error (DB_ERRCODE + 9, "Database request not found")
	    if (j == 0) {
	        if (streq (ref, DB_KEY(dic, i)))
		    break
	    } else {
		if (strlen (DB_KEY(dic, i)) == j - 1)
		    if (strncmp (ref, DB_KEY(dic, i), j - 1) == 0)
		        break
	    }
	}

	offset = DB_OFFSET(dic, i)
	sz_elem = DB_SZ_ELEM(dic, i)
	nwritten = nelems
	if (j > 0) {
	    j = ctoi (ref, j + 1, index)
	    if (j > 0) {
		if (nelems > DB_DIM(dic, i) - index + 1) {
		    call error (DB_ERRCODE + 10, "Database request out of bounds")
		}
		offset = offset + (index - 1) * sz_elem
	    }
	}

	# Seek and write the data
	call seek (DB_FD(db), offset)
	call write (DB_FD(db), buf, sz_elem * nwritten)
	return
end


# DBCLOSE -- Update the dictionary in the database, close the database
# and free DBIO descriptor.

procedure dbclose (db)

pointer	db

begin
	# Update dictionary in database
	if (DB_UPDATE(db) == YES)
	    call dbwrite (db, "db_dictionary", Memi[DB_DIC(db)],
		DB_DIM(DB_DIC(db), 1))

	call close (DB_FD(db))
	call db_freedes (db)
end


# Procedures accessing the DBIO descriptor list.
#
# DB_GETDES  -- Allocate and return a DBIO descriptor.  Post error recovery.
# DB_FREEDES -- Close a database and free allocated memory.
# DB_ERROR   -- Take error recovery action by closing all open databases.

procedure db_getdes (db)

pointer	db			# Allocated DBIO descriptor

extern	db_error()
errchk	malloc()

int	ndes			# Number of allocated DBIO descriptors
pointer	dbdes[MAX_DB_DES]	# DBIO descriptor list

common	/dbiocom/ ndes, dbdes

int	init
data	init/YES/

begin
	if (init == YES) {
	    ndes = 0
	    init = NO
	}

	# Check to see if the requested descriptor would overflow the descriptor
	# list.  If not allocate memory for the descriptor otherwise
	# start error handling.  On the first call post the error handler.

	if (ndes == MAX_DB_DES)
	    call error (DB_ERRCODE + 11, "Attempt to open too many database files")

	ndes = ndes + 1
	call malloc (dbdes[ndes], LEN_DB_DES, TY_STRUCT)
	db = dbdes[ndes]
	DB_FD(db) = NULL
	DB_DIC(db) = NULL
	DB_UPDATE(db) = NO

	if (ndes == 1)
	    call onerror (db_error)
end


# DB_FREEDES -- Close a database and free allocated memory.

procedure db_freedes (db)

pointer	db			# DBIO descriptor to be freed

int	i

int	ndes			# Number of allocated DBIO descriptors
pointer	dbdes[MAX_DB_DES]	# DBIO descriptor list

common	/dbiocom/ ndes, dbdes

begin

	# Locate the specified descriptor in the descriptor list.
	# If the descriptor is not in the list do nothing.
	# If the descriptor is in the list free allocated
	# memory and remove the entry from the list.

	for (i = 1; (i <= ndes) && (db != dbdes[i]); i = i + 1)
	    ;
	if (i > ndes)
	    return

	if (DB_DIC(db) != NULL)
	    call mfree (DB_DIC(db), TY_STRUCT)
	call mfree (db, TY_STRUCT)

	if (i < ndes)
	    dbdes[i] = dbdes[ndes]
	ndes = ndes - 1
end


# DB_ERROR   -- Take error recovery action by closing all open databases.

procedure db_error (error_code)

int	error_code		# Error code

int	i

int	ndes			# Number of allocated DBIO descriptors
pointer	dbdes[MAX_DB_DES]	# DBIO descriptor list

common	/dbiocom/ ndes, dbdes

begin
	# Let fio_cleanup deal with the open files and the system
	# restart deal with freeing the stack.  This procedure
	# cleans up the dbio descriptors and updates the database
	# dictionary.

	do i = 1, ndes
	    # Update dictionary in database.  Catch errors.
	    if (DB_UPDATE(dbdes[i]) == YES)
	        iferr (call dbwrite (dbdes[i], "db_dictionary",
		    Memi[DB_DIC(dbdes[i])], DB_DIM(DB_DIC(dbdes[i]), 1)))
	            call erract (EA_WARN)

	    call db_freedes (dbdes[i])
end


int procedure dbgeti (db, key, type)

pointer	db
char	key[ARB]
char	type[ARB]

int	i
pointer	dic

bool	streq()

begin
	dic = DB_DIC(db)
	for (i = 1; i <= DB_DIM(dic, 1); i = i + 1) {
	    if (DB_DIM(dic, i) == 0)
		call error (0, "Key not in database")
	    if (streq (key, DB_KEY(dic, i)))
		break
	}
	if (i > DB_DIM(dic, 1))
	    call error (0, "Key not in database")
	
	if (streq (type, "r_len"))
	    return (DB_DIM(dic, i))
	else if (streq (type, "r_size"))
	    return (DB_SZ_ELEM(dic, i))
	else
	    call error (0, "Unknown database key attribute")
end