Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dtinfo / dtinfogen / infolib / etc / DataBase.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: DataBase.C /main/5 1998/04/17 11:43:17 mgreess $ 
24  *
25  * (c) Copyright 1996 Digital Equipment Corporation.
26  * (c) Copyright 1996 Hewlett-Packard Company.
27  * (c) Copyright 1996 International Business Machines Corp.
28  * (c) Copyright 1996 Sun Microsystems, Inc.
29  * (c) Copyright 1996 Novell, Inc. 
30  * (c) Copyright 1996 FUJITSU LIMITED.
31  * (c) Copyright 1996 Hitachi.
32  */
33
34 /* imported interfaces... */
35 #include <string.h>
36 #include <stdarg.h>
37 #include <assert.h>
38 #include <unistd.h>
39 #include <errno.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 #include "StringList.h"
43 #include "Token.h"
44
45 /* exported interfaces... */
46 #include "DataBase.h"
47
48
49 #define FRIENDLY_ASSERT(e) \
50   if(!(e)) Token::signalError(Token::Internal, Token::Fatal, \
51                               __FILE__, __LINE__, \
52                               "assertion failed: " #e);
53
54 DB::DB(const char *name)
55 {
56   f_name = new char[strlen(name)+1];
57   strcpy(f_name, name);
58 }
59
60
61 static int isdir(const char* filename)
62 {
63   int ret = 0;
64   struct stat sb;
65
66   if(stat(filename, &sb) == 0){
67     if(S_ISDIR(sb.st_mode)){
68       ret = 1;
69     }
70   }
71
72   return ret;
73 }
74
75
76 static void makedir(const char *path) /* throw(PosixError) */
77 {
78   if(mkdir((char*)path, DATABASE_DIRECTORY_MODE) != 0){
79     throw(PosixError(errno, path));
80   }
81 }
82
83
84 DBTable *DB::table(const char *tname, int scode, int cols,
85                    int access)
86      /* throw(PosixError); */
87 {
88   DBTable *ret = 0; /* keep compiler happy */
89
90   switch(access){
91   case CREATE:
92     if(!isdir(f_name)){
93       makedir(f_name);
94     }
95
96     ret = new DBTable(this, scode, cols, tname);
97     break;
98
99   case READ:
100     ret = new DBTable(this, scode, cols, tname);
101     ret->file(DB::READ);
102     break;
103
104   default:
105     abort();
106   }
107
108   return ret;
109 }
110
111
112
113 DBTable::DBTable(DB *database, int schema_code, int cols, const char *name)
114 {
115   f_database = database;
116   f_schema_code = schema_code;
117   f_cols = cols;
118
119   f_name = new char[strlen(name)+1];
120   strcpy(f_name, name);
121
122   f_file = NULL;
123   f_start = 0;
124 }
125
126
127 DBTable::~DBTable()
128 {
129   if(f_file && strcmp(f_name, DATABASE_STDIO) != 0) fclose(f_file);
130   delete f_name; 
131 }
132
133
134 FILE *
135 DBTable::file(DB::Access access)
136 {
137   if(!f_file){
138     if(strcmp(f_name, DATABASE_STDIO) == 0){
139
140       f_file = access == DB::CREATE ? stdout : stdin;
141
142     }else{
143
144       const char *p = f_database->path();
145       char *path = new char[strlen(p) + 1 + strlen(f_name) + 1];
146       sprintf(path, "%s/%s", p, f_name);
147       
148       f_file = fopen(path, access == DB::CREATE ? "w" : "r");
149       
150       if(!f_file){
151         throw(PosixError(errno, path));
152       }
153
154       delete path;
155     }
156   }
157
158   return f_file;
159 }
160
161
162 //----------------------------------------------------------
163 void DBTable::insert(int typecode, ...)
164 {
165   FILE *out = file(DB::CREATE);
166   va_list ap;
167
168   va_start(ap, typecode);
169
170   fprintf(out, "%d\n%d\n", f_schema_code, f_cols);
171
172   int cols_found = 0;
173   
174   while(typecode != 0){
175     switch(typecode){
176     case STRING_CODE:
177       {
178         const char *str = va_arg(ap, const char*);
179         fprintf(out, "%d\n%ld\t%s\n", STRING_CODE, (long)strlen(str), str);
180       }
181       break;
182
183     case -STRING_CODE:
184       {
185         const char *str = va_arg(ap, const char*);
186         size_t len = va_arg(ap, size_t);
187         fprintf(out, "%d\n%ld\t", STRING_CODE, (long)len );
188         fwrite ( str, len, 1, out );
189         fputc( '\n', out);
190       }
191       break;
192
193     case COMPRESSED_STRING_CODE:
194       {
195         const char *comp_agent  = va_arg(ap, const char* );
196         const char *str = va_arg(ap, const char* );
197
198         fprintf(out, "%d\n%s\n%ld\t%s\n", COMPRESSED_STRING_CODE,comp_agent,
199                 (long)strlen(str), str );
200       }
201       break;
202
203    case -COMPRESSED_STRING_CODE:
204       {
205         const char *comp_agent  = va_arg(ap, const char* );
206         const char *str = va_arg(ap, const char* );
207
208         size_t len = va_arg(ap, size_t );
209         fprintf(out, "%d\n%s\n%ld\t", COMPRESSED_STRING_CODE,
210                                      comp_agent,
211                                      (long)len
212                 );
213
214         fwrite( str, len, 1, out );
215         fputc('\n', out);
216       }
217       break;
218         
219
220     case OID_CODE:
221       {
222         const char *oid = va_arg(ap, const char*);
223         fprintf(out, "%d\n%s\n", OID_CODE, oid);
224       }
225       break;
226       
227     case INTEGER_CODE:
228       {
229         int x = va_arg(ap, int);
230         fprintf(out, "%d\n%d\n", INTEGER_CODE, x);
231       }
232       break;
233
234     case SHORT_LIST_CODE:
235       {
236         int qty = va_arg(ap, int);
237         int code = va_arg(ap, int);
238
239         fprintf(out, "%d\n#\n", SHORT_LIST_CODE);
240         
241         switch(code){
242         case INTEGER_CODE:
243           {
244             int *items = va_arg(ap, int*);
245
246             for(int i = 0; i < qty; i++){
247               fprintf(out, "%d\n%d\n", code, items[i]);
248             }
249           }
250           break;
251
252         case STRING_CODE:
253           {
254             const char **items = va_arg(ap, const char**);
255
256             for(int i = 0; i < qty; i++){
257               fprintf(out, "%d\n%ld\t%s\n",
258                       code, (long)strlen(items[i]), items[i]);
259             }
260           }
261           break;
262           
263         case OID_CODE:
264           {
265             const char **items = va_arg(ap, const char**);
266
267             for(int i = 0; i < qty; i++){
268               fprintf(out, "%d\n%s\n",
269                       code, items[i]);
270             }
271           }
272           break;
273           
274         default:
275           fprintf(stderr, "Internal error: unknown database type code: %d\n",
276                   code);
277           abort();
278         }
279
280         fprintf(out, "#\n");
281
282         break;
283       }
284       
285     case OID_LIST_CODE:
286       {
287         int qty = va_arg(ap, int);
288
289         fprintf(out, "%d\n#\n", OID_LIST_CODE);
290         
291         const char **items = va_arg(ap, const char**);
292
293         for(int i = 0; i < qty; i++){
294           fprintf(out, "%s\n", items[i]);
295         }
296
297         fprintf(out, "#\n");
298
299         break;
300       }
301       
302     default:
303       fprintf(stderr, "Internal error: unknown database type code: %d\n",
304               typecode);
305       abort();
306     }
307
308     cols_found++;
309     
310     typecode = va_arg(ap, int);
311   }
312
313   va_end(ap);
314   
315   fflush(out); /* @# some routines are sloppy and don't
316                 * close their tables!
317                 */
318   
319   assert(cols_found == f_cols);
320 }
321
322
323
324 //----------------------------------------------------------
325 void DBTable::insert_untagged(int typecode, ...)
326 {
327   FILE *out = file(DB::CREATE);
328   va_list ap;
329
330   va_start(ap, typecode);
331
332   fprintf(out, "%d\n", f_schema_code);
333
334   if(f_start){
335     fprintf(out, "+");
336     f_start = 0;
337   }
338
339   int cols_found = 0;
340   
341   while(typecode != 0){
342     switch(typecode){
343     case STRING_CODE:
344       {
345         const char *str = va_arg(ap, const char*);
346         fprintf(out, "%ld\t%s\n", (long)strlen(str), str);
347       }
348       break;
349
350     case -STRING_CODE:
351       {
352         const char *str = va_arg(ap, const char*);
353         size_t len = va_arg(ap, size_t);
354         fprintf(out, "%ld\t", (long)len );
355         fwrite ( str, len, 1, out );
356         fputc( '\n', out);
357       }
358       break;
359
360     case OID_CODE:
361       {
362         const char *oid = va_arg(ap, const char*);
363         fprintf(out, "%s\n", oid);
364       }
365       break;
366       
367     case INTEGER_CODE:
368       {
369         int x = va_arg(ap, int);
370         fprintf(out, "%d\n", x);
371       }
372       break;
373
374     default:
375       fprintf(stderr, "Internal error: unknown database type code: %d\n",
376               typecode);
377       abort();
378     }
379
380     cols_found++;
381     
382     typecode = va_arg(ap, int);
383   }
384
385   va_end(ap);
386   
387   fflush(out); /* @# some routines are sloppy and don't
388                 * close their tables!
389                 */
390   
391   assert(cols_found == f_cols);
392 }
393
394
395
396 //----------------------------------------------------------
397 void DBTable::start_list()
398 {
399   this->f_start = 1;
400 }
401
402
403 //----------------------------------------------------------
404 void DBTable::end_list()
405 {
406   fprintf(file(DB::CREATE), "-\n");
407   f_start = 0;
408 }
409
410
411 //----------------------------------------------------------
412 DBCursor::DBCursor(DBTable &t)
413 {
414   f_table = &t;
415   f_start = -1;
416   
417   f_fields = new StringList();
418   f_list = NULL;
419 }
420
421
422 //----------------------------------------------------------
423 DBCursor::~DBCursor()
424 {
425
426   // this is for the last record
427   delete f_fields;
428   if ( f_list ) delete f_list;
429 }
430
431
432 //----------------------------------------------------------
433
434 void DBCursor::string_field(FILE *fp, char **out, int *lenOut)
435 {
436   int len = 0;
437   int io;
438
439   /* fscanf is wierd, so we do it ourselves... */
440   while(isdigit(io = fgetc(fp))){
441     len = len * 10 + (io - '0');
442   }
443   FRIENDLY_ASSERT(io == '\t');
444   
445   char *str = new char[len + 1];
446   io = fread(str, sizeof(str[0]), len+1, fp); /* read \n also */
447
448   FRIENDLY_ASSERT(io == len+1);
449
450   str[len] = 0; /* replace \n with 0 (just in case...) */
451
452   if(out){
453     f_fields->add(str);
454     *out = str;
455
456     if(lenOut) *lenOut = len;
457   }else{
458     delete str;
459   }
460 }
461
462
463
464 void DBCursor::int_field(FILE *fp, int *out)
465 {
466   int an_int;
467   int io;
468   
469   io = fscanf(fp, "%d\n", &an_int);
470
471   FRIENDLY_ASSERT(io == 1);
472           
473   if(out) *out = an_int;
474 }
475
476
477 void DBCursor::short_list(FILE *fp, int *qout, int ltype, void *out)
478 {
479   int c;
480       
481   c = fgetc(fp);
482   FRIENDLY_ASSERT(c == '#');
483
484   c = fgetc(fp);
485   FRIENDLY_ASSERT(c == '\n');
486       
487   switch(ltype){
488   case STRING_CODE:
489     {
490       typedef const char** ccpp;
491       ccpp *cpout = (ccpp*)out;
492           
493       assert(f_list == NULL); /* only one SHORT_LIST per record supported */
494       f_list = new StringList;
495
496       while((c = fgetc(fp)) != '#'){
497         char *item;
498         int ftype;
499         
500         ungetc(c, fp);
501         fscanf(fp, "%d\n", &ftype);
502         FRIENDLY_ASSERT(ftype == STRING_CODE);
503         
504         string_field(fp, &item, NULL);
505         f_list->append(item);
506       }
507
508       *cpout = f_list->array();
509       *qout = f_list->qty();
510     }
511     break;
512         
513   default:
514     abort(); /* only strings supported */
515   }
516
517   c = fgetc(fp);
518
519   FRIENDLY_ASSERT(c == '\n');
520 }
521
522
523 int DBCursor::next(int typeCode, ...)
524 {
525   int ret = 1;
526   FILE *fp = f_table->file(DB::READ);
527   int io;
528   int recordClass;
529     
530   if(f_start < 0){ /* on first call to next(), reset the file */
531     rewind(fp);
532   }
533   
534   f_start = ftell(fp);
535   io = fscanf(fp, "%d\n", &recordClass); /* get record code */
536
537   if(io != EOF){ /* got any data? */
538     
539     FRIENDLY_ASSERT(io == 1);
540
541     // clean up previous fields first if they exist
542     f_fields->reset();
543     delete f_list; f_list = NULL;
544
545     va_list ap;
546     va_start(ap, typeCode);
547
548     int fieldQty;
549
550     io = fscanf(fp, "%d\n", &fieldQty); /* get field count */
551     FRIENDLY_ASSERT(io == 1);
552
553     /* iterate over fields in the input stream... */
554     while(fieldQty--){
555       int fieldCode;
556   
557       io = fscanf(fp, "%d\n", &fieldCode); /* get field type */
558       FRIENDLY_ASSERT(io == 1);
559       
560       assert(typeCode); /* make sure caller didn't give too few args */
561       
562       switch(fieldCode){
563       case STRING_CODE:
564         {
565           char **data = NULL;
566           int *len = NULL;
567
568           if (fieldCode == typeCode || (fieldCode + typeCode) == 0) {
569             data = va_arg(ap, char**);
570
571             if (fieldCode != typeCode) {
572               len = va_arg(ap, int*);
573             }
574           }
575           
576           string_field(fp, data, len);
577         }
578         break;
579
580       case INTEGER_CODE:
581         int_field(fp, fieldCode == typeCode ? va_arg(ap, int*) : 0);
582         break;
583
584       case SHORT_LIST_CODE:
585         {
586           int *qout = va_arg(ap, int *);
587           int ltype = va_arg(ap, int);
588           void *out = va_arg(ap, void*);
589           short_list(fp, qout, ltype, out);
590         }
591         break;
592     
593       default:
594         abort();
595       }
596
597       typeCode = va_arg(ap, int);
598     }
599
600     assert(typeCode == 0); /* check for end marker */
601     
602     va_end(ap);
603     
604   }else{
605     ret = 0; /* EOF found... no record */
606   }
607
608   return ret;
609 }
610
611
612 void DBCursor::undoNext()
613 {
614   FILE *fp = f_table->file(DB::READ);
615
616   if(f_start >= 0){
617     if(fseek(fp, f_start, 0) < 0){
618       throw(PosixError(errno, f_table->name()));
619     }
620   }else{
621     abort(); /* @# throw("no next to undo!") */
622   }
623 }
624
625 //----------------------------------------------------------------
626 void DBCursor::local_rewind()
627 {
628   f_start = -1;
629 }
630
631 //----------------------------------------------------------------
632 int DBCursor::tell()
633 {
634   if ( f_start == -1 ) {
635     return(0);
636   }
637   else {
638     return f_start;
639   }
640 }
641
642 //----------------------------------------------------------------
643 void DBCursor::seekToRec( int pos )
644 {
645   FILE *fp = f_table->file(DB::READ);
646   if ( pos >= 0 ){
647     if (fseek(fp, pos, 0) < 0 ) {
648       throw(PosixError(errno, f_table->name()));
649     }
650   }
651   else{
652     abort();
653   }
654 }  
655   
656   
657