/* * command.c * * Copyright (c) 1999 Oliver Fromme * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)$Id: command.c,v 1.14 1999/12/19 07:14:01 olli Exp $ */ static const char cvsid[] = "@(#)$Id: command.c,v 1.14 1999/12/19 07:14:01 olli Exp $"; #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "command.h" #include "directory.h" #include "ophoto.h" #include "proto.h" typedef int (cmdfunc) (kodacon *, int, char **, int); typedef struct { char *name; /* name of command */ cmdfunc *func; /* command function */ int flag; /* command flags */ } usercommand; char *monthname[16] = { "???", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "???", "???", "???" }; /* * Parse a picture ID ("#XXX-YYYY") and create a pathname for * the camera's CompactFlash memory. * * The picture ID is displayed in the upper right corner when * you switch the camera to "review" mode. It consists of a * directory number (three decimal digits "XXX", starting at * 100) and a file number (up to four decimal digits "YYYY", * starting at 0001). * * Returns a pathname that looks like this: * /dcim/XXXdc240/dcp_YYYY.jpg */ static char * parse_picid (char *picid) { char *eptr; int x, y; /* Skip any initial hashes and dashes. */ if (*picid == '#') picid++; if (*picid == '-') picid++; y = 1; x = strtol(picid, &eptr, 10); if (!*eptr) { if (strlen(picid) == 7) { /* * Only one number with 7 digits specified. * Assume that the user omitted the '-'. */ y = x % 10000; x = x / 10000; } else { /* * Only one number specified. * Assume this is the file number, * and the directory number is 100. */ y = x; x = 100; } } else if (*eptr == '-') y = strtol(eptr + 1, &eptr, 10); if (*eptr) errx (EX_USAGE, "malformed picture ID \"%s\" (must be \"xxx-yyyy\")", picid); if (x < 100 || x > 999) errx (EX_USAGE, "illegal directory number %d " "(must be between 100 and 999)", x); if (x < 1 || x > 9999) errx (EX_USAGE, "illegal file number %d " "(must be between 1 and 9999)", y); asprintf (&eptr, "/dcim/%03ddc240/dcp_%04d.jpg", x, y); return eptr; } static char * make_local_name (char *camname, bool is_thumbnail) { char *cptr, *new; /* * Use the last pathname component as the local name. */ if ((cptr = strrchr(camname, '/'))) cptr++; else cptr = camname; if (is_thumbnail) { new = tmalloc(strlen(cptr) + 7); strcpy (new, "thumb_"); strcat (new, cptr); } else new = strdup(cptr); return new; } /* * Get pathname of the last taken picture. */ static char * lasttaken_name (kodacon *kc) { int result; char *cptr; if (kc->fd_in < 0) init_connection (kc); result = get_lasttaken(kc); if (verbosity > 1 || result < 0) print_debug (kc, result, "reading last taken picture"); cptr = (char *) kc->data; if (strncasecmp(cptr, "\\pccard\\", 8) == 0) cptr += 7; return canon_path(cptr); } /* * Get pathname of the last reviewed picture. */ static char * lastreviewed_name (kodacon *kc) { int result; char *cptr; if (kc->fd_in < 0) init_connection (kc); result = get_lastreviewed(kc); if (verbosity > 1 || result < 0) print_debug (kc, result, "reading last reviewed picture"); cptr = (char *) kc->data; if (strncasecmp(cptr, "\\pccard\\", 8) == 0) cptr += 7; return canon_path(cptr); } /* * Construct the destination file name. * If a directory is specified, append the default name. * Also handle the case if ``-'' is specified (stdout). */ static void set_locname (char *dirfile, char **deflt) { int dlen, remslash; struct stat stst; char *newname; /* * Check if the user specified ``-'' (stdout). */ if (strcmp(dirfile, "-") == 0) { if (*deflt) free (*deflt); *deflt = "-"; return; } /* * If an empty string was specified, use the default. */ if (!*dirfile) return; /* * Remove any trailing slashes. * If we removed any and then discover that there is * no directory with that name, an error is returned. */ remslash = 0; while ((dlen = strlen(dirfile)) > 1 && dirfile[dlen - 1] == '/') { dirfile[dlen - 1] = 0; remslash = 1; } /* * Check if this is a directory. */ if (stat(dirfile, &stst) < 0) { /* * Doesn't exist, so we assume that the * user wants to create a new file * (unless we removed a slash before). */ if (remslash) err (EX_CANTCREAT, "%s", dirfile); if (*deflt) free (*deflt); *deflt = strdup(dirfile); return; } if ((stst.st_mode & S_IFMT) != S_IFDIR) { /* * It's not a directory, so we assume that * the user wants to write to this file * (unless we removed a slash before). */ if (remslash) errx (EX_CANTCREAT, "%s/: not a directory", dirfile); if (*deflt) free (*deflt); *deflt = strdup(dirfile); return; } /* * It's a directory, so we append the * default filename. */ asprintf (&newname, "%s/%s", dirfile, *deflt ? *deflt : "foo"); if (*deflt) free (*deflt); *deflt = newname; } /* * Try to find an optimal HPBS (host packet buffer size) * for the given transfer size. The HPBS should be as * big as possible (0x8000 + 2 max.) to minimize packet * transfer overhead, but we should also try to minimize * unused space in the last packet of a transfer. */ static void optimize_hpbs (kodacon *kc, unsigned int xfersize) { int result; unsigned int piece; unsigned int numpackets; numpackets = (xfersize + 0x7fff) / 0x8000; piece = (xfersize + numpackets - 1) / numpackets + 2; if (piece < 0x0202) piece = 0x0202; result = set_hpbs(kc, piece); if (result < 0) print_debug (kc, result, "setting HPBS"); if (verbosity > 1) fprintf (stderr, "%s: file size %u, HPBS %u\n", me, xfersize, piece); } /* * Read a picture or thumbnail from the camera. */ static int cp_cmd (kodacon *kc, int parc, char **parv, int moveflag) { char *camname, *locname; directory_entry *dirent; int result, is_thumbnail, argsused; unsigned int jpegsize; if (parc > 0 && strcmp(parv[0], "-t") == 0) { is_thumbnail = 1; parc--; parv++; argsused = parc > 0 ? 2 : 1; } else { is_thumbnail = 0; argsused = parc > 0 ? 1 : 0; } if (parc < 1 || !**parv || strcmp(parv[0], "-") == 0) camname = lasttaken_name(kc); else if (strcmp(parv[0], "--") == 0) camname = lastreviewed_name(kc); else if (strpbrk(parv[0], "\\/")) camname = canon_path(parv[0]); else camname = parse_picid(parv[0]); locname = make_local_name(camname, is_thumbnail); if (verbosity > 2) fprintf (stderr, "%s: CF pathname: %s\n", me, camname); if (parc > 1) { set_locname (parv[1], &locname); argsused++; } if (verbosity > 2) fprintf (stderr, "%s: local name: %s\n", me, locname); if (kc->fd_in < 0) init_connection (kc); if (!(dirent = check_dir_entry(kc, camname, 0))) errx (EX_UNAVAILABLE, "Nonexistent file %s", camname); result = read_pictureinfo(kc, camname); if (verbosity > 1 || result < 0) print_debug (kc, result, "reading picture info"); if (is_thumbnail) jpegsize = big2ul(kc->data + 92); else jpegsize = big2ul(kc->data + 104); optimize_hpbs (kc, jpegsize); if (is_thumbnail) result = read_thumbnail(kc, camname, jpegsize); else result = read_file(kc, camname, jpegsize); if (verbosity > 1 || result < 0) print_debug (kc, result, "reading picture from camera"); if (save_file(locname, kc->data, jpegsize) < 0) err (EX_CANTCREAT, "saving file \"%s\", %d bytes", locname, jpegsize); if (verbosity > 1) fprintf (stderr, "%s: retrieved picture successfully\n", me); if (moveflag) { result = delete_file(kc, camname); if (verbosity > 1 || result < 0) print_debug (kc, result, "deleting file"); if (verbosity > 1) fprintf (stderr, "%s: deleted file successfully\n", me); } free (camname); free (locname); return argsused; } /* * List directory contents. */ static int ls_cmd (kodacon *kc, int parc, char **parv, int notused) { char *camname; directory_entry *dirent; int result, argsused; int numentries, i, crdate, crtime, numfiles; unsigned long totalsize; unsigned int filesize; byte *data; char name[13]; byte attr; argsused = 0; if (parc < 1 || !**parv || strcmp(parv[0], ".") == 0) camname = strdup("/dcim"); else camname = canon_path(parv[0]); if (verbosity > 2) fprintf (stderr, "%s: CF pathname: %s\n", me, camname); if (parc > 0) argsused++; if (kc->fd_in < 0) init_connection (kc); if (!(dirent = check_dir_entry(kc, camname, 1))) errx (EX_UNAVAILABLE, "Nonexistent directory %s", camname); result = read_directory(kc, camname); /*XXXXXX check file! */ if (verbosity > 1 || result < 0) print_debug (kc, result, "reading directory"); numentries = kc->data[0] << 8 | kc->data[1]; if (kc->verbosity >= 0) fprintf (stderr, "\n"); printf (" %s (%d entries):\n", camname, numentries); free (camname); totalsize = 0; numfiles = 0; for (i = 0; i < numentries; i++) { data = kc->data + i * 20 + 2; parse_fatname (name, (char *) data); attr = data[11]; crtime = data[12] << 8 | data[13]; crdate = data[14] << 8 | data[15]; filesize = big2ul(data + 16); printf ("%crw-rw-rw- 1 root wheel %7u %s %2d " "%02d:%02d:%02d %4d %s\n", (attr & 0x10) ? 'd' : '-', filesize, monthname[crdate >> 5 & 15], crdate & 31, crtime >> 11, crtime >> 5 & 63, (crtime & 31) * 2, (crdate >> 9) + 1980, name); totalsize += filesize; if (!(attr & 0x10)) numfiles++; } printf (" %ld bytes total in %d files\n", totalsize, numfiles); return argsused; } /* * Delete a file from the camera. */ static int rm_cmd (kodacon *kc, int parc, char **parv, int notused) { char *camname; directory_entry *dirent; int result; if (parc < 1) errx (EX_USAGE, "\rm\" command requires an argument"); if (!**parv || strcmp(parv[0], "-") == 0) camname = lasttaken_name(kc); else if (strcmp(parv[0], "--") == 0) camname = lastreviewed_name(kc); else if (strpbrk(parv[0], "\\/")) camname = canon_path(parv[0]); else camname = parse_picid(parv[0]); if (verbosity > 2) fprintf (stderr, "%s: CF pathname: %s\n", me, camname); if (!(dirent = check_dir_entry(kc, camname, 0))) errx (EX_UNAVAILABLE, "Nonexistent file %s", camname); if (kc->fd_in < 0) init_connection (kc); result = delete_file(kc, camname); /*XXXXXX check_dir_entry() */ if (verbosity > 1 || result < 0) print_debug (kc, result, "deleting file"); if (verbosity > 1) fprintf (stderr, "%s: deleted file successfully\n", me); free (camname); return 1; } /* * Capture a photo ("Cheese!") */ static int take_cmd (kodacon *kc, int parc, char **parv, int notused) { int result; if (kc->fd_in < 0) init_connection (kc); result = take_picture(kc); if (verbosity > 1 || result < 0) print_debug (kc, result, "taking picture"); return 0; } int parse_cardstatus (byte cs) { if (cs & 0x80) { printf ("CompactFlash memory card inserted. "); if (cs & 0x10) printf ("Card is unformatted"); else printf ("Card is formatted"); if (cs & 0x08) printf (" and opened.\n"); else printf (" and not opened.\n"); } else printf ("No memory card inserted.\n"); return (cs & 0x90) == 0x80; } /* * Retrieve memory card information. */ static int cardinfo_cmd (kodacon *kc, int parc, char **parv, int notused) { int result; unsigned long bytesfree; if (kc->fd_in < 0) init_connection (kc); result = get_cardstatus(kc); if (verbosity > 1 || result < 0) print_debug (kc, result, "reading card status"); if (parse_cardstatus(kc->data[0])) { bytesfree = big2ul(kc->data + 3); printf ("%lu bytes free (%.2f Mbytes)\n", bytesfree, (double) bytesfree / 1048576); } return 0; } /* * Retrieve camera status information. */ static int camerainfo_cmd (kodacon *kc, int parc, char **parv, int notused) { int result; unsigned int zoom; byte *d; char *s; char name[13]; if (kc->fd_in < 0) init_connection (kc); result = read_camerastatus(kc); if (verbosity > 1 || result < 0) print_debug (kc, result, "reading camera status"); d = kc->data; printf ("Camera type: "); switch (d[1]) { case 5: printf ("DC240"); break; case 6: printf ("DC280"); break; default: printf ("unknown (%d)", d[1]); } s = justify(strndup((char *) d + 28, 32)); printf (", camera ID: \"%s\"\n", s); free (s); printf ("Firmware version %d.%02x, 32bit ROM version %d.%02d, " "8bit ROM version %d.%02d\n", d[2], d[3], d[4], d[5], d[6], d[7]); printf ("Total number of pictures taken: %u, total number of " "flashes: %u\n", big2us(d + 66), big2us(d + 68)); printf ("Battery status: "); switch (d[8]) { case 0: printf ("OK"); break; case 1: printf ("weak"); break; case 2: printf ("empty"); break; default: printf ("unknown (%d)", d[8]); } switch (d[9]) { case 0: printf (", no AC adapter"); break; case 1: printf (", AC adapter in use"); break; default: printf (", unknown AC adapter flag (%d)", d[9]); } switch (d[10]) { case 0: printf (", flash is not charged\n"); break; case 1: printf (", flash is charged\n"); break; default: printf (", unknown flash flag (%d)\n", d[10]); } printf ("Video format: "); switch (d[12]) { case 0: printf ("NTSC"); break; case 1: printf ("PAL"); break; default: printf ("unknown (%d)", d[12]); } switch (d[71]) { case 0: printf (", beeps are off\n"); break; case 1: printf (", beeps are limited\n"); break; case 2: printf (", beeps are on\n"); break; default: printf (", unknown beep mode (%d)\n", d[72]); } parse_cardstatus(d[11]); s = justify(strndup((char *) d + 16, 11)); printf ("Card volume ID: \"%s\", %u pictures on card.\n", s, big2us(d + 14)); free (s); printf ("Remaining pics: %u (good quality), %u (better quality), %u " "(best quality)\n", big2us(d + 60), big2us(d + 62), big2us(d + 64)); switch (d[78]) { case 3: printf ("File type: JPEG/EXIF"); break; default: printf ("Unknown file type (%d)", d[78]); } switch (d[81]) { case 0: printf (", IP chain enabled"); break; default: printf (", invalid IP chain flag (%d)", d[81]); } switch (d[82]) { case 0: printf (", image complete\n"); break; default: printf (", invalid image complete flag (%d)\n", d[82]); } printf ("Resolution: "); switch ((int)d[79] + 0x100 * (int)d[1]) { case 0x500: printf ("640 x 480"); break; case 0x501: printf ("1280 x 960"); break; case 0x600: printf ("896 x 592"); break; case 0x601: printf ("1760 x 1168"); break; default: printf ("unknown (%d)", d[79]); } printf (", image quality: "); switch (d[80]) { case 1: printf ("best\n"); break; case 2: printf ("better\n"); break; case 3: printf ("good\n"); break; default: printf ("unknown (%d)\n", d[80]); } printf ("Shutter delay: "); switch (d[83]) { case 0: printf ("off"); break; case 1: printf ("on"); break; default: printf ("unknown (%d)", d[83]); } printf (", camera clock: %u/%d/%d %d:%02d:%02d.%03d\n", big2us(d + 88), d[90], d[91], d[92], d[93], d[94], d[95] * 10); printf ("Flash mode: "); switch (d[97]) { case 0: printf ("auto"); break; case 1: printf ("fill"); break; case 2: printf ("off"); break; case 3: printf ("red-eye"); break; default: printf ("unknown (%d)", d[97]); } printf (", white-balance mode: "); switch (d[103]) { case 0: printf ("auto\n"); break; case 1: printf ("fluorescent\n"); break; case 2: printf ("tungsten\n"); break; case 3: printf ("daylight/flash\n"); break; default: printf ("unknown (%d)\n", d[103]); } printf ("Exposure compensation: %.2f, auto-exposure mode: ", (double) big2ss(d + 98) / 100); switch (d[100]) { case 0: printf ("auto"); break; case 1: printf ("center-weight"); break; default: printf ("unknown (%d)", d[100]); } printf (", exposure lock: "); switch (d[152]) { case 0: printf ("off\n"); break; case 1: printf ("on\n"); break; default: printf ("unknown (%d)\n", d[152]); } printf ("Focus mode: "); switch (d[101]) { case 0: printf ("auto"); break; case 2: printf ("close-up"); break; case 3: printf ("infinity"); break; default: printf ("unknown (%d)", d[101]); } printf (", auto-focus mode: "); switch (d[102]) { case 2: printf ("spot\n"); break; default: printf ("unknown (%d)\n", d[102]); } zoom = big2ul(d + 104); printf ("Zoom magnification: %.2fx", (double) zoom / 100); if (zoom > 300) printf (" (3.00x optical x %.2fx digital)\n", (double) zoom / 300); else if (zoom > 100) printf (" (optical)\n"); else printf (" (none)\n"); printf ("Exposure mode: "); switch (d[129]) { case 0: printf ("auto (TTL on), normal"); break; default: printf ("unknown (%02xh)", d[129]); } printf (", exposure time: %.2fms\n", (double) big2ul(d + 132) / 100); printf ("Sharpness control: "); switch (d[129]) { case 0xff: printf ("soften"); break; case 0: printf ("normal"); break; case 1: printf ("sharpen"); break; default: printf ("unknown (%02xh)", d[129]); } printf (", aperture: f/%.2f\n", (double) big2us(d + 136) / 100); printf ("Date/time stamp: "); switch (d[139]) { case 0: printf ("off"); break; case 1: case 4: printf ("YYYY MM DD"); break; case 2: case 5: printf ("DD MM YYYY"); break; case 3: case 6: printf ("MM DD YYYY"); break; default: printf ("unknown (%d)", d[139]); } if (d[139] >= 4 && d[139] <= 6) printf (" hh:mm"); printf (", image effects:"); if (d[138] == 0) printf (" none\n"); else { if (d[138] & 1) printf (" grayscale"); if (d[138] & 2) printf (" sepia"); if (d[138] & 4) printf (" document"); if (d[138] & 8) { printf (" border (%s)", parse_fatname(name, (char *) d + 140)); } printf ("\n"); } return 0; } /* * Be nice to the poor user and give a little help. :) */ static int help_cmd (kodacon *kc, int parc, char **parv, int notused) { if (kc->fd_in >= 0) done_connection (kc); free (kc); fprintf (stderr, "--- %s command summary ---\n", me); fprintf (stderr, "help print this summary and exit\n"); fprintf (stderr, "take capture a photo (\"Cheese!\")\n"); fprintf (stderr, "cp copy picture from camera to host\n"); fprintf (stderr, "cp -t copy only thumbnail of picture\n"); fprintf (stderr, "mv move picture from camera to host\n"); fprintf (stderr, "rm remove file from camera\n"); fprintf (stderr, "ls list directory contents\n"); fprintf (stderr, "cardinfo display free space on the memory card\n"); fprintf (stderr, "camerainfo display camera state and information\n"); fprintf (stderr, "--- command arguments ---\n"); fprintf (stderr, " is either a picture ID (xxx-yyyy) as displayed in \"review\" mode,\n"); fprintf (stderr, " or a pathname on the CompactFlash card (absolute or relative to /dcim),\n"); fprintf (stderr, " or \"-\" (the last taken picture, or \"--\" (the last reviewed picture).\n"); fprintf (stderr, " is a local directory (e.g. \".\"), or filename, or \"-\" (stdout).\n"); fprintf (stderr, " is a pathname on the CompactFlash card (absolute or relative to /dcim).\n"); exit (EX_OK); } usercommand commands[] = { {"help", help_cmd, 0}, {"take", take_cmd, 0}, {"cp", cp_cmd, 0}, {"mv", cp_cmd, 1}, {"ls", ls_cmd, 0}, {"rm", rm_cmd, 0}, {"cardinfo", cardinfo_cmd, 0}, {"camerainfo", camerainfo_cmd, 0}, {NULL, NULL} }; int execute_command (kodacon *kc, int cmdc, char *cmdv[]) { usercommand *uc; int cmdlen; cmdlen = strlen(cmdv[0]); for (uc = commands; uc->name; uc++) if (strncmp(cmdv[0], uc->name, cmdlen) == 0) return uc->func(kc, cmdc - 1, cmdv + 1, uc->flag) + 1; errx (EX_USAGE, "unknown command \"%s\", please type \"%s help\"", cmdv[0], me); } /* EOF */