svn update: 51457 (latest:51480)
[framework/uifw/eet.git] / src / examples / eet-data-file_descriptor.c
1 #include <Eina.h>
2 #include <Eet.h>
3 #include <stdio.h>
4 #include <limits.h>
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <unistd.h>
8
9 // complex real-world structures based on elmdentica database
10 typedef struct
11 {
12    const char * screen_name;
13    const char * name;
14    const char * message;
15    unsigned int id;
16    unsigned int status_id;
17    unsigned int date;
18    unsigned int timeline;
19 } My_Message;
20
21 typedef struct
22 {
23    const char * dm_to;
24    const char * message;
25 } My_Post;
26
27 typedef struct
28 {
29    unsigned int id;
30    const char * name;
31    Eina_List *  messages;
32    Eina_List *  posts;
33 } My_Account;
34
35 typedef struct
36 {
37    unsigned int version; // it is recommended to use versioned configuration!
38    Eina_List *  accounts;
39 } My_Cache;
40
41 // string that represents the entry in eet file, you might like to have
42 // different profiles or so in the same file, this is possible with
43 // different strings
44 static const char MY_CACHE_FILE_ENTRY[] = "cache";
45
46 // keep the descriptor static global, so it can be
47 // shared by different functions (load/save) of this and only this
48 // file.
49 static Eet_Data_Descriptor * _my_cache_descriptor;
50 static Eet_Data_Descriptor * _my_account_descriptor;
51 static Eet_Data_Descriptor * _my_message_descriptor;
52 static Eet_Data_Descriptor * _my_post_descriptor;
53
54 // keep file handle alive, so mmap()ed strings are all alive as well
55 static Eet_File * _my_cache_file = NULL;
56 static Eet_Dictionary * _my_cache_dict = NULL;
57
58 static void
59 _my_cache_descriptor_init(void)
60 {
61    Eet_Data_Descriptor_Class eddc;
62
63    // The FILE variant is good for caches and things that are just
64    // appended, but needs to take care when changing strings and files must
65    // be kept open so mmap()ed strings will be kept alive.
66    EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, My_Cache);
67    _my_cache_descriptor = eet_data_descriptor_file_new(&eddc);
68
69    EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, My_Account);
70    _my_account_descriptor = eet_data_descriptor_file_new(&eddc);
71
72    EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, My_Message);
73    _my_message_descriptor = eet_data_descriptor_file_new(&eddc);
74
75    EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, My_Post);
76    _my_post_descriptor = eet_data_descriptor_file_new(&eddc);
77
78    // Describe the members to be saved:
79    // Use a temporary macro so we don't type a lot, also avoid errors:
80
81 #define ADD_BASIC(member, eet_type)\
82    EET_DATA_DESCRIPTOR_ADD_BASIC\
83       (_my_message_descriptor, My_Message, # member, member, eet_type)
84    ADD_BASIC(screen_name, EET_T_STRING);
85    ADD_BASIC(name,        EET_T_STRING);
86    ADD_BASIC(message,     EET_T_STRING);
87    ADD_BASIC(id,          EET_T_UINT);
88    ADD_BASIC(status_id,   EET_T_UINT);
89    ADD_BASIC(date,        EET_T_UINT);
90    ADD_BASIC(timeline,    EET_T_UINT);
91 #undef ADD_BASIC
92
93 #define ADD_BASIC(member, eet_type)\
94    EET_DATA_DESCRIPTOR_ADD_BASIC\
95       (_my_post_descriptor, My_Post, # member, member, eet_type)
96    ADD_BASIC(dm_to,   EET_T_STRING);
97    ADD_BASIC(message, EET_T_STRING);
98 #undef ADD_BASIC
99
100 #define ADD_BASIC(member, eet_type)\
101    EET_DATA_DESCRIPTOR_ADD_BASIC\
102       (_my_account_descriptor, My_Account, # member, member, eet_type)
103    ADD_BASIC(name, EET_T_STRING);
104    ADD_BASIC(id,   EET_T_UINT);
105 #undef ADD_BASIC
106
107    EET_DATA_DESCRIPTOR_ADD_LIST
108       (_my_account_descriptor, My_Account, "messages", messages,
109       _my_message_descriptor);
110    EET_DATA_DESCRIPTOR_ADD_LIST
111       (_my_account_descriptor, My_Account, "posts",    posts,
112       _my_post_descriptor);
113
114 #define ADD_BASIC(member, eet_type)\
115    EET_DATA_DESCRIPTOR_ADD_BASIC\
116       (_my_cache_descriptor, My_Cache, # member, member, eet_type)
117    ADD_BASIC(version, EET_T_UINT);
118 #undef ADD_BASIC
119
120    EET_DATA_DESCRIPTOR_ADD_LIST
121       (_my_cache_descriptor, My_Cache, "accounts", accounts,
122       _my_account_descriptor);
123 } /* _my_cache_descriptor_init */
124
125 static void
126 _my_cache_descriptor_shutdown(void)
127 {
128    eet_data_descriptor_free(_my_cache_descriptor);
129    eet_data_descriptor_free(_my_account_descriptor);
130    eet_data_descriptor_free(_my_message_descriptor);
131    eet_data_descriptor_free(_my_post_descriptor);
132 } /* _my_cache_descriptor_shutdown */
133
134 // need to check if the pointer came from mmaped area in eet_dictionary
135 // or it was allocated with eina_stringshare_add()
136 static void
137 _eet_string_free(const char * str)
138 {
139    if (!str)
140       return;
141
142    if ((_my_cache_dict) && (eet_dictionary_string_check(_my_cache_dict, str)))
143       return;
144
145    eina_stringshare_del(str);
146 } /* _eet_string_free */
147
148 static My_Message *
149 _my_message_new(const char * message)
150 {
151    My_Message * msg = calloc(1, sizeof(My_Message));
152    if (!msg)
153      {
154         fprintf(stderr, "ERROR: could not calloc My_Message\n");
155         return NULL;
156      }
157
158    msg->message = eina_stringshare_add(message);
159    return msg;
160 } /* _my_message_new */
161
162 static void
163 _my_message_free(My_Message * msg)
164 {
165    _eet_string_free(msg->screen_name);
166    _eet_string_free(msg->name);
167    _eet_string_free(msg->message);
168    free(msg);
169 } /* _my_message_free */
170
171 static My_Post *
172 _my_post_new(const char * message)
173 {
174    My_Post * post = calloc(1, sizeof(My_Post));
175    if (!post)
176      {
177         fprintf(stderr, "ERROR: could not calloc My_Post\n");
178         return NULL;
179      }
180
181    post->message = eina_stringshare_add(message);
182    return post;
183 } /* _my_post_new */
184
185 static void
186 _my_post_free(My_Post * post)
187 {
188    _eet_string_free(post->dm_to);
189    _eet_string_free(post->message);
190    free(post);
191 } /* _my_post_free */
192
193 static My_Account *
194 _my_account_new(const char * name)
195 {
196    My_Account * acc = calloc(1, sizeof(My_Account));
197    if (!acc)
198      {
199         fprintf(stderr, "ERROR: could not calloc My_Account\n");
200         return NULL;
201      }
202
203    acc->name = eina_stringshare_add(name);
204    return acc;
205 } /* _my_account_new */
206
207 static void
208 _my_account_free(My_Account * acc)
209 {
210    My_Message * m;
211    My_Post * p;
212
213    _eet_string_free(acc->name);
214
215    EINA_LIST_FREE(acc->messages, m)
216    _my_message_free(m);
217
218    EINA_LIST_FREE(acc->posts, p)
219    _my_post_free(p);
220
221    free(acc);
222 } /* _my_account_free */
223
224 static My_Cache *
225 _my_cache_new(void)
226 {
227    My_Cache * my_cache = calloc(1, sizeof(My_Cache));
228    if (!my_cache)
229      {
230         fprintf(stderr, "ERROR: could not calloc My_Cache\n");
231         return NULL;
232      }
233
234    my_cache->version = 1;
235    return my_cache;
236 } /* _my_cache_new */
237
238 static void
239 _my_cache_free(My_Cache * my_cache)
240 {
241    My_Account * acc;
242    EINA_LIST_FREE(my_cache->accounts, acc)
243    _my_account_free(acc);
244    free(my_cache);
245 } /* _my_cache_free */
246
247 static My_Account *
248 _my_cache_account_find(My_Cache * my_cache, const char * name)
249 {
250    My_Account * acc;
251    Eina_List * l;
252    EINA_LIST_FOREACH(my_cache->accounts, l, acc)
253    if (strcmp(acc->name, name) == 0)
254       return acc;
255
256    return NULL;
257 } /* _my_cache_account_find */
258
259 static My_Cache *
260 _my_cache_load(const char * filename)
261 {
262    My_Cache * my_cache;
263    Eet_File * ef = eet_open(filename, EET_FILE_MODE_READ);
264    if (!ef)
265      {
266         fprintf(stderr, "ERROR: could not open '%s' for read\n", filename);
267         return NULL;
268      }
269
270    my_cache = eet_data_read(ef, _my_cache_descriptor, MY_CACHE_FILE_ENTRY);
271    if (!my_cache)
272      {
273         eet_close(ef);
274         return NULL;
275      }
276
277    if (my_cache->version < 1)
278      {
279         fprintf(stderr,
280                 "WARNING: version %#x was too old, upgrading it to %#x\n",
281                 my_cache->version, 1);
282
283         my_cache->version = 1;
284      }
285
286    if (_my_cache_file)
287       eet_close(_my_cache_file);
288
289    _my_cache_file = ef;
290    _my_cache_dict = eet_dictionary_get(ef);
291
292    return my_cache;
293 } /* _my_cache_load */
294
295 static Eina_Bool
296 _my_cache_save(const My_Cache * my_cache, const char * filename)
297 {
298    char tmp[PATH_MAX];
299    Eet_File * ef;
300    Eina_Bool ret;
301    unsigned int i, len;
302    struct stat st;
303
304    len = eina_strlcpy(tmp, filename, sizeof(tmp));
305    if (len + 12 >= (int)sizeof(tmp))
306      {
307         fprintf(stderr, "ERROR: file name is too big: %s\n", filename);
308         return EINA_FALSE;
309      }
310
311    i = 0;
312    do
313      {
314         snprintf(tmp + len, 12, ".%u", i);
315         i++;
316      }
317    while (stat(tmp, &st) == 0);
318
319    ef = eet_open(tmp, EET_FILE_MODE_WRITE);
320    if (!ef)
321      {
322         fprintf(stderr, "ERROR: could not open '%s' for write\n", tmp);
323         return EINA_FALSE;
324      }
325
326    ret = eet_data_write
327          (ef, _my_cache_descriptor, MY_CACHE_FILE_ENTRY, my_cache, EINA_TRUE);
328
329    // VERY IMPORTANT NOTE:
330    // after eet_close(), all strings mmaped from file will be GONE, invalid!
331    // you'll need to free the old cache and open the new one.
332    // For cache this is okay, as you should be saving not so often or just
333    // at end.
334    //
335    // This is a trade off, you save memory by using mmap()ed strings, but
336    // you have to care about this.
337    eet_close(ef);
338
339    if (ret)
340      {
341         unlink(filename);
342         rename(tmp, filename);
343      }
344
345    return ret;
346 } /* _my_cache_save */
347
348 int main(int argc, char * argv[])
349 {
350    My_Cache * my_cache;
351    const Eina_List * l_acc;
352    My_Account * acc;
353    int ret = 0;
354
355    if (argc < 3)
356      {
357         fprintf(stderr,
358                 "Usage:\n\t%s <input> <output> [action] [action-params]\n\n"
359                 "Where actions and their parameters:\n"
360                 "\tacc <name>\n"
361                 "\tpost <account-name> <message>\n"
362                 "\tmessage <account-name> <message>\n"
363                 "\n",
364                 argv[0]);
365         return -1;
366      }
367
368    eina_init();
369    eet_init();
370    _my_cache_descriptor_init();
371
372    my_cache = _my_cache_load(argv[1]);
373    if (!my_cache)
374      {
375         printf("creating new cache.\n");
376         my_cache = _my_cache_new();
377         if (!my_cache)
378           {
379              ret = -2;
380              goto end;
381           }
382      }
383
384    if (argc > 3)
385      {
386         if (strcmp(argv[3], "acc") == 0)
387           {
388              if (argc == 5)
389                {
390                   My_Account * acc = _my_cache_account_find(my_cache, argv[4]);
391                   if (!acc)
392                     {
393                        acc = _my_account_new(argv[4]);
394                        my_cache->accounts = eina_list_append
395                              (my_cache->accounts, acc);
396                     }
397                   else
398                      fprintf(stderr, "ERROR: account '%s' already exists.\n",
399                              argv[4]);
400                }
401              else
402                 fprintf(stderr,
403                         "ERROR: wrong number of parameters (%d).\n",
404                         argc);
405           }
406         else if (strcmp(argv[3], "post") == 0)
407           {
408              if (argc == 6)
409                {
410                   My_Account * acc = _my_cache_account_find(my_cache, argv[4]);
411                   if (acc)
412                     {
413                        My_Post * post = _my_post_new(argv[5]);
414                        acc->posts = eina_list_append(acc->posts, post);
415                     }
416                   else
417                      fprintf(stderr, "ERROR: unknown account: '%s'\n", argv[4]);
418                }
419              else
420                 fprintf(stderr,
421                         "ERROR: wrong number of parameters (%d).\n",
422                         argc);
423           }
424         else if (strcmp(argv[3], "message") == 0)
425           {
426              if (argc == 6)
427                {
428                   My_Account * acc = _my_cache_account_find(my_cache, argv[4]);
429                   if (acc)
430                     {
431                        My_Message * msg = _my_message_new(argv[5]);
432                        acc->messages = eina_list_append(acc->messages, msg);
433                     }
434                   else
435                      fprintf(stderr, "ERROR: unknown account: '%s'\n", argv[4]);
436                }
437              else
438                 fprintf(stderr,
439                         "ERROR: wrong number of parameters (%d).\n",
440                         argc);
441           }
442         else
443            fprintf(stderr, "ERROR: unknown action '%s'\n", argv[2]);
444      }
445
446    printf("My_Cache:\n"
447           "\tversion.: %#x\n"
448           "\taccounts: %u\n",
449           my_cache->version,
450           eina_list_count(my_cache->accounts));
451    EINA_LIST_FOREACH(my_cache->accounts, l_acc, acc)
452    {
453       const My_Post * post;
454
455       printf("\t  > %-#8x '%.20s' stats: m=%u, p=%u\n",
456              acc->id, acc->name ? acc->name : "",
457              eina_list_count(acc->messages),
458              eina_list_count(acc->posts));
459
460       if (eina_list_count(acc->messages))
461         {
462            const Eina_List * l;
463            const My_Message * msg;
464            printf("\t  |messages:\n");
465
466            EINA_LIST_FOREACH(acc->messages, l, msg)
467            {
468               printf("\t  |   %-8x '%s' [%s]: '%.20s'\n",
469                      msg->id,
470                      msg->name ? msg->name : "",
471                      msg->screen_name ? msg->screen_name : "",
472                      msg->message ? msg->message : "");
473            }
474         }
475
476       if (eina_list_count(acc->posts))
477         {
478            const Eina_List * l;
479            const My_Post * post;
480            printf("\t  |posts:\n");
481
482            EINA_LIST_FOREACH(acc->posts, l, post)
483            {
484               if (post->dm_to)
485                  printf("\t  |  @%s: '%.20s'\n", post->dm_to, post->message);
486               else
487                  printf("\t  |  '%.20s'\n",      post->message);
488            }
489         }
490
491       printf("\n");
492    }
493
494    if (!_my_cache_save(my_cache, argv[2]))
495       ret = -3;
496
497    _my_cache_free(my_cache);
498
499 end:
500    _my_cache_descriptor_shutdown();
501    eet_shutdown();
502    eina_shutdown();
503
504    return ret;
505 } /* main */
506