]> git.deb.at Git - pkg/abook.git/blob - database.c
- declare more variables as static
[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
327         adjust_list_capacity();
328
329         select_none();
330 }
331
332 char *
333 get_surname(char *s)
334 {
335         char *p = s + strlen(s);
336
337         assert(s != NULL);
338
339         while(p > s && *(p - 1) != ' ')
340                 p--;
341
342         return strdup(p);
343 }
344
345 static int
346 surnamecmp(const void *i1, const void *i2)
347 {
348         int ret;
349         list_item a,b;
350         char *s1, *s2;
351
352         itemcpy(a, i1);
353         itemcpy(b, i2);
354
355         s1 = get_surname(a[NAME]);
356         s2 = get_surname(b[NAME]);
357
358         if( !(ret = safe_strcoll(s1, s2)) )
359                 ret = safe_strcoll(a[NAME], b[NAME]);
360
361         free(s1);
362         free(s2);
363
364         return ret;
365 }
366
367 static int sort_field = -1;
368
369 static int
370 namecmp(const void *i1, const void *i2)
371 {
372         list_item a, b;
373
374         assert(sort_field >= 0 && sort_field <= LAST_FIELD);
375
376         itemcpy(a, i1);
377         itemcpy(b, i2);
378
379         return safe_strcoll( a[sort_field], b[sort_field] );
380 }
381
382 static int
383 name2field(char *name)
384 {
385         int i, ret = -1;
386
387         for(i = 0; i < ITEM_FIELDS; i++) {
388                 if(!strcasecmp(name, abook_fields[i].key)) {
389                         ret = i;
390                         break;
391                 }
392         }
393
394         return ret;
395 }
396
397 void
398 sort_by_field(int field)
399 {
400         select_none();
401
402         assert(field <= LAST_FIELD);
403
404         if(field < 0) {
405                 field = name2field(opt_get_str(STR_SORT_FIELD));
406                 if(field < 0) {
407                         statusline_msg("Not valid field value defined "
408                                 "in configuration");
409                         return;
410                 }
411         }
412
413         sort_field = field;
414
415         qsort((void *)database, items, sizeof(list_item), namecmp);
416
417         refresh_screen();
418 }
419
420 void
421 sort_surname()
422 {
423         select_none();
424
425         qsort((void *)database, items, sizeof(list_item), surnamecmp);
426
427         refresh_screen();
428 }
429
430 int
431 find_item(char *str, int start, int search_fields[])
432 {
433         int i;
434         char *findstr = NULL;
435         char *tmp = NULL;
436         int ret = -1; /* not found */
437         struct db_enumerator e = init_db_enumerator(ENUM_ALL);
438
439         if(list_is_empty() || !is_valid_item(start))
440                 return -2; /* error */
441
442         findstr = strdup(str);
443         findstr = strlower(findstr);
444
445         e.item = start - 1; /* must be "real start" - 1 */
446         db_enumerate_items(e) {
447                 for( i = 0; search_fields[i] >= 0; i++ ) {
448                         tmp = safe_strdup(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