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