]> git.deb.at Git - pkg/abook.git/blob - database.c
Initial revision
[pkg/abook.git] / database.c
1
2 /*
3  * database.c
4  * by JH <jheinonen@bigfoot.com>
5  *
6  * Copyright (C) Jaakko Heinonen
7  */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <fcntl.h>
14 #include "abook_curses.h"
15 #include "abook.h"
16 #include "database.h"
17 #include "list.h"
18 #include "misc.h"
19 #include "options.h"
20 #include "filter.h"
21 #ifdef HAVE_CONFIG_H
22 #       include "config.h"
23 #endif
24
25 static void     free_item(int i);
26
27
28 list_item *database = NULL;
29
30 int items = 0;
31
32 #define INITIAL_LIST_CAPACITY   30
33
34 int list_capacity = 0;
35
36 extern int first_list_item;
37 extern int curitem;
38 extern char *selected;
39
40 extern char *datafile;
41 extern char *rcfile;
42
43 /*
44  * field definitions
45  */
46
47 #include "edit.h"
48
49 /*
50  * notes about adding fields:
51  *      - do not change any fields in TAB_CONTACT
52  *      - do not add fields to contact tab
53  *      - 6 fields per tab is maximum
54  *      - reorganize the field numbers in database.h
55  */
56
57 struct abook_field abook_fields[ITEM_FIELDS] = {
58         {"Name",        "name",         TAB_CONTACT},/* NAME */
59         {"E-mails",     "email",        TAB_CONTACT},/* EMAIL */
60         {"Address",     "address",      TAB_ADDRESS},/* ADDRESS */
61         {"City",        "city",         TAB_ADDRESS},/* CITY */
62         {"State/Province","state",      TAB_ADDRESS},/* STATE */
63         {"ZIP/Postal Code","zip",       TAB_ADDRESS},/* ZIP */
64         {"Country",     "country",      TAB_ADDRESS},/* COUNTRY */
65         {"Home Phone",  "phone",        TAB_PHONE},/* PHONE */
66         {"Work Phone",  "workphone",    TAB_PHONE},/* WORKPHONE */
67         {"Fax",         "fax",          TAB_PHONE},/* FAX */
68         {"Mobile",      "mobile",       TAB_PHONE},/* MOBILEPHONE */
69         {"Nickname/Alias", "nick",      TAB_OTHER},/* NICK */
70         {"URL",         "url",          TAB_OTHER},/* URL */
71         {"Notes",       "notes",        TAB_OTHER},/* NOTES */
72 };
73
74
75 int
76 parse_database(FILE *in)
77 {
78         char *line = NULL;
79         char *tmp;
80         int sec=0, i;
81         list_item item;
82
83         memset(&item, 0, sizeof(item));
84         
85         for(;;) {
86                 line = getaline(in);
87                 if( feof(in) ) {
88                         if( item[NAME] && sec )
89                                 add_item2database(item);
90                         else
91                                 free_list_item(item);
92                         break;
93                 }
94
95                 if( !*line || *line == '\n' || *line == '#' ) {
96                         free(line);
97                         continue;
98                 } else if( *line == '[' ) {
99                         if( item[NAME] && sec )
100                                 add_item2database(item);
101                         else
102                                 free_list_item(item);
103                         memset(&item, 0, sizeof(item));
104                         sec = 1;
105                         if ( !(tmp = strchr(line, ']')))
106                                 sec = 0; /*incorrect section lines are skipped*/
107                 } else if((tmp = strchr(line, '=') ) && sec ) {
108                         *tmp++ = '\0';
109                         for(i=0; i<ITEM_FIELDS; i++)
110                                 if( !strcmp(abook_fields[i].key, line) )
111                                         item[i] = strdup(tmp);
112                 }
113                 free(line);
114         }
115
116         free(line);
117         return 0;
118 }
119
120                 
121
122 int
123 load_database(char *filename)
124 {
125         FILE *in;
126
127         if( database != NULL )
128                 close_database();
129
130         if ( (in = abook_fopen(filename, "r")) == NULL )
131                 return -1;
132         
133         parse_database(in);
134
135         if ( items == 0 )
136                 return 2;
137
138         return 0;
139 }
140
141 int
142 write_database(FILE *out)
143 {
144         int i,j;
145
146         fprintf(out, "# abook addressbook file\n\n");
147         fprintf(out, "[format]\n");
148         fprintf(out, "program=" PACKAGE "\n");
149         fprintf(out, "version=" VERSION "\n");
150         fprintf(out, "\n\n");
151
152         for( i = 0; i < items; i++ ) {
153                 fprintf(out, "[%d]\n", i);
154                 for(j=0; j<ITEM_FIELDS; j++) {
155                         if( database[i][j] != NULL )
156                                 if( *database[i][j] )
157                                         fprintf(out, "%s=%s\n",
158                                                 abook_fields[j].key, database[i][j]);
159                 }
160                 fputc('\n', out);
161         }
162
163         return 0;
164 }
165
166 int
167 save_database()
168 {
169         FILE *out;
170
171         if( (out = abook_fopen(datafile, "w")) == NULL )
172                 return -1;
173
174         if( items < 1 ) {
175                 fclose(out);
176                 unlink(datafile);
177                 return 1;
178         }
179
180         
181         write_database(out);
182         
183         fclose(out);
184         
185         return 0;
186 }
187
188 static void
189 free_item(int item)
190 {
191         free_list_item(database[item]);
192 }
193
194 void
195 free_list_item(list_item item)
196 {
197         int i;
198
199         for(i=0; i<ITEM_FIELDS; i++)
200                 my_free(item[i]);
201 }
202
203 void
204 close_database()
205 {
206         int i;
207         
208         for(i=0; i < items; i++)
209                 free_item(i);
210
211         free(database);
212         free(selected);
213
214         database = NULL;
215         selected = NULL;
216
217         items = 0;
218         first_list_item = curitem = -1;
219         list_capacity = 0;
220 }
221
222 #define _MAX_FIELD_LEN(X)       (X == EMAIL ? MAX_EMAILSTR_LEN:MAX_FIELD_LEN)
223
224 inline static void
225 validate_item(list_item item)
226 {
227         int i;
228         char *tmp;
229         
230         if(item[EMAIL] == NULL)
231                 item[EMAIL] = strdup("");
232
233         for(i=0; i<ITEM_FIELDS; i++)
234                 if( item[i] && (strlen(item[i]) > _MAX_FIELD_LEN(i) ) ) {
235                         tmp = item[i];
236                         item[i][_MAX_FIELD_LEN(i)-1] = 0;
237                         item[i] = strdup(item[i]);
238                         free(tmp);
239                 }
240 }
241
242
243 static void
244 adjust_list_capacity()
245 {
246         if(list_capacity < 1)
247                 list_capacity = INITIAL_LIST_CAPACITY;
248         else if(items >= list_capacity)
249                 list_capacity *= 2;
250         else if(list_capacity / 2 > items)
251                 list_capacity /= 2;
252         else
253                 return;
254
255         database = abook_realloc(database,
256                         sizeof(list_item) * list_capacity);
257         selected = abook_realloc(selected, list_capacity);
258 }
259
260 int
261 add_item2database(list_item item)
262 {
263         if( item[NAME] == NULL || ! *item[NAME] ) {
264                 free_list_item(item);
265                 return 1;
266         }
267
268         if( ++items > list_capacity)
269                 adjust_list_capacity();
270
271         validate_item(item);
272
273         selected[LAST_ITEM] = 0;
274         itemcpy(database[LAST_ITEM], item);
275
276         return 0;
277 }
278
279 void
280 remove_items()
281 {
282         int i, j;
283
284         if( items < 1 || curitem < 0 )
285                 return;
286
287         statusline_addstr("Remove selected item(s) (Y/n)");
288         switch( getch() ) {
289                 case '\r':
290                 case 'y':
291                 case 'Y': break;
292                 default:
293                           clear_statusline();
294                           return;
295         }
296
297         if( ! selected_items() )
298                 selected[ curitem ] = 1;
299         
300         for( j = LAST_ITEM; j >= 0; j-- ) {
301                 if( selected[j] ) {
302                         free_item(j); /* added for .4 data_s_ */
303                         for( i = j; i < LAST_ITEM; i++ ) {
304                                 itemcpy(database[ i ], database[ i + 1 ]);
305                                 selected[ i ] = selected[ i + 1 ];
306                         }
307                         items--;        
308                 }
309         }
310
311         if( curitem > LAST_ITEM && items > 0 )
312                 curitem = LAST_ITEM;
313
314
315         adjust_list_capacity();
316
317         select_none();
318         clear_statusline();     
319         refresh_list();
320 }
321
322 char *
323 get_surname(char *s)
324 {
325         int i, a;
326         int len = strlen(s);
327         char *name = strdup(s);
328
329         for( a = 0, i = len - 1; i >= 0; i--, a++ ) {
330                 name[a] = s[i];
331                 if(name[a] == ' ')
332                         break;
333         }
334
335         name[ a ] = 0;
336
337         revstr(name);
338
339         return name;
340 }
341
342 static int
343 surnamecmp(const void *i1, const void *i2)
344 {
345         int ret;
346         list_item a,b;
347         char *s1, *s2;
348
349         itemcpy(a, i1);
350         itemcpy(b, i2);
351
352         s1 = get_surname(a[NAME]);
353         s2 = get_surname(b[NAME]);
354
355         if( !(ret = safe_strcmp(s1, s2)) )
356                 ret = safe_strcmp(a[NAME], b[NAME]);
357
358         free(s1);
359         free(s2);
360
361         return ret;
362 }
363
364 static int
365 namecmp(const void *i1, const void *i2)
366 {
367         list_item a, b;
368
369         itemcpy(a, i1);
370         itemcpy(b, i2);
371         
372         return safe_strcmp( a[NAME], b[NAME] );
373 }
374
375 void
376 sort_database()
377 {
378         select_none();
379         
380         qsort((void *)database, items, sizeof(list_item), namecmp);
381
382         refresh_screen();
383 }
384
385 void
386 sort_surname()
387 {
388         select_none();
389
390         qsort((void *)database, items, sizeof(list_item), surnamecmp);
391
392         refresh_screen();
393 }
394
395 void
396 clear_database()
397 {
398
399         statusline_addstr("Clear WHOLE database (y/N)");
400         switch( getch() ) {
401                 case 'y':
402                 case 'Y': break;
403                 default:
404                         clear_statusline();
405                         return;
406         }
407
408         close_database();
409
410         refresh_screen();
411 }
412
413 void
414 find(int next)
415 {
416         int i;
417         static char findstr[81];
418         char tmp[81];
419
420 #ifdef DEBUG
421         fprintf(stderr, "find(): findstr = |%s|\n", findstr);
422 #endif
423         
424         if(next) {
425                 if( !*findstr )
426                         return;
427         } else {
428                 clear_statusline();
429                 statusline_addstr("/");
430                 statusline_getnstr(findstr, 67, 0);
431                 strupper(findstr);
432                 clear_statusline();
433         }
434
435         if(items < 1)
436                 return;
437
438         for( i = (curitem < LAST_ITEM) && next ? curitem+1 : curitem;
439                         i < items; i++ ) {
440                 strcpy(tmp, database[i][NAME]);
441                 if( strstr(strupper(tmp), findstr) != NULL ) {
442                         curitem = i;
443                         refresh_list();
444                         break;
445                 }
446         }
447 }
448
449
450 void
451 print_number_of_items()
452 {
453         char *str =
454                 mkstr("     |%3d/%3d", selected_items(), items);
455
456         mvaddstr(0, COLS-strlen(str), str);
457
458         free(str);
459 }
460
461 void
462 read_database()
463 {
464         if(items > 0) {
465                 statusline_addstr("Your current data will be lost - Press 'y' to continue");
466                 switch( getch() ) {
467                         case 'y':
468                         case 'Y': break;
469                         default: clear_statusline();
470                                  return;
471                 }
472                 clear_statusline();
473         }
474
475         load_database(datafile);
476         refresh_list();
477 }
478
479
480 void
481 print_database()
482 {
483         FILE *handle;
484         char *command = options_get_str("print_command");
485
486         statusline_addstr("Print addressbook? (y/N)");
487         switch( getch() ) {
488                 case 'y':
489                 case 'Y':
490                         break;
491                 default: clear_statusline(); return;
492         }
493         clear_statusline();
494
495         if( ! *command || (handle = popen(command, "w")) == NULL)
496                 return;
497
498         fexport("text", handle);
499         
500         pclose(handle);
501 }
502