]> git.deb.at Git - pkg/abook.git/blob - database.c
Minor cleanups
[pkg/abook.git] / database.c
1
2 /*
3  * database.c
4  * by JH <jheinonen@bigfoot.com>
5  *
6  * Copyright (C) Jaakko Heinonen
7  */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <fcntl.h>
14 #include "abook_curses.h"
15 #include "abook.h"
16 #include "database.h"
17 #include "list.h"
18 #include "misc.h"
19 #include "options.h"
20 #include "filter.h"
21 #ifdef HAVE_CONFIG_H
22 #       include "config.h"
23 #endif
24
25 static void     free_item(int i);
26
27
28 list_item *database = NULL;
29
30 int items = 0;
31
32 #define INITIAL_LIST_CAPACITY   30
33
34 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 extern char *rcfile;
42
43 /*
44  * field definitions
45  */
46
47 #include "edit.h"
48
49 /*
50  * notes about adding fields:
51  *      - do not change any fields in TAB_CONTACT
52  *      - do not add fields to contact tab
53  *      - 6 fields per tab is maximum
54  *      - reorganize the field numbers in database.h
55  */
56
57 struct abook_field abook_fields[ITEM_FIELDS] = {
58         {"Name",        "name",         TAB_CONTACT},/* NAME */
59         {"E-mails",     "email",        TAB_CONTACT},/* EMAIL */
60         {"Address",     "address",      TAB_ADDRESS},/* ADDRESS */
61         {"City",        "city",         TAB_ADDRESS},/* CITY */
62         {"State/Province","state",      TAB_ADDRESS},/* STATE */
63         {"ZIP/Postal Code","zip",       TAB_ADDRESS},/* ZIP */
64         {"Country",     "country",      TAB_ADDRESS},/* COUNTRY */
65         {"Home Phone",  "phone",        TAB_PHONE},/* PHONE */
66         {"Work Phone",  "workphone",    TAB_PHONE},/* WORKPHONE */
67         {"Fax",         "fax",          TAB_PHONE},/* FAX */
68         {"Mobile",      "mobile",       TAB_PHONE},/* MOBILEPHONE */
69         {"Nickname/Alias", "nick",      TAB_OTHER},/* NICK */
70         {"URL",         "url",          TAB_OTHER},/* URL */
71         {"Notes",       "notes",        TAB_OTHER},/* NOTES */
72 };
73
74
75 int
76 parse_database(FILE *in)
77 {
78         char *line = NULL;
79         char *tmp;
80         int sec=0, i;
81         list_item item;
82
83         memset(&item, 0, sizeof(item));
84         
85         for(;;) {
86                 line = getaline(in);
87                 if( feof(in) ) {
88                         if( item[NAME] && sec )
89                                 add_item2database(item);
90                         else
91                                 free_list_item(item);
92                         break;
93                 }
94
95                 if( !*line || *line == '\n' || *line == '#' ) {
96                         free(line);
97                         continue;
98                 } else if( *line == '[' ) {
99                         if( item[NAME] && sec )
100                                 add_item2database(item);
101                         else
102                                 free_list_item(item);
103                         memset(&item, 0, sizeof(item));
104                         sec = 1;
105                         if ( !(tmp = strchr(line, ']')))
106                                 sec = 0; /*incorrect section lines are skipped*/
107                 } else if((tmp = strchr(line, '=') ) && sec ) {
108                         *tmp++ = '\0';
109                         for(i=0; i<ITEM_FIELDS; i++)
110                                 if( !strcmp(abook_fields[i].key, line) )
111                                         item[i] = strdup(tmp);
112                 }
113                 free(line);
114         }
115
116         free(line);
117         return 0;
118 }
119
120                 
121
122 int
123 load_database(char *filename)
124 {
125         FILE *in;
126
127         if( database != NULL )
128                 close_database();
129
130         if ( (in = abook_fopen(filename, "r")) == NULL )
131                 return -1;
132         
133         parse_database(in);
134
135         if ( items == 0 )
136                 return 2;
137
138         return 0;
139 }
140
141 int
142 write_database(FILE *out)
143 {
144         int i,j;
145
146         fprintf(out, "# abook addressbook file\n\n");
147         fprintf(out, "[format]\n");
148         fprintf(out, "program=" PACKAGE "\n");
149         fprintf(out, "version=" VERSION "\n");
150         fprintf(out, "\n\n");
151
152         for( i = 0; i < items; i++ ) {
153                 fprintf(out, "[%d]\n", i);
154                 for(j=0; j<ITEM_FIELDS; j++) {
155                         if( database[i][j] != NULL && *database[i][j] )
156                                 fprintf(out, "%s=%s\n",
157                                         abook_fields[j].key, database[i][j]);
158                 }
159                 fputc('\n', out);
160         }
161
162         return 0;
163 }
164
165 int
166 save_database()
167 {
168         FILE *out;
169
170         if( (out = abook_fopen(datafile, "w")) == NULL )
171                 return -1;
172
173         if( items < 1 ) {
174                 fclose(out);
175                 unlink(datafile);
176                 return 1;
177         }
178
179         
180         write_database(out);
181         
182         fclose(out);
183         
184         return 0;
185 }
186
187 static void
188 free_item(int item)
189 {
190         free_list_item(database[item]);
191 }
192
193 void
194 free_list_item(list_item item)
195 {
196         int i;
197
198         for(i=0; i<ITEM_FIELDS; i++)
199                 my_free(item[i]);
200 }
201
202 void
203 close_database()
204 {
205         int i;
206         
207         for(i=0; i < items; i++)
208                 free_item(i);
209
210         free(database);
211         free(selected);
212
213         database = NULL;
214         selected = NULL;
215
216         items = 0;
217         first_list_item = curitem = -1;
218         list_capacity = 0;
219 }
220
221 #define _MAX_FIELD_LEN(X)       (X == EMAIL ? MAX_EMAILSTR_LEN:MAX_FIELD_LEN)
222
223 inline static void
224 validate_item(list_item item)
225 {
226         int i;
227         char *tmp;
228         
229         if(item[EMAIL] == NULL)
230                 item[EMAIL] = strdup("");
231
232         for(i=0; i<ITEM_FIELDS; i++)
233                 if( item[i] && (strlen(item[i]) > _MAX_FIELD_LEN(i) ) ) {
234                         tmp = item[i];
235                         item[i][_MAX_FIELD_LEN(i)-1] = 0;
236                         item[i] = strdup(item[i]);
237                         free(tmp);
238                 }
239 }
240
241
242 static void
243 adjust_list_capacity()
244 {
245         if(list_capacity < 1)
246                 list_capacity = INITIAL_LIST_CAPACITY;
247         else if(items >= list_capacity)
248                 list_capacity *= 2;
249         else if(list_capacity / 2 > items)
250                 list_capacity /= 2;
251         else
252                 return;
253
254         database = abook_realloc(database,
255                         sizeof(list_item) * list_capacity);
256         selected = abook_realloc(selected, list_capacity);
257 }
258
259 int
260 add_item2database(list_item item)
261 {
262         if( item[NAME] == NULL || ! *item[NAME] ) {
263                 free_list_item(item);
264                 return 1;
265         }
266
267         if( ++items > list_capacity)
268                 adjust_list_capacity();
269
270         validate_item(item);
271
272         selected[LAST_ITEM] = 0;
273         itemcpy(database[LAST_ITEM], item);
274
275         return 0;
276 }
277
278 void
279 remove_items()
280 {
281         int i, j;
282
283         if( items < 1 || curitem < 0 )
284                 return;
285
286         statusline_addstr("Remove selected item(s) (Y/n)");
287         switch( getch() ) {
288                 case '\r':
289                 case 'y':
290                 case 'Y': break;
291                 default:
292                           clear_statusline();
293                           return;
294         }
295
296         if( ! selected_items() )
297                 selected[ curitem ] = 1;
298         
299         for( j = LAST_ITEM; j >= 0; j-- ) {
300                 if( selected[j] ) {
301                         free_item(j); /* added for .4 data_s_ */
302                         for( i = j; i < LAST_ITEM; i++ ) {
303                                 itemcpy(database[ i ], database[ i + 1 ]);
304                                 selected[ i ] = selected[ i + 1 ];
305                         }
306                         items--;        
307                 }
308         }
309
310         if( curitem > LAST_ITEM && items > 0 )
311                 curitem = LAST_ITEM;
312
313
314         adjust_list_capacity();
315
316         select_none();
317         clear_statusline();     
318         refresh_list();
319 }
320
321 char *
322 get_surname(char *s)
323 {
324         int i, a;
325         int len = strlen(s);
326         char *name = strdup(s);
327
328         for( a = 0, i = len - 1; i >= 0; i--, a++ ) {
329                 name[a] = s[i];
330                 if(name[a] == ' ')
331                         break;
332         }
333
334         name[ a ] = 0;
335
336         revstr(name);
337
338         return name;
339 }
340
341 static int
342 surnamecmp(const void *i1, const void *i2)
343 {
344         int ret;
345         list_item a,b;
346         char *s1, *s2;
347
348         itemcpy(a, i1);
349         itemcpy(b, i2);
350
351         s1 = get_surname(a[NAME]);
352         s2 = get_surname(b[NAME]);
353
354         if( !(ret = safe_strcmp(s1, s2)) )
355                 ret = safe_strcmp(a[NAME], b[NAME]);
356
357         free(s1);
358         free(s2);
359
360         return ret;
361 }
362
363 static int
364 namecmp(const void *i1, const void *i2)
365 {
366         list_item a, b;
367
368         itemcpy(a, i1);
369         itemcpy(b, i2);
370         
371         return safe_strcmp( a[NAME], b[NAME] );
372 }
373
374 void
375 sort_database()
376 {
377         select_none();
378         
379         qsort((void *)database, items, sizeof(list_item), namecmp);
380
381         refresh_screen();
382 }
383
384 void
385 sort_surname()
386 {
387         select_none();
388
389         qsort((void *)database, items, sizeof(list_item), surnamecmp);
390
391         refresh_screen();
392 }
393
394 void
395 clear_database()
396 {
397
398         statusline_addstr("Clear WHOLE database (y/N)");
399         switch( getch() ) {
400                 case 'y':
401                 case 'Y': break;
402                 default:
403                         clear_statusline();
404                         return;
405         }
406
407         close_database();
408
409         refresh_screen();
410 }
411
412 void
413 find(int next)
414 {
415         int i;
416         static char findstr[81];
417         char tmp[81];
418
419 #ifdef DEBUG
420         fprintf(stderr, "find(): findstr = |%s|\n", findstr);
421 #endif
422         
423         if(next) {
424                 if( !*findstr )
425                         return;
426         } else {
427                 clear_statusline();
428                 statusline_addstr("/");
429                 statusline_getnstr(findstr, 67, 0);
430                 strupper(findstr);
431                 clear_statusline();
432         }
433
434         if(items < 1)
435                 return;
436
437         for( i = (curitem < LAST_ITEM) && next ? curitem+1 : curitem;
438                         i < items; i++ ) {
439                 strcpy(tmp, database[i][NAME]);
440                 if( strstr(strupper(tmp), findstr) != NULL ) {
441                         curitem = i;
442                         refresh_list();
443                         break;
444                 }
445         }
446 }
447
448
449 void
450 print_number_of_items()
451 {
452         char *str = mkstr("     " "|%3d/%3d", selected_items(), items);
453
454         mvaddstr(0, COLS-strlen(str), str);
455
456         free(str);
457 }
458
459 void
460 read_database()
461 {
462         if(items > 0) {
463                 statusline_addstr("Your current data will be lost - Press 'y' to continue");
464                 switch( getch() ) {
465                         case 'y':
466                         case 'Y': break;
467                         default: clear_statusline();
468                                  return;
469                 }
470                 clear_statusline();
471         }
472
473         load_database(datafile);
474         refresh_list();
475 }
476
477
478 void
479 print_database()
480 {
481         FILE *handle;
482         char *command = options_get_str("print_command");
483
484         statusline_addstr("Print addressbook? (y/N)");
485         switch( getch() ) {
486                 case 'y':
487                 case 'Y':
488                         break;
489                 default: clear_statusline(); return;
490         }
491         clear_statusline();
492
493         if( ! *command || (handle = popen(command, "w")) == NULL)
494                 return;
495
496         fexport("text", handle);
497         
498         pclose(handle);
499 }
500