/* print-camera-list - print libgphoto2 camera list in different formats * * Copyright 2002,2005 Hans Ulrich Niedermann * Portions Copyright 2002 Lutz Mueller * Portions Copyright 2005 Julien BLACHE * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA */ #define GP_USB_HOTPLUG_SCRIPT "usbcam" /* Not sure whether this is the best possible name */ #define ARGV0 "print-camera-list" #define HELP_TEXT \ ARGV0 " - print libgphoto2 camera list in different formats" \ "\n" \ "Syntax:\n" \ " " ARGV0 " [] []\n" \ "\n" \ "Options:\n" \ " --debug print all debug output\n" \ " --help print this help message\n" \ " --verbose also print comments with camera model names\n" \ "\n" \ ARGV0 " prints the camera list in the specified format FORMAT on stdout.\n" \ "\n" \ "All other messages are printed on stderr. In case of any error, the \n" \ "program aborts regardless of data printed on stdout and returns a non-zero\n" \ "status code.\n" #include #include #include #include #include #include #include /* for detailed version message */ #include #include "config.h" #ifndef TRUE #define TRUE (0==0) #endif #ifndef FALSE #define FALSE (0!=0) #endif typedef struct { char *name; GPVersionFunc version_func; } module_version; const module_version module_versions[] = { { "libgphoto2", gp_library_version }, { "libgphoto2_port", gp_port_library_version }, { NULL, NULL } }; typedef char *string_array_t[]; typedef string_array_t *string_array_p; typedef struct { int number_of_cameras; int add_comments; int argc; string_array_p argv; } func_params_t; typedef int (* begin_func_t) (const func_params_t *params, void **data); typedef int (* middle_func_t) (const func_params_t *params, void **data); typedef int (* camera_func_t) (const func_params_t *params, const int i, const int total, const CameraAbilities *ca, void *data); typedef int (* end_func_t) (const func_params_t *params, void *data); #define GP_USB_HOTPLUG_MATCH_VENDOR_ID 0x0001 #define GP_USB_HOTPLUG_MATCH_PRODUCT_ID 0x0002 #define GP_USB_HOTPLUG_MATCH_INT_CLASS 0x0080 #define GP_USB_HOTPLUG_MATCH_INT_SUBCLASS 0x0100 #define GP_USB_HOTPLUG_MATCH_INT_PROTOCOL 0x0200 #ifdef __GNUC__ #define CR(result) \ do { \ int r = (result); \ if (r < 0) { \ fprintf(stderr, ARGV0 ": " \ "Fatal error running `%s'.\n" \ "Aborting.\n", #result ); \ return (r); \ } \ } while (0) #else /* !__GNUC__ */ #define CR(result) \ do { \ int r = (result); \ if (r < 0) { \ fprintf(stderr, ARGV0 ": " \ "Fatal error detected, aborting.\n"); \ return (r); \ } \ } while (0) #endif /* __GNUC__ */ #define FATAL(msg...) \ do { \ fprintf(stderr, ARGV0 ": Fatal: " msg); \ fprintf(stderr, "\n"); \ exit(13); \ } while (0) #define ASSERT(cond) \ do { \ if (!(cond)) { \ FATAL("Assertion failed: %s", #cond); \ } \ } while (0) /* print_version_comment * Print comment to output containing information on library versions * * out the file to write the comment to * startline printed at the start of each line, e.g. "# " or " | " * endline printed as the end of each line, e.g. "\n" or "\n" * firstline printed before first line, e.g. NULL or "\n" */ static void print_version_comment(FILE *out, const char *startline, const char *endline, const char *firstline, const char *lastline) { unsigned int n; if (out == NULL) { FATAL("Internal error: NULL out in print_version_comment()"); } if (firstline != NULL) { fputs(firstline, out); } if (startline != NULL) { fputs(startline, out); } fputs("Created from this library:", out); if (endline != NULL) { fputs(endline, out); } for (n=0; (module_versions[n].name != NULL) && (module_versions[n].version_func != NULL); n++) { const char *name = module_versions[n].name; GPVersionFunc func = module_versions[n].version_func; const char **v = func(GP_VERSION_SHORT); unsigned int i; if (!v) { continue; } if (!v[0]) { continue; } if (startline != NULL) { fputs(startline, out); } fputs(" ", out); fprintf(out,"%-15s %-14s ", name, v[0]); for (i=1; v[i] != NULL; i++) { fputs(v[i], out); if (v[i+1] != NULL) { fputs(", ", out); } } if (endline != NULL) { fputs(endline, out); } } if (lastline != NULL) { fputs(lastline, out); } } static int hotplug_begin_func (const func_params_t *params, void **data) { if (params->add_comments) { printf("# linux-hotplug configuration file " "for libgphoto2 supported devices\n"); print_version_comment(stdout, "# ", "\n", NULL, "#\n"); } return 0; } /* print_usb_usermap * * Print out lines that can be included into usb.usermap * - for all cams supported by our instance of libgphoto2. * * usb.usermap is a file used by * Linux Hotplug http://linux-hotplug.sourceforge.net/ */ static int hotplug_camera_func (const func_params_t *params, const int i, const int total, const CameraAbilities *a, void *data) { int flags = 0; int class = 0, subclass = 0, proto = 0; const char *usermap_script = ((*params->argv)[0] != NULL) ?((*params->argv)[0]) :(GP_USB_HOTPLUG_SCRIPT); if (a->port & GP_PORT_USB) { if (a->usb_vendor) { /* usb product id may be zero! */ class = 0; subclass = 0; proto = 0; flags = (GP_USB_HOTPLUG_MATCH_VENDOR_ID | GP_USB_HOTPLUG_MATCH_PRODUCT_ID); } else if ((a->usb_class) && (a->usb_class != 666)) { class = a->usb_class; subclass = a->usb_subclass; proto = a->usb_protocol; flags = GP_USB_HOTPLUG_MATCH_INT_CLASS; if (subclass != -1) flags |= GP_USB_HOTPLUG_MATCH_INT_SUBCLASS; else subclass = 0; if (proto != -1) flags |= GP_USB_HOTPLUG_MATCH_INT_PROTOCOL; else proto = 0; } } else { /* not a USB camera */ return 0; } if (params->add_comments) { printf ("# %s\n", a->model); } /* The first 3 lone bytes are the device class. * the second 3 lone bytes are the interface class. * for PTP we want the interface class. */ printf ("%-20s " "0x%04x 0x%04x 0x%04x 0x0000 " "0x0000 0x00 0x00 " "0x00 0x%02x 0x%02x " "0x%02x 0x00000000\n", usermap_script, flags, a->usb_vendor, a->usb_product, class, subclass, proto); return 0; } static void print_headline (void) { printf("No.|%-20s|%-20s|%s\n", "camlib", "driver name", "camera model"); } static void print_hline (void) { printf("---+%-20s+%-20s+%s\n", "--------------------", "--------------------", "-------------------------------------------"); } static int human_begin_func (const func_params_t *params, void **data) { print_hline(); print_headline(); print_hline(); return 0; } static int human_end_func (const func_params_t *params, void *data) { print_hline(); print_headline(); return 0; } /** C equivalent of basename(1) */ static const char * path_basename (const char *pathname) { char *result, *tmp; /* remove path part from camlib name */ for (result=tmp=(char *)pathname; (*tmp!='\0'); tmp++) { if ((*tmp == gp_system_dir_delim) && (*(tmp+1) != '\0')) { result = tmp+1; } } return (const char *)result; } static int human_camera_func (const func_params_t *params, const int i, const int total, const CameraAbilities *a, void *data) { const char *camlib_basename; camlib_basename = path_basename(a->library); printf("%3d|%-20s|%-20s|%s\n", i+1, camlib_basename, a->id, a->model); return 0; } static int idlist_camera_func (const func_params_t *params, const int i, const int total, const CameraAbilities *a, void *data) { if (a->usb_vendor) { /* usb product id may be zero! */ printf("%04x:%04x %s\n", a->usb_vendor, a->usb_product, a->model); } return 0; } typedef enum { UDEV_PRE_0_98 = 0, UDEV_0_98 = 1, UDEV_136 = 2, UDEV_175 = 3, UDEV_201 = 4 } udev_version_t; static const StringFlagItem udev_version_t_map[] = { { "pre-0.98", UDEV_PRE_0_98 }, { "0.98", UDEV_0_98 }, { "136", UDEV_136 }, { "175", UDEV_175 }, { "201", UDEV_201 }, { NULL, 0 } }; typedef struct { udev_version_t version; char *mode; char *owner; char *group; char *script; const char *begin_string; const char *usbcam_string; const char *usbdisk_string; } udev_persistent_data_t; static void udev_parse_params (const func_params_t *params, void **data) { /* Note: 2 lines because we need to use || ... having them on the same * line would mean &&. */ static const char * const begin_strings[] = { /* UDEV_PRE_0_98 */ "ACTION!=\"add|bind\", GOTO=\"libgphoto2_rules_end\"\n" "BUS!=\"usb_device\", GOTO=\"libgphoto2_usb_end\"\n\n", /* UDEV_0_98 */ "ACTION!=\"add|bind\", GOTO=\"libgphoto2_rules_end\"\n" "SUBSYSTEM!=\"usb|usb_device\", GOTO=\"libgphoto2_usb_end\"\n\n", /* UDEV_136 */ "ACTION!=\"add|bind\", GOTO=\"libgphoto2_rules_end\"\n" "SUBSYSTEM!=\"usb\", GOTO=\"libgphoto2_usb_end\"\n" "ENV{DEVTYPE}!=\"usb_device\", GOTO=\"libgphoto2_usb_end\"\n\n" "ENV{ID_USB_INTERFACES}==\"\", IMPORT{program}=\"usb_id --export %%p\"\n" /* ignore mass storage class having devices in mark-up */ "ENV{ID_USB_INTERFACES}==\"*:08*:*\", GOTO=\"libgphoto2_usb_end\"\n" /* shortcut the most common camera driver, ptp class, so we avoid parsing 1000 * more rules . It will be completed in udev_begin_func() */ "ENV{ID_USB_INTERFACES}==\"*:060101:*\", ENV{ID_GPHOTO2}=\"1\", ENV{GPHOTO2_DRIVER}=\"PTP\", ", /* UDEV_175 */ "ACTION!=\"add|bind\", GOTO=\"libgphoto2_rules_end\"\n" "SUBSYSTEM!=\"usb\", GOTO=\"libgphoto2_usb_end\"\n" "ENV{DEVTYPE}!=\"usb_device\", GOTO=\"libgphoto2_usb_end\"\n\n" "ENV{ID_USB_INTERFACES}==\"\", IMPORT{builtin}=\"usb_id\"\n" /* ignore mass storage class having devices in mark-up */ "ENV{ID_USB_INTERFACES}==\"*:08*:*\", GOTO=\"libgphoto2_usb_end\"\n" /* shortcut the most common camera driver, ptp class, so we avoid parsing 1000 * more rules . It will be completed in udev_begin_func() */ "ENV{ID_USB_INTERFACES}==\"*:060101:*\", ENV{ID_GPHOTO2}=\"1\", ENV{GPHOTO2_DRIVER}=\"PTP\", ", /* UDEV_201 ... regular stuff is done via hwdb, only scsi generic here. */ "ACTION!=\"add|bind\", GOTO=\"libgphoto2_rules_end\"\n" "SUBSYSTEM!=\"usb\", GOTO=\"libgphoto2_usb_end\"\n" "ENV{ID_USB_INTERFACES}==\"\", IMPORT{builtin}=\"usb_id\"\n" /* shortcut the most common camera driver, ptp class, so we avoid parsing 1000 * more rules . It will be completed in udev_begin_func() */ "ENV{ID_USB_INTERFACES}==\"*:060101:*\", ENV{ID_GPHOTO2}=\"1\", ENV{GPHOTO2_DRIVER}=\"PTP\", ", }; static const char * const usbcam_strings[] = { /* UDEV_PRE_0_98 */ "SYSFS{idVendor}==\"%04x\", SYSFS{idProduct}==\"%04x\"", /* UDEV_0_98 */ "ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\"", /* UDEV_136 */ "ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ENV{ID_GPHOTO2}=\"1\", ENV{GPHOTO2_DRIVER}=\"proprietary\"", /* UDEV_175 */ "ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ENV{ID_GPHOTO2}=\"1\", ENV{GPHOTO2_DRIVER}=\"proprietary\"", /* UDEV_201 */ "" }; static const char * const usbdisk_strings[] = { /* UDEV_PRE_0_98 */ "KERNEL==\"%s\", SYSFS{idVendor}==\"%04x\", SYSFS{idProduct}==\"%04x\"", /* UDEV_0_98 */ "KERNEL==\"%s\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\"", /* UDEV_136 */ "KERNEL==\"%s\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ENV{ID_GPHOTO2}=\"1\", ENV{GPHOTO2_DRIVER}=\"proprietary\"", /* UDEV_175 */ "KERNEL==\"%s\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ENV{ID_GPHOTO2}=\"1\", ENV{GPHOTO2_DRIVER}=\"proprietary\"", /* UDEV_201 */ "KERNEL==\"%s\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ENV{ID_GPHOTO2}=\"1\", ENV{GPHOTO2_DRIVER}=\"proprietary\"" }; udev_persistent_data_t *pdata; pdata = calloc(1, sizeof(udev_persistent_data_t)); pdata->version = UDEV_0_98; ASSERT(data != NULL); *data = (void *) pdata; if (1) { int i; char *key = NULL, *val = NULL; for (i=0; ((key=(*params->argv)[i]) != NULL) && ((val=(*params->argv)[i+1]) != NULL); i+=2) { if (0) { /* nothing */ } else if (strcmp("script", key)==0) { pdata->script = val; } else if (strcmp("owner", key)==0) { pdata->owner = val; } else if (strcmp("group", key)==0) { pdata->group = val; } else if (strcmp("version", key)==0) { unsigned int *ver = &pdata->version; if (gpi_string_to_enum(val, ver, udev_version_t_map)) { FATAL("Unrecognized udev version: \"%s\"", val); } } else if (strcmp("mode", key)==0) { pdata->mode = val; } else { FATAL("Unknown key argument: %s", key); } } if ((key != NULL) && (val == NULL)) { FATAL("Single argument remaining; need pairs of key and value"); } } if ((0==0) && (pdata->mode == NULL) && (pdata->group == NULL) && (pdata->owner == NULL) && (pdata->script == NULL) && (pdata->version <= UDEV_0_98)) { FATAL("Either