]> git.deb.at Git - pkg/abook.git/blob - database.c
- whitespace changes
[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                         free(line);
113                         continue;
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] = strdup(tmp);
128                                         goto next;
129                                 }
130                 }
131 next:
132                 free(line);
133         }
134
135         free(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] = strdup("");
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] = strdup(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 strdup(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("Not valid 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 = strdup(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                         tmp = safe_strdup(database[e.item][search_fields[i]]);
448                         if( tmp && strstr(strlower(tmp), findstr) ) {
449                                 ret = e.item;
450                                 goto out;
451                         }
452                         xfree(tmp);
453                 }
454         }
455
456 out:
457         free(findstr);
458         free(tmp);
459         return ret;
460 }
461
462
463 int
464 is_selected(int item)
465 {
466         return selected[item];
467 }
468
469 int
470 is_valid_item(int item)
471 {
472         return item <= LAST_ITEM && item >= 0;
473 }
474
475 int
476 real_db_enumerate_items(struct db_enumerator e)
477 {
478         int item = max(0, e.item + 1);
479         int i;
480
481         switch(e.mode) {
482 #ifdef DEBUG
483                 case ENUM_ALL:
484                         break;
485 #endif
486                 case ENUM_SELECTED:
487                         for(i = item; i < items; i++) {
488                                 if(is_selected(i)) {
489                                         item = i;
490                                         goto out;
491                                 }
492                         }
493                         return -1;
494 #ifdef DEBUG
495                 default:
496                         fprintf(stderr, "real_db_enumerate_items() "
497                                         "BUG: unknown db_enumerator mode: %d\n",
498                                         e.mode);
499                         break;
500 #endif
501         }
502 out:
503         return (item > LAST_ITEM || item < 0) ? -1 : item;
504 }
505
506 struct db_enumerator
507 init_db_enumerator(int mode)
508 {
509         struct db_enumerator e;
510
511         e.item = -1; /* important - means "start from beginning" */
512         e.mode = mode;
513
514         return e;
515 }
516
517 static int
518 assign_fieldname(const char *name, int i)
519 {
520         char *s;
521
522         assert(name);
523         assert(i >= 0 && i < ITEM_FIELDS);
524
525         if(strcasecmp(abook_fields[i].name, name)) { /* name differs */
526                 /*
527                  * check if we are overwriting statically allocated default
528                  */
529                 if(strcasecmp(abook_fields[i].name, abook_fields[i].key))
530                         xfree(abook_fields[i].name);
531
532                 s = xmalloc_inc(MAX_FIELDNAME_LEN, 1);
533                 snprintf(s, MAX_FIELDNAME_LEN, "%s", name);
534                 abook_fields[i].name = s;
535         }
536
537         return 0;
538 }
539
540 int
541 change_custom_field_name(const char *name, int n)
542 {
543         int i;
544         char keyname[21];
545
546         assert(name);
547
548         snprintf(keyname, sizeof(keyname), "custom%d", n);
549
550         for(i = CUSTOM_MIN; i <= CUSTOM_MAX; i++) {
551                 if(!strcasecmp(abook_fields[i].key, keyname)) {
552                         assign_fieldname(name, i);
553                         return i;
554                 }
555         }
556
557         return -1;
558 }
559