source: mainline/uspace/app/contacts/contacts.c@ 1099f25

ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1099f25 was ee8d4d6, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 6 years ago

Fix build with -O1

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