source: mainline/uspace/app/contacts/contacts.c@ 1dcba91

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

Configuration repository for volume server.

  • Property mode set to 100644
File size: 11.3 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_load(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_load(node, contacts);
153 if (rc != EOK)
154 goto error;
155 }
156
157 contacts->repo = repo;
158 *rcontacts = contacts;
159
160 return EOK;
161error:
162 if (trans != NULL)
163 sif_trans_abort(trans);
164 if (repo != NULL)
165 (void) sif_close(repo);
166 if (contacts != NULL)
167 free(contacts);
168 return rc;
169}
170
171/** Load contact entries from SIF repository.
172 *
173 * @param nentries Entries node
174 * @param contacts Contacts object to load to
175 * @return EOK on success or error code
176 */
177static errno_t contacts_load(sif_node_t *nentries, contacts_t *contacts)
178{
179 sif_node_t *nentry;
180 contacts_entry_t *entry;
181 const char *name;
182
183 contacts->nentries = nentries;
184
185 nentry = sif_node_first_child(nentries);
186 while (nentry != NULL) {
187 if (str_cmp(sif_node_get_type(nentry), "entry") != 0)
188 return EIO;
189
190 entry = calloc(1, sizeof(contacts_entry_t));
191 if (entry == NULL)
192 return ENOMEM;
193
194 name = sif_node_get_attr(nentry, "name");
195 if (name == NULL) {
196 free(entry);
197 return EIO;
198 }
199
200 entry->name = str_dup(name);
201 if (entry->name == NULL) {
202 free(entry);
203 return ENOMEM;
204 }
205
206 entry->contacts = contacts;
207 entry->nentry = nentry;
208 list_append(&entry->lentries, &contacts->entries);
209
210 nentry = sif_node_next_child(nentry);
211 }
212
213 return EOK;
214}
215
216
217/** Interaction to create new contact.
218 *
219 * @param contacts Contacts
220 */
221static errno_t contacts_create_contact(contacts_t *contacts)
222{
223 tinput_t *tinput;
224 sif_trans_t *trans = NULL;
225 sif_node_t *nentry;
226 contacts_entry_t *entry = NULL;
227 errno_t rc;
228 char *cname = NULL;
229
230 tinput = tinput_new();
231 if (tinput == NULL)
232 return ENOMEM;
233
234 printf("Contact name:\n");
235
236 rc = tinput_set_prompt(tinput, "?> ");
237 if (rc != EOK)
238 goto error;
239
240 rc = tinput_read(tinput, &cname);
241 if (rc != EOK)
242 goto error;
243
244 entry = calloc(1, sizeof(contacts_entry_t));
245 if (entry == NULL) {
246 rc = ENOMEM;
247 goto error;
248 }
249
250 rc = sif_trans_begin(contacts->repo, &trans);
251 if (rc != EOK)
252 goto error;
253
254 rc = sif_node_append_child(trans, contacts->nentries, "entry", &nentry);
255 if (rc != EOK)
256 goto error;
257
258 rc = sif_node_set_attr(trans, nentry, "name", cname);
259 if (rc != EOK)
260 goto error;
261
262 rc = sif_trans_end(trans);
263 if (rc != EOK)
264 goto error;
265
266 trans = NULL;
267 entry->contacts = contacts;
268 entry->nentry = nentry;
269 entry->name = cname;
270 list_append(&entry->lentries, &contacts->entries);
271
272 tinput_destroy(tinput);
273 return EOK;
274error:
275 if (trans != NULL)
276 sif_trans_abort(trans);
277 if (cname != NULL)
278 free(cname);
279 if (entry != NULL)
280 free(entry);
281 return rc;
282}
283
284/** Interaction to delete contact.
285 *
286 * @param contacts Contacts
287 */
288static errno_t contacts_delete_contact(contacts_t *contacts)
289{
290 nchoice_t *choice = NULL;
291 contacts_entry_t *entry;
292 sif_trans_t *trans = NULL;
293 errno_t rc;
294 void *sel;
295
296 rc = nchoice_create(&choice);
297 if (rc != EOK) {
298 assert(rc == ENOMEM);
299 printf("Out of memory.\n");
300 goto error;
301 }
302
303 rc = nchoice_set_prompt(choice, "Select contact to delete");
304 if (rc != EOK) {
305 assert(rc == ENOMEM);
306 printf("Out of memory.\n");
307 goto error;
308 }
309
310 entry = contacts_first(contacts);
311 while (entry != NULL) {
312 rc = nchoice_add(choice, entry->name, (void *)entry, 0);
313 if (rc != EOK) {
314 assert(rc == ENOMEM);
315 printf("Out of memory.\n");
316 goto error;
317 }
318
319 entry = contacts_next(entry);
320 }
321
322 rc = nchoice_add(choice, "Cancel", NULL, 0);
323 if (rc != EOK) {
324 assert(rc == ENOMEM);
325 printf("Out of memory.\n");
326 goto error;
327 }
328
329 rc = nchoice_get(choice, &sel);
330 if (rc != EOK) {
331 printf("Error getting user selection.\n");
332 return rc;
333 }
334
335 if (sel != NULL) {
336 entry = (contacts_entry_t *)sel;
337
338 rc = sif_trans_begin(contacts->repo, &trans);
339 if (rc != EOK)
340 goto error;
341
342 sif_node_destroy(trans, entry->nentry);
343
344 rc = sif_trans_end(trans);
345 if (rc != EOK)
346 goto error;
347
348 trans = NULL;
349
350 list_remove(&entry->lentries);
351 contacts_entry_delete(entry);
352 }
353
354 nchoice_destroy(choice);
355 return EOK;
356error:
357 if (trans != NULL)
358 sif_trans_abort(trans);
359 if (choice != NULL)
360 nchoice_destroy(choice);
361 return rc;
362}
363
364/** Close contacts repo.
365 *
366 * @param contacts Contacts
367 */
368static void contacts_close(contacts_t *contacts)
369{
370 contacts_entry_t *entry;
371
372 sif_close(contacts->repo);
373
374 entry = contacts_first(contacts);
375 while (entry != NULL) {
376 list_remove(&entry->lentries);
377 contacts_entry_delete(entry);
378 entry = contacts_first(contacts);
379 }
380
381 free(contacts);
382}
383
384/** Get first contacts entry.
385 *
386 * @param contacts Contacts
387 * @return First entry or @c NULL if there is none
388 */
389static contacts_entry_t *contacts_first(contacts_t *contacts)
390{
391 link_t *link;
392
393 link = list_first(&contacts->entries);
394 if (link == NULL)
395 return NULL;
396
397 return list_get_instance(link, contacts_entry_t, lentries);
398}
399
400/** Get next contacts entry.
401 *
402 * @param cur Current entry
403 * @return Next entry or @c NULL if there is none
404 */
405static contacts_entry_t *contacts_next(contacts_entry_t *cur)
406{
407 link_t *link;
408
409 link = list_next(&cur->lentries, &cur->contacts->entries);
410 if (link == NULL)
411 return NULL;
412
413 return list_get_instance(link, contacts_entry_t, lentries);
414}
415
416/** Delete entry structure from memory.
417 *
418 * @param entry Contacts entry
419 */
420static void contacts_entry_delete(contacts_entry_t *entry)
421{
422 if (entry == NULL)
423 return;
424
425 if (entry->name != NULL)
426 free(entry->name);
427
428 free(entry);
429}
430
431/** List all contacts.
432 *
433 * @param contacts Contacts
434 */
435static void contacts_list_all(contacts_t *contacts)
436{
437 contacts_entry_t *entry;
438
439 entry = contacts_first(contacts);
440 while (entry != NULL) {
441 printf(" * %s\n", entry->name);
442 entry = contacts_next(entry);
443 }
444}
445
446/** Run contacts main menu.
447 *
448 * @param contacts Contacts
449 * @return EOK on success or error code
450 */
451static errno_t contacts_main(contacts_t *contacts)
452{
453 nchoice_t *choice = NULL;
454 errno_t rc;
455 bool quit = false;
456 void *sel;
457
458 rc = nchoice_create(&choice);
459 if (rc != EOK) {
460 assert(rc == ENOMEM);
461 printf("Out of memory.\n");
462 goto error;
463 }
464
465 rc = nchoice_set_prompt(choice, "Select action");
466 if (rc != EOK) {
467 assert(rc == ENOMEM);
468 printf("Out of memory.\n");
469 goto error;
470 }
471
472 rc = nchoice_add(choice, "Create contact",
473 (void *)ac_create_contact, 0);
474 if (rc != EOK) {
475 assert(rc == ENOMEM);
476 printf("Out of memory.\n");
477 goto error;
478 }
479
480 rc = nchoice_add(choice, "Delete contact",
481 (void *)ac_delete_contact, 0);
482 if (rc != EOK) {
483 assert(rc == ENOMEM);
484 printf("Out of memory.\n");
485 goto error;
486 }
487
488 rc = nchoice_add(choice, "Exit",
489 (void *)ac_exit, 0);
490 if (rc != EOK) {
491 assert(rc == ENOMEM);
492 printf("Out of memory.\n");
493 goto error;
494 }
495
496 while (!quit) {
497 contacts_list_all(contacts);
498
499 rc = nchoice_get(choice, &sel);
500 if (rc != EOK) {
501 printf("Error getting user selection.\n");
502 return rc;
503 }
504
505 switch ((contact_action_t)sel) {
506 case ac_create_contact:
507 (void) contacts_create_contact(contacts);
508 break;
509 case ac_delete_contact:
510 (void) contacts_delete_contact(contacts);
511 break;
512 case ac_exit:
513 quit = true;
514 break;
515 }
516 }
517
518 nchoice_destroy(choice);
519 return EOK;
520error:
521 if (choice != NULL)
522 nchoice_destroy(choice);
523 return rc;
524}
525
526int main(void)
527{
528 errno_t rc;
529 contacts_t *contacts;
530
531 rc = contacts_open("contacts.sif", &contacts);
532 if (rc != EOK)
533 return 1;
534
535 rc = contacts_main(contacts);
536 contacts_close(contacts);
537
538 if (rc != EOK)
539 return 1;
540
541 return 0;
542}
Note: See TracBrowser for help on using the repository browser.