source: mainline/uspace/app/contacts/contacts.c@ 42964a7

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 42964a7 was 42964a7, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Deleting a contact.

  • Property mode set to 100644
File size: 11.4 KB
Line 
1/*
2 * Copyright (c) 2018 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup contacts
30 * @{
31 */
32
33/**
34 * @file Contact list application.
35 *
36 * Maintain a contact list / address book. The main purpose of this
37 * trivial application is to serve as an example of using SIF.
38 */
39
40#include <adt/list.h>
41#include <errno.h>
42#include <nchoice.h>
43#include <sif.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <str.h>
47
48/** Contacts */
49typedef struct {
50 /** Open SIF repository */
51 sif_sess_t *repo;
52 /** Entries SIF node */
53 sif_node_t *nentries;
54 /** Entries list (of contacts_entry_t) */
55 list_t entries;
56} contacts_t;
57
58/** Contact entry */
59typedef struct {
60 /** Containing contacts */
61 contacts_t *contacts;
62 /** Link to contacts->entries */
63 link_t lentries;
64 /** SIF node for this entry */
65 sif_node_t *nentry;
66 /** Contact name */
67 char *name;
68} contacts_entry_t;
69
70/** Actions in contact menu */
71typedef enum {
72 /** Create new contact */
73 ac_create_contact,
74 /** Delete contact */
75 ac_delete_contact,
76 /** Exit */
77 ac_exit
78} contact_action_t;
79
80static errno_t contacts_unmarshal(sif_node_t *, contacts_t *);
81static contacts_entry_t *contacts_first(contacts_t *);
82static contacts_entry_t *contacts_next(contacts_entry_t *);
83static void contacts_entry_delete(contacts_entry_t *);
84
85/** Open contacts repo or create it if it does not exist.
86 *
87 * @param fname File name
88 * @param rcontacts Place to store pointer to contacts object.
89 *
90 * @return EOK on success or error code
91 */
92static errno_t contacts_open(const char *fname, contacts_t **rcontacts)
93{
94 contacts_t *contacts = NULL;
95 sif_sess_t *repo = NULL;
96 sif_trans_t *trans = NULL;
97 sif_node_t *node;
98 const char *ntype;
99 errno_t rc;
100
101 contacts = calloc(1, sizeof(contacts_t));
102 if (contacts == NULL)
103 return ENOMEM;
104
105 list_initialize(&contacts->entries);
106
107 /* Try to open an existing repository. */
108 rc = sif_open(fname, &repo);
109 if (rc != EOK) {
110 /* Failed to open existing, create new repository */
111 rc = sif_create(fname, &repo);
112 if (rc != EOK)
113 goto error;
114
115 /* Start a transaction */
116 rc = sif_trans_begin(repo, &trans);
117 if (rc != EOK)
118 goto error;
119
120 /* Create 'entries' node container for all entries */
121 rc = sif_node_append_child(trans, sif_get_root(repo), "entries",
122 &contacts->nentries);
123 if (rc != EOK)
124 goto error;
125
126 /* Finish the transaction */
127 rc = sif_trans_end(trans);
128 if (rc != EOK)
129 goto error;
130
131 trans = NULL;
132 } else {
133 /*
134 * Opened an existing repository. Find the 'entries' node.
135 * It should be the very first child of the root node.
136 * This is okay to do in general, as long as we don't
137 * require forward compatibility (which we don't).
138 */
139 node = sif_node_first_child(sif_get_root(repo));
140 if (node == NULL) {
141 rc = EIO;
142 goto error;
143 }
144
145 /* Verify it's the correct node type(!) */
146 ntype = sif_node_get_type(node);
147 if (str_cmp(ntype, "entries") != 0) {
148 rc = EIO;
149 goto error;
150 }
151
152 rc = contacts_unmarshal(node, contacts);
153 if (rc != EOK)
154 goto error;
155
156 contacts->nentries = node;
157 }
158
159 contacts->repo = repo;
160 *rcontacts = contacts;
161
162 return EOK;
163error:
164 if (trans != NULL)
165 sif_trans_abort(trans);
166 if (repo != NULL)
167 (void) sif_close(repo);
168 if (contacts != NULL)
169 free(contacts);
170 return rc;
171}
172
173/** Unmarshal contact entries from SIF repository.
174 *
175 * @param nentries Entries node
176 * @param contacts Contacts object to unmarshal to
177 * @return EOK on success or error code
178 */
179static errno_t contacts_unmarshal(sif_node_t *nentries, contacts_t *contacts)
180{
181 sif_node_t *nentry;
182 contacts_entry_t *entry;
183 const char *name;
184
185 contacts->nentries = nentries;
186
187 nentry = sif_node_first_child(nentries);
188 while (nentry != NULL) {
189 if (str_cmp(sif_node_get_type(nentry), "entry") != 0)
190 return EIO;
191
192 entry = calloc(1, sizeof(contacts_entry_t));
193 if (entry == NULL)
194 return ENOMEM;
195
196 name = sif_node_get_attr(nentry, "name");
197 if (name == NULL) {
198 free(entry);
199 return EIO;
200 }
201
202 entry->name = str_dup(name);
203 if (entry->name == NULL) {
204 free(entry);
205 return ENOMEM;
206 }
207
208 entry->contacts = contacts;
209 entry->nentry = nentry;
210 list_append(&entry->lentries, &contacts->entries);
211
212 nentry = sif_node_next_child(nentry);
213 }
214
215 return EOK;
216}
217
218
219/** Interaction to create new contact.
220 *
221 * @param contacts Contacts
222 */
223static errno_t contacts_create_contact(contacts_t *contacts)
224{
225 tinput_t *tinput;
226 sif_trans_t *trans = NULL;
227 sif_node_t *nentry;
228 contacts_entry_t *entry = NULL;
229 errno_t rc;
230 char *cname = NULL;
231
232 tinput = tinput_new();
233 if (tinput == NULL)
234 return ENOMEM;
235
236 printf("Contact name:\n");
237
238 rc = tinput_set_prompt(tinput, "?> ");
239 if (rc != EOK)
240 goto error;
241
242 rc = tinput_read(tinput, &cname);
243 if (rc != EOK)
244 goto error;
245
246 entry = calloc(1, sizeof(contacts_entry_t));
247 if (entry == NULL) {
248 rc = ENOMEM;
249 goto error;
250 }
251
252 rc = sif_trans_begin(contacts->repo, &trans);
253 if (rc != EOK)
254 goto error;
255
256 rc = sif_node_append_child(trans, contacts->nentries, "entry", &nentry);
257 if (rc != EOK)
258 goto error;
259
260 rc = sif_node_set_attr(trans, nentry, "name", cname);
261 if (rc != EOK)
262 goto error;
263
264 rc = sif_trans_end(trans);
265 if (rc != EOK)
266 goto error;
267
268 trans = NULL;
269 entry->contacts = contacts;
270 entry->nentry = nentry;
271 entry->name = cname;
272 list_append(&entry->lentries, &contacts->entries);
273
274 tinput_destroy(tinput);
275 return EOK;
276error:
277 if (trans != NULL)
278 sif_trans_abort(trans);
279 if (cname != NULL)
280 free(cname);
281 if (entry != NULL)
282 free(entry);
283 return rc;
284}
285
286/** Interaction to delete contact.
287 *
288 * @param contacts Contacts
289 */
290static errno_t contacts_delete_contact(contacts_t *contacts)
291{
292 nchoice_t *choice = NULL;
293 contacts_entry_t *entry;
294 sif_trans_t *trans = NULL;
295 errno_t rc;
296 void *sel;
297
298 rc = nchoice_create(&choice);
299 if (rc != EOK) {
300 assert(rc == ENOMEM);
301 printf("Out of memory.\n");
302 goto error;
303 }
304
305 rc = nchoice_set_prompt(choice, "Select contact to delete");
306 if (rc != EOK) {
307 assert(rc == ENOMEM);
308 printf("Out of memory.\n");
309 goto error;
310 }
311
312 entry = contacts_first(contacts);
313 while (entry != NULL) {
314 rc = nchoice_add(choice, entry->name, (void *)entry, 0);
315 if (rc != EOK) {
316 assert(rc == ENOMEM);
317 printf("Out of memory.\n");
318 goto error;
319 }
320
321 entry = contacts_next(entry);
322 }
323
324 rc = nchoice_add(choice, "Cancel", NULL, 0);
325 if (rc != EOK) {
326 assert(rc == ENOMEM);
327 printf("Out of memory.\n");
328 goto error;
329 }
330
331 rc = nchoice_get(choice, &sel);
332 if (rc != EOK) {
333 printf("Error getting user selection.\n");
334 return rc;
335 }
336
337 if (sel != NULL) {
338 entry = (contacts_entry_t *)sel;
339
340 rc = sif_trans_begin(contacts->repo, &trans);
341 if (rc != EOK)
342 goto error;
343
344 sif_node_destroy(trans, entry->nentry);
345
346 rc = sif_trans_end(trans);
347 if (rc != EOK)
348 goto error;
349
350 trans = NULL;
351
352 list_remove(&entry->lentries);
353 contacts_entry_delete(entry);
354 }
355
356 nchoice_destroy(choice);
357 return EOK;
358error:
359 if (trans != NULL)
360 sif_trans_abort(trans);
361 if (choice != NULL)
362 nchoice_destroy(choice);
363 return rc;
364}
365
366/** Close contacts repo.
367 *
368 * @param contacts Contacts
369 */
370static void contacts_close(contacts_t *contacts)
371{
372 contacts_entry_t *entry;
373
374 sif_close(contacts->repo);
375
376 entry = contacts_first(contacts);
377 while (entry != NULL) {
378 list_remove(&entry->lentries);
379 contacts_entry_delete(entry);
380 entry = contacts_first(contacts);
381 }
382
383 free(contacts);
384}
385
386/** Get first contacts entry.
387 *
388 * @param contacts Contacts
389 * @return First entry or @c NULL if there is none
390 */
391static contacts_entry_t *contacts_first(contacts_t *contacts)
392{
393 link_t *link;
394
395 link = list_first(&contacts->entries);
396 if (link == NULL)
397 return NULL;
398
399 return list_get_instance(link, contacts_entry_t, lentries);
400}
401
402/** Get next contacts entry.
403 *
404 * @param cur Current entry
405 * @return Next entry or @c NULL if there is none
406 */
407static contacts_entry_t *contacts_next(contacts_entry_t *cur)
408{
409 link_t *link;
410
411 link = list_next(&cur->lentries, &cur->contacts->entries);
412 if (link == NULL)
413 return NULL;
414
415 return list_get_instance(link, contacts_entry_t, lentries);
416}
417
418/** Delete entry structure from memory.
419 *
420 * @param entry Contacts entry
421 */
422static void contacts_entry_delete(contacts_entry_t *entry)
423{
424 if (entry == NULL)
425 return;
426
427 if (entry->name != NULL)
428 free(entry->name);
429
430 free(entry);
431}
432
433/** List all contacts.
434 *
435 * @param contacts Contacts
436 */
437static void contacts_list_all(contacts_t *contacts)
438{
439 contacts_entry_t *entry;
440
441 entry = contacts_first(contacts);
442 while (entry != NULL) {
443 printf(" * %s\n", entry->name);
444 entry = contacts_next(entry);
445 }
446}
447
448/** Run contacts main menu.
449 *
450 * @param contacts Contacts
451 * @return EOK on success or error code
452 */
453static errno_t contacts_main(contacts_t *contacts)
454{
455 nchoice_t *choice = NULL;
456 errno_t rc;
457 bool quit = false;
458 void *sel;
459
460 rc = nchoice_create(&choice);
461 if (rc != EOK) {
462 assert(rc == ENOMEM);
463 printf("Out of memory.\n");
464 goto error;
465 }
466
467 rc = nchoice_set_prompt(choice, "Select action");
468 if (rc != EOK) {
469 assert(rc == ENOMEM);
470 printf("Out of memory.\n");
471 goto error;
472 }
473
474 rc = nchoice_add(choice, "Create contact",
475 (void *)ac_create_contact, 0);
476 if (rc != EOK) {
477 assert(rc == ENOMEM);
478 printf("Out of memory.\n");
479 goto error;
480 }
481
482 rc = nchoice_add(choice, "Delete contact",
483 (void *)ac_delete_contact, 0);
484 if (rc != EOK) {
485 assert(rc == ENOMEM);
486 printf("Out of memory.\n");
487 goto error;
488 }
489
490 rc = nchoice_add(choice, "Exit",
491 (void *)ac_exit, 0);
492 if (rc != EOK) {
493 assert(rc == ENOMEM);
494 printf("Out of memory.\n");
495 goto error;
496 }
497
498 while (!quit) {
499 contacts_list_all(contacts);
500
501 rc = nchoice_get(choice, &sel);
502 if (rc != EOK) {
503 printf("Error getting user selection.\n");
504 return rc;
505 }
506
507 switch ((contact_action_t)sel) {
508 case ac_create_contact:
509 (void) contacts_create_contact(contacts);
510 break;
511 case ac_delete_contact:
512 (void) contacts_delete_contact(contacts);
513 break;
514 case ac_exit:
515 quit = true;
516 break;
517 }
518 }
519
520 nchoice_destroy(choice);
521 return EOK;
522error:
523 if (choice != NULL)
524 nchoice_destroy(choice);
525 return rc;
526}
527
528int main(void)
529{
530 errno_t rc;
531 contacts_t *contacts;
532
533 rc = contacts_open("contacts.sif", &contacts);
534 if (rc != EOK)
535 return 1;
536
537 rc = contacts_main(contacts);
538 contacts_close(contacts);
539
540 if (rc != EOK)
541 return 1;
542
543 return 0;
544}
Note: See TracBrowser for help on using the repository browser.