[a6b4c0df] | 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 | */ |
---|
[bfb4c547] | 66 | tid = hAllocEntry((void*) &dbListTables, |
---|
[a6b4c0df] | 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); |
---|
[bfb4c547] | 170 | hFree((void *) &dbListTables, table); |
---|
[a6b4c0df] | 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); |
---|
[ee3afa2] | 665 | #ifdef CE |
---|
[a6b4c0df] | 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); |
---|
[ee3afa2] | 856 | #ifdef CE |
---|
[a6b4c0df] | 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 | |
---|
[ee3afa2] | 1022 | if (basicProdDir != NULL) { |
---|
| 1023 | bfree(B_L, basicProdDir); |
---|
[a6b4c0df] | 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 | /******************************************************************************/ |
---|