1 | /* |
---|
2 | * emfdb.c -- EMF database compatability functions for GoAhead WebServer. |
---|
3 | * |
---|
4 | * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. |
---|
5 | * |
---|
6 | * See the file "license.txt" for usage and redistribution license requirements |
---|
7 | * |
---|
8 | * $Id$ |
---|
9 | */ |
---|
10 | |
---|
11 | /******************************** Description *********************************/ |
---|
12 | /* |
---|
13 | * Textfile-based database support for WebServer 2.1. |
---|
14 | */ |
---|
15 | |
---|
16 | /********************************* Includes ***********************************/ |
---|
17 | |
---|
18 | #include "emfdb.h" |
---|
19 | #include "wsIntrn.h" |
---|
20 | |
---|
21 | /********************************* Defines ************************************/ |
---|
22 | |
---|
23 | #define KEYWORD_TABLE T("TABLE") |
---|
24 | #define KEYWORD_ROW T("ROW") |
---|
25 | |
---|
26 | /*********************************** Locals ***********************************/ |
---|
27 | |
---|
28 | /* |
---|
29 | * Variable to support the basicSet and basicGet functions. |
---|
30 | */ |
---|
31 | |
---|
32 | static char_t *basicProdDir = NULL; |
---|
33 | static char_t *basicDefaultDir = T("."); /* Default set to current */ |
---|
34 | |
---|
35 | /* |
---|
36 | * hAlloc chain list of table schemas to be closed |
---|
37 | */ |
---|
38 | |
---|
39 | static int dbMaxTables = 0; |
---|
40 | static dbTable_t **dbListTables = NULL; |
---|
41 | |
---|
42 | /****************************** Forward Declarations **************************/ |
---|
43 | |
---|
44 | static int crack(char_t *buf, char_t **key, char_t **val); |
---|
45 | static char_t *trim(char_t *str); |
---|
46 | static int GetColumnIndex(int tid, char_t *colName); |
---|
47 | |
---|
48 | /******************************************************************************/ |
---|
49 | /* |
---|
50 | * Add a schema to the module-internal schema database |
---|
51 | */ |
---|
52 | |
---|
53 | int dbRegisterDBSchema(dbTable_t *pTableRegister) |
---|
54 | { |
---|
55 | dbTable_t *pTable; |
---|
56 | int tid; |
---|
57 | |
---|
58 | a_assert(pTableRegister); |
---|
59 | |
---|
60 | trace(4, T("DB: Registering database table <%s>\n"), |
---|
61 | pTableRegister->name); |
---|
62 | |
---|
63 | /* |
---|
64 | * Bump up the size of the table array |
---|
65 | */ |
---|
66 | tid = hAllocEntry((void***) &dbListTables, |
---|
67 | &dbMaxTables, sizeof(dbTable_t)); |
---|
68 | |
---|
69 | /* |
---|
70 | * Copy the table schema to the last spot in schema array |
---|
71 | */ |
---|
72 | a_assert(dbListTables); |
---|
73 | pTable = dbListTables[tid]; |
---|
74 | a_assert(pTable); |
---|
75 | |
---|
76 | /* |
---|
77 | * Copy the name of the table |
---|
78 | */ |
---|
79 | pTable->name = bstrdup(B_L, pTableRegister->name); |
---|
80 | |
---|
81 | /* |
---|
82 | * Copy the number of columns |
---|
83 | */ |
---|
84 | pTable->nColumns = pTableRegister->nColumns; |
---|
85 | |
---|
86 | /* |
---|
87 | * Copy the column definitions |
---|
88 | */ |
---|
89 | if (pTable->nColumns > 0) { |
---|
90 | int i; |
---|
91 | pTable->columnNames = balloc(B_L, sizeof(char_t *) * pTable->nColumns); |
---|
92 | pTable->columnTypes = balloc(B_L, sizeof(int *) * pTable->nColumns); |
---|
93 | |
---|
94 | for (i = 0; (i < pTableRegister->nColumns); i++) { |
---|
95 | pTable->columnNames[i] = |
---|
96 | bstrdup(B_L, pTableRegister->columnNames[i]); |
---|
97 | pTable->columnTypes[i] = pTableRegister->columnTypes[i]; |
---|
98 | } |
---|
99 | |
---|
100 | } else { |
---|
101 | pTable->columnNames = NULL; |
---|
102 | pTable->columnTypes = NULL; |
---|
103 | } |
---|
104 | |
---|
105 | /* |
---|
106 | * Zero out the table's data (very important!) |
---|
107 | */ |
---|
108 | pTable->nRows = 0; |
---|
109 | pTable->rows = NULL; |
---|
110 | |
---|
111 | return 0; |
---|
112 | } |
---|
113 | |
---|
114 | /******************************************************************************/ |
---|
115 | /* |
---|
116 | * This is provided for compatibility with EMF. Tables are "registered" |
---|
117 | * with staticly defined schemas. There is only one did in this package: 0. |
---|
118 | */ |
---|
119 | |
---|
120 | int dbOpen(char_t *tablename, char_t *filename, |
---|
121 | int (*gettime)(int did), int flags) |
---|
122 | { |
---|
123 | basicProdDir = NULL; |
---|
124 | basicDefaultDir = T("."); |
---|
125 | dbMaxTables = 0; |
---|
126 | dbListTables = NULL; |
---|
127 | return 0; |
---|
128 | } |
---|
129 | |
---|
130 | /******************************************************************************/ |
---|
131 | /* |
---|
132 | * Delete all the rows of the tables, and all of the tables |
---|
133 | */ |
---|
134 | |
---|
135 | void dbClose(int did) |
---|
136 | { |
---|
137 | int table, column; |
---|
138 | dbTable_t *pTable; |
---|
139 | |
---|
140 | /* |
---|
141 | * Before doing anything, delete all the contents of the database |
---|
142 | */ |
---|
143 | dbZero(did); |
---|
144 | |
---|
145 | /* |
---|
146 | * Now delete the tables |
---|
147 | */ |
---|
148 | for (table = 0; table < dbMaxTables; table++) { |
---|
149 | pTable = dbListTables[table]; |
---|
150 | |
---|
151 | if (pTable != NULL) { |
---|
152 | /* |
---|
153 | * Delete the table schema |
---|
154 | */ |
---|
155 | if (pTable->nColumns) { |
---|
156 | for (column = 0; column < pTable->nColumns; column++) { |
---|
157 | bfreeSafe(B_L, pTable->columnNames[column]); |
---|
158 | } |
---|
159 | bfreeSafe(B_L, pTable->columnNames); |
---|
160 | bfreeSafe(B_L, pTable->columnTypes); |
---|
161 | } |
---|
162 | /* |
---|
163 | * Delete the table name |
---|
164 | */ |
---|
165 | bfreeSafe(B_L, pTable->name); |
---|
166 | /* |
---|
167 | * Free the table |
---|
168 | */ |
---|
169 | bfreeSafe(B_L, pTable); |
---|
170 | hFree((void ***) &dbListTables, table); |
---|
171 | } |
---|
172 | } |
---|
173 | |
---|
174 | if (dbListTables) { |
---|
175 | bfree(B_L, dbListTables); |
---|
176 | } |
---|
177 | |
---|
178 | /* |
---|
179 | * Set the global table list to a safe value |
---|
180 | */ |
---|
181 | dbListTables = NULL; |
---|
182 | dbMaxTables = 0; |
---|
183 | } |
---|
184 | |
---|
185 | |
---|
186 | /******************************************************************************/ |
---|
187 | /* |
---|
188 | * Delete all the data records in all tables |
---|
189 | */ |
---|
190 | |
---|
191 | void dbZero(int did) |
---|
192 | { |
---|
193 | int table, row, column, nRows, nColumns; |
---|
194 | int *pRow; |
---|
195 | dbTable_t *pTable; |
---|
196 | |
---|
197 | /* |
---|
198 | * Delete all data from all tables |
---|
199 | */ |
---|
200 | for (table = 0; table < dbMaxTables; table++) { |
---|
201 | pTable = dbListTables[table]; |
---|
202 | /* |
---|
203 | * Delete the row data contained within the schema |
---|
204 | */ |
---|
205 | if (pTable) { |
---|
206 | nColumns = pTable->nColumns; |
---|
207 | nRows = pTable->nRows; |
---|
208 | for (row = 0; row < nRows; row++) { |
---|
209 | pRow = pTable->rows[row]; |
---|
210 | if (pRow) { |
---|
211 | /* |
---|
212 | * Only delete the contents of rows not previously deleted! |
---|
213 | */ |
---|
214 | for (column = 0; column < nColumns; column++) { |
---|
215 | if (pTable->columnTypes[column] == T_STRING) { |
---|
216 | bfreeSafe(B_L, (char_t *)(pRow[column])); |
---|
217 | pRow[column] = (int)NULL; |
---|
218 | } |
---|
219 | } |
---|
220 | |
---|
221 | bfreeSafe(B_L, pRow); |
---|
222 | hFree((void ***) &pTable->rows, row); |
---|
223 | } |
---|
224 | } |
---|
225 | |
---|
226 | pTable->rows = NULL; |
---|
227 | pTable->nRows = 0; |
---|
228 | } |
---|
229 | } |
---|
230 | } |
---|
231 | |
---|
232 | /******************************************************************************/ |
---|
233 | /* |
---|
234 | * Find the a row in the table with the given string in the given column |
---|
235 | */ |
---|
236 | |
---|
237 | int dbSearchStr(int did, char_t *tablename, |
---|
238 | char_t *colName, char_t *value, int flags) |
---|
239 | { |
---|
240 | int tid, nRows, nColumns, column; |
---|
241 | dbTable_t *pTable; |
---|
242 | |
---|
243 | a_assert(tablename); |
---|
244 | a_assert(colName); |
---|
245 | a_assert(value); |
---|
246 | |
---|
247 | tid = dbGetTableId(0, tablename); |
---|
248 | a_assert(tid >= 0); |
---|
249 | |
---|
250 | if ((tid >= 0) && (tid < dbMaxTables) && (dbListTables[tid] != NULL)) { |
---|
251 | pTable = dbListTables[tid]; |
---|
252 | } else { |
---|
253 | return DB_ERR_TABLE_NOT_FOUND; |
---|
254 | } |
---|
255 | |
---|
256 | nColumns = pTable->nColumns; |
---|
257 | nRows = pTable->nRows; |
---|
258 | column = GetColumnIndex(tid, colName); |
---|
259 | a_assert (column >= 0); |
---|
260 | |
---|
261 | if (column >= 0) { |
---|
262 | char_t *compareVal; |
---|
263 | int row, *pRow; |
---|
264 | /* |
---|
265 | * Scan through rows until we find a match. |
---|
266 | * Note that some of these rows may be deleted! |
---|
267 | */ |
---|
268 | row = 0; |
---|
269 | while (row < nRows) { |
---|
270 | pRow = pTable->rows[row]; |
---|
271 | if (pRow) { |
---|
272 | compareVal = (char_t *)(pRow[column]); |
---|
273 | if (compareVal && (gstrcmp(compareVal, value) == 0)) { |
---|
274 | return row; |
---|
275 | } |
---|
276 | } |
---|
277 | row++; |
---|
278 | } |
---|
279 | } else { |
---|
280 | /* |
---|
281 | * Return -2 if search column was not found |
---|
282 | */ |
---|
283 | trace(3, T("DB: Unable to find column <%s> in table <%s>\n"), |
---|
284 | colName, tablename); |
---|
285 | return DB_ERR_COL_NOT_FOUND; |
---|
286 | } |
---|
287 | |
---|
288 | return -1; |
---|
289 | } |
---|
290 | |
---|
291 | /******************************************************************************/ |
---|
292 | /* |
---|
293 | * Add a new row to the given table. Return the new row ID. |
---|
294 | */ |
---|
295 | |
---|
296 | int dbAddRow(int did, char_t *tablename) |
---|
297 | { |
---|
298 | int tid, size; |
---|
299 | dbTable_t *pTable; |
---|
300 | |
---|
301 | a_assert(tablename); |
---|
302 | |
---|
303 | tid = dbGetTableId(0, tablename); |
---|
304 | a_assert(tid >= 0); |
---|
305 | |
---|
306 | if ((tid >= 0) && (tid < dbMaxTables) && (dbListTables[tid] != NULL)) { |
---|
307 | pTable = dbListTables[tid]; |
---|
308 | } else { |
---|
309 | return DB_ERR_TABLE_NOT_FOUND; |
---|
310 | } |
---|
311 | |
---|
312 | a_assert(pTable); |
---|
313 | |
---|
314 | if (pTable) { |
---|
315 | trace(5, T("DB: Adding a row to table <%s>\n"), tablename); |
---|
316 | |
---|
317 | size = pTable->nColumns * max(sizeof(int), sizeof(char_t *)); |
---|
318 | return hAllocEntry((void***) &(pTable->rows), &(pTable->nRows), size); |
---|
319 | } |
---|
320 | |
---|
321 | return -1; |
---|
322 | } |
---|
323 | |
---|
324 | /******************************************************************************/ |
---|
325 | /* |
---|
326 | * Delete a row in the table. |
---|
327 | */ |
---|
328 | |
---|
329 | int dbDeleteRow(int did, char_t *tablename, int row) |
---|
330 | { |
---|
331 | int tid, nColumns, nRows; |
---|
332 | dbTable_t *pTable; |
---|
333 | |
---|
334 | a_assert(tablename); |
---|
335 | tid = dbGetTableId(0, tablename); |
---|
336 | a_assert(tid >= 0); |
---|
337 | |
---|
338 | if ((tid >= 0) && (tid < dbMaxTables) && (dbListTables[tid] != NULL)) { |
---|
339 | pTable = dbListTables[tid]; |
---|
340 | } else { |
---|
341 | return DB_ERR_TABLE_NOT_FOUND; |
---|
342 | } |
---|
343 | |
---|
344 | nColumns = pTable->nColumns; |
---|
345 | nRows = pTable->nRows; |
---|
346 | |
---|
347 | if ((row >= 0) && (row < nRows)) { |
---|
348 | int *pRow = pTable->rows[row]; |
---|
349 | |
---|
350 | if (pRow) { |
---|
351 | int column = 0; |
---|
352 | /* |
---|
353 | * Free up any allocated strings |
---|
354 | */ |
---|
355 | while (column < nColumns) { |
---|
356 | if (pRow[column] && |
---|
357 | (pTable->columnTypes[column] == T_STRING)) { |
---|
358 | bfree(B_L, (char_t *)pRow[column]); |
---|
359 | } |
---|
360 | |
---|
361 | column++; |
---|
362 | } |
---|
363 | /* |
---|
364 | * Zero out the row for safety |
---|
365 | */ |
---|
366 | memset(pRow, 0, nColumns * max(sizeof(int), sizeof(char_t *))); |
---|
367 | |
---|
368 | bfreeSafe(B_L, pRow); |
---|
369 | pTable->nRows = hFree((void ***)&pTable->rows, row); |
---|
370 | trace(5, T("DB: Deleted row <%d> from table <%s>\n"), |
---|
371 | row, tablename); |
---|
372 | } |
---|
373 | return 0; |
---|
374 | } else { |
---|
375 | trace(3, T("DB: Unable to delete row <%d> from table <%s>\n"), |
---|
376 | row, tablename); |
---|
377 | } |
---|
378 | |
---|
379 | return -1; |
---|
380 | } |
---|
381 | |
---|
382 | /*****************************************************************************/ |
---|
383 | /* |
---|
384 | * Grow the rows in the table to the nominated size. |
---|
385 | */ |
---|
386 | |
---|
387 | int dbSetTableNrow(int did, char_t *tablename, int nNewRows) |
---|
388 | { |
---|
389 | int nRet, tid, nRows, nColumns; |
---|
390 | dbTable_t *pTable; |
---|
391 | |
---|
392 | a_assert(tablename); |
---|
393 | tid = dbGetTableId(0, tablename); |
---|
394 | a_assert(tid >= 0) ; |
---|
395 | |
---|
396 | if ((tid >= 0) && (tid < dbMaxTables) && (dbListTables[tid] != NULL)) { |
---|
397 | pTable = dbListTables[tid]; |
---|
398 | } else { |
---|
399 | return DB_ERR_TABLE_NOT_FOUND; |
---|
400 | } |
---|
401 | |
---|
402 | nRet = -1; |
---|
403 | |
---|
404 | a_assert(pTable); |
---|
405 | if (pTable) { |
---|
406 | nColumns = pTable->nColumns; |
---|
407 | nRows = pTable->nRows; |
---|
408 | nRet = 0; |
---|
409 | |
---|
410 | if (nRows >= nNewRows) { |
---|
411 | /* |
---|
412 | * If number of rows already allocated exceeds requested number, do nothing |
---|
413 | */ |
---|
414 | trace(4, T("DB: Ignoring row set to <%d> in table <%s>\n"), |
---|
415 | nNewRows, tablename); |
---|
416 | } else { |
---|
417 | trace(4, T("DB: Setting rows to <%d> in table <%s>\n"), |
---|
418 | nNewRows, tablename); |
---|
419 | while (pTable->nRows < nNewRows) { |
---|
420 | if (dbAddRow(did, tablename) < 0) { |
---|
421 | return -1; |
---|
422 | } |
---|
423 | } |
---|
424 | } |
---|
425 | } |
---|
426 | |
---|
427 | return nRet; |
---|
428 | } |
---|
429 | |
---|
430 | /******************************************************************************/ |
---|
431 | /* |
---|
432 | * Return the number of rows in the given table |
---|
433 | */ |
---|
434 | |
---|
435 | int dbGetTableNrow(int did, char_t *tablename) |
---|
436 | { |
---|
437 | int tid; |
---|
438 | |
---|
439 | a_assert(tablename); |
---|
440 | tid = dbGetTableId(did, tablename); |
---|
441 | |
---|
442 | if ((tid >= 0) && (tid < dbMaxTables) && (dbListTables[tid] != NULL)) { |
---|
443 | return (dbListTables[tid])->nRows; |
---|
444 | } else { |
---|
445 | return -1; |
---|
446 | } |
---|
447 | } |
---|
448 | |
---|
449 | /******************************************************************************/ |
---|
450 | /* |
---|
451 | * Do table driven read of the database |
---|
452 | */ |
---|
453 | |
---|
454 | int dbReadInt(int did, char_t *table, char_t *column, int row, int *returnValue) |
---|
455 | { |
---|
456 | int colIndex, *pRow, tid; |
---|
457 | dbTable_t *pTable; |
---|
458 | |
---|
459 | a_assert(table); |
---|
460 | a_assert(column); |
---|
461 | a_assert(returnValue); |
---|
462 | |
---|
463 | tid = dbGetTableId(0, table); |
---|
464 | a_assert(tid >= 0); |
---|
465 | |
---|
466 | /* |
---|
467 | * Return -6 if table is not found |
---|
468 | */ |
---|
469 | if (tid < 0) { |
---|
470 | return DB_ERR_TABLE_NOT_FOUND; |
---|
471 | } |
---|
472 | |
---|
473 | /* |
---|
474 | * Return -7 if table id has been deleted |
---|
475 | */ |
---|
476 | pTable = dbListTables[tid]; |
---|
477 | if (pTable == NULL) { |
---|
478 | return DB_ERR_TABLE_DELETED; |
---|
479 | } |
---|
480 | |
---|
481 | a_assert(row >= 0); |
---|
482 | |
---|
483 | if ((row >= 0) && (row < pTable->nRows)) { |
---|
484 | colIndex = GetColumnIndex(tid, column); |
---|
485 | a_assert(colIndex >= 0); |
---|
486 | |
---|
487 | if (colIndex >= 0) { |
---|
488 | pRow = pTable->rows[row]; |
---|
489 | if (pRow) { |
---|
490 | *returnValue = pRow[colIndex]; |
---|
491 | return 0; |
---|
492 | } |
---|
493 | return DB_ERR_ROW_DELETED; |
---|
494 | } |
---|
495 | return DB_ERR_COL_NOT_FOUND; |
---|
496 | } |
---|
497 | |
---|
498 | return DB_ERR_ROW_NOT_FOUND; |
---|
499 | } |
---|
500 | |
---|
501 | /******************************************************************************/ |
---|
502 | /* |
---|
503 | * dbReadStr calls dbReadInt to do table driven read of database |
---|
504 | */ |
---|
505 | |
---|
506 | int dbReadStr(int did, char_t *table, char_t *column, int row, |
---|
507 | char_t **returnValue) |
---|
508 | { |
---|
509 | return dbReadInt(did, table, column, row, (int *)returnValue); |
---|
510 | } |
---|
511 | |
---|
512 | /******************************************************************************/ |
---|
513 | /* |
---|
514 | * The dbWriteInt function writes a value into a table at a given row and |
---|
515 | * column. The existence of the row and column is verified before the |
---|
516 | * write. 0 is returned on succes, -1 is returned on error. |
---|
517 | */ |
---|
518 | |
---|
519 | int dbWriteInt(int did, char_t *table, char_t *column, int row, int iData) |
---|
520 | { |
---|
521 | int tid, colIndex, *pRow; |
---|
522 | dbTable_t *pTable; |
---|
523 | |
---|
524 | a_assert(table); |
---|
525 | a_assert(column); |
---|
526 | |
---|
527 | /* |
---|
528 | * Make sure that this table exists |
---|
529 | */ |
---|
530 | tid = dbGetTableId(0, table); |
---|
531 | a_assert(tid >= 0); |
---|
532 | |
---|
533 | if (tid < 0) { |
---|
534 | return DB_ERR_TABLE_NOT_FOUND; |
---|
535 | } |
---|
536 | |
---|
537 | pTable = dbListTables[tid]; |
---|
538 | |
---|
539 | if (pTable) { |
---|
540 | /* |
---|
541 | * Make sure that the column exists |
---|
542 | */ |
---|
543 | colIndex = GetColumnIndex(tid, column); |
---|
544 | a_assert(colIndex >= 0); |
---|
545 | if (colIndex >= 0) { |
---|
546 | /* |
---|
547 | * Make sure that the row exists |
---|
548 | */ |
---|
549 | a_assert((row >= 0) && (row < pTable->nRows)); |
---|
550 | if ((row >= 0) && (row < pTable->nRows)) { |
---|
551 | pRow = pTable->rows[row]; |
---|
552 | if (pRow) { |
---|
553 | pRow[colIndex] = iData; |
---|
554 | return 0; |
---|
555 | } |
---|
556 | return DB_ERR_ROW_DELETED; |
---|
557 | } |
---|
558 | return DB_ERR_ROW_NOT_FOUND; |
---|
559 | } |
---|
560 | return DB_ERR_COL_NOT_FOUND; |
---|
561 | } |
---|
562 | |
---|
563 | return DB_ERR_TABLE_DELETED; |
---|
564 | } |
---|
565 | |
---|
566 | /******************************************************************************/ |
---|
567 | /* |
---|
568 | * The dbWriteStr function writes a string value into a table at a given row |
---|
569 | * and column. The existence of the row and column is verified before the |
---|
570 | * write. The column is also checked to confirm it is a string field. |
---|
571 | * 0 is returned on succes, -1 is returned on error. |
---|
572 | */ |
---|
573 | |
---|
574 | int dbWriteStr(int did, char_t *table, char_t *column, int row, char_t *s) |
---|
575 | { |
---|
576 | int tid, colIndex; |
---|
577 | int *pRow; |
---|
578 | char_t *ptr; |
---|
579 | dbTable_t *pTable; |
---|
580 | |
---|
581 | a_assert(table); |
---|
582 | a_assert(column); |
---|
583 | |
---|
584 | tid = dbGetTableId(0, table); |
---|
585 | a_assert(tid >= 0); |
---|
586 | |
---|
587 | if (tid < 0) { |
---|
588 | return DB_ERR_TABLE_NOT_FOUND; |
---|
589 | } |
---|
590 | |
---|
591 | /* |
---|
592 | * Make sure that this table exists |
---|
593 | */ |
---|
594 | pTable = dbListTables[tid]; |
---|
595 | a_assert(pTable); |
---|
596 | if (!pTable) { |
---|
597 | return DB_ERR_TABLE_DELETED; |
---|
598 | } |
---|
599 | |
---|
600 | /* |
---|
601 | * Make sure that this column exists |
---|
602 | */ |
---|
603 | colIndex = GetColumnIndex(tid, column); |
---|
604 | if (colIndex < 0) { |
---|
605 | return DB_ERR_COL_NOT_FOUND; |
---|
606 | } |
---|
607 | |
---|
608 | /* |
---|
609 | * Make sure that this column is a string column |
---|
610 | */ |
---|
611 | if (pTable->columnTypes[colIndex] != T_STRING) { |
---|
612 | return DB_ERR_BAD_FORMAT; |
---|
613 | } |
---|
614 | |
---|
615 | /* |
---|
616 | * Make sure that the row exists |
---|
617 | */ |
---|
618 | a_assert((row >= 0) && (row < pTable->nRows)); |
---|
619 | if ((row >= 0) && (row < pTable->nRows)) { |
---|
620 | pRow = pTable->rows[row]; |
---|
621 | } else { |
---|
622 | return DB_ERR_ROW_NOT_FOUND; |
---|
623 | } |
---|
624 | |
---|
625 | if (!pRow) { |
---|
626 | return DB_ERR_ROW_DELETED; |
---|
627 | } |
---|
628 | |
---|
629 | /* |
---|
630 | * If the column already has a value, be sure to delete it to prevent |
---|
631 | * memory leaks. |
---|
632 | */ |
---|
633 | if (pRow[colIndex]) { |
---|
634 | bfree(B_L, (char_t *) pRow[colIndex]); |
---|
635 | } |
---|
636 | |
---|
637 | /* |
---|
638 | * Make sure we make a copy of the string to write into the column. |
---|
639 | * This allocated string will be deleted when the row is deleted. |
---|
640 | */ |
---|
641 | ptr = bstrdup(B_L, s); |
---|
642 | pRow[colIndex] = (int)ptr; |
---|
643 | |
---|
644 | return 0; |
---|
645 | } |
---|
646 | |
---|
647 | /******************************************************************************/ |
---|
648 | /* |
---|
649 | * Print a key-value pair to a file |
---|
650 | */ |
---|
651 | |
---|
652 | static int dbWriteKeyValue(int fd, char_t *key, char_t *value) |
---|
653 | { |
---|
654 | int rc; |
---|
655 | int len; |
---|
656 | char_t *pLineOut; |
---|
657 | |
---|
658 | a_assert(key && *key); |
---|
659 | a_assert(value); |
---|
660 | |
---|
661 | fmtAlloc(&pLineOut, BUF_MAX, T("%s=%s\n"), key, value); |
---|
662 | |
---|
663 | if (pLineOut) { |
---|
664 | len = gstrlen(pLineOut); |
---|
665 | #if CE |
---|
666 | rc = writeUniToAsc(fd, pLineOut, len); |
---|
667 | #else |
---|
668 | rc = gwrite(fd, pLineOut, len); |
---|
669 | #endif |
---|
670 | bfree(B_L, pLineOut); |
---|
671 | } else { |
---|
672 | rc = -1; |
---|
673 | } |
---|
674 | |
---|
675 | return rc; |
---|
676 | } |
---|
677 | |
---|
678 | /******************************************************************************/ |
---|
679 | /* |
---|
680 | * Persist a database to a file |
---|
681 | */ |
---|
682 | |
---|
683 | int dbSave(int did, char_t *filename, int flags) |
---|
684 | { |
---|
685 | int row, column, nColumns, nRows, fd, rc; |
---|
686 | int *colTypes, *pRow, nRet, tid; |
---|
687 | char_t *path, *tmpFile, *tmpNum; |
---|
688 | char_t **colNames; |
---|
689 | dbTable_t *pTable; |
---|
690 | |
---|
691 | trace(5, T("DB: About to save database to file\n")); |
---|
692 | |
---|
693 | a_assert(dbMaxTables > 0); |
---|
694 | |
---|
695 | /* |
---|
696 | * First write to a temporary file, then switch around later. |
---|
697 | */ |
---|
698 | fmtAlloc(&tmpFile, FNAMESIZE, T("%s/data.tmp"), basicGetProductDir()); |
---|
699 | if ((fd = gopen(tmpFile, |
---|
700 | O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0666)) < 0) { |
---|
701 | trace(1, T("WARNING: Failed to open file %s\n"), tmpFile); |
---|
702 | bfree(B_L, tmpFile); |
---|
703 | return -1; |
---|
704 | } |
---|
705 | |
---|
706 | nRet = 0; |
---|
707 | |
---|
708 | for (tid = 0; (tid < dbMaxTables) && (nRet != -1); tid++) { |
---|
709 | pTable = dbListTables[tid]; |
---|
710 | |
---|
711 | if (pTable) { |
---|
712 | /* |
---|
713 | * Print the TABLE=tableName directive to the file |
---|
714 | */ |
---|
715 | rc = dbWriteKeyValue(fd, KEYWORD_TABLE, pTable->name); |
---|
716 | |
---|
717 | nColumns = pTable->nColumns; |
---|
718 | nRows = pTable->nRows; |
---|
719 | |
---|
720 | for (row = 0; (row < nRows) && (nRet == 0); row++) { |
---|
721 | pRow = pTable->rows[row]; |
---|
722 | /* |
---|
723 | * if row is NULL, the row has been deleted, so don't |
---|
724 | * write it out. |
---|
725 | */ |
---|
726 | if ((pRow == NULL) || (pRow[0] == '\0') || |
---|
727 | (*(char_t *)(pRow[0]) == '\0')) { |
---|
728 | continue; |
---|
729 | } |
---|
730 | /* |
---|
731 | * Print the ROW=rowNumber directive to the file |
---|
732 | */ |
---|
733 | fmtAlloc(&tmpNum, 20, T("%d"), row); |
---|
734 | rc = dbWriteKeyValue(fd, KEYWORD_ROW, tmpNum); |
---|
735 | bfreeSafe(B_L, tmpNum); |
---|
736 | |
---|
737 | colNames = pTable->columnNames; |
---|
738 | colTypes = pTable->columnTypes; |
---|
739 | /* |
---|
740 | * Print the key-value pairs (COLUMN=value) for data cells |
---|
741 | */ |
---|
742 | for (column = 0; (column < nColumns) && (rc >= 0); |
---|
743 | column++, colNames++, colTypes++) { |
---|
744 | if (*colTypes == T_STRING) { |
---|
745 | rc = dbWriteKeyValue(fd, *colNames, |
---|
746 | (char_t *)(pRow[column])); |
---|
747 | } else { |
---|
748 | fmtAlloc(&tmpNum, 20, T("%d"), pRow[column]); |
---|
749 | rc = dbWriteKeyValue(fd, *colNames, tmpNum); |
---|
750 | bfreeSafe(B_L, tmpNum); |
---|
751 | } |
---|
752 | } |
---|
753 | |
---|
754 | if (rc < 0) { |
---|
755 | trace(1, T("WARNING: Failed to write to file %s\n"), |
---|
756 | tmpFile); |
---|
757 | nRet = -1; |
---|
758 | } |
---|
759 | } |
---|
760 | } |
---|
761 | } |
---|
762 | |
---|
763 | gclose(fd); |
---|
764 | |
---|
765 | /* |
---|
766 | * Replace the existing file with the temporary file, if no errors |
---|
767 | */ |
---|
768 | if (nRet == 0) { |
---|
769 | fmtAlloc(&path, FNAMESIZE, T("%s/%s"), basicGetProductDir(), filename); |
---|
770 | |
---|
771 | gunlink(path); |
---|
772 | if (grename(tmpFile, path) != 0) { |
---|
773 | trace(1, T("WARNING: Failed to rename %s to %s\n"), tmpFile, path); |
---|
774 | nRet = -1; |
---|
775 | } |
---|
776 | |
---|
777 | bfree(B_L, path); |
---|
778 | } |
---|
779 | |
---|
780 | bfree(B_L, tmpFile); |
---|
781 | |
---|
782 | return nRet; |
---|
783 | } |
---|
784 | |
---|
785 | /******************************************************************************/ |
---|
786 | /* |
---|
787 | * Crack a keyword=value string into keyword and value. We can change buf. |
---|
788 | */ |
---|
789 | |
---|
790 | static int crack(char_t *buf, char_t **key, char_t **val) |
---|
791 | { |
---|
792 | char_t *ptr; |
---|
793 | |
---|
794 | if ((ptr = gstrrchr(buf, '\n')) != NULL || |
---|
795 | (ptr = gstrrchr(buf, '\r')) != NULL) { |
---|
796 | *ptr = '\0'; |
---|
797 | } |
---|
798 | |
---|
799 | /* |
---|
800 | * Find the = sign. It must exist. |
---|
801 | */ |
---|
802 | if ((ptr = gstrstr(buf, T("="))) == NULL) { |
---|
803 | return -1; |
---|
804 | } |
---|
805 | |
---|
806 | *ptr++ = '\0'; |
---|
807 | *key = trim(buf); |
---|
808 | *val = trim(ptr); |
---|
809 | |
---|
810 | return 0; |
---|
811 | } |
---|
812 | |
---|
813 | /******************************************************************************/ |
---|
814 | /* |
---|
815 | * Parse the file. These files consist of key-value pairs, separated by the |
---|
816 | * "=" sign. Parsing of tables starts with the "TABLE=value" pair, and rows |
---|
817 | * are parsed starting with the "ROW=value" pair. |
---|
818 | */ |
---|
819 | |
---|
820 | int dbLoad(int did, char_t *filename, int flags) |
---|
821 | { |
---|
822 | gstat_t sbuf; |
---|
823 | char_t *buf, *keyword, *value, *path, *ptr; |
---|
824 | char_t *tablename; |
---|
825 | int fd, tid, row; |
---|
826 | dbTable_t *pTable; |
---|
827 | |
---|
828 | a_assert(did >= 0); |
---|
829 | |
---|
830 | fmtAlloc(&path, FNAMESIZE, T("%s/%s"), basicGetProductDir(), filename); |
---|
831 | trace(4, T("DB: About to read data file <%s>\n"), path); |
---|
832 | |
---|
833 | if (gstat(path, &sbuf) < 0) { |
---|
834 | trace(3, T("DB: Failed to stat persistent data file.\n")); |
---|
835 | bfree(B_L, path); |
---|
836 | return -1; |
---|
837 | } |
---|
838 | |
---|
839 | fd = gopen(path, O_RDONLY | O_BINARY, 0666); |
---|
840 | bfree(B_L, path); |
---|
841 | |
---|
842 | if (fd < 0) { |
---|
843 | trace(3, T("DB: No persistent data file present.\n")); |
---|
844 | return -1; |
---|
845 | } |
---|
846 | |
---|
847 | if (sbuf.st_size <= 0) { |
---|
848 | trace(3, T("DB: Persistent data file is empty.\n")); |
---|
849 | gclose(fd); |
---|
850 | return -1; |
---|
851 | } |
---|
852 | /* |
---|
853 | * Read entire file into temporary buffer |
---|
854 | */ |
---|
855 | buf = balloc(B_L, sbuf.st_size + 1); |
---|
856 | #if CE |
---|
857 | if (readAscToUni(fd, &buf, sbuf.st_size) != (int)sbuf.st_size) { |
---|
858 | #else |
---|
859 | if (gread(fd, buf, sbuf.st_size) != (int)sbuf.st_size) { |
---|
860 | #endif |
---|
861 | trace(3, T("DB: Persistent data read failed.\n")); |
---|
862 | bfree(B_L, buf); |
---|
863 | gclose(fd); |
---|
864 | return -1; |
---|
865 | } |
---|
866 | |
---|
867 | gclose(fd); |
---|
868 | *(buf + sbuf.st_size) = '\0'; |
---|
869 | |
---|
870 | row = -1; |
---|
871 | tid = -1; |
---|
872 | pTable = NULL; |
---|
873 | ptr = gstrtok(buf, T("\n")); |
---|
874 | tablename = NULL; |
---|
875 | |
---|
876 | do { |
---|
877 | if (crack(ptr, &keyword, &value) < 0) { |
---|
878 | trace(5, T("DB: Failed to crack line %s\n"), ptr); |
---|
879 | continue; |
---|
880 | } |
---|
881 | |
---|
882 | a_assert(keyword && *keyword); |
---|
883 | |
---|
884 | if (gstrcmp(keyword, KEYWORD_TABLE) == 0) { |
---|
885 | /* |
---|
886 | * Table name found, check to see if it's registered |
---|
887 | */ |
---|
888 | if (tablename) { |
---|
889 | bfree(B_L, tablename); |
---|
890 | } |
---|
891 | |
---|
892 | tablename = bstrdup(B_L, value); |
---|
893 | tid = dbGetTableId(did, tablename); |
---|
894 | |
---|
895 | if (tid >= 0) { |
---|
896 | pTable = dbListTables[tid]; |
---|
897 | } else { |
---|
898 | pTable = NULL; |
---|
899 | } |
---|
900 | |
---|
901 | } else if (gstrcmp(keyword, KEYWORD_ROW) == 0) { |
---|
902 | /* |
---|
903 | * Row/Record indicator found, add a new row to table |
---|
904 | */ |
---|
905 | if (tid >= 0) { |
---|
906 | int nRows = dbGetTableNrow(did, tablename); |
---|
907 | |
---|
908 | if (dbSetTableNrow(did, tablename, nRows + 1) == 0) { |
---|
909 | row = nRows; |
---|
910 | } |
---|
911 | } |
---|
912 | |
---|
913 | } else if (row != -1) { |
---|
914 | /* |
---|
915 | * some other data found, assume it's a COLUMN=value |
---|
916 | */ |
---|
917 | int nColumn = GetColumnIndex(tid, keyword); |
---|
918 | |
---|
919 | if ((nColumn >= 0) && (pTable != NULL)) { |
---|
920 | int nColumnType = pTable->columnTypes[nColumn]; |
---|
921 | if (nColumnType == T_STRING) { |
---|
922 | dbWriteStr(did, tablename, keyword, row, value); |
---|
923 | } else { |
---|
924 | dbWriteInt(did, tablename, keyword, row, gstrtoi(value)); |
---|
925 | } |
---|
926 | } |
---|
927 | } |
---|
928 | } while ((ptr = gstrtok(NULL, T("\n"))) != NULL); |
---|
929 | |
---|
930 | if (tablename) { |
---|
931 | bfree(B_L, tablename); |
---|
932 | } |
---|
933 | |
---|
934 | bfree(B_L, buf); |
---|
935 | |
---|
936 | return 0; |
---|
937 | } |
---|
938 | |
---|
939 | /******************************************************************************/ |
---|
940 | /* |
---|
941 | * Return a table id given the table name |
---|
942 | */ |
---|
943 | |
---|
944 | int dbGetTableId(int did, char_t *tablename) |
---|
945 | { |
---|
946 | int tid; |
---|
947 | dbTable_t *pTable; |
---|
948 | |
---|
949 | a_assert(tablename); |
---|
950 | |
---|
951 | for (tid = 0; (tid < dbMaxTables); tid++) { |
---|
952 | if ((pTable = dbListTables[tid]) != NULL) { |
---|
953 | if (gstrcmp(tablename, pTable->name) == 0) { |
---|
954 | return tid; |
---|
955 | } |
---|
956 | } |
---|
957 | } |
---|
958 | |
---|
959 | return -1; |
---|
960 | } |
---|
961 | |
---|
962 | /******************************************************************************/ |
---|
963 | /* |
---|
964 | * Return a pointer to the table name, given its ID |
---|
965 | */ |
---|
966 | |
---|
967 | char_t *dbGetTableName(int did, int tid) |
---|
968 | { |
---|
969 | if ((tid >= 0) && (tid < dbMaxTables) && (dbListTables[tid] != NULL)) { |
---|
970 | return (dbListTables[tid])->name; |
---|
971 | } |
---|
972 | |
---|
973 | return NULL; |
---|
974 | } |
---|
975 | |
---|
976 | /******************************************************************************/ |
---|
977 | /* |
---|
978 | * Trim leading white space. |
---|
979 | */ |
---|
980 | |
---|
981 | static char_t *trim(char_t *str) |
---|
982 | { |
---|
983 | while (isspace((int)*str)) { |
---|
984 | str++; |
---|
985 | } |
---|
986 | return str; |
---|
987 | } |
---|
988 | |
---|
989 | /******************************************************************************/ |
---|
990 | /* |
---|
991 | * Return a column index given the column name |
---|
992 | */ |
---|
993 | |
---|
994 | static int GetColumnIndex(int tid, char_t *colName) |
---|
995 | { |
---|
996 | int column; |
---|
997 | dbTable_t *pTable; |
---|
998 | |
---|
999 | a_assert(colName); |
---|
1000 | |
---|
1001 | if ((tid >= 0) && (tid < dbMaxTables) && (dbListTables[tid] != NULL)) { |
---|
1002 | pTable = dbListTables[tid]; |
---|
1003 | |
---|
1004 | for (column = 0; (column < pTable->nColumns); column++) { |
---|
1005 | if (gstrcmp(colName, pTable->columnNames[column]) == 0) |
---|
1006 | return column; |
---|
1007 | } |
---|
1008 | } |
---|
1009 | |
---|
1010 | return -1; |
---|
1011 | } |
---|
1012 | |
---|
1013 | /******************************************************************************/ |
---|
1014 | /* |
---|
1015 | * Set the prefix-directory |
---|
1016 | */ |
---|
1017 | |
---|
1018 | void basicSetProductDir(char_t *proddir) |
---|
1019 | { |
---|
1020 | int len; |
---|
1021 | |
---|
1022 | if (basicProdDir != NULL); { |
---|
1023 | bfree(B_L, basicProdDir); |
---|
1024 | } |
---|
1025 | |
---|
1026 | basicProdDir = bstrdup(B_L, proddir); |
---|
1027 | /* |
---|
1028 | * Make sure that prefix-directory doesn't end with a '/' |
---|
1029 | */ |
---|
1030 | len = gstrlen(basicProdDir); |
---|
1031 | if ((len > 0) && *(basicProdDir + len - 1) == '/') { |
---|
1032 | *(basicProdDir+len-1) = '\0'; |
---|
1033 | } |
---|
1034 | } |
---|
1035 | |
---|
1036 | /******************************************************************************/ |
---|
1037 | /* |
---|
1038 | * Return the prefix-directory |
---|
1039 | */ |
---|
1040 | |
---|
1041 | char_t *basicGetProductDir() |
---|
1042 | { |
---|
1043 | if (basicProdDir) { |
---|
1044 | return basicProdDir; |
---|
1045 | } else { |
---|
1046 | return basicDefaultDir; |
---|
1047 | } |
---|
1048 | } |
---|
1049 | |
---|
1050 | /******************************************************************************/ |
---|