Remove generated files
[framework/connectivity/libgphoto2.git] / packaging / generic / print-camera-list.c
1 /* print-camera-list - print libgphoto2 camera list in different formats
2  *
3  * Copyright © 2002,2005 Hans Ulrich Niedermann <hun@users.sourceforge.net>
4  * Portions Copyright © 2002 Lutz Müller <lutz@users.sourceforge.net>
5  * Portions Copyright © 2005 Julien BLACHE <jblache@debian.org>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful, 
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of 
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details. 
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23
24 #define GP_USB_HOTPLUG_SCRIPT "usbcam"
25
26 /* Not sure whether this is the best possible name */
27 #define ARGV0 "print-camera-list"
28
29 #define HELP_TEXT \
30 ARGV0 " - print libgphoto2 camera list in different formats" \
31 "\n" \
32 "Syntax:\n" \
33 "    " ARGV0 " [<options>] <FORMAT> [<format specific arguments>]\n" \
34 "\n" \
35 "Options:\n" \
36 " --debug          print all debug output\n" \
37 " --help           print this help message\n" \
38 " --verbose        also print comments with camera model names\n" \
39 "\n" \
40 ARGV0 " prints the camera list in the specified format FORMAT on stdout.\n" \
41 "\n" \
42 "All other messages are printed on stderr. In case of any error, the \n" \
43 "program aborts regardless of data printed on stdout and returns a non-zero\n" \
44 "status code.\n"
45
46 #ifdef _GPHOTO2_SAMSUNG_PATCH_
47 #define RM_CMD                  "/bin/rm -rf"
48 #define MKDIR_CMD               "/bin/mkdir -p"
49 #define TOUCH_CMD               "/bin/touch"
50 #define CAMERA_NAME_INFO_PATH   "/tmp/camera"
51 #define SYS_EVENT_PATH          "/usr/bin/sys_event"
52 #define SYS_CAMERA_ADD_EVENT    "device_camera_add"
53 #define SYS_CAMERA_REMOVE_EVENT "device_camera_remove"
54 #endif
55
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <sys/time.h>
59 #include <time.h>
60 #include <string.h>
61
62 #include <gphoto2/gphoto2-camera.h>
63 #include <gphoto2/gphoto2-port-log.h>
64
65 /* for detailed version message */
66 #include <gphoto2/gphoto2-version.h>
67
68 #include "config.h"
69
70 #ifndef TRUE
71 #define TRUE  (0==0)
72 #endif
73 #ifndef FALSE
74 #define FALSE (0!=0)
75 #endif
76
77 typedef struct {
78         char *name;
79         GPVersionFunc version_func;
80 } module_version;
81
82 const module_version module_versions[] = {
83         { "libgphoto2", gp_library_version },
84         { "libgphoto2_port", gp_port_library_version },
85         { NULL, NULL }
86 };
87
88 typedef char *string_array_t[];
89
90 typedef string_array_t *string_array_p;
91
92 typedef struct {
93         int number_of_cameras;
94         int add_comments;
95         int argc;
96         string_array_p argv;
97 } func_params_t;
98
99
100 typedef int (* begin_func_t)  (const func_params_t *params,
101                                void **data);
102 typedef int (* middle_func_t)  (const func_params_t *params,
103                                void **data);
104 typedef int (* camera_func_t) (const func_params_t *params, 
105                                const int i,
106                                const int total,
107                                const CameraAbilities *ca,
108                                void *data);
109 typedef int (* end_func_t)    (const func_params_t *params,
110                                void *data);
111
112
113 #define GP_USB_HOTPLUG_MATCH_VENDOR_ID          0x0001
114 #define GP_USB_HOTPLUG_MATCH_PRODUCT_ID         0x0002
115
116 #define GP_USB_HOTPLUG_MATCH_INT_CLASS          0x0080
117 #define GP_USB_HOTPLUG_MATCH_INT_SUBCLASS       0x0100
118 #define GP_USB_HOTPLUG_MATCH_INT_PROTOCOL       0x0200
119
120
121 #ifdef __GNUC__
122 #define CR(result) \
123         do { \
124                 int r = (result); \
125                 if (r < 0) { \
126                         fprintf(stderr, ARGV0 ": " \
127                                 "Fatal error running `%s'.\n" \
128                                 "Aborting.\n", #result ); \
129                         return (r); \
130                 } \
131         } while (0)
132 #else /* !__GNUC__ */
133 #define CR(result) \
134         do { \
135                 int r = (result); \
136                 if (r < 0) { \
137                         fprintf(stderr, ARGV0 ": " \
138                                 "Fatal error detected, aborting.\n"); \
139                         return (r); \
140                 } \
141         } while (0)
142 #endif /* __GNUC__ */
143
144 #define FATAL(msg...) \
145         do { \
146                 fprintf(stderr, ARGV0 ": Fatal: " msg); \
147                 fprintf(stderr, "\n"); \
148                 exit(13); \
149         } while (0)
150
151 #define ASSERT(cond) \
152         do { \
153                 if (!(cond)) { \
154                         FATAL("Assertion failed: %s", #cond); \
155                 } \
156         } while (0)
157
158
159 /* print_version_comment
160  * Print comment to output containing information on library versions
161  *
162  * out        the file to write the comment to
163  * startline  printed at the start of each line, e.g. "# " or "    | "
164  * endline    printed as the end of each line,   e.g. "\n" or "\n"
165  * firstline  printed before first line,         e.g. NULL or "<!--+\n"
166  * lastline   printed after last line,           e.g. "\n" or "    +-->\n"
167  */
168
169 static void
170 print_version_comment(FILE *out,
171                       const char *startline, const char *endline,
172                       const char *firstline, const char *lastline)
173 {
174         unsigned int n;
175         if (out == NULL) { FATAL("Internal error: NULL out in print_version_comment()"); }
176         if (firstline != NULL) { fputs(firstline, out); }
177         fputs(startline, out);
178         fputs("Created from this library:", out);
179         fputs(endline, out);
180         for (n=0; (module_versions[n].name != NULL) && (module_versions[n].version_func != NULL); n++) {
181                 const char *name = module_versions[n].name;
182                 GPVersionFunc func = module_versions[n].version_func;
183                 const char **v = func(GP_VERSION_SHORT);
184                 unsigned int i;
185                 if (!v) { continue; }
186                 if (!v[0]) { continue; }
187                 if (startline != NULL) { fputs(startline, out); }
188                 fputs("  ", out);
189                 fprintf(out,"%-15s %-14s ", name, v[0]);
190                 for (i=1; v[i] != NULL; i++) {
191                         fputs(v[i], out);
192                         if (v[i+1] != NULL) {
193                                 fputs(", ", out);
194                         }
195                 }
196                 if (endline != NULL) { fputs(endline, out); }
197         }
198         if (lastline != NULL) { fputs(lastline, out); }
199 }
200
201
202 static int
203 hotplug_begin_func (const func_params_t *params, void **data)
204 {
205         if (params->add_comments) {
206                 printf("# linux-hotplug configuration file "
207                        "for libgphoto2 supported devices\n");
208                 print_version_comment(stdout, "# ", "\n", NULL, "#\n");
209         }
210         return 0;
211 }
212
213
214 /* print_usb_usermap
215  *
216  * Print out lines that can be included into usb.usermap 
217  * - for all cams supported by our instance of libgphoto2.
218  *
219  * usb.usermap is a file used by
220  * Linux Hotplug http://linux-hotplug.sourceforge.net/
221  */
222
223 static int
224 hotplug_camera_func (const func_params_t *params, 
225                      const int i,
226                      const int total,
227                      const CameraAbilities *a,
228                      void *data)
229 {
230         int flags = 0;
231         int class = 0, subclass = 0, proto = 0;
232         int usb_vendor = 0, usb_product = 0;
233         const char *usermap_script = 
234                 ((*params->argv)[0] != NULL)
235                 ?((*params->argv)[0])
236                 :(GP_USB_HOTPLUG_SCRIPT);
237
238         if (a->port & GP_PORT_USB) {
239                 if (a->usb_vendor) { /* usb product id may be zero! */
240                         class = 0;
241                         subclass = 0;
242                         proto = 0;
243                         flags = (GP_USB_HOTPLUG_MATCH_VENDOR_ID 
244                                  | GP_USB_HOTPLUG_MATCH_PRODUCT_ID);
245                         usb_vendor = a->usb_vendor;
246                         usb_product = a->usb_product;
247                 } else if ((a->usb_class) && (a->usb_class != 666)) {
248                         class = a->usb_class;
249                         subclass = a->usb_subclass;
250                         proto = a->usb_protocol;
251                         flags = GP_USB_HOTPLUG_MATCH_INT_CLASS;
252                         if (subclass != -1)
253                                 flags |= GP_USB_HOTPLUG_MATCH_INT_SUBCLASS;
254                         else
255                                 subclass = 0;
256                         if (proto != -1)
257                                 flags |= GP_USB_HOTPLUG_MATCH_INT_PROTOCOL;
258                         else
259                                 proto = 0;
260                         usb_vendor = 0;
261                         usb_product = 0;
262                 }
263         } else {
264                 /* not a USB camera */
265                 return 0;
266         }
267
268         if (params->add_comments) {
269                 printf ("# %s\n", 
270                         a->model);
271         }
272         /* The first 3 lone bytes are the device class.
273          * the second 3 lone bytes are the interface class.
274          * for PTP we want the interface class.
275          */
276         printf ("%-20s "
277                 "0x%04x      0x%04x   0x%04x    0x0000       "
278                 "0x0000      0x00         0x00            "
279                 "0x00            0x%02x            0x%02x               "
280                 "0x%02x               0x00000000\n",
281                 usermap_script, flags, 
282                 a->usb_vendor, a->usb_product,
283                 class, subclass, proto);
284         return 0;
285 }
286
287
288 static void
289 print_headline (void)
290 {
291         printf("No.|%-20s|%-20s|%s\n",
292                "camlib",
293                "driver name",
294                "camera model");
295 }
296
297
298 static void
299 print_hline (void)
300 {
301         printf("---+%-20s+%-20s+%s\n",
302                "--------------------",
303                "--------------------",
304                "-------------------------------------------");
305 }
306
307
308 static int
309 human_begin_func (const func_params_t *params, void **data)
310 {
311         print_hline();
312         print_headline();
313         print_hline();
314         return 0;
315 }
316
317
318 static int
319 human_end_func (const func_params_t *params, void *data)
320 {
321         print_hline();
322         print_headline();
323         return 0;
324 }
325
326
327 /** C equivalent of basename(1) */
328 static const char *
329 path_basename (const char *pathname)
330 {
331         char *result, *tmp;
332         /* remove path part from camlib name */
333         for (result=tmp=(char *)pathname; (*tmp!='\0'); tmp++) {
334                 if ((*tmp == gp_system_dir_delim) 
335                     && (*(tmp+1) != '\0')) {
336                         result = tmp+1;
337                 }
338         }
339         return (const char *)result;
340 }
341
342
343 static int
344 human_camera_func (const func_params_t *params, 
345                    const int i, 
346                    const int total,
347                    const CameraAbilities *a,
348                    void *data)
349 {
350         const char *camlib_basename;
351         camlib_basename = path_basename(a->library);
352         printf("%3d|%-20s|%-20s|%s\n",
353                i+1, 
354                camlib_basename,
355                a->id,
356                a->model);
357         return 0;
358 }
359
360
361 static int
362 idlist_camera_func (const func_params_t *params, 
363                     const int i, 
364                     const int total,
365                     const CameraAbilities *a,
366                     void *data)
367 {
368         if (a->usb_vendor) { /* usb product id may be zero! */
369                 printf("%04x:%04x %s\n",
370                        a->usb_vendor,
371                        a->usb_product,
372                        a->model);
373         }
374         return 0;
375 }
376
377
378 typedef enum {
379                 UDEV_PRE_0_98 = 0,
380                 UDEV_0_98 = 1,
381                 UDEV_136 = 2
382 } udev_version_t;
383
384 static const StringFlagItem udev_version_t_map[] = {
385         { "pre-0.98", UDEV_PRE_0_98 },
386         { "0.98", UDEV_0_98 },
387         { "136", UDEV_136 },
388         { NULL, 0 }
389 };
390
391
392 typedef struct {
393         udev_version_t version;
394         char *mode;
395         char *owner;
396         char *group;
397         char *script;
398         const char *begin_string;
399         const char *usbcam_string;
400         const char *usbdisk_string;
401 } udev_persistent_data_t;
402
403
404 static void
405 udev_parse_params (const func_params_t *params, void **data)
406 {
407         /* Note: 2 lines because we need to use || ... having them on the same
408          * line would mean &&.
409          */
410         static const char * const begin_strings[] = {
411                 /* UDEV_PRE_0_98 */
412                 "ACTION!=\"add\", GOTO=\"libgphoto2_rules_end\"\n"
413                 "BUS!=\"usb_device\", GOTO=\"libgphoto2_usb_end\"\n\n",
414                 /* UDEV_0_98 */
415                 "ACTION!=\"add\", GOTO=\"libgphoto2_rules_end\"\n"
416                 "SUBSYSTEM!=\"usb|usb_device\", GOTO=\"libgphoto2_usb_end\"\n\n",
417                 /* UDEV_136 */
418                 "ACTION!=\"add\", GOTO=\"libgphoto2_rules_end\"\n"
419                 "SUBSYSTEM!=\"usb\", GOTO=\"libgphoto2_usb_end\"\n"
420                 "ENV{DEVTYPE}!=\"usb_device\", GOTO=\"libgphoto2_usb_end\"\n\n"
421                 "ENV{ID_USB_INTERFACES}==\"\", IMPORT{program}=\"usb_id --export %%p\"\n"
422                 /* ignore mass storage class having devices in mark-up */
423                 "ENV{ID_USB_INTERFACES}==\"*:08*:*\", GOTO=\"libgphoto2_usb_end\"\n"
424                 /* shortcut the most common camera driver, ptp class, so we avoid parsing 1000
425                  * more rules . It will be completed in udev_begin_func() */
426                 "ENV{ID_USB_INTERFACES}==\"*:060101:*\", ENV{ID_GPHOTO2}=\"1\", ENV{GPHOTO2_DRIVER}=\"PTP\", "
427         };
428         static const char * const usbcam_strings[] = {
429                 /* UDEV_PRE_0_98 */
430                 "SYSFS{idVendor}==\"%04x\", SYSFS{idProduct}==\"%04x\"",
431                 /* UDEV_0_98 */
432                 "ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\"",
433                 /* UDEV_136 */
434                 "ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ENV{ID_GPHOTO2}=\"1\", ENV{GPHOTO2_DRIVER}=\"proprietary\""
435         };
436         static const char * const usbdisk_strings[] = {
437                 /* UDEV_PRE_0_98 */
438                 "KERNEL==\"%s\", SYSFS{idVendor}==\"%04x\", SYSFS{idProduct}==\"%04x\"",
439                 /* UDEV_0_98 */
440                 "KERNEL==\"%s\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\"",
441                 /* UDEV_136 */
442                 "KERNEL==\"%s\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ENV{ID_GPHOTO2}=\"1\", ENV{GPHOTO2_DRIVER}=\"proprietary\""
443         };
444         udev_persistent_data_t *pdata;
445         pdata = calloc(1, sizeof(udev_persistent_data_t));
446         pdata->version = UDEV_0_98;
447         ASSERT(data != NULL);
448         *data = (void *) pdata;
449         if (1) {
450                 int i;
451                 char *key = NULL, *val = NULL;
452                 for (i=0; ((key=(*params->argv)[i])   != NULL) && 
453                           ((val=(*params->argv)[i+1]) != NULL); i+=2) {
454                         if (0) {
455                                 /* nothing */
456                         } else if (strcmp("script", key)==0) {
457                                 pdata->script = val;
458                         } else if (strcmp("owner", key)==0) {
459                                 pdata->owner = val;
460                         } else if (strcmp("group", key)==0) {
461                                 pdata->group = val;
462                         } else if (strcmp("version", key)==0) {
463                                 unsigned int *ver = &pdata->version;
464                                 if (gpi_string_to_enum(val, ver,
465                                                        udev_version_t_map)) {
466                                         FATAL("Unrecognized udev version: \"%s\"", val);
467                                 }
468                         } else if (strcmp("mode", key)==0) {
469                                 pdata->mode = val;
470                         } else {
471                                 FATAL("Unknown key argument: %s", key);
472                         }
473                 }
474                 if ((key != NULL) && (val == NULL)) {
475                         FATAL("Single argument remaining; need pairs of key and value");
476                 }
477         }
478         if ((0==0)
479             && (pdata->mode == NULL)
480             && (pdata->group == NULL)
481             && (pdata->owner == NULL)
482             && (pdata->script == NULL)
483             && (pdata->version <= UDEV_0_98)) {
484                 FATAL("Either <script> or <mode,group,owner> parameters must be given.");
485         }
486         if ((pdata->script != NULL) && (pdata->mode != NULL 
487                                         || pdata->group != NULL 
488                                         || pdata->owner != NULL)) {
489                 FATAL("The <script> parameter conflicts with the <mode,group,owner> parameters.");
490         }
491
492         pdata->begin_string = begin_strings[pdata->version];
493         pdata->usbcam_string = usbcam_strings[pdata->version];
494         pdata->usbdisk_string = usbdisk_strings[pdata->version];
495 }
496
497
498 static const char *
499 get_version_str(udev_version_t version)
500 {
501         return gpi_enum_to_string(version, udev_version_t_map);
502 }
503
504
505 static int
506 udev_begin_func (const func_params_t *params, void **data)
507 {
508         udev_parse_params(params, data);
509         if (1) {
510                 udev_persistent_data_t *pdata = (udev_persistent_data_t *) (*data);
511                 const char *version_str = get_version_str(pdata->version);
512                 ASSERT(pdata != NULL);
513                 ASSERT(version_str != NULL);
514                 printf ("# udev rules file for libgphoto2 devices (for udev %s version)\n",
515                         version_str);
516                 print_version_comment(stdout, "# ", "\n", NULL, "#\n");
517                 printf ("# this file is autogenerated, local changes will be LOST on upgrades\n");
518                 printf (pdata->begin_string);
519
520                 if (pdata->version == UDEV_136) {
521                         if (pdata->mode != NULL || pdata->owner != NULL || pdata->group != NULL) {
522                                 if (pdata->mode != NULL) {
523                                         printf("MODE=\"%s\", ", pdata->mode);
524                                 }
525                                 if (pdata->owner != NULL) {
526                                         printf("OWNER=\"%s\", ", pdata->owner);
527                                 }
528                                 if (pdata->group != NULL) {
529                                         printf("GROUP=\"%s\", ", pdata->group);
530                                 }
531                         }
532
533                         printf ("GOTO=\"libgphoto2_usb_end\"\n\n");
534                 }
535         }
536         return 0;
537 }
538
539
540 static int
541 udev_middle_func (const func_params_t *params, void **data)
542 {
543         printf ("\nLABEL=\"libgphoto2_usb_end\"\n\n");
544         return 0;
545 }
546
547
548 static int
549 udev_end_func (const func_params_t *params, void *data)
550 {
551         if (data != NULL) {
552                 free(data);
553         }
554
555 #ifdef _GPHOTO2_SAMSUNG_PATCH_
556         printf ("\n");
557         printf ("ENV{ID_GPHOTO2}==\"1\", RUN+=\"%s %s\"\n",
558                         RM_CMD, CAMERA_NAME_INFO_PATH);
559         printf ("ENV{ID_GPHOTO2}==\"1\", RUN+=\"%s %s\"\n",
560                         MKDIR_CMD, CAMERA_NAME_INFO_PATH);
561         printf ("ENV{ID_GPHOTO2}==\"1\", RUN+=\"%s %s/$env{ID_MODEL}\"\n",
562                         TOUCH_CMD, CAMERA_NAME_INFO_PATH);
563         printf ("ENV{ID_GPHOTO2}==\"1\", RUN+=\"%s %s\"\n",
564                         SYS_EVENT_PATH, SYS_CAMERA_ADD_EVENT);
565
566         printf ("\nLABEL=\"libgphoto2_rules_end\"\n");
567
568         printf ("ACTION==\"remove\", ENV{DEVTYPE}==\"usb_interface\", ENV{INTERFACE}==\"6/[0-9]/[0-9]\", RUN+=\"%s %s\"\n",
569                         RM_CMD, CAMERA_NAME_INFO_PATH);
570         printf ("ACTION==\"remove\", ENV{DEVTYPE}==\"usb_interface\", ENV{INTERFACE}==\"6/[0-9]/[0-9]\", RUN+=\"%s %s\"\n",
571                         SYS_EVENT_PATH, SYS_CAMERA_REMOVE_EVENT);
572
573         return 0;
574 #endif
575         printf ("\nLABEL=\"libgphoto2_rules_end\"\n");
576         return 0;
577 }
578
579
580
581
582 static int
583 udev_camera_func (const func_params_t *params, 
584                   const int i,
585                   const int total,
586                   const CameraAbilities *a,
587                   void *data)
588 {
589         int flags = 0;
590         int class = 0, subclass = 0, proto = 0;
591         int usb_vendor = 0, usb_product = 0;
592         int has_valid_rule = 0;
593         udev_persistent_data_t *pdata = (udev_persistent_data_t *) data;
594         ASSERT(pdata != NULL);
595
596         if (!(a->port & GP_PORT_USB))
597                 return 0;
598
599         if (a->usb_vendor) { /* usb product id may be zero! */
600                 class = 0;
601                 subclass = 0;
602                 proto = 0;
603                 flags = (GP_USB_HOTPLUG_MATCH_VENDOR_ID 
604                          | GP_USB_HOTPLUG_MATCH_PRODUCT_ID);
605                 usb_vendor = a->usb_vendor;
606                 usb_product = a->usb_product;
607         } else {
608                 if (a->usb_class) {
609                         class = a->usb_class;
610                         subclass = a->usb_subclass;
611                         proto = a->usb_protocol;
612                         flags = GP_USB_HOTPLUG_MATCH_INT_CLASS;
613                         if (subclass != -1)
614                                 flags |= GP_USB_HOTPLUG_MATCH_INT_SUBCLASS;
615                         else
616                                 subclass = 0;
617                         if (proto != -1)
618                                 flags |= GP_USB_HOTPLUG_MATCH_INT_PROTOCOL;
619                         else
620                                 proto = 0;
621                         usb_vendor = 0;
622                         usb_product = 0;
623                 }
624         }
625
626         if (params->add_comments) {
627                 printf ("# %s\n", a->model);
628         }
629
630         if (flags & GP_USB_HOTPLUG_MATCH_INT_CLASS) {
631                 if ((flags & (GP_USB_HOTPLUG_MATCH_INT_CLASS|GP_USB_HOTPLUG_MATCH_INT_SUBCLASS|GP_USB_HOTPLUG_MATCH_INT_PROTOCOL)) == (GP_USB_HOTPLUG_MATCH_INT_CLASS|GP_USB_HOTPLUG_MATCH_INT_SUBCLASS|GP_USB_HOTPLUG_MATCH_INT_PROTOCOL)) {
632                         if (pdata->version == UDEV_136) {
633                                 printf("ENV{ID_USB_INTERFACES}==\"*:%02d%02d%02d:*\", ENV{ID_GPHOTO2}=\"1\", ENV{GPHOTO2_DRIVER}=\"PTP\"", class, subclass, proto);
634                         } else {
635                                 printf("PROGRAM=\"check-ptp-camera %02d/%02d/%02d\"", class, subclass, proto);
636                         }
637                         has_valid_rule = 1;
638                 } else {
639                         if (class == 666) {
640                                 printf("# not working yet: PROGRAM=\"check-mtp-device\", ");
641                                 has_valid_rule = 1;
642                         } else {
643                                 fprintf(stderr, "unhandled interface match flags %x\n", flags);
644                         }
645                 }
646         } else {
647                 if (flags & GP_USB_HOTPLUG_MATCH_VENDOR_ID) {
648                         printf (pdata->usbcam_string, a->usb_vendor, a->usb_product);
649                         has_valid_rule = 1;
650                 } else {
651                         fprintf (stderr, "Error: Trying to output device %d/%d with incorrect match flags.\n",
652                                 a->usb_vendor, a->usb_product
653                         );
654                 }
655         }
656         if (has_valid_rule != 0) {
657                 if (a->device_type & GP_DEVICE_AUDIO_PLAYER)
658                         printf(", ENV{ID_MEDIA_PLAYER}=\"1\"");
659
660                 if (pdata->script != NULL || pdata->mode != NULL || pdata->owner != NULL || pdata->group != NULL)
661                         printf(", ");
662
663                 if (pdata->script != NULL) {
664                         printf("RUN+=\"%s\"\n", pdata->script);
665                 } else if (pdata->mode != NULL || pdata->owner != NULL || pdata->group != NULL) {
666                         if (pdata->mode != NULL) {
667                                 printf("MODE=\"%s\"", pdata->mode);
668                                 if (pdata->owner != NULL || pdata->group != NULL) {
669                                         printf(", ");
670                                 }
671                         }
672                         if (pdata->owner != NULL) {
673                                 printf("OWNER=\"%s\"", pdata->owner);
674                                 if (pdata->group != NULL) {
675                                         printf(", ");
676                                 }
677                         }
678                         if (pdata->group != NULL) {
679                                 printf("GROUP=\"%s\"", pdata->group);
680                         }
681                         printf("\n");
682                 } else {
683                         printf("\n");
684                         if (pdata->version < UDEV_136)
685                                 FATAL("udev_camera_func(): illegal branch");
686                 }
687         }
688         return 0;
689 }
690
691
692 static int
693 udev_camera_func2 (const func_params_t *params, 
694                    const int i,
695                    const int total,
696                    const CameraAbilities *a,
697                    void *data)
698 {
699         int has_valid_rule = 0;
700         udev_persistent_data_t *pdata = (udev_persistent_data_t *) data;
701         ASSERT(pdata != NULL);
702
703         if (a->port & GP_PORT_USB_DISK_DIRECT) {
704                 printf (pdata->usbdisk_string, "sd[a-z]*",
705                         a->usb_vendor, a->usb_product);
706                 has_valid_rule = 1;
707         }
708         if (a->port & GP_PORT_USB_SCSI) {
709                 printf (pdata->usbdisk_string, "sg[0-9]*",
710                         a->usb_vendor, a->usb_product);
711                 has_valid_rule = 1;
712         }
713         if (has_valid_rule != 0) {
714                 if (pdata->script != NULL || pdata->mode != NULL || pdata->owner != NULL || pdata->group != NULL)
715                         printf(", ");
716
717                 if (pdata->script != NULL) {
718                         printf("RUN+=\"%s\"\n", pdata->script);
719                 } else if (pdata->mode != NULL || pdata->owner != NULL || pdata->group != NULL) {
720                         if (pdata->mode != NULL) {
721                                 printf("MODE=\"%s\"", pdata->mode);
722                                 if (pdata->owner != NULL || pdata->group != NULL) {
723                                         printf(", ");
724                                 }
725                         }
726                         if (pdata->owner != NULL) {
727                                 printf("OWNER=\"%s\"", pdata->owner);
728                                 if (pdata->group != NULL) {
729                                         printf(", ");
730                                 }
731                         }
732                         if (pdata->group != NULL) {
733                                 printf("GROUP=\"%s\"", pdata->group);
734                         }
735                         printf("\n");
736                 } else {
737                         printf("\n");
738                         if (pdata->version < UDEV_136)
739                                 FATAL("udev_camera_func(): illegal branch");
740                 }
741         }
742
743         return 0;
744 }
745
746
747 static int
748 empty_begin_func (const func_params_t *params, void **data)
749 {
750         return 0;
751 }
752
753 static int
754 empty_end_func (const func_params_t *params, void *data)
755 {
756         return 0;
757 }
758
759
760
761 #ifdef ENABLED_GP2DDB
762
763 static int
764 ddb_begin_func (const func_params_t *params, void **data)
765 {
766         printf("# Beginning of gphoto2 device database (PRE-ALPHA format!)\n\n");
767         print_version_comment(stdout, "# ", "\n", NULL, "\n");
768         return 0;
769 }
770
771
772 static int
773 ddb_end_func (const func_params_t *params, void *data)
774 {
775         printf("# End of gphoto2 device database (PRE-ALPHA format!).\n");
776         return 0;
777 }
778
779
780 static void
781 ddb_list_out_func(const char *str, void *data)
782 {
783         int *first = (int *)data;
784         printf("%s %s", (*first)?"":",", str);
785         *first = 0;
786 }
787
788
789 static void
790 ddb_delayed_head(const func_params_t *params, 
791                  const int i,
792                  const int total,
793                  const CameraAbilities *a,
794                  const char *camlib_basename)
795 {
796         int first = 1;
797         printf("# This is a PRE-ALPHA data format. Do not use it.\n\n");
798
799         if (params->add_comments) {
800                 printf ("# %s\n", a->model);
801         }
802
803         printf("# automatically generated camera record %d/%d\n", i+1, total);
804         printf("device \"%s\" {\n", a->model);
805
806         printf("    device_type");
807         first = 1;
808         gpi_enum_to_string(a->device_type, 
809                            gpi_gphoto_device_type_map,
810                            ddb_list_out_func,
811                            (void *) &first);
812         printf(";\n");
813
814         printf("    driver \"%s\";\n", camlib_basename);
815
816         printf("    driver_status");
817         first = 1;
818         gpi_enum_to_string(a->status, 
819                            gpi_camera_driver_status_map,
820                            ddb_list_out_func,
821                            (void *) &first);
822         printf(";\n");
823
824         printf("    operations");
825         first = 1;
826         gpi_flags_to_string_list(a->operations, 
827                                  gpi_camera_operation_map,
828                                  ddb_list_out_func,
829                                  (void *) &first);
830         printf(";\n");
831
832         printf("    file_operations");
833         first = 1;
834         gpi_flags_to_string_list(a->file_operations, 
835                                  gpi_file_operation_map,
836                                  ddb_list_out_func,
837                                  (void *) &first);
838         printf(";\n");
839
840         printf("    folder_operations");
841         first = 1;
842         gpi_flags_to_string_list(a->folder_operations, 
843                                  gpi_folder_operation_map,
844                                  ddb_list_out_func,
845                                  (void *) &first);
846         printf(";\n");
847 }
848
849
850 static int
851 ddb_camera_func (const func_params_t *params, 
852                  const int i,
853                  const int total,
854                  const CameraAbilities *a,
855                  void *data)
856 {
857         const char *camlib_basename = path_basename(a->library);
858         int head_printed = 0;
859 #define DELAYED_HEAD() \
860         do { \
861                 if (!head_printed) {                          \
862                         ddb_delayed_head(params, i, total,    \
863                                          a, camlib_basename); \
864                         head_printed = 1;                     \
865                 } \
866         } while (0)
867
868         if ((a->port & GP_PORT_SERIAL)) {
869                 int first = 1;
870                 DELAYED_HEAD();
871                 printf("    interface serial {\n");
872                 if (a->speed[0] != 0) {
873                         unsigned int i;
874                         printf("        speeds");
875                         for (i=0; 
876                              ((i < (sizeof(a->speed)/sizeof(a->speed[0]))) &&
877                               (a->speed[i] != 0)); i++) {
878                                 printf("%s %d", (first)?"":",", a->speed[i]);
879                                 first = 0;
880                         }
881                         printf(";\n");
882                 }
883                 printf("    };\n");
884         }
885
886         if ((a->port & GP_PORT_USB)) {
887                 if (a->usb_vendor) { 
888                         /* usb product id may have the legal value zero! */
889                         DELAYED_HEAD();
890                         printf("    interface usb {\n");
891                         printf("        vendor  0x%04x;\n", a->usb_vendor);
892                         printf("        product 0x%04x;\n", a->usb_product);
893                         printf("    };\n");
894                 }
895                 if ((a->usb_class) && (a->usb_class != 666)) {
896                         DELAYED_HEAD();
897                         printf("    interface usb {\n");
898                         printf("        class 0x%02x;\n", a->usb_class);
899                         if (a->usb_subclass != -1) {
900                                 printf("        subclass 0x%02x;\n", a->usb_subclass);
901                         }
902                         if (a->usb_protocol != -1) {
903                                 printf("        protocol 0x%02x;\n", a->usb_protocol);
904                         }
905                         printf("    };\n");
906                 }
907         }
908         if ((a->port & GP_PORT_DISK)) {
909                 DELAYED_HEAD();
910                 printf("    interface disk;\n");
911         }
912         if ((a->port & GP_PORT_PTPIP)) {
913                 DELAYED_HEAD();
914                 printf("    interface ptpip;\n");
915         }
916 #undef DELAYED_HEAD
917
918         if (head_printed) {
919                 printf("    # driver_options {\n");
920                 printf("    #      option \"foo\";\n");
921                 printf("    #      option \"bar\" \"bla\";\n");
922                 printf("    # };\n");
923
924                 printf("}; # %s\n\n", a->model);
925         }
926         return 0;
927 }
928
929 #endif /* ENABLED_GP2DDB */
930
931
932 /* print_fdi_map
933  *
934  * Print FDI Device Information file for HAL with information on 
935  * all cams supported by our instance of libgphoto2.
936  *
937  * For specs, look around on http://freedesktop.org/ and search
938  * for FDI on the HAL pages.
939  */
940
941 static int
942 fdi_begin_func (const func_params_t *params, void **data)
943 {
944         printf("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?> <!-- -*- SGML -*- -->\n");
945         printf("<!-- This file was generated by %s - - fdi -->\n",
946                "libgphoto2 " ARGV0);
947         print_version_comment(stdout, "    | ", "\n", "<!--+\n", "    +-->\n");
948         printf("<deviceinfo version=\"0.2\">\n");
949         printf(" <device>\n");
950         printf("  <match key=\"info.subsystem\" string=\"usb\">\n");
951         return 0;
952 }
953
954
955 static int
956 fdi_camera_func (const func_params_t *params, 
957                  const int i,
958                  const int total,
959                  const CameraAbilities *a,
960                  void *data)
961 {
962         char    *s, *d, model[256];
963
964         if (!(a->port & GP_PORT_USB))
965                 return 0;
966
967         s = (char *) a->model;
968         d = model;
969         while (*s && (d < &d[sizeof(d)-1])) {
970                 if (*s == '&') {
971                         strcpy(d,"&amp;");
972                         d += strlen(d);
973                 } else {
974                         *d++ = *s;
975                 }
976                 s++;
977         }
978         *d = '\0';
979
980         if ((a->port & GP_PORT_USB)) {
981                 if (a->usb_vendor == 0x07b4 && (a->usb_product == 0x105 || a->usb_product == 0x109) ) {
982                         /* Marcus says: The Olympus Sierra/Storage dual mode camera firmware.
983                          * Some HAL using software gets deeply confused by this being here
984                          * and also detected as mass storage elsewhere, so blacklist
985                          * it here.
986                          */
987                         return 0;
988                 }
989                 if (a->usb_vendor) { /* usb product id might be 0! */
990                         printf("   <match key=\"usb.vendor_id\" int=\"%d\">\n", a->usb_vendor);
991                         printf("    <match key=\"usb.product_id\" int=\"%d\">\n", a->usb_product);
992                         if (a->usb_vendor == 0x05ac) { /* Apple iPhone, PTP user. */
993                                 printf("     <match key=\"usb.interface.class\" int=\"6\">\n");
994                                 printf("      <match key=\"usb.interface.subclass\" int=\"1\">\n");
995                                 printf("       <match key=\"usb.interface.protocol\" int=\"1\">\n");
996                         }
997                         if (a->device_type & GP_DEVICE_AUDIO_PLAYER) {
998                                 printf("     <merge key=\"info.category\" type=\"string\">portable_audio_player</merge>\n");
999                                 printf("     <addset key=\"info.capabilities\" type=\"strlist\">portable_audio_player</addset>\n");
1000                                 printf("     <merge key=\"portable_audio_player.access_method\" type=\"string\">user</merge>\n");
1001                                 printf("     <merge key=\"portable_audio_player.type\" type=\"string\">mtp</merge>\n");
1002                                 
1003                                 /* FIXME: needs true formats ... But all of them can do MP3 */
1004                                 printf("     <append key=\"portable_audio_player.output_formats\" type=\"strlist\">audio/mpeg</append>\n");
1005                         } else {
1006                                 printf("     <merge key=\"info.category\" type=\"string\">camera</merge>\n");
1007                                 printf("     <addset key=\"info.capabilities\" type=\"strlist\">camera</addset>\n");
1008
1009                                 /* HACK alert ... but the HAL / gnome-volume-manager guys want that */
1010                                 if (NULL!=strstr(a->library,"ptp"))
1011                                         printf("     <merge key=\"camera.access_method\" type=\"string\">ptp</merge>\n");
1012                                 else
1013                                         printf("     <merge key=\"camera.access_method\" type=\"string\">proprietary</merge>\n");
1014                         }
1015                         /* leave them here even for audio players */
1016                         printf("     <merge key=\"camera.libgphoto2.name\" type=\"string\">%s</merge>\n", model);
1017                         printf("     <merge key=\"camera.libgphoto2.support\" type=\"bool\">true</merge>\n");
1018                         if (a->usb_vendor == 0x05ac) { /* Apple iPhone */
1019                                 printf("       </match>\n");
1020                                 printf("      </match>\n");
1021                                 printf("     </match>\n");
1022                         }
1023                         printf("    </match>\n");
1024                         printf("   </match>\n");
1025                         
1026                 } else if ((a->usb_class) && (a->usb_class != 666)) {
1027                         printf("   <match key=\"usb.interface.class\" int=\"%d\">\n", a->usb_class);
1028                         printf("    <match key=\"usb.interface.subclass\" int=\"%d\">\n", a->usb_subclass);
1029                         printf("     <match key=\"usb.interface.protocol\" int=\"%d\">\n", a->usb_protocol);
1030                         printf("      <merge key=\"info.category\" type=\"string\">camera</merge>\n");
1031                         printf("      <addset key=\"info.capabilities\" type=\"strlist\">camera</addset>\n");
1032                         if (a->usb_class == 6) {
1033                                 printf("      <merge key=\"camera.access_method\" type=\"string\">ptp</merge>\n");
1034                         } else {
1035                                 if (a->usb_class == 8) {
1036                                         printf("      <merge key=\"camera.access_method\" type=\"string\">storage</merge>\n");
1037                                 } else {
1038                                         printf("      <merge key=\"camera.access_method\" type=\"string\">proprietary</merge>\n");
1039                                 }
1040                         }
1041                         printf("      <merge key=\"camera.libgphoto2.name\" type=\"string\">%s</merge>\n", model);
1042                         printf("      <merge key=\"camera.libgphoto2.support\" type=\"bool\">true</merge>\n");
1043                         printf("     </match>\n");
1044                         printf("    </match>\n");
1045                         printf("   </match>\n");
1046                 }
1047         } /* camera has USB connection */
1048         return 0;
1049 }
1050
1051 static int
1052 fdi_end_func (const func_params_t *params, void *data)
1053 {
1054         printf("  </match>\n");
1055         printf(" </device>\n");
1056         printf("</deviceinfo>\n");
1057         return 0;
1058 }
1059
1060
1061 /* print_fdi_device_map
1062  *
1063  * Print FDI Device Information file for HAL with information on 
1064  * all cams supported by our instance of libgphoto2.
1065  *
1066  * For specs, look around on http://freedesktop.org/ and search
1067  * for FDI on the HAL pages.
1068  */
1069
1070 static int
1071 fdi_device_begin_func (const func_params_t *params, void **data)
1072 {
1073         printf("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?> <!-- -*- SGML -*- -->\n");
1074         printf("<!-- This file was generated by %s - - fdi-device -->\n",
1075                "libgphoto2 " ARGV0);
1076         print_version_comment(stdout, "    | ", "\n", "<!--+\n", "    +-->\n");
1077         printf("<deviceinfo version=\"0.2\">\n");
1078         printf(" <device>\n");
1079         printf("  <match key=\"info.subsystem\" string=\"usb\">\n");
1080         return 0;
1081 }
1082
1083
1084 static int
1085 fdi_device_camera_func (const func_params_t *params, 
1086                         const int i,
1087                         const int total,
1088                         const CameraAbilities *a,
1089                         void *data)
1090 {
1091         char    *s, *d, model[256];
1092
1093         if (!(a->port & GP_PORT_USB))
1094                 return 0;
1095
1096         s = (char *) a->model;
1097         d = model;
1098         while (*s && (d < &d[sizeof(d)-1])) {
1099                 if (*s == '&') {
1100                         strcpy(d,"&amp;");
1101                         d += strlen(d);
1102                 } else {
1103                         *d++ = *s;
1104                 }
1105                 s++;
1106         }
1107         *d = '\0';
1108
1109         if ((a->port & GP_PORT_USB)) {
1110
1111                 if (a->usb_vendor == 0x07b4 && (a->usb_product == 0x105 || a->usb_product == 0x109)) {
1112                         /* Marcus says: The Olympus Sierra/Storage dual mode camera firmware.
1113                          * Some HAL using software gets deeply confused by this being here
1114                          * and also detected as mass storage elsewhere, so blacklist
1115                          * it here.
1116                          */
1117                         return 0;
1118                 }
1119                 if (a->usb_vendor) { /* usb product id might be 0! */
1120                         /* do not set category. We don't really know what this device really is.
1121                          * But we do now that is capable of being a camera, so add to capabilities
1122                          */
1123                         printf("   <match key=\"usb_device.vendor_id\" int=\"%d\">\n", a->usb_vendor);
1124                         printf("    <match key=\"usb_device.product_id\" int=\"%d\">\n", a->usb_product);
1125                         if (params->add_comments) {
1126                                 printf("     <!-- %s -->\n", a->model);
1127                         }
1128                         if (a->device_type & GP_DEVICE_AUDIO_PLAYER)
1129                                 printf("     <append key=\"info.capabilities\" type=\"strlist\">portable_audio_player</append>\n");
1130                         else
1131                                 printf("     <append key=\"info.capabilities\" type=\"strlist\">camera</append>\n");
1132                         printf("    </match>\n");
1133                         printf("   </match>\n");
1134                 }
1135 #if 0
1136                 /* would need to be able to merge upwards ... but cannot currently */
1137                 else if ((a->usb_class) && (a->usb_class != 666)) {
1138                         printf("   <match key=\"usb.interface.class\" int=\"%d\">\n", a->usb_class);
1139                         printf("    <match key=\"usb.interface.subclass\" int=\"%d\">\n", a->usb_subclass);
1140                         printf("     <match key=\"usb.interface.protocol\" int=\"%d\">\n", a->usb_protocol);
1141                         printf("      <append key=\"info.capabilities\" type=\"strlist\">camera</append>\n");
1142                         printf("     </match>\n");
1143                         printf("    </match>\n");
1144                         printf("   </match>\n");
1145                 }
1146 #endif
1147         }
1148         return 0;
1149 }
1150
1151 static int
1152 fdi_device_end_func (const func_params_t *params, void *data)
1153 {
1154         printf("  </match>\n");
1155         printf(" </device>\n");
1156         printf("</deviceinfo>\n");
1157         return 0;
1158 }
1159
1160
1161 /* time zero for debug log time stamps */
1162 struct timeval glob_tv_zero = { 0, 0 };
1163
1164 static void
1165 debug_func (GPLogLevel level, const char *domain, const char *format,
1166             va_list args, void *data)
1167 {
1168         struct timeval tv;
1169         gettimeofday (&tv,NULL);
1170         fprintf (stderr, "%li.%06li %s(%i): ", 
1171                  tv.tv_sec - glob_tv_zero.tv_sec, 
1172                  (1000000 + tv.tv_usec - glob_tv_zero.tv_usec) % 1000000,
1173                  domain, level);
1174         vfprintf (stderr, format, args);
1175         fprintf (stderr, "\n");
1176 }
1177
1178
1179 typedef struct {
1180         char *name;
1181         char *descr;
1182         char *help;
1183         char *paramdescr;
1184         begin_func_t begin_func;
1185         camera_func_t camera_func;
1186         middle_func_t middle_func;
1187         camera_func_t camera_func2;
1188         end_func_t end_func;
1189 } output_format_t;
1190
1191
1192 static int
1193 iterate_camera_list (const int add_comments, 
1194                      const output_format_t *format, 
1195                      string_array_p argv)
1196 {
1197         int number_of_cameras;
1198         CameraAbilitiesList *al;
1199         CameraAbilities a;
1200         func_params_t params;
1201         void *data = NULL;
1202         int ret;
1203
1204         CR (gp_abilities_list_new (&al));
1205         ret = gp_abilities_list_load (al, NULL); /* use NULL context */
1206         if (ret < GP_OK) {
1207                 gp_abilities_list_free (al);
1208                 return ret;
1209         }
1210         number_of_cameras = gp_abilities_list_count (al);
1211         if (number_of_cameras < GP_OK) {
1212                 gp_abilities_list_free (al);
1213                 return ret;
1214         }
1215
1216         params.add_comments = add_comments;
1217         params.number_of_cameras = number_of_cameras;
1218         params.argv = argv;
1219
1220         if (format->begin_func != NULL) {
1221                 format->begin_func(&params, &data);
1222         }
1223
1224         if (format->camera_func != NULL) {
1225                 int i;
1226                 for (i = 0; i < number_of_cameras; i++) {
1227                         ret = gp_abilities_list_get_abilities (al, i, &a);
1228                         if (ret < GP_OK)
1229                                 continue;
1230                         format->camera_func(&params, i, number_of_cameras, &a, data);
1231                 }
1232         }
1233
1234         if (format->middle_func != NULL) {
1235                 format->middle_func(&params, &data);
1236         }
1237
1238         if (format->camera_func2 != NULL) {
1239                 int i;
1240                 for (i = 0; i < number_of_cameras; i++) {
1241                         ret = gp_abilities_list_get_abilities (al, i, &a);
1242                         if (ret < GP_OK)
1243                                 continue;
1244                         format->camera_func2(&params, i, number_of_cameras, &a, data);
1245                 }
1246         }
1247
1248         if (format->end_func != NULL) {
1249                 format->end_func(&params, data);
1250         }
1251         CR (gp_abilities_list_free (al));
1252         return 0;
1253 }
1254
1255
1256 /* FIXME: Add hyperlink to format documentation. */
1257
1258 /** list of supported output formats */
1259 static const output_format_t formats[] = {
1260         {"human-readable",
1261          "human readable list of cameras",
1262          NULL,
1263          NULL,
1264          human_begin_func,
1265          human_camera_func,
1266          NULL,
1267          NULL,
1268          human_end_func
1269         },
1270         {"usb-usermap",
1271          "usb.usermap include file for linux-hotplug",
1272          "If no <scriptname> is given, uses the script name "
1273          "\"" GP_USB_HOTPLUG_SCRIPT "\".\n"
1274          "        Put this into /etc/hotplug/usb/<scriptname>.usermap",
1275          "<NAME_OF_HOTPLUG_SCRIPT>",
1276          hotplug_begin_func,
1277          hotplug_camera_func,
1278          NULL,
1279          NULL,
1280          empty_end_func
1281         },
1282         {"hal-fdi",
1283          "fdi file for HAL",
1284          "Put it into /usr/share/hal/fdi/information/20thirdparty/10-camera-libgphoto2.fdi",
1285          NULL,
1286          fdi_begin_func,
1287          fdi_camera_func,
1288          NULL,
1289          NULL,
1290          fdi_end_func
1291         },
1292         {"hal-fdi-device",
1293          "fdi device file for HAL",
1294          "Put it into /usr/share/hal/fdi/information/20thirdparty/10-camera-libgphoto2-device.fdi",
1295          NULL,
1296          fdi_device_begin_func,
1297          fdi_device_camera_func,
1298          NULL,
1299          NULL,
1300          fdi_device_end_func
1301         },
1302         {"udev-rules",
1303          "udev rules file",
1304          "For modes \"pre-0.98\" and \"0.98\" (and later), put it into\n"
1305          "        /etc/udev/rules.d/90-libgphoto2.rules, set file mode, owner, group\n"
1306          "        or add script to run. This rule files also uses the\n"
1307          "        check-ptp-camera script included in libgphoto2 source. Either put it to\n"
1308          "        /lib/udev/check-ptp-camera or adjust the path in the generated rules file.\n"
1309          "        If you give a script parameter, the mode, owner, group parameters will be ignored.\n"
1310          "        For mode \"136\" put it into /lib/udev/rules.d/40-libgphoto2.rules;\n"
1311          "        you can still use mode/owner/group, but the preferred mode of operation\n"
1312          "        is to use udev-extras for dynamic access permissions.\n",
1313          "[script <PATH_TO_SCRIPT>|version <version>|mode <mode>|owner <owner>|group <group>]*",
1314          udev_begin_func, 
1315          udev_camera_func,
1316          udev_middle_func,
1317          udev_camera_func2,
1318          udev_end_func
1319         },
1320         {"idlist",
1321          "list of IDs and names",
1322          "grep for an ID to find the device name",
1323          NULL,
1324          empty_begin_func, 
1325          idlist_camera_func,
1326          NULL,
1327          NULL,
1328          empty_end_func
1329         },
1330 #ifdef ENABLED_GP2DDB
1331         {"gp2ddb",
1332          "gphoto2 device database (PRE-ALPHA)",
1333          "PRE-ALPHA test stage, do not use for production! Machine parseable.",
1334          NULL,
1335          ddb_begin_func,
1336          ddb_camera_func,
1337          NULL,
1338          NULL,
1339          ddb_end_func
1340         },
1341 #endif
1342         {NULL, NULL, NULL, NULL, 
1343          NULL, NULL, NULL}
1344 };
1345
1346
1347 /** \brief print list of output formats with descriptions
1348  */
1349
1350 static int print_format_list(const output_format_t *formats)
1351 {
1352         int i;
1353         printf("List of output formats:\n");
1354         for (i=0; formats[i].name != NULL; ++i) {
1355                 if (formats[i].paramdescr != NULL) {
1356                         printf("    %s %s\n        %s\n", 
1357                                formats[i].name,
1358                                formats[i].paramdescr,
1359                                formats[i].descr);
1360                 } else {
1361                         printf("    %s\n        %s\n", 
1362                                formats[i].name, formats[i].descr);
1363                 }
1364                 if (formats[i].help != NULL) {
1365                         printf("        %s\n", formats[i].help);
1366                 }
1367         }
1368         return 0;
1369 }
1370
1371
1372 /** \brief print program help
1373  */
1374
1375 static int print_help()
1376 {
1377         puts(HELP_TEXT);
1378         return print_format_list(formats);
1379 }
1380
1381
1382 /** \brief main program: parse and check arguments, then delegate the work
1383  */
1384
1385 int main(int argc, char *argv[])
1386 {
1387         int add_comments = FALSE; /* whether to add cam model as a comment */
1388         int debug_mode = FALSE;   /* whether we should output debug messages */
1389
1390         char *format_name = NULL; /* name of desired output format */
1391         int format_index;         /* index number of (desired) output format */
1392         static char *fmt_argv[16]; /* format specific arguments */
1393
1394         int i, j;
1395         unsigned int ui;
1396
1397         /* initialize parameters to NULL */
1398         for (ui=0; ui<(sizeof(fmt_argv)/sizeof(fmt_argv[0])); ui++) {
1399                 fmt_argv[ui] = NULL;
1400         }
1401
1402         /* walk through command line arguments until format is encountered */
1403         for (i=1; (i<argc) && (format_name == NULL); i++) {
1404                 if (0 == strcmp(argv[i], "--verbose")) {
1405                         if (add_comments) {
1406                                 fprintf(stderr, "Error: duplicate parameter: option \"%s\"\n", argv[i]);
1407                                 return 1;
1408                         }
1409                         add_comments = TRUE;
1410                 } else if (0 == strcmp(argv[i], "--debug")) {
1411                         if (debug_mode) {
1412                                 fprintf(stderr, "Error: duplicate parameter: option \"%s\"\n", argv[i]);
1413                                 return 1;
1414                         }
1415                         debug_mode = TRUE;
1416                         /* now is time zero for debug log time stamps */
1417                         gettimeofday (&glob_tv_zero, NULL);
1418                         gp_log_add_func (GP_LOG_ALL, debug_func, NULL);
1419                 } else if (0 == strcmp(argv[i], "--help")) {
1420                         return print_help();
1421                 } else {
1422                         format_name = argv[i];
1423                 }
1424         }
1425
1426         /* walk through output format list, searching for the requested one */
1427         if (format_name == NULL) {
1428                 return print_help();
1429         }
1430         format_index=0;
1431         while (formats[format_index].name != NULL) {
1432                 if (strcmp(formats[format_index].name, format_name) == 0) {
1433                         break;
1434                 }
1435                 format_index++;
1436         }
1437         if ((formats[format_index].name == NULL) || 
1438             (strcmp(formats[format_index].name, format_name) != 0)) {
1439                 return print_help();
1440         }
1441
1442         /* copy remaining arguments */
1443         for (j=i; 
1444              (j<argc) && ((j-i)<(int)((sizeof(fmt_argv)/sizeof(fmt_argv[0]))-1)); 
1445              j++) {
1446                 fmt_argv[j-i] = argv[j];
1447         }
1448
1449         /* execute the work using the given parameters*/
1450         return iterate_camera_list(add_comments, &formats[format_index],
1451                                    &fmt_argv
1452                 );
1453 }
1454
1455 /*
1456  * Local Variables:
1457  * c-file-style:"linux"
1458  * indent-tabs-mode:t
1459  * End:
1460  */