2 * Copyright © 2006 Carl Worth
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2, or (at your option)
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA."
25 #include <sys/types.h>
31 typedef struct _item {
44 typedef struct _category {
51 typedef struct _mnemon {
56 category_t *categories;
70 fprintf (stderr, "Error: out of memory\n");
78 xrealloc (void *ptr, size_t size)
82 ret = realloc (ptr, size);
84 fprintf (stderr, "Error: out of memory\n");
92 xstrdup (const char *s)
98 fprintf (stderr, "Error: out of memory\n");
106 xasprintf (char **strp, const char *fmt, ...)
112 ret = vasprintf (strp, fmt, ap);
116 fprintf (stderr, "Error: out of memory\n");
122 item_init (item_t *item,
124 const char *challenge,
125 const char *response)
129 item->challenge = xmalloc (strlen (challenge) + 1 +
130 strlen (response) + 1);
131 item->response = item->challenge + strlen (challenge) + 1;
133 strcpy (item->challenge, challenge);
134 strcpy (item->response, response);
138 item_fini (item_t *item)
140 /* item->response shares allocation with item->challenge, so
141 * doesn't require a separate call to free */
142 free (item->challenge);
146 category_init (category_t *category,
149 category->name = xstrdup (name);
151 category->items_size = 0;
152 category->num_items = 0;
153 category->items = NULL;
157 category_fini (category_t *category)
161 for (i = 0; i < category->num_items; i++)
162 item_fini (&category->items[i]);
164 free (category->items);
166 free (category->name);
170 category_grow (category_t *category)
172 if (category->items_size)
173 category->items_size *= 2;
175 category->items_size = 1;
177 category->items = xrealloc (category->items,
178 category->items_size * sizeof (item_t));
182 category_add_item (category_t *category,
184 const char *challenge,
185 const char *response)
189 if (category->num_items == category->items_size)
190 category_grow (category);
192 item = &category->items[category->num_items++];
194 item_init (item, count, challenge, response);
200 category_print (category_t *category,
206 for (i = 0; i < category->num_items; i++) {
207 item = &category->items[i];
209 fprintf (file, "\n");
210 fprintf (file, "%d\n%s\n%s\n",
218 bin_init (bin_t *bin,
229 bin_fini (bin_t *bin)
235 bin_grow (bin_t *bin)
238 bin->items_size *= 2;
242 bin->items = xrealloc (bin->items,
243 bin->items_size * sizeof (item_t*));
247 bin_add_item (bin_t *bin,
250 assert (item->count == bin->count);
252 if (bin->num_items == bin->items_size)
255 bin->items[bin->num_items++] = item;
259 mnemon_init (mnemon_t *mnemon)
263 home = getenv ("HOME");
267 xasprintf (&mnemon->dir_name, "%s/.mnemon", getenv ("HOME"));
269 mnemon->categories_size = 0;
270 mnemon->num_categories = 0;
271 mnemon->categories = NULL;
273 mnemon->bins_size = 0;
274 mnemon->num_bins = 0;
279 mnemon_fini (mnemon_t *mnemon)
283 for (i = 0; i < mnemon->num_bins; i++)
284 bin_fini (&mnemon->bins[i]);
287 for (i = 0; i < mnemon->num_categories; i++)
288 category_fini (&mnemon->categories[i]);
289 free (mnemon->categories);
291 free (mnemon->dir_name);
295 mnemon_categories_grow (mnemon_t *mnemon)
297 if (mnemon->categories_size)
298 mnemon->categories_size *= 2;
300 mnemon->categories_size = 1;
302 mnemon->categories = xrealloc (mnemon->categories,
303 mnemon->categories_size * sizeof (category_t));
307 mnemon_get_category (mnemon_t *mnemon,
311 category_t *category;
313 for (i = 0; i < mnemon->num_categories; i++)
314 if (strcmp (mnemon->categories[i].name, name) == 0)
315 return &mnemon->categories[i];
317 mnemon_categories_grow (mnemon);
319 category = &mnemon->categories[mnemon->num_categories++];
321 category_init (category, name);
327 mnemon_bins_grow (mnemon_t *mnemon)
329 if (mnemon->bins_size)
330 mnemon->bins_size *= 2;
332 mnemon->bins_size = 1;
334 mnemon->bins = xrealloc (mnemon->bins,
335 mnemon->bins_size * sizeof (bin_t));
339 mnemon_get_bin (mnemon_t *mnemon,
345 for (i = 0; i < mnemon->num_bins; i++)
346 if (mnemon->bins[i].count == count)
347 return &mnemon->bins[i];
348 else if (mnemon->bins[i].count > count)
351 mnemon_bins_grow (mnemon);
353 bin = &mnemon->bins[i];
355 /* Make room to insert new bin at its sorted location. */
356 memmove (bin + 1, bin, (mnemon->num_bins - i) * sizeof (bin_t));
359 bin_init (bin, count);
367 int len = strlen (s);
370 if (s[len - 1] == '\n')
375 mnemon_load_category (mnemon_t *mnemon,
379 char *line = NULL, *end;
380 size_t line_size = 0;
384 category_t *category;
387 path = xmalloc (strlen (mnemon->dir_name) + 1 + strlen (name) + 1);
388 sprintf (path, "%s/%s", mnemon->dir_name, name);
390 file = fopen (path, "r");
392 fprintf (stderr, "Error: Failed to open %s: %s\n",
393 path, strerror (errno));
397 category = mnemon_get_category (mnemon, name);
401 char *challenge, *response;
403 /* Read bin number (ignoring blank separator lines) */
405 bytes_read = getline (&line, &line_size, file);
406 if (bytes_read == -1)
410 } while (*line == '\0');
412 count = strtol (line, &end, 10);
414 fprintf (stderr, "Failed to parse bin number from \"%s\" at %s:%d\n",
415 line, path, line_count);
420 bytes_read = getline (&line, &line_size, file);
421 if (bytes_read == -1)
425 challenge = strdup (line);
428 bytes_read = getline (&line, &line_size, file);
429 if (bytes_read == -1)
435 category_add_item (category, count, challenge, response);
445 /* Resize category items to fit exactly. */
446 category->items_size = category->num_items;
447 category->items = xrealloc (category->items, category->items_size * sizeof (item_t));
449 /* Now that the category is completely loaded, with stable
450 * pointers to every item, we can add each item to its appropriate
452 for (i = 0; i < category->num_items; i++) {
453 item_t *item = &category->items[i];
454 bin_t *bin = mnemon_get_bin (mnemon, item->count);
456 bin_add_item (bin, item);
461 mnemon_load (mnemon_t *mnemon)
464 struct dirent *dirent;
466 dir = opendir (mnemon->dir_name);
468 fprintf (stderr, "Error: Failed to open directory %s: %s\n",
469 mnemon->dir_name, strerror (errno));
474 dirent = readdir (dir);
478 if (dirent->d_type == DT_REG) {
479 /* Ignore files matching *~, (yes, this shouldn't be
480 * hard-coded in such an ad-hoc way, but there you go. */
481 if (dirent->d_name[strlen(dirent->d_name)-1] != '~')
482 mnemon_load_category (mnemon, dirent->d_name);
490 mnemon_save (mnemon_t *mnemon)
493 char *filename, *lock_filename;
495 category_t *category;
497 for (i = 0; i < mnemon->num_categories; i++) {
498 category = &mnemon->categories[i];
500 xasprintf (&filename, "%s/%s",
501 mnemon->dir_name, category->name);
502 xasprintf (&lock_filename, "%s/.#%s",
503 mnemon->dir_name, category->name);
505 file = fopen (lock_filename, "w");
507 fprintf (stderr, "Error: Failed to open %s for writing: %s\n",
508 lock_filename, strerror (errno));
512 category_print (category, file);
516 err = rename (lock_filename, filename);
518 fprintf (stderr, "Error: Failes to rename %s to %s: %s\n",
519 lock_filename, filename, strerror (errno));
524 free (lock_filename);
529 main (int argc, char *argv[])
533 mnemon_init (&mnemon);
535 mnemon_load (&mnemon);
537 mnemon_save (&mnemon);
539 mnemon_fini (&mnemon);