Tizen Native API  3.0
File descriptor data example
00001 //Compile with:
00002 // gcc -o eet-data-file_descriptor_01 eet-data-file_descriptor_01.c `pkg-config --cflags --libs eet eina`
00003 
00004 #include <Eina.h>
00005 #include <Eet.h>
00006 #include <stdio.h>
00007 #include <limits.h>
00008 #include <sys/types.h>
00009 #include <sys/stat.h>
00010 #include <unistd.h>
00011 
00012 // complex real-world structures based on elmdentica database
00013 typedef struct
00014 {
00015    const char  *screen_name;
00016    const char  *name;
00017    const char  *message;
00018    unsigned int id;
00019    unsigned int status_id;
00020    unsigned int date;
00021    unsigned int timeline;
00022 } My_Message;
00023 
00024 typedef struct
00025 {
00026    const char *dm_to;
00027    const char *message;
00028 } My_Post;
00029 
00030 typedef struct
00031 {
00032    unsigned int id;
00033    const char  *name;
00034    Eina_List   *messages;
00035    My_Post     *posts;
00036    int          posts_count;
00037 } My_Account;
00038 
00039 typedef struct
00040 {
00041    unsigned int version; // it is recommended to use versioned configuration!
00042    Eina_Hash   *accounts;
00043 } My_Cache;
00044 
00045 // string that represents the entry in eet file, you might like to have
00046 // different profiles or so in the same file, this is possible with
00047 // different strings
00048 static const char MY_CACHE_FILE_ENTRY[] = "cache";
00049 
00050 // keep the descriptor static global, so it can be
00051 // shared by different functions (load/save) of this and only this
00052 // file.
00053 static Eet_Data_Descriptor *_my_cache_descriptor;
00054 static Eet_Data_Descriptor *_my_account_descriptor;
00055 static Eet_Data_Descriptor *_my_message_descriptor;
00056 static Eet_Data_Descriptor *_my_post_descriptor;
00057 
00058 // keep file handle alive, so mmap()ed strings are all alive as well
00059 static Eet_File *_my_cache_file = NULL;
00060 static Eet_Dictionary *_my_cache_dict = NULL;
00061 
00062 static void
00063 _my_cache_descriptor_init(void)
00064 {
00065    Eet_Data_Descriptor_Class eddc;
00066 
00067    // The FILE variant is good for caches and things that are just
00068    // appended, but needs to take care when changing strings and files must
00069    // be kept open so mmap()ed strings will be kept alive.
00070    EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, My_Cache);
00071    _my_cache_descriptor = eet_data_descriptor_file_new(&eddc);
00072 
00073    EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, My_Account);
00074    _my_account_descriptor = eet_data_descriptor_file_new(&eddc);
00075 
00076    EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, My_Message);
00077    _my_message_descriptor = eet_data_descriptor_file_new(&eddc);
00078 
00079    EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, My_Post);
00080    _my_post_descriptor = eet_data_descriptor_file_new(&eddc);
00081 
00082    // Describe the members to be saved:
00083    // Use a temporary macro so we don't type a lot, also avoid errors:
00084 
00085 #define ADD_BASIC(member, eet_type) \
00086   EET_DATA_DESCRIPTOR_ADD_BASIC     \
00087     (_my_message_descriptor, My_Message, # member, member, eet_type)
00088    ADD_BASIC(screen_name, EET_T_STRING);
00089    ADD_BASIC(name, EET_T_STRING);
00090    ADD_BASIC(message, EET_T_STRING);
00091    ADD_BASIC(id, EET_T_UINT);
00092    ADD_BASIC(status_id, EET_T_UINT);
00093    ADD_BASIC(date, EET_T_UINT);
00094    ADD_BASIC(timeline, EET_T_UINT);
00095 #undef ADD_BASIC
00096 
00097 #define ADD_BASIC(member, eet_type) \
00098   EET_DATA_DESCRIPTOR_ADD_BASIC     \
00099     (_my_post_descriptor, My_Post, # member, member, eet_type)
00100    ADD_BASIC(dm_to, EET_T_STRING);
00101    ADD_BASIC(message, EET_T_STRING);
00102 #undef ADD_BASIC
00103 
00104 #define ADD_BASIC(member, eet_type) \
00105   EET_DATA_DESCRIPTOR_ADD_BASIC     \
00106     (_my_account_descriptor, My_Account, # member, member, eet_type)
00107    ADD_BASIC(name, EET_T_STRING);
00108    ADD_BASIC(id, EET_T_UINT);
00109 #undef ADD_BASIC
00110 
00111    EET_DATA_DESCRIPTOR_ADD_LIST
00112      (_my_account_descriptor, My_Account, "messages", messages,
00113      _my_message_descriptor);
00114    EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY
00115      (_my_account_descriptor, My_Account, "posts", posts,
00116      _my_post_descriptor);
00117 
00118 #define ADD_BASIC(member, eet_type) \
00119   EET_DATA_DESCRIPTOR_ADD_BASIC     \
00120     (_my_cache_descriptor, My_Cache, # member, member, eet_type)
00121    ADD_BASIC(version, EET_T_UINT);
00122 #undef ADD_BASIC
00123 
00124    EET_DATA_DESCRIPTOR_ADD_HASH
00125      (_my_cache_descriptor, My_Cache, "accounts", accounts,
00126      _my_account_descriptor);
00127 } /* _my_cache_descriptor_init */
00128 
00129 static void
00130 _my_cache_descriptor_shutdown(void)
00131 {
00132    eet_data_descriptor_free(_my_cache_descriptor);
00133    eet_data_descriptor_free(_my_account_descriptor);
00134    eet_data_descriptor_free(_my_message_descriptor);
00135    eet_data_descriptor_free(_my_post_descriptor);
00136 } /* _my_cache_descriptor_shutdown */
00137 
00138 // need to check if the pointer came from mmaped area in eet_dictionary
00139 // or it was allocated with eina_stringshare_add()
00140 static void
00141 _eet_string_free(const char *str)
00142 {
00143    if (!str)
00144      return;
00145 
00146    if ((_my_cache_dict) && (eet_dictionary_string_check(_my_cache_dict, str)))
00147      return;
00148 
00149    eina_stringshare_del(str);
00150 } /* _eet_string_free */
00151 
00152 static My_Message *
00153 _my_message_new(const char *message)
00154 {
00155    My_Message *msg = calloc(1, sizeof(My_Message));
00156    if (!msg)
00157      {
00158         fprintf(stderr, "ERROR: could not calloc My_Message\n");
00159         return NULL;
00160      }
00161 
00162    msg->message = eina_stringshare_add(message);
00163    return msg;
00164 } /* _my_message_new */
00165 
00166 static void
00167 _my_message_free(My_Message *msg)
00168 {
00169    _eet_string_free(msg->screen_name);
00170    _eet_string_free(msg->name);
00171    _eet_string_free(msg->message);
00172    free(msg);
00173 } /* _my_message_free */
00174 
00175 static Eina_Bool
00176 _my_post_add(My_Account *acc,
00177              const char *message)
00178 {
00179    int new_count = acc->posts_count + 1;
00180    My_Post *post = realloc(acc->posts, new_count * sizeof(My_Post));
00181    if (!post)
00182      {
00183         fprintf(stderr, "ERROR: could add My_Post\n");
00184         return EINA_FALSE;
00185      }
00186 
00187    post[acc->posts_count].message = eina_stringshare_add(message);
00188    post[acc->posts_count].dm_to = NULL;
00189    acc->posts_count = new_count;
00190    acc->posts = post;
00191    return EINA_TRUE;
00192 } /* _my_post_new */
00193 
00194 static void
00195 _my_post_free(My_Post *post)
00196 {
00197    _eet_string_free(post->dm_to);
00198    _eet_string_free(post->message);
00199 } /* _my_post_free */
00200 
00201 static My_Account *
00202 _my_account_new(const char *name)
00203 {
00204    My_Account *acc = calloc(1, sizeof(My_Account));
00205    if (!acc)
00206      {
00207         fprintf(stderr, "ERROR: could not calloc My_Account\n");
00208         return NULL;
00209      }
00210 
00211    acc->name = eina_stringshare_add(name);
00212    return acc;
00213 } /* _my_account_new */
00214 
00215 static void
00216 _my_account_free(My_Account *acc)
00217 {
00218    My_Message *m;
00219    int i;
00220 
00221    _eet_string_free(acc->name);
00222 
00223    EINA_LIST_FREE(acc->messages, m)
00224      _my_message_free(m);
00225 
00226    for (i = 0; i < acc->posts_count; i++)
00227      _my_post_free(&acc->posts[i]);
00228    free(acc->posts);
00229 
00230    free(acc);
00231 } /* _my_account_free */
00232 
00233 static My_Cache *
00234 _my_cache_new(void)
00235 {
00236    My_Cache *my_cache = calloc(1, sizeof(My_Cache));
00237    if (!my_cache)
00238      {
00239         fprintf(stderr, "ERROR: could not calloc My_Cache\n");
00240         return NULL;
00241      }
00242 
00243    my_cache->accounts = eina_hash_string_small_new(NULL);
00244 
00245    my_cache->version = 1;
00246    return my_cache;
00247 } /* _my_cache_new */
00248 
00249 static Eina_Bool
00250 _my_cache_account_free_cb(const Eina_Hash *hash EINA_UNUSED,
00251                           const void      *key EINA_UNUSED,
00252                           void            *data,
00253                           void            *fdata EINA_UNUSED)
00254 {
00255    _my_account_free(data);
00256    return EINA_TRUE;
00257 }
00258 
00259 static void
00260 _my_cache_free(My_Cache *my_cache)
00261 {
00262    eina_hash_foreach(my_cache->accounts, _my_cache_account_free_cb, NULL);
00263    eina_hash_free(my_cache->accounts);
00264    free(my_cache);
00265 } /* _my_cache_free */
00266 
00267 static My_Account *
00268 _my_cache_account_find(My_Cache   *my_cache,
00269                        const char *name)
00270 {
00271    return eina_hash_find(my_cache->accounts, name);
00272 } /* _my_cache_account_find */
00273 
00274 static My_Cache *
00275 _my_cache_load(const char *filename)
00276 {
00277    My_Cache *my_cache;
00278    Eet_File *ef = eet_open(filename, EET_FILE_MODE_READ);
00279    if (!ef)
00280      {
00281         fprintf(stderr, "ERROR: could not open '%s' for read\n", filename);
00282         return NULL;
00283      }
00284 
00285    my_cache = eet_data_read(ef, _my_cache_descriptor, MY_CACHE_FILE_ENTRY);
00286    if (!my_cache)
00287      {
00288         eet_close(ef);
00289         return NULL;
00290      }
00291 
00292    if (my_cache->version < 1)
00293      {
00294         fprintf(stderr,
00295                 "WARNING: version %#x was too old, upgrading it to %#x\n",
00296                 my_cache->version, 1);
00297 
00298         my_cache->version = 1;
00299      }
00300 
00301    if (_my_cache_file)
00302      eet_close(_my_cache_file);
00303 
00304    _my_cache_file = ef;
00305    _my_cache_dict = eet_dictionary_get(ef);
00306 
00307    return my_cache;
00308 } /* _my_cache_load */
00309 
00310 static Eina_Bool
00311 _my_cache_save(const My_Cache *my_cache,
00312                const char     *filename)
00313 {
00314    char tmp[PATH_MAX];
00315    Eet_File *ef;
00316    Eina_Bool ret;
00317    unsigned int i, len;
00318    struct stat st;
00319 
00320    len = eina_strlcpy(tmp, filename, sizeof(tmp));
00321    if (len + 12 >= (int)sizeof(tmp))
00322      {
00323         fprintf(stderr, "ERROR: file name is too big: %s\n", filename);
00324         return EINA_FALSE;
00325      }
00326 
00327    i = 0;
00328    do
00329      {
00330         snprintf(tmp + len, 12, ".%u", i);
00331         i++;
00332      }
00333    while (stat(tmp, &st) == 0);
00334 
00335    ef = eet_open(tmp, EET_FILE_MODE_WRITE);
00336    if (!ef)
00337      {
00338         fprintf(stderr, "ERROR: could not open '%s' for write\n", tmp);
00339         return EINA_FALSE;
00340      }
00341 
00342    ret = eet_data_write
00343        (ef, _my_cache_descriptor, MY_CACHE_FILE_ENTRY, my_cache, EINA_TRUE);
00344 
00345    // VERY IMPORTANT NOTE:
00346    // after eet_close(), all strings mmaped from file will be GONE, invalid!
00347    // you'll need to free the old cache and open the new one.
00348    // For cache this is okay, as you should be saving not so often or just
00349    // at end.
00350    //
00351    // This is a trade off, you save memory by using mmap()ed strings, but
00352    // you have to care about this.
00353    eet_close(ef);
00354 
00355    if (ret)
00356      {
00357         unlink(filename);
00358         rename(tmp, filename);
00359      }
00360 
00361    return ret;
00362 } /* _my_cache_save */
00363 
00364 int
00365 main(int   argc,
00366      char *argv[])
00367 {
00368    My_Cache *my_cache;
00369    Eina_Iterator *it;
00370    My_Account *acc;
00371    int ret = 0;
00372 
00373    if (argc < 3)
00374      {
00375         fprintf(stderr,
00376                 "Usage:\n\t%s <input> <output> [action] [action-params]\n\n"
00377                 "Where actions and their parameters:\n"
00378                 "\tacc <name>\n"
00379                 "\tpost <account-name> <message>\n"
00380                 "\tmessage <account-name> <message>\n"
00381                 "\n",
00382                 argv[0]);
00383         return -1;
00384      }
00385 
00386    eina_init();
00387    eet_init();
00388    _my_cache_descriptor_init();
00389 
00390    my_cache = _my_cache_load(argv[1]);
00391    if (!my_cache)
00392      {
00393         printf("creating new cache.\n");
00394         my_cache = _my_cache_new();
00395         if (!my_cache)
00396           {
00397              ret = -2;
00398              goto end;
00399           }
00400      }
00401 
00402    if (argc > 3)
00403      {
00404         if (strcmp(argv[3], "acc") == 0)
00405           {
00406              if (argc == 5)
00407                {
00408                   My_Account *acc_ = _my_cache_account_find(my_cache, argv[4]);
00409                   if (!acc_)
00410                     {
00411                        acc_ = _my_account_new(argv[4]);
00412                        eina_hash_direct_add(my_cache->accounts, acc_->name, acc);
00413                     }
00414                   else
00415                     fprintf(stderr, "ERROR: account '%s' already exists.\n",
00416                             argv[4]);
00417                }
00418              else
00419                fprintf(stderr,
00420                        "ERROR: wrong number of parameters (%d).\n",
00421                        argc);
00422           }
00423         else if (strcmp(argv[3], "post") == 0)
00424           {
00425              if (argc == 6)
00426                {
00427                   My_Account *acc_ = _my_cache_account_find(my_cache, argv[4]);
00428                   if (acc_)
00429                     {
00430                        _my_post_add(acc_, argv[5]);
00431                     }
00432                   else
00433                     fprintf(stderr, "ERROR: unknown account: '%s'\n", argv[4]);
00434                }
00435              else
00436                fprintf(stderr,
00437                        "ERROR: wrong number of parameters (%d).\n",
00438                        argc);
00439           }
00440         else if (strcmp(argv[3], "message") == 0)
00441           {
00442              if (argc == 6)
00443                {
00444                   My_Account *acc_ = _my_cache_account_find(my_cache, argv[4]);
00445                   if (acc_)
00446                     {
00447                        My_Message *msg = _my_message_new(argv[5]);
00448                        acc_->messages = eina_list_append(acc_->messages, msg);
00449                     }
00450                   else
00451                     fprintf(stderr, "ERROR: unknown account: '%s'\n", argv[4]);
00452                }
00453              else
00454                fprintf(stderr,
00455                        "ERROR: wrong number of parameters (%d).\n",
00456                        argc);
00457           }
00458         else
00459           fprintf(stderr, "ERROR: unknown action '%s'\n", argv[2]);
00460      }
00461 
00462    printf("My_Cache:\n"
00463           "\tversion.: %#x\n"
00464           "\taccounts: %u\n",
00465           my_cache->version,
00466           eina_hash_population(my_cache->accounts));
00467    it = eina_hash_iterator_data_new(my_cache->accounts);
00468    EINA_ITERATOR_FOREACH(it, acc)
00469      {
00470         printf("\t  > %-#8x '%.20s' stats: m=%u, p=%u\n",
00471                acc->id, acc->name ? acc->name : "",
00472                eina_list_count(acc->messages),
00473                acc->posts_count);
00474 
00475         if (eina_list_count(acc->messages))
00476           {
00477              const Eina_List *l;
00478              const My_Message *msg;
00479              printf("\t  |messages:\n");
00480 
00481              EINA_LIST_FOREACH(acc->messages, l, msg)
00482                {
00483                   printf("\t  |   %-8x '%s' [%s]: '%.20s'\n",
00484                          msg->id,
00485                          msg->name ? msg->name : "",
00486                          msg->screen_name ? msg->screen_name : "",
00487                          msg->message ? msg->message : "");
00488                }
00489           }
00490 
00491         if (acc->posts_count)
00492           {
00493              const My_Post *post;
00494              int i;
00495              printf("\t  |posts:\n");
00496 
00497              for (i = 0; i < acc->posts_count; i++)
00498                {
00499                   post = &acc->posts[i];
00500                   if (post->dm_to)
00501                     printf("\t  |  @%s: '%.20s'\n", post->dm_to, post->message);
00502                   else
00503                     printf("\t  |  '%.20s'\n", post->message);
00504                }
00505           }
00506 
00507         printf("\n");
00508      }
00509    eina_iterator_free(it);
00510 
00511    if (!_my_cache_save(my_cache, argv[2]))
00512      ret = -3;
00513 
00514    _my_cache_free(my_cache);
00515 
00516 end:
00517    if (_my_cache_file)
00518      eet_close(_my_cache_file);
00519    _my_cache_descriptor_shutdown();
00520    eet_shutdown();
00521    eina_shutdown();
00522 
00523    return ret;
00524 } /* main */
00525