]> git.deb.at Git - pkg/abook.git/blob - database.c
First attempt of the better interface for filters
[pkg/abook.git] / database.c
1
2 /*
3  * $Id$
4  *
5  * by JH <jheinonen@bigfoot.com>
6  *
7  * Copyright (C) Jaakko Heinonen
8  */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <fcntl.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, struct db_enumerator e)
143 {
144         int j;
145         int i = 0;
146
147         fprintf(out, "# abook addressbook file\n\n");
148         fprintf(out, "[format]\n");
149         fprintf(out, "program=" PACKAGE "\n");
150         fprintf(out, "version=" VERSION "\n");
151         fprintf(out, "\n\n");
152
153         db_enumerate_items(e) {
154                 fprintf(out, "[%d]\n", i);
155                 for(j=0; j<ITEM_FIELDS; j++) {
156                         if( database[e.item][j] != NULL &&
157                                         *database[e.item][j] )
158                                 fprintf(out, "%s=%s\n",
159                                         abook_fields[j].key,
160                                         database[e.item][j]
161                                         );
162                 }
163                 fputc('\n', out);
164                 i++;
165         }
166
167         return 0;
168 }
169
170 int
171 save_database()
172 {
173         FILE *out;
174         struct db_enumerator e = init_db_enumerator(ENUM_ALL);
175
176         if( (out = abook_fopen(datafile, "w")) == NULL )
177                 return -1;
178
179         if( list_is_empty() ) {
180                 fclose(out);
181                 unlink(datafile);
182                 return 1;
183         }
184
185         
186         write_database(out, e);
187         
188         fclose(out);
189         
190         return 0;
191 }
192
193 static void
194 free_item(int item)
195 {
196         free_list_item(database[item]);
197 }
198
199 void
200 free_list_item(list_item item)
201 {
202         int i;
203
204         for(i=0; i<ITEM_FIELDS; i++)
205                 my_free(item[i]);
206 }
207
208 void
209 close_database()
210 {
211         int i;
212         
213         for(i=0; i < items; i++)
214                 free_item(i);
215
216         free(database);
217         free(selected);
218
219         database = NULL;
220         selected = NULL;
221
222         items = 0;
223         first_list_item = curitem = -1;
224         list_capacity = 0;
225 }
226
227 #define _MAX_FIELD_LEN(X)       (X == EMAIL ? MAX_EMAILSTR_LEN:MAX_FIELD_LEN)
228
229 inline static void
230 validate_item(list_item item)
231 {
232         int i;
233         char *tmp;
234         
235         if(item[EMAIL] == NULL)
236                 item[EMAIL] = strdup("");
237
238         for(i=0; i<ITEM_FIELDS; i++)
239                 if( item[i] && (strlen(item[i]) > _MAX_FIELD_LEN(i) ) ) {
240                         tmp = item[i];
241                         item[i][_MAX_FIELD_LEN(i)-1] = 0;
242                         item[i] = strdup(item[i]);
243                         free(tmp);
244                 }
245 }
246
247
248 static void
249 adjust_list_capacity()
250 {
251         if(list_capacity < 1)
252                 list_capacity = INITIAL_LIST_CAPACITY;
253         else if(items >= list_capacity)
254                 list_capacity *= 2;
255         else if(list_capacity / 2 > items)
256                 list_capacity /= 2;
257         else
258                 return;
259
260         database = abook_realloc(database,
261                         sizeof(list_item) * list_capacity);
262         selected = abook_realloc(selected, list_capacity);
263 }
264
265 int
266 add_item2database(list_item item)
267 {
268         if( item[NAME] == NULL || ! *item[NAME] ) {
269                 free_list_item(item);
270                 return 1;
271         }
272
273         if( ++items > list_capacity)
274                 adjust_list_capacity();
275
276         validate_item(item);
277
278         selected[LAST_ITEM] = 0;
279         itemcpy(database[LAST_ITEM], item);
280
281         return 0;
282 }
283
284 void
285 remove_selected_items()
286 {
287         int i, j;
288
289         if( list_is_empty() )
290                 return;
291
292         if( ! selected_items() )
293                 selected[ curitem ] = 1;
294         
295         for( j = LAST_ITEM; j >= 0; j-- ) {
296                 if( selected[j] ) {
297                         free_item(j); /* added for .4 data_s_ */
298                         for( i = j; i < LAST_ITEM; i++ ) {
299                                 itemcpy(database[ i ], database[ i + 1 ]);
300                                 selected[ i ] = selected[ i + 1 ];
301                         }
302                         items--;        
303                 }
304         }
305
306         if( curitem > LAST_ITEM && items > 0 )
307                 curitem = LAST_ITEM;
308
309
310         adjust_list_capacity();
311
312         select_none();
313 }
314
315 char *
316 get_surname(char *s)
317 {
318         int i, a;
319         int len = strlen(s);
320         char *name = strdup(s);
321
322         for( a = 0, i = len - 1; i >= 0; i--, a++ ) {
323                 name[a] = s[i];
324                 if(name[a] == ' ')
325                         break;
326         }
327
328         name[ a ] = 0;
329
330         revstr(name);
331
332         return name;
333 }
334
335 static int
336 surnamecmp(const void *i1, const void *i2)
337 {
338         int ret;
339         list_item a,b;
340         char *s1, *s2;
341
342         itemcpy(a, i1);
343         itemcpy(b, i2);
344
345         s1 = get_surname(a[NAME]);
346         s2 = get_surname(b[NAME]);
347
348         if( !(ret = safe_strcmp(s1, s2)) )
349                 ret = safe_strcmp(a[NAME], b[NAME]);
350
351         free(s1);
352         free(s2);
353
354         return ret;
355 }
356
357 static int
358 namecmp(const void *i1, const void *i2)
359 {
360         list_item a, b;
361
362         itemcpy(a, i1);
363         itemcpy(b, i2);
364         
365         return safe_strcmp( a[NAME], b[NAME] );
366 }
367
368 void
369 sort_database()
370 {
371         select_none();
372         
373         qsort((void *)database, items, sizeof(list_item), namecmp);
374
375         refresh_screen();
376 }
377
378 void
379 sort_surname()
380 {
381         select_none();
382
383         qsort((void *)database, items, sizeof(list_item), surnamecmp);
384
385         refresh_screen();
386 }
387
388 int
389 find_item(char *str, int start)
390 {
391         int i;
392         char *findstr = NULL;
393         char *tmp = NULL;
394         int ret = -1; /* not found */
395
396         if(items < 1 || start < 0 || start >= LAST_ITEM)
397                 return -2; /* error */
398
399         findstr = strdup(str);
400         findstr = strupper(findstr);
401
402         for( i = start; i < items; i++ ) {
403                 tmp = strdup(database[i][NAME]);
404                 if( strstr(strupper(tmp), findstr) != NULL ) {
405                         ret = i;
406                         goto out;
407                 }
408                 my_free(tmp);
409         }
410
411 out:
412         free(findstr);
413         free(tmp);
414         return ret;
415 }
416
417
418 int
419 is_selected(int item)
420 {
421         return selected[item];
422 }
423
424 int
425 real_db_enumerate_items(struct db_enumerator e)
426 {
427         int item = max(0, e.item + 1);
428         int i;
429         
430         switch(e.mode) {
431 #ifdef DEBUG
432                 case ENUM_ALL:
433                         break;
434 #endif
435                 case ENUM_SELECTED:
436                         for(i = item; i < items; i++) {
437                                 if(is_selected(i)) {
438                                         item = i;
439                                         goto out;
440                                 }
441                         }
442                         return -1;
443 #ifdef DEBUG
444                 default:
445                         fprintf(stderr, "real_db_enumerate_items() "
446                                         "BUG: unknown db_enumerator mode: %d\n",
447                                         e.mode);
448                         break;
449 #endif
450         }
451 out:
452         return (item > LAST_ITEM || item < 0) ? -1 : item;
453 }
454
455 struct db_enumerator
456 init_db_enumerator(int mode)
457 {
458         struct db_enumerator new;
459
460         new.item = -1; /* important - means "start from beginning" */
461         new.mode = mode;
462
463         return new;
464 }