]> git.deb.at Git - pkg/abook.git/blob - database.c
- spelling fixes (Cedric Duval & co)
[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 "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         struct db_enumerator e = init_db_enumerator(ENUM_ALL);
193
194         if( (out = abook_fopen(datafile, "w")) == NULL )
195                 return -1;
196
197         if(list_is_empty()) {
198                 fclose(out);
199                 unlink(datafile);
200                 return 1;
201         }
202
203
204         write_database(out, e);
205
206         fclose(out);
207
208         return 0;
209 }
210
211 static void
212 free_item(int item)
213 {
214         free_list_item(database[item]);
215 }
216
217 void
218 free_list_item(list_item item)
219 {
220         int i;
221
222         for(i=0; i<ITEM_FIELDS; i++)
223                 xfree(item[i]);
224 }
225
226 void
227 close_database()
228 {
229         int i;
230
231         for(i=0; i < items; i++)
232                 free_item(i);
233
234         free(database);
235         free(selected);
236
237         database = NULL;
238         selected = NULL;
239
240         items = 0;
241         first_list_item = curitem = -1;
242         list_capacity = 0;
243 }
244
245 #define _MAX_FIELD_LEN(X)       (X == EMAIL ? MAX_EMAILSTR_LEN:MAX_FIELD_LEN)
246
247 static void
248 validate_item(list_item item)
249 {
250         int i;
251         char *tmp;
252
253         if(item[EMAIL] == NULL)
254                 item[EMAIL] = xstrdup("");
255
256         for(i=0; i<ITEM_FIELDS; i++)
257                 if( item[i] && ((int)strlen(item[i]) > _MAX_FIELD_LEN(i) ) ) {
258                         tmp = item[i];
259                         item[i][_MAX_FIELD_LEN(i)-1] = 0;
260                         item[i] = xstrdup(item[i]);
261                         free(tmp);
262                 }
263 }
264
265
266 static void
267 adjust_list_capacity()
268 {
269         if(list_capacity < 1)
270                 list_capacity = INITIAL_LIST_CAPACITY;
271         else if(items >= list_capacity)
272                 list_capacity *= 2;
273         else if(list_capacity / 2 > items)
274                 list_capacity /= 2;
275         else
276                 return;
277
278         database = xrealloc(database, sizeof(list_item) * list_capacity);
279         selected = xrealloc(selected, list_capacity);
280 }
281
282 int
283 add_item2database(list_item item)
284 {
285         if(item[NAME] == NULL || ! *item[NAME]) {
286                 free_list_item(item);
287                 return 1;
288         }
289
290         if(++items > list_capacity)
291                 adjust_list_capacity();
292
293         validate_item(item);
294
295         selected[LAST_ITEM] = 0;
296         itemcpy(database[LAST_ITEM], item);
297
298         return 0;
299 }
300
301 void
302 remove_selected_items()
303 {
304         int i, j;
305
306         if(list_is_empty())
307                 return;
308
309         if(!selected_items())
310                 selected[curitem] = 1;
311
312         for(j = LAST_ITEM; j >= 0; j--) {
313                 if(selected[j]) {
314                         free_item(j); /* added for .4 data_s_ */
315                         for(i = j; i < LAST_ITEM; i++) {
316                                 itemcpy(database[i], database[i + 1]);
317                                 selected[i] = selected[i + 1];
318                         }
319                         items--;
320                 }
321         }
322
323         if(curitem > LAST_ITEM && items > 0)
324                 curitem = LAST_ITEM;
325
326         adjust_list_capacity();
327
328         select_none();
329 }
330
331 char *
332 get_surname(char *s)
333 {
334         char *p = s + strlen(s);
335
336         assert(s != NULL);
337
338         while(p > s && *(p - 1) != ' ')
339                 p--;
340
341         return xstrdup(p);
342 }
343
344 static int
345 surnamecmp(const void *i1, const void *i2)
346 {
347         int ret;
348         list_item a,b;
349         char *s1, *s2;
350
351         itemcpy(a, i1);
352         itemcpy(b, i2);
353
354         s1 = get_surname(a[NAME]);
355         s2 = get_surname(b[NAME]);
356
357         if( !(ret = safe_strcoll(s1, s2)) )
358                 ret = safe_strcoll(a[NAME], b[NAME]);
359
360         free(s1);
361         free(s2);
362
363         return ret;
364 }
365
366 static int sort_field = -1;
367
368 static int
369 namecmp(const void *i1, const void *i2)
370 {
371         list_item a, b;
372
373         assert(sort_field >= 0 && sort_field <= LAST_FIELD);
374
375         itemcpy(a, i1);
376         itemcpy(b, i2);
377
378         return safe_strcoll(a[sort_field], b[sort_field]);
379 }
380
381 static int
382 name2field(char *name)
383 {
384         int i, ret = -1;
385
386         for(i = 0; i < ITEM_FIELDS; i++) {
387                 if(!strcasecmp(name, abook_fields[i].key)) {
388                         ret = i;
389                         break;
390                 }
391         }
392
393         return ret;
394 }
395
396 void
397 sort_by_field(int field)
398 {
399         select_none();
400
401         assert(field <= LAST_FIELD);
402
403         if(field < 0) {
404                 field = name2field(opt_get_str(STR_SORT_FIELD));
405                 if(field < 0) {
406                         statusline_msg(_("Invalid field value defined "
407                                 "in configuration"));
408                         return;
409                 }
410         }
411
412         sort_field = field;
413
414         qsort((void *)database, items, sizeof(list_item), namecmp);
415
416         refresh_screen();
417 }
418
419 void
420 sort_surname()
421 {
422         select_none();
423
424         qsort((void *)database, items, sizeof(list_item), surnamecmp);
425
426         refresh_screen();
427 }
428
429 int
430 find_item(char *str, int start, int search_fields[])
431 {
432         int i;
433         char *findstr = NULL;
434         char *tmp = NULL;
435         int ret = -1; /* not found */
436         struct db_enumerator e = init_db_enumerator(ENUM_ALL);
437
438         if(list_is_empty() || !is_valid_item(start))
439                 return -2; /* error */
440
441         findstr = xstrdup(str);
442         findstr = strlower(findstr);
443
444         e.item = start - 1; /* must be "real start" - 1 */
445         db_enumerate_items(e) {
446                 for(i = 0; search_fields[i] >= 0; i++) {
447                         if(database[e.item][search_fields[i]] == NULL)
448                                 continue;
449                         tmp = xstrdup(database[e.item][search_fields[i]]);
450                         if( tmp && strstr(strlower(tmp), findstr) ) {
451                                 ret = e.item;
452                                 goto out;
453                         }
454                         xfree(tmp);
455                 }
456         }
457
458 out:
459         free(findstr);
460         free(tmp);
461         return ret;
462 }
463
464
465 int
466 is_selected(int item)
467 {
468         return selected[item];
469 }
470
471 int
472 is_valid_item(int item)
473 {
474         return item <= LAST_ITEM && item >= 0;
475 }
476
477 int
478 real_db_enumerate_items(struct db_enumerator e)
479 {
480         int item = max(0, e.item + 1);
481         int i;
482
483         switch(e.mode) {
484 #ifdef DEBUG
485                 case ENUM_ALL:
486                         break;
487 #endif
488                 case ENUM_SELECTED:
489                         for(i = item; i < items; i++) {
490                                 if(is_selected(i)) {
491                                         item = i;
492                                         goto out;
493                                 }
494                         }
495                         return -1;
496 #ifdef DEBUG
497                 default:
498                         fprintf(stderr, "real_db_enumerate_items() "
499                                         "BUG: unknown db_enumerator mode: %d\n",
500                                         e.mode);
501                         break;
502 #endif
503         }
504 out:
505         return (item > LAST_ITEM || item < 0) ? -1 : item;
506 }
507
508 struct db_enumerator
509 init_db_enumerator(int mode)
510 {
511         struct db_enumerator e;
512
513         e.item = -1; /* important - means "start from beginning" */
514         e.mode = mode;
515
516         return e;
517 }
518
519 static int
520 assign_fieldname(const char *name, int i)
521 {
522         char *s;
523
524         assert(name);
525         assert(i >= 0 && i < ITEM_FIELDS);
526
527         if(strcasecmp(abook_fields[i].name, name)) { /* name differs */
528                 /*
529                  * check if we are overwriting statically allocated default
530                  */
531                 if(strcasecmp(abook_fields[i].name, abook_fields[i].key))
532                         xfree(abook_fields[i].name);
533
534                 s = xmalloc_inc(MAX_FIELDNAME_LEN, 1);
535                 snprintf(s, MAX_FIELDNAME_LEN, "%s", name);
536                 abook_fields[i].name = s;
537         }
538
539         return 0;
540 }
541
542 int
543 change_custom_field_name(const char *name, int n)
544 {
545         int i;
546         char keyname[21];
547
548         assert(name);
549
550         snprintf(keyname, sizeof(keyname), "custom%d", n);
551
552         for(i = CUSTOM_MIN; i <= CUSTOM_MAX; i++) {
553                 if(!strcasecmp(abook_fields[i].key, keyname)) {
554                         assign_fieldname(name, i);
555                         return i;
556                 }
557         }
558
559         return -1;
560 }
561