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