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