source: mainline/uspace/app/contacts/contacts.c@ 233a3a06

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

Contacts listing and unmarshalling.

  • Property mode set to 100644
File size: 9.5 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 *);
81
82/** Open contacts repo or create it if it does not exist.
83 *
84 * @param fname File name
85 * @param rcontacts Place to store pointer to contacts object.
86 *
87 * @return EOK on success or error code
88 */
89static errno_t contacts_open(const char *fname, contacts_t **rcontacts)
90{
91 contacts_t *contacts = NULL;
92 sif_sess_t *repo = NULL;
93 sif_trans_t *trans = NULL;
94 sif_node_t *node;
95 const char *ntype;
96 errno_t rc;
97
98 contacts = calloc(1, sizeof(contacts_t));
99 if (contacts == NULL)
100 return ENOMEM;
101
102 list_initialize(&contacts->entries);
103
104 /* Try to open an existing repository. */
105 rc = sif_open(fname, &repo);
106 if (rc != EOK) {
107 /* Failed to open existing, create new repository */
108 rc = sif_create(fname, &repo);
109 if (rc != EOK)
110 goto error;
111
112 /* Start a transaction */
113 rc = sif_trans_begin(repo, &trans);
114 if (rc != EOK)
115 goto error;
116
117 /* Create 'entries' node container for all entries */
118 rc = sif_node_append_child(trans, sif_get_root(repo), "entries",
119 &contacts->nentries);
120 if (rc != EOK)
121 goto error;
122
123 /* Finish the transaction */
124 rc = sif_trans_end(trans);
125 if (rc != EOK)
126 goto error;
127
128 trans = NULL;
129 } else {
130 /*
131 * Opened an existing repository. Find the 'entries' node.
132 * It should be the very first child of the root node.
133 * This is okay to do in general, as long as we don't
134 * require forward compatibility (which we don't).
135 */
136 node = sif_node_first_child(sif_get_root(repo));
137 if (node == NULL) {
138 rc = EIO;
139 goto error;
140 }
141
142 /* Verify it's the correct node type(!) */
143 ntype = sif_node_get_type(node);
144 if (str_cmp(ntype, "entries") != 0) {
145 rc = EIO;
146 goto error;
147 }
148
149 rc = contacts_unmarshal(node, contacts);
150 if (rc != EOK)
151 goto error;
152
153 contacts->nentries = node;
154 }
155
156 contacts->repo = repo;
157 *rcontacts = contacts;
158
159 return EOK;
160error:
161 if (trans != NULL)
162 sif_trans_abort(trans);
163 if (repo != NULL)
164 (void) sif_close(repo);
165 if (contacts != NULL)
166 free(contacts);
167 return rc;
168}
169
170/** Unmarshal contact entries from SIF repository.
171 *
172 * @param nentries Entries node
173 * @param contacts Contacts object to unmarshal to
174 * @return EOK on success or error code
175 */
176static errno_t contacts_unmarshal(sif_node_t *nentries, contacts_t *contacts)
177{
178 sif_node_t *nentry;
179 contacts_entry_t *entry;
180 const char *name;
181
182 contacts->nentries = nentries;
183
184 nentry = sif_node_first_child(nentries);
185 while (nentry != NULL) {
186 if (str_cmp(sif_node_get_type(nentry), "entry") != 0)
187 return EIO;
188
189 entry = calloc(1, sizeof(contacts_entry_t));
190 if (entry == NULL)
191 return ENOMEM;
192
193 name = sif_node_get_attr(nentry, "name");
194 if (name == NULL) {
195 free(entry);
196 return EIO;
197 }
198
199 entry->name = str_dup(name);
200 if (entry->name == NULL) {
201 free(entry);
202 return ENOMEM;
203 }
204
205 entry->contacts = contacts;
206 entry->nentry = nentry;
207 list_append(&entry->lentries, &contacts->entries);
208
209 nentry = sif_node_next_child(nentry);
210 }
211
212 return EOK;
213}
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 return EOK;
290}
291
292/** Close contacts repo.
293 *
294 * @param contacts Contacts
295 */
296static void contacts_close(contacts_t *contacts)
297{
298 printf("Closing repo %p\n", contacts->repo);
299 sif_close(contacts->repo);
300 free(contacts);
301}
302
303/** Get first contacts entry.
304 *
305 * @param contacts Contacts
306 * @return First entry or @c NULL if there is none
307 */
308static contacts_entry_t *contacts_first(contacts_t *contacts)
309{
310 link_t *link;
311
312 link = list_first(&contacts->entries);
313 if (link == NULL)
314 return NULL;
315
316 return list_get_instance(link, contacts_entry_t, lentries);
317}
318
319/** Get next contacts entry.
320 *
321 * @param cur Current entry
322 * @return Next entry or @c NULL if there is none
323 */
324static contacts_entry_t *contacts_next(contacts_entry_t *cur)
325{
326 link_t *link;
327
328 link = list_next(&cur->lentries, &cur->contacts->entries);
329 if (link == NULL)
330 return NULL;
331
332 return list_get_instance(link, contacts_entry_t, lentries);
333}
334
335/** List all contacts.
336 *
337 * @param contacts Contacts
338 */
339static void contacts_list_all(contacts_t *contacts)
340{
341 contacts_entry_t *entry;
342
343 entry = contacts_first(contacts);
344 while (entry != NULL) {
345 printf(" * %s\n", entry->name);
346 entry = contacts_next(entry);
347 }
348}
349
350/** Run contacts main menu.
351 *
352 * @param contacts Contacts
353 * @return EOK on success or error code
354 */
355static errno_t contacts_main(contacts_t *contacts)
356{
357 nchoice_t *choice = NULL;
358 errno_t rc;
359 bool quit = false;
360 void *sel;
361
362 rc = nchoice_create(&choice);
363 if (rc != EOK) {
364 assert(rc == ENOMEM);
365 printf("Out of memory.\n");
366 goto error;
367 }
368
369 rc = nchoice_set_prompt(choice, "Select action");
370 if (rc != EOK) {
371 assert(rc == ENOMEM);
372 printf("Out of memory.\n");
373 goto error;
374 }
375
376 rc = nchoice_add(choice, "Create contact",
377 (void *)ac_create_contact, 0);
378 if (rc != EOK) {
379 assert(rc == ENOMEM);
380 printf("Out of memory.\n");
381 goto error;
382 }
383
384 rc = nchoice_add(choice, "Delete contact",
385 (void *)ac_delete_contact, 0);
386 if (rc != EOK) {
387 assert(rc == ENOMEM);
388 printf("Out of memory.\n");
389 goto error;
390 }
391
392 rc = nchoice_add(choice, "Exit",
393 (void *)ac_exit, 0);
394 if (rc != EOK) {
395 assert(rc == ENOMEM);
396 printf("Out of memory.\n");
397 goto error;
398 }
399
400 while (!quit) {
401 contacts_list_all(contacts);
402
403 rc = nchoice_get(choice, &sel);
404 if (rc != EOK) {
405 printf("Error getting user selection.\n");
406 return rc;
407 }
408
409 switch ((contact_action_t)sel) {
410 case ac_create_contact:
411 (void) contacts_create_contact(contacts);
412 break;
413 case ac_delete_contact:
414 (void) contacts_delete_contact(contacts);
415 break;
416 case ac_exit:
417 quit = true;
418 break;
419 }
420 }
421
422 nchoice_destroy(choice);
423 return EOK;
424error:
425 if (choice != NULL)
426 nchoice_destroy(choice);
427 return rc;
428}
429
430int main(void)
431{
432 errno_t rc;
433 contacts_t *contacts;
434
435 rc = contacts_open("contacts.sif", &contacts);
436 if (rc != EOK)
437 return 1;
438
439 rc = contacts_main(contacts);
440 contacts_close(contacts);
441
442 if (rc != EOK)
443 return 1;
444
445 return 0;
446}
Note: See TracBrowser for help on using the repository browser.