]> git.deb.at Git - pkg/abook.git/blob - database.c
Imported Upstream version 0.5.3
[pkg/abook.git] / database.c
1
2 /*
3  * $Id: database.c,v 1.20 2003/11/22 13:46:02 jheinonen Exp $
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         {"Custom1",     "custom1",      TAB_CUSTOM},/* CUSTOM1 */
75         {"Custom2",     "custom2",      TAB_CUSTOM},/* CUSTOM2 */
76         {"Custom3",     "custom3",      TAB_CUSTOM},/* CUSTOM3 */
77         {"Custom4",     "custom4",      TAB_CUSTOM},/* CUSTOM4 */
78         {"Custom5",     "custom5",      TAB_CUSTOM},/* CUSTOM5 */
79 };
80
81
82 int
83 find_field(const char *field)
84 {
85         int i;
86
87         for(i = 0; i < ITEM_FIELDS; i++)
88                 if( !strcmp(abook_fields[i].key, field) )
89                         return i;
90
91         return -1;
92 }
93
94 int
95 parse_database(FILE *in)
96 {
97         char *line = NULL;
98         char *tmp;
99         int sec=0, i;
100         list_item item;
101
102         memset(&item, 0, sizeof(item));
103         
104         for(;;) {
105                 line = getaline(in);
106                 if( feof(in) ) {
107                         if( item[NAME] && sec )
108                                 add_item2database(item);
109                         else
110                                 free_list_item(item);
111                         break;
112                 }
113
114                 if( !*line || *line == '\n' || *line == '#' ) {
115                         free(line);
116                         continue;
117                 } else if( *line == '[' ) {
118                         if( item[NAME] && sec )
119                                 add_item2database(item);
120                         else
121                                 free_list_item(item);
122                         memset(&item, 0, sizeof(item));
123                         sec = 1;
124                         if ( !(tmp = strchr(line, ']')))
125                                 sec = 0; /*incorrect section lines are skipped*/
126                 } else if((tmp = strchr(line, '=') ) && sec ) {
127                         *tmp++ = '\0';
128                         for(i=0; i<ITEM_FIELDS; i++)
129                                 if( !strcmp(abook_fields[i].key, line) ) {
130                                         item[i] = strdup(tmp);
131                                         goto next;
132                                 }
133                 }
134 next:
135                 free(line);
136         }
137
138         free(line);
139         return 0;
140 }
141
142                 
143
144 int
145 load_database(char *filename)
146 {
147         FILE *in;
148
149         if( database != NULL )
150                 close_database();
151
152         if ( (in = abook_fopen(filename, "r")) == NULL )
153                 return -1;
154         
155         parse_database(in);
156
157         return (items == 0) ? 2 : 0;
158 }
159
160 int
161 write_database(FILE *out, struct db_enumerator e)
162 {
163         int j;
164         int i = 0;
165
166         fprintf(out,
167                 "# abook addressbook file\n\n"
168                 "[format]\n"
169                 "program=" PACKAGE "\n"
170                 "version=" VERSION "\n"
171                 "\n\n"
172         );
173
174         db_enumerate_items(e) {
175                 fprintf(out, "[%d]\n", i);
176                 for(j = 0; j < ITEM_FIELDS; j++) {
177                         if( database[e.item][j] != NULL &&
178                                         *database[e.item][j] )
179                                 fprintf(out, "%s=%s\n",
180                                         abook_fields[j].key,
181                                         database[e.item][j]
182                                         );
183                 }
184                 fputc('\n', out);
185                 i++;
186         }
187
188         return 0;
189 }
190
191 int
192 save_database()
193 {
194         FILE *out;
195         struct db_enumerator e = init_db_enumerator(ENUM_ALL);
196
197         if( (out = abook_fopen(datafile, "w")) == NULL )
198                 return -1;
199
200         if( list_is_empty() ) {
201                 fclose(out);
202                 unlink(datafile);
203                 return 1;
204         }
205
206         
207         write_database(out, e);
208         
209         fclose(out);
210         
211         return 0;
212 }
213
214 static void
215 free_item(int item)
216 {
217         free_list_item(database[item]);
218 }
219
220 void
221 free_list_item(list_item item)
222 {
223         int i;
224
225         for(i=0; i<ITEM_FIELDS; i++)
226                 my_free(item[i]);
227 }
228
229 void
230 close_database()
231 {
232         int i;
233         
234         for(i=0; i < items; i++)
235                 free_item(i);
236
237         free(database);
238         free(selected);
239
240         database = NULL;
241         selected = NULL;
242
243         items = 0;
244         first_list_item = curitem = -1;
245         list_capacity = 0;
246 }
247
248 #define _MAX_FIELD_LEN(X)       (X == EMAIL ? MAX_EMAILSTR_LEN:MAX_FIELD_LEN)
249
250 static void
251 validate_item(list_item item)
252 {
253         int i;
254         char *tmp;
255         
256         if(item[EMAIL] == NULL)
257                 item[EMAIL] = strdup("");
258
259         for(i=0; i<ITEM_FIELDS; i++)
260                 if( item[i] && ((int)strlen(item[i]) > _MAX_FIELD_LEN(i) ) ) {
261                         tmp = item[i];
262                         item[i][_MAX_FIELD_LEN(i)-1] = 0;
263                         item[i] = strdup(item[i]);
264                         free(tmp);
265                 }
266 }
267
268
269 static void
270 adjust_list_capacity()
271 {
272         if(list_capacity < 1)
273                 list_capacity = INITIAL_LIST_CAPACITY;
274         else if(items >= list_capacity)
275                 list_capacity *= 2;
276         else if(list_capacity / 2 > items)
277                 list_capacity /= 2;
278         else
279                 return;
280
281         database = (list_item *)abook_realloc(database,
282                         sizeof(list_item) * list_capacity);
283         selected = (char *)abook_realloc(selected, list_capacity);
284 }
285
286 int
287 add_item2database(list_item item)
288 {
289         if( item[NAME] == NULL || ! *item[NAME] ) {
290                 free_list_item(item);
291                 return 1;
292         }
293
294         if( ++items > list_capacity)
295                 adjust_list_capacity();
296
297         validate_item(item);
298
299         selected[LAST_ITEM] = 0;
300         itemcpy(database[LAST_ITEM], item);
301
302         return 0;
303 }
304
305 void
306 remove_selected_items()
307 {
308         int i, j;
309
310         if( list_is_empty() )
311                 return;
312
313         if( ! selected_items() )
314                 selected[ curitem ] = 1;
315         
316         for( j = LAST_ITEM; j >= 0; j-- ) {
317                 if( selected[j] ) {
318                         free_item(j); /* added for .4 data_s_ */
319                         for( i = j; i < LAST_ITEM; i++ ) {
320                                 itemcpy(database[ i ], database[ i + 1 ]);
321                                 selected[ i ] = selected[ i + 1 ];
322                         }
323                         items--;        
324                 }
325         }
326
327         if( curitem > LAST_ITEM && items > 0 )
328                 curitem = LAST_ITEM;
329
330
331         adjust_list_capacity();
332
333         select_none();
334 }
335
336 char *
337 get_surname(char *s)
338 {
339         char *p = s + strlen(s);
340
341         assert(s != NULL);
342
343         while(p > s && *(p - 1) != ' ')
344                 p--;
345
346         return strdup(p);
347 }
348
349 static int
350 surnamecmp(const void *i1, const void *i2)
351 {
352         int ret;
353         list_item a,b;
354         char *s1, *s2;
355
356         itemcpy(a, i1);
357         itemcpy(b, i2);
358
359         s1 = get_surname(a[NAME]);
360         s2 = get_surname(b[NAME]);
361
362         if( !(ret = safe_strcoll(s1, s2)) )
363                 ret = safe_strcoll(a[NAME], b[NAME]);
364
365         free(s1);
366         free(s2);
367
368         return ret;
369 }
370
371 static int sort_field = -1;
372
373 static int
374 namecmp(const void *i1, const void *i2)
375 {
376         list_item a, b;
377
378         assert(sort_field >= 0 && sort_field <= LAST_FIELD);
379
380         itemcpy(a, i1);
381         itemcpy(b, i2);
382         
383         return safe_strcoll( a[sort_field], b[sort_field] );
384 }
385
386 static int
387 name2field(char *name)
388 {
389         int i, ret = -1;
390
391         for(i = 0; i < ITEM_FIELDS; i++) {
392                 if(!strcasecmp(name, abook_fields[i].key)) {
393                         ret = i;
394                         break;
395                 }
396         }
397
398         return ret;
399 }
400
401 void
402 sort_by_field(int field)
403 {
404         select_none();
405
406         assert(field <= LAST_FIELD);
407
408         if(field < 0) {
409                 field = name2field(opt_get_str(STR_SORT_FIELD));
410                 if(field < 0) {
411                         statusline_msg("Not valid field value defined "
412                                 "in configuration");
413                         return;
414                 }
415         }
416
417         sort_field = field;
418         
419         qsort((void *)database, items, sizeof(list_item), namecmp);
420
421         refresh_screen();
422 }
423
424 void
425 sort_surname()
426 {
427         select_none();
428
429         qsort((void *)database, items, sizeof(list_item), surnamecmp);
430
431         refresh_screen();
432 }
433
434 int
435 find_item(char *str, int start, int search_fields[])
436 {
437         int i;
438         char *findstr = NULL;
439         char *tmp = NULL;
440         int ret = -1; /* not found */
441         struct db_enumerator e = init_db_enumerator(ENUM_ALL);
442
443         if(list_is_empty() || !is_valid_item(start))
444                 return -2; /* error */
445
446         findstr = strdup(str);
447         findstr = strlower(findstr);
448
449         e.item = start - 1; /* must be "real start" - 1 */
450         db_enumerate_items(e) {
451                 for( i = 0; search_fields[i] >= 0; i++ ) {
452                         tmp = safe_strdup(database[e.item][search_fields[i]]);
453                         if( tmp && strstr(strlower(tmp), findstr) ) {
454                                 ret = e.item;
455                                 goto out;
456                         }
457                         my_free(tmp);
458                 }
459         }
460
461 out:
462         free(findstr);
463         free(tmp);
464         return ret;
465 }
466
467
468 int
469 is_selected(int item)
470 {
471         return selected[item];
472 }
473
474 int
475 is_valid_item(int item)
476 {
477         return item <= LAST_ITEM && item >= 0;
478 }
479
480 int
481 real_db_enumerate_items(struct db_enumerator e)
482 {
483         int item = max(0, e.item + 1);
484         int i;
485         
486         switch(e.mode) {
487 #ifdef DEBUG
488                 case ENUM_ALL:
489                         break;
490 #endif
491                 case ENUM_SELECTED:
492                         for(i = item; i < items; i++) {
493                                 if(is_selected(i)) {
494                                         item = i;
495                                         goto out;
496                                 }
497                         }
498                         return -1;
499 #ifdef DEBUG
500                 default:
501                         fprintf(stderr, "real_db_enumerate_items() "
502                                         "BUG: unknown db_enumerator mode: %d\n",
503                                         e.mode);
504                         break;
505 #endif
506         }
507 out:
508         return (item > LAST_ITEM || item < 0) ? -1 : item;
509 }
510
511 struct db_enumerator
512 init_db_enumerator(int mode)
513 {
514         struct db_enumerator e;
515
516         e.item = -1; /* important - means "start from beginning" */
517         e.mode = mode;
518
519         return e;
520 }
521
522 static int
523 assign_fieldname(const char *name, int i)
524 {
525         char *s;
526
527         assert(name);
528         assert(i >= 0 && i < ITEM_FIELDS);
529
530         if(strcasecmp(abook_fields[i].name, name)) { /* name differs */
531                 /*
532                  * check if we are overwriting statically allocated default
533                  */
534                 if(strcasecmp(abook_fields[i].name, abook_fields[i].key))
535                         my_free(abook_fields[i].name);
536                 
537                 s = abook_malloc(MAX_FIELDNAME_LEN + 1);
538                 snprintf(s, MAX_FIELDNAME_LEN, "%s", name);
539                 abook_fields[i].name = s;
540         }
541
542         return 0;
543 }
544
545 int
546 change_custom_field_name(const char *name, int n)
547 {
548         int i;
549         char keyname[21];
550
551         assert(name);
552
553         snprintf(keyname, sizeof(keyname), "custom%d", n);
554
555         for(i = CUSTOM_MIN; i <= CUSTOM_MAX; i++) {
556                 if(!strcasecmp(abook_fields[i].key, keyname)) {
557                         assign_fieldname(name, i);
558                         return i;
559                 }
560         }
561
562         return -1;
563 }
564