diff --git a/examples/cpp-test.cpp b/examples/cpp-test.cpp index 1293613..a0eb2ed 100644 --- a/examples/cpp-test.cpp +++ b/examples/cpp-test.cpp @@ -6,6 +6,10 @@ int main (int argc, char **argv) { - fp_init (); + FpContext *ctx; + + ctx = fp_context_new (); + g_object_unref (ctx); + return 0; } diff --git a/examples/enroll.c b/examples/enroll.c index 9814f68..7542f30 100644 --- a/examples/enroll.c +++ b/examples/enroll.c @@ -2,6 +2,7 @@ * Example fingerprint enrollment program * Enrolls your right index finger and saves the print to disk * Copyright (C) 2007 Daniel Drake + * Copyright (C) 2019 Marco Trevisan * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,91 +20,128 @@ */ #include -#include -#include - #include #include "storage.h" -struct fp_dscv_dev *discover_device(struct fp_dscv_dev **discovered_devs) +typedef struct _EnrollData { + GMainLoop *loop; + int ret_value; +} EnrollData; + +static void +enroll_data_free (EnrollData *enroll_data) { - struct fp_dscv_dev *ddev = discovered_devs[0]; - struct fp_driver *drv; - if (!ddev) + g_main_loop_unref (enroll_data->loop); + g_free (enroll_data); +} +G_DEFINE_AUTOPTR_CLEANUP_FUNC (EnrollData, enroll_data_free) + +FpDevice *discover_device (GPtrArray *devices) +{ + FpDevice *dev; + if (!devices->len) return NULL; - - drv = fp_dscv_dev_get_driver(ddev); - printf("Found device claimed by %s driver\n", fp_driver_get_full_name(drv)); - return ddev; + + dev = g_ptr_array_index (devices, 0); + printf("Found device claimed by %s driver\n", fp_device_get_driver (dev)); + return dev; } -struct fp_print_data *enroll(struct fp_dev *dev) { - struct fp_print_data *enrolled_print = NULL; - int r; +static void +on_device_closed (FpDevice *dev, GAsyncResult *res, void *user_data) { + EnrollData *enroll_data = user_data; + g_autoptr(GError) error = NULL; - printf("You will need to successfully scan your finger %d times to " - "complete the process.\n", fp_dev_get_nr_enroll_stages(dev)); + fp_device_close_finish (dev, res, &error); - do { - struct fp_img *img = NULL; + if (error) + g_warning ("Failed closing device %s\n", error->message); - printf("\nScan your finger now.\n"); + g_main_loop_quit (enroll_data->loop); +} - r = fp_enroll_finger_img(dev, &enrolled_print, &img); - if (img) { - fp_img_save_to_file(img, "enrolled.pgm"); - printf("Wrote scanned image to enrolled.pgm\n"); - fp_img_free(img); +static void +on_enroll_completed (FpDevice *dev, GAsyncResult *res, void *user_data) { + EnrollData *enroll_data = user_data; + g_autoptr(FpPrint) print = NULL; + g_autoptr(GError) error = NULL; + + print = fp_device_enroll_finish (dev, res, &error); + + if (!error) { + enroll_data->ret_value = EXIT_SUCCESS; + + if (!fp_device_has_storage (dev)) { + g_debug("Device has not storage, saving locally"); + int r = print_data_save(print, FP_FINGER_RIGHT_INDEX); + if (r < 0) { + g_warning("Data save failed, code %d", r); + enroll_data->ret_value = EXIT_FAILURE; + } } - if (r < 0) { - printf("Enroll failed with error %d\n", r); - return NULL; - } - - switch (r) { - case FP_ENROLL_COMPLETE: - printf("Enroll complete!\n"); - break; - case FP_ENROLL_FAIL: - printf("Enroll failed, something wen't wrong :(\n"); - return NULL; - case FP_ENROLL_PASS: - printf("Enroll stage passed. Yay!\n"); - break; - case FP_ENROLL_RETRY: - printf("Didn't quite catch that. Please try again.\n"); - break; - case FP_ENROLL_RETRY_TOO_SHORT: - printf("Your swipe was too short, please try again.\n"); - break; - case FP_ENROLL_RETRY_CENTER_FINGER: - printf("Didn't catch that, please center your finger on the " - "sensor and try again.\n"); - break; - case FP_ENROLL_RETRY_REMOVE_FINGER: - printf("Scan failed, please remove your finger and then try " - "again.\n"); - break; - } - } while (r != FP_ENROLL_COMPLETE); - - if (!enrolled_print) { - fprintf(stderr, "Enroll complete but no print?\n"); - return NULL; + } else { + g_warning("Enroll failed with error %s\n", error->message); } - printf("Enrollment completed!\n\n"); - return enrolled_print; + fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed, + enroll_data); +} + +static void +on_enroll_progress (FpDevice *device, + gint completed_stages, + FpPrint *print, + gpointer user_data, + GError *error) +{ + if (error) { + g_warning ("Enroll stage %d of %d failed with error %s", + completed_stages, + fp_device_get_nr_enroll_stages (device), + error->message); + return; + } + + if (fp_device_supports_capture (device) && + print_image_save (print, "enrolled.pgm")) { + printf ("Wrote scanned image to enrolled.pgm\n"); + } + + printf ("Enroll stage %d of %d passed. Yay!\n", completed_stages, + fp_device_get_nr_enroll_stages (device)); +} + +static void +on_device_opened (FpDevice *dev, GAsyncResult *res, void *user_data) +{ + EnrollData *enroll_data = user_data; + FpPrint *print_template; + g_autoptr(GError) error = NULL; + + if (!fp_device_open_finish (dev, res, &error)) { + g_warning ("Failed to open device: %s", error->message); + g_main_loop_quit (enroll_data->loop); + return; + } + + printf ("Opened device. It's now time to enroll your finger.\n\n"); + printf ("You will need to successfully scan your finger %d times to " + "complete the process.\n\n", fp_device_get_nr_enroll_stages (dev)); + printf ("Scan your finger now.\n"); + + print_template = print_create_template (dev, FP_FINGER_RIGHT_INDEX); + fp_device_enroll (dev, print_template, NULL, on_enroll_progress, NULL, + NULL, (GAsyncReadyCallback) on_enroll_completed, + enroll_data); } int main(void) { - int r = 1; - struct fp_dscv_dev *ddev; - struct fp_dscv_dev **discovered_devs; - struct fp_dev *dev; - struct fp_print_data *data; + g_autoptr (FpContext) ctx = NULL; + g_autoptr (EnrollData) enroll_data = NULL; + GPtrArray *devices; + FpDevice *dev; printf("This program will enroll your right index finger, " "unconditionally overwriting any right-index print that was enrolled " @@ -112,48 +150,29 @@ int main(void) getchar(); setenv ("G_MESSAGES_DEBUG", "all", 0); - setenv ("LIBUSB_DEBUG", "3", 0); - r = fp_init(); - if (r < 0) { - fprintf(stderr, "Failed to initialize libfprint\n"); - exit(1); + ctx = fp_context_new (); + + devices = fp_context_get_devices (ctx); + if (!devices) { + g_warning("Impossible to get devices"); + return EXIT_FAILURE; } - discovered_devs = fp_discover_devs(); - if (!discovered_devs) { - fprintf(stderr, "Could not discover devices\n"); - goto out; - } - - ddev = discover_device(discovered_devs); - if (!ddev) { - fprintf(stderr, "No devices detected.\n"); - goto out; - } - - dev = fp_dev_open(ddev); - fp_dscv_devs_free(discovered_devs); + dev = discover_device (devices); if (!dev) { - fprintf(stderr, "Could not open device.\n"); - goto out; + g_warning("No devices detected."); + return EXIT_FAILURE; } - printf("Opened device. It's now time to enroll your finger.\n\n"); - data = enroll(dev); - if (!data) - goto out_close; + enroll_data = g_new0 (EnrollData, 1); + enroll_data->ret_value = EXIT_FAILURE; + enroll_data->loop = g_main_loop_new (NULL, FALSE); - r = print_data_save(data, RIGHT_INDEX); - if (r < 0) - fprintf(stderr, "Data save failed, code %d\n", r); + fp_device_open (dev, NULL, (GAsyncReadyCallback) on_device_opened, + enroll_data); - fp_print_data_free(data); -out_close: - fp_dev_close(dev); -out: - fp_exit(); - return r; + g_main_loop_run (enroll_data->loop); + + return enroll_data->ret_value; } - - diff --git a/examples/meson.build b/examples/meson.build index 9c84e6b..90c5257 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -1,22 +1,22 @@ -examples = [ 'verify_live', 'enroll', 'verify', 'img_capture' ] +examples = [ 'enroll', 'verify', 'manage-prints' ] foreach example: examples executable(example, [example + '.c', 'storage.c'], - dependencies: [libfprint_dep], + dependencies: [libfprint_dep, glib_dep], include_directories: [ root_inc, ], c_args: common_cflags) endforeach -# executable('cpp-test', -# 'cpp-test.cpp', -# dependencies: libfprint_dep, -# include_directories: [ -# root_inc, -# ], -# c_args: common_cflags) +executable('cpp-test', + 'cpp-test.cpp', + dependencies: libfprint_dep, + include_directories: [ + root_inc, + ], + c_args: common_cflags) # if get_option('x11-examples') # executable('img_capture_continuous', diff --git a/examples/storage.c b/examples/storage.c index aa399e0..d2f4832 100644 --- a/examples/storage.c +++ b/examples/storage.c @@ -2,6 +2,7 @@ * Trivial storage driver for example programs * * Copyright (C) 2019 Benjamin Berg + * Copyright (C) 2019 Marco Trevisan * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,41 +19,41 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include +#include + +#include #include #include #include -#include - #define STORAGE_FILE "test-storage.variant" static char * -get_print_data_descriptor (struct fp_print_data *data, struct fp_dev *dev, enum fp_finger finger) +get_print_data_descriptor (FpPrint *print, FpDevice *dev, FpFinger finger) { - gint drv_id; - gint devtype; + const char *driver; + const char *dev_id; - if (data) { - drv_id = fp_print_data_get_driver_id (data); - devtype = fp_print_data_get_devtype (data); + if (print) { + driver = fp_print_get_driver (print); + dev_id = fp_print_get_device_id (print); } else { - drv_id = fp_driver_get_driver_id(fp_dev_get_driver (dev)); - devtype = fp_dev_get_devtype (dev); + driver = fp_device_get_driver (dev); + dev_id = fp_device_get_device_id (dev); } - return g_strdup_printf("%x/%08x/%x", - drv_id, - devtype, + return g_strdup_printf("%s/%s/%x", + driver, + dev_id, finger); } -static GVariantDict* +static GVariantDict * load_data(void) { GVariantDict *res; GVariant *var; - gchar *contents = NULL; + g_autofree gchar *contents = NULL; gssize length = 0; if (!g_file_get_contents (STORAGE_FILE, &contents, &length, NULL)) { @@ -88,49 +89,125 @@ save_data(GVariant *data) } int -print_data_save(struct fp_print_data *fp_data, enum fp_finger finger) +print_data_save(FpPrint *print, FpFinger finger) { - gchar *descr = get_print_data_descriptor (fp_data, NULL, finger); - GVariantDict *dict; + g_autofree gchar *descr = get_print_data_descriptor (print, NULL, finger); + g_autoptr (GError) error = NULL; + g_autoptr (GVariantDict) dict = NULL; + g_autofree guchar *data = NULL; GVariant *val; - guchar *data; gsize size; int res; dict = load_data(); - size = fp_print_data_get_data(fp_data, &data); + fp_print_serialize (print, &data, &size, &error); + if (error) { + g_warning ("Error serializing data: %s", error->message); + return -1; + } val = g_variant_new_fixed_array (G_VARIANT_TYPE("y"), data, size, 1); g_variant_dict_insert_value (dict, descr, val); res = save_data(g_variant_dict_end(dict)); - g_variant_dict_unref(dict); return res; } -struct fp_print_data* -print_data_load(struct fp_dev *dev, enum fp_finger finger) +FpPrint * +print_data_load(FpDevice *dev, FpFinger finger) { - gchar *descr = get_print_data_descriptor (NULL, dev, finger); - GVariantDict *dict; - guchar *stored_data; + g_autofree gchar *descr = get_print_data_descriptor (NULL, dev, finger); + g_autoptr (GVariant) val = NULL; + g_autoptr (GVariantDict) dict = NULL; + g_autofree guchar *stored_data = NULL; gsize stored_len; - GVariant *val; - struct fp_print_data *res = NULL; dict = load_data(); val = g_variant_dict_lookup_value (dict, descr, G_VARIANT_TYPE ("ay")); if (val) { - stored_data = (guchar*) g_variant_get_fixed_array (val, &stored_len, 1); - res = fp_print_data_from_data(stored_data, stored_len); + FpPrint *print; + g_autoptr (GError) error = NULL; - g_variant_unref(val); + stored_data = (guchar*) g_variant_get_fixed_array (val, &stored_len, 1); + print = fp_print_deserialize (stored_data, stored_len, &error); + + if (error) + g_warning ("Error deserializing data: %s", error->message); + + return print; } - g_variant_dict_unref(dict); - g_free(descr); - - return res; + return NULL; +} + +FpPrint * +print_create_template (FpDevice *dev, FpFinger finger) +{ + g_autoptr(GDateTime) datetime = NULL; + FpPrint *template = NULL; + GDate *date = NULL; + gint year, month, day; + + template = fp_print_new (dev); + fp_print_set_finger (template, finger); + fp_print_set_username (template, g_get_user_name ()); + datetime = g_date_time_new_now_local (); + g_date_time_get_ymd (datetime, &year, &month, &day); + date = g_date_new_dmy (day, month, year); + fp_print_set_enroll_date (template, date); + g_date_free (date); + + return template; +} + + +static gboolean +save_image_to_pgm (FpImage *img, const char *path) +{ + FILE *fd = fopen (path, "w"); + size_t write_size; + const guchar *data = fp_image_get_data (img, &write_size); + int r; + + if (!fd) { + g_warning("could not open '%s' for writing: %d", path, errno); + return FALSE; + } + + r = fprintf (fd, "P5 %d %d 255\n", + fp_image_get_width (img), fp_image_get_height (img)); + if (r < 0) { + fclose(fd); + g_critical("pgm header write failed, error %d", r); + return FALSE; + } + + r = fwrite (data, 1, write_size, fd); + if (r < write_size) { + fclose(fd); + g_critical("short write (%d)", r); + return FALSE; + } + + fclose (fd); + g_debug ("written to '%s'", path); + + return TRUE; +} + +gboolean print_image_save (FpPrint *print, const char *path) +{ + g_autoptr(FpImage) img = NULL; + + g_return_val_if_fail (FP_IS_PRINT (print), FALSE); + g_return_val_if_fail (path != NULL, FALSE); + + img = fp_print_get_image (print); + + if (img) + return save_image_to_pgm (img, path); + + return FALSE; } diff --git a/examples/storage.h b/examples/storage.h index fc86813..f419995 100644 --- a/examples/storage.h +++ b/examples/storage.h @@ -21,7 +21,10 @@ #ifndef __STORAGE_H #define __STORAGE_H -int print_data_save(struct fp_print_data *fp_data, enum fp_finger finger); -struct fp_print_data* print_data_load(struct fp_dev *dev, enum fp_finger finger); + +int print_data_save(FpPrint *print, FpFinger finger); +FpPrint * print_data_load(FpDevice *dev, FpFinger finger); +FpPrint * print_create_template(FpDevice *dev, FpFinger finger); +gboolean print_image_save(FpPrint *print, const char *path); #endif /* __STORAGE_H */ diff --git a/examples/verify.c b/examples/verify.c index 38b925c..69e3cbd 100644 --- a/examples/verify.c +++ b/examples/verify.c @@ -2,6 +2,7 @@ * Example fingerprint verification program, which verifies the right index * finger which has been previously enrolled to disk. * Copyright (C) 2007 Daniel Drake + * Copyright (C) 2019 Marco Trevisan * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,131 +20,216 @@ */ #include -#include -#include - #include #include "storage.h" -struct fp_dscv_dev *discover_device(struct fp_dscv_dev **discovered_devs) +typedef struct _VerifyData { + GMainLoop *loop; + int ret_value; +} VerifyData; + +static void +verify_data_free (VerifyData *verify_data) { - struct fp_dscv_dev *ddev = discovered_devs[0]; - struct fp_driver *drv; - if (!ddev) + g_main_loop_unref (verify_data->loop); + g_free (verify_data); +} +G_DEFINE_AUTOPTR_CLEANUP_FUNC (VerifyData, verify_data_free) + +FpDevice *discover_device (GPtrArray *devices) +{ + FpDevice *dev; + if (!devices->len) return NULL; - - drv = fp_dscv_dev_get_driver(ddev); - printf("Found device claimed by %s driver\n", fp_driver_get_full_name(drv)); - return ddev; + + dev = g_ptr_array_index (devices, 0); + printf("Found device claimed by %s driver\n", fp_device_get_driver (dev)); + return dev; } -int verify(struct fp_dev *dev, struct fp_print_data *data) +static void +on_device_closed (FpDevice *dev, GAsyncResult *res, void *user_data) { + VerifyData *verify_data = user_data; + g_autoptr(GError) error = NULL; + + fp_device_close_finish (dev, res, &error); + + if (error) + g_warning ("Failed closing device %s\n", error->message); + + g_main_loop_quit (verify_data->loop); +} + +static void start_verification (FpDevice *dev, VerifyData *verify_data); + +static void +on_verify_completed (FpDevice *dev, GAsyncResult *res, void *user_data) { - int r; + VerifyData *verify_data = user_data; + g_autoptr(FpPrint) print = NULL; + g_autoptr(GError) error = NULL; + char buffer[20]; + gboolean match; - do { - struct fp_img *img = NULL; + if (!fp_device_verify_finish (dev, res, &match, &print, &error)) { + g_warning ("Failed to verify print: %s", error->message); + g_main_loop_quit (verify_data->loop); + return; + } - sleep(1); - printf("\nScan your finger now.\n"); - r = fp_verify_finger_img(dev, data, &img); - if (img) { - fp_img_save_to_file(img, "verify.pgm"); - printf("Wrote scanned image to verify.pgm\n"); - fp_img_free(img); + if (match) { + g_print ("MATCH!\n"); + if (fp_device_supports_capture (dev) && + print_image_save (print, "verify.pgm")) { + g_print("Print image saved as verify.pgm"); } - if (r < 0) { - printf("verification failed with error %d :(\n", r); - return r; + + verify_data->ret_value = EXIT_SUCCESS; + } else { + g_print ("NO MATCH!\n"); + verify_data->ret_value = EXIT_FAILURE; + } + + g_print ("Verify again? [Y/n]? "); + if (fgets (buffer, sizeof (buffer), stdin) && + (buffer[0] == 'Y' || buffer[0] == 'y')) { + start_verification (dev, verify_data); + return; + } + + fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed, + verify_data); +} + +static void +on_list_completed (FpDevice *dev, GAsyncResult *res, gpointer user_data) +{ + VerifyData *verify_data = user_data; + g_autoptr(GPtrArray) prints = NULL; + g_autoptr(GError) error = NULL; + + prints = fp_device_list_prints_finish (dev, res, &error); + + if (!error) { + FpPrint *verify_print = NULL; + guint i; + + if (!prints->len) + g_warning ("No prints saved on device"); + + for (i = 0; i < prints->len; ++i) { + FpPrint *print = prints->pdata[i]; + + if (fp_print_get_finger (print) == FP_FINGER_RIGHT_INDEX && + g_strcmp0 (fp_print_get_username (print), g_get_user_name ()) == 0) { + if (!verify_print || + (g_date_compare (fp_print_get_enroll_date (print), + fp_print_get_enroll_date (verify_print)) >= 0)) + verify_print = print; + } } - switch (r) { - case FP_VERIFY_NO_MATCH: - printf("NO MATCH!\n"); - return 0; - case FP_VERIFY_MATCH: - printf("MATCH!\n"); - return 0; - case FP_VERIFY_RETRY: - printf("Scan didn't quite work. Please try again.\n"); - break; - case FP_VERIFY_RETRY_TOO_SHORT: - printf("Swipe was too short, please try again.\n"); - break; - case FP_VERIFY_RETRY_CENTER_FINGER: - printf("Please center your finger on the sensor and try again.\n"); - break; - case FP_VERIFY_RETRY_REMOVE_FINGER: - printf("Please remove finger from the sensor and try again.\n"); - break; + + if (!verify_print) { + g_warning ("Did you remember to enroll your right index " + "finger first?"); + g_main_loop_quit (verify_data->loop); + return; } - } while (1); + + g_debug ("Comparing print with %s", + fp_print_get_description (verify_print)); + + g_print ("Print loaded. Time to verify!\n"); + fp_device_verify (dev, verify_print, NULL, + (GAsyncReadyCallback) on_verify_completed, + verify_data); + } else { + g_warning ("Loading prints failed with error %s", error->message); + g_main_loop_quit (verify_data->loop); + } +} + +static void +start_verification (FpDevice *dev, VerifyData *verify_data) +{ + if (fp_device_has_storage (dev)) { + g_print ("Creating finger template, using device storage...\n"); + fp_device_list_prints (dev, NULL, + (GAsyncReadyCallback) on_list_completed, + verify_data); + } else { + g_print ("Loading previously enrolled right index finger data...\n"); + g_autoptr(FpPrint) verify_print; + + verify_print = print_data_load (dev, FP_FINGER_RIGHT_INDEX); + + if (!verify_print) { + g_warning ("Failed to load fingerprint data"); + g_warning ("Did you remember to enroll your right index " + "finger first?"); + g_main_loop_quit (verify_data->loop); + return; + } + + g_print ("Print loaded. Time to verify!\n"); + fp_device_verify (dev, verify_print, NULL, + (GAsyncReadyCallback) on_verify_completed, + verify_data); + } +} + +static void +on_device_opened (FpDevice *dev, GAsyncResult *res, void *user_data) +{ + VerifyData *verify_data = user_data; + g_autoptr(GError) error = NULL; + + if (!fp_device_open_finish (dev, res, &error)) { + g_warning ("Failed to open device: %s", error->message); + g_main_loop_quit (verify_data->loop); + return; + } + + g_print ("Opened device. "); + + start_verification (dev, verify_data); } int main(void) { - int r = 1; - struct fp_dscv_dev *ddev; - struct fp_dscv_dev **discovered_devs; - struct fp_dev *dev; - struct fp_print_data *data; + g_autoptr (FpContext) ctx = NULL; + g_autoptr (VerifyData) verify_data = NULL; + GPtrArray *devices; + FpDevice *dev; setenv ("G_MESSAGES_DEBUG", "all", 0); setenv ("LIBUSB_DEBUG", "3", 0); - r = fp_init(); - if (r < 0) { - fprintf(stderr, "Failed to initialize libfprint\n"); - exit(1); + ctx = fp_context_new (); + + devices = fp_context_get_devices (ctx); + if (!devices) { + g_warning("Impossible to get devices"); + return EXIT_FAILURE; } - discovered_devs = fp_discover_devs(); - if (!discovered_devs) { - fprintf(stderr, "Could not discover devices\n"); - goto out; - } - - ddev = discover_device(discovered_devs); - if (!ddev) { - fprintf(stderr, "No devices detected.\n"); - goto out; - } - - dev = fp_dev_open(ddev); - fp_dscv_devs_free(discovered_devs); + dev = discover_device (devices); if (!dev) { - fprintf(stderr, "Could not open device.\n"); - goto out; + g_warning("No devices detected."); + return EXIT_FAILURE; } - printf("Opened device. Loading previously enrolled right index finger " - "data...\n"); + verify_data = g_new0 (VerifyData, 1); + verify_data->ret_value = EXIT_FAILURE; + verify_data->loop = g_main_loop_new (NULL, FALSE); - data = print_data_load(dev, RIGHT_INDEX); - if (!data) { - fprintf(stderr, "Failed to load fingerprint, error %d\n", r); - fprintf(stderr, "Did you remember to enroll your right index finger " - "first?\n"); - goto out_close; - } + fp_device_open (dev, NULL, (GAsyncReadyCallback) on_device_opened, + verify_data); - printf("Print loaded. Time to verify!\n"); - do { - char buffer[20]; + g_main_loop_run (verify_data->loop); - verify(dev, data); - printf("Verify again? [Y/n]? "); - fgets(buffer, sizeof(buffer), stdin); - if (buffer[0] != '\n' && buffer[0] != 'y' && buffer[0] != 'Y') - break; - } while (1); - - fp_print_data_free(data); -out_close: - fp_dev_close(dev); -out: - fp_exit(); - return r; + return verify_data->ret_value; }