]> git.deb.at Git - pkg/abook.git/blob - database.c
- code cleanups and minor fixes
[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 #include "xmalloc.h"
23 #ifdef HAVE_CONFIG_H
24 #       include "config.h"
25 #endif
26
27 static void     free_item(int i);
28
29
30 list_item *database = NULL;
31
32 int items = 0;
33
34 #define INITIAL_LIST_CAPACITY   30
35
36 int list_capacity = 0;
37
38 extern int first_list_item;
39 extern int curitem;
40 extern char *selected;
41
42 extern char *datafile;
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                 xfree(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 = xrealloc(database, sizeof(list_item) * list_capacity);
282         selected = xrealloc(selected, list_capacity);
283 }
284
285 int
286 add_item2database(list_item item)
287 {
288         if( item[NAME] == NULL || ! *item[NAME] ) {
289                 free_list_item(item);
290                 return 1;
291         }
292
293         if( ++items > list_capacity)
294                 adjust_list_capacity();
295
296         validate_item(item);
297
298         selected[LAST_ITEM] = 0;
299         itemcpy(database[LAST_ITEM], item);
300
301         return 0;
302 }
303
304 void
305 remove_selected_items()
306 {
307         int i, j;
308
309         if( list_is_empty() )
310                 return;
311
312         if( ! selected_items() )
313                 selected[ curitem ] = 1;
314
315         for( j = LAST_ITEM; j >= 0; j-- ) {
316                 if( selected[j] ) {
317                         free_item(j); /* added for .4 data_s_ */
318                         for( i = j; i < LAST_ITEM; i++ ) {
319                                 itemcpy(database[ i ], database[ i + 1 ]);
320                                 selected[ i ] = selected[ i + 1 ];
321                         }
322                         items--;
323                 }
324         }
325
326         if( curitem > LAST_ITEM && items > 0 )
327                 curitem = LAST_ITEM;
328
329
330         adjust_list_capacity();
331
332         select_none();
333 }
334
335 char *
336 get_surname(char *s)
337 {
338         char *p = s + strlen(s);
339
340         assert(s != NULL);
341
342         while(p > s && *(p - 1) != ' ')
343                 p--;
344
345         return strdup(p);
346 }
347
348 static int
349 surnamecmp(const void *i1, const void *i2)
350 {
351         int ret;
352         list_item a,b;
353         char *s1, *s2;
354
355         itemcpy(a, i1);
356         itemcpy(b, i2);
357
358         s1 = get_surname(a[NAME]);
359         s2 = get_surname(b[NAME]);
360
361         if( !(ret = safe_strcoll(s1, s2)) )
362                 ret = safe_strcoll(a[NAME], b[NAME]);
363
364         free(s1);
365         free(s2);
366
367         return ret;
368 }
369
370 static int sort_field = -1;
371
372 static int
373 namecmp(const void *i1, const void *i2)
374 {
375         list_item a, b;
376
377         assert(sort_field >= 0 && sort_field <= LAST_FIELD);
378
379         itemcpy(a, i1);
380         itemcpy(b, i2);
381
382         return safe_strcoll( a[sort_field], b[sort_field] );
383 }
384
385 static int
386 name2field(char *name)
387 {
388         int i, ret = -1;
389
390         for(i = 0; i < ITEM_FIELDS; i++) {
391                 if(!strcasecmp(name, abook_fields[i].key)) {
392                         ret = i;
393                         break;
394                 }
395         }
396
397         return ret;
398 }
399
400 void
401 sort_by_field(int field)
402 {
403         select_none();
404
405         assert(field <= LAST_FIELD);
406
407         if(field < 0) {
408                 field = name2field(opt_get_str(STR_SORT_FIELD));
409                 if(field < 0) {
410                         statusline_msg("Not valid field value defined "
411                                 "in configuration");
412                         return;
413                 }
414         }
415
416         sort_field = field;
417
418         qsort((void *)database, items, sizeof(list_item), namecmp);
419
420         refresh_screen();
421 }
422
423 void
424 sort_surname()
425 {
426         select_none();
427
428         qsort((void *)database, items, sizeof(list_item), surnamecmp);
429
430         refresh_screen();
431 }
432
433 int
434 find_item(char *str, int start, int search_fields[])
435 {
436         int i;
437         char *findstr = NULL;
438         char *tmp = NULL;
439         int ret = -1; /* not found */
440         struct db_enumerator e = init_db_enumerator(ENUM_ALL);
441
442         if(list_is_empty() || !is_valid_item(start))
443                 return -2; /* error */
444
445         findstr = strdup(str);
446         findstr = strlower(findstr);
447
448         e.item = start - 1; /* must be "real start" - 1 */
449         db_enumerate_items(e) {
450                 for( i = 0; search_fields[i] >= 0; i++ ) {
451                         tmp = safe_strdup(database[e.item][search_fields[i]]);
452                         if( tmp && strstr(strlower(tmp), findstr) ) {
453                                 ret = e.item;
454                                 goto out;
455                         }
456                         xfree(tmp);
457                 }
458         }
459
460 out:
461         free(findstr);
462         free(tmp);
463         return ret;
464 }
465
466
467 int
468 is_selected(int item)
469 {
470         return selected[item];
471 }
472
473 int
474 is_valid_item(int item)
475 {
476         return item <= LAST_ITEM && item >= 0;
477 }
478
479 int
480 real_db_enumerate_items(struct db_enumerator e)
481 {
482         int item = max(0, e.item + 1);
483         int i;
484
485         switch(e.mode) {
486 #ifdef DEBUG
487                 case ENUM_ALL:
488                         break;
489 #endif
490                 case ENUM_SELECTED:
491                         for(i = item; i < items; i++) {
492                                 if(is_selected(i)) {
493                                         item = i;
494                                         goto out;
495                                 }
496                         }
497                         return -1;
498 #ifdef DEBUG
499                 default:
500                         fprintf(stderr, "real_db_enumerate_items() "
501                                         "BUG: unknown db_enumerator mode: %d\n",
502                                         e.mode);
503                         break;
504 #endif
505         }
506 out:
507         return (item > LAST_ITEM || item < 0) ? -1 : item;
508 }
509
510 struct db_enumerator
511 init_db_enumerator(int mode)
512 {
513         struct db_enumerator e;
514
515         e.item = -1; /* important - means "start from beginning" */
516         e.mode = mode;
517
518         return e;
519 }
520
521 static int
522 assign_fieldname(const char *name, int i)
523 {
524         char *s;
525
526         assert(name);
527         assert(i >= 0 && i < ITEM_FIELDS);
528
529         if(strcasecmp(abook_fields[i].name, name)) { /* name differs */
530                 /*
531                  * check if we are overwriting statically allocated default
532                  */
533                 if(strcasecmp(abook_fields[i].name, abook_fields[i].key))
534                         xfree(abook_fields[i].name);
535
536                 s = xmalloc_inc(MAX_FIELDNAME_LEN, 1);
537                 snprintf(s, MAX_FIELDNAME_LEN, "%s", name);
538                 abook_fields[i].name = s;
539         }
540
541         return 0;
542 }
543
544 int
545 change_custom_field_name(const char *name, int n)
546 {
547         int i;
548         char keyname[21];
549
550         assert(name);
551
552         snprintf(keyname, sizeof(keyname), "custom%d", n);
553
554         for(i = CUSTOM_MIN; i <= CUSTOM_MAX; i++) {
555                 if(!strcasecmp(abook_fields[i].key, keyname)) {
556                         assign_fieldname(name, i);
557                         return i;
558                 }
559         }
560
561         return -1;
562 }
563