/* * Adjust the EXIF timestamp of a series of photos. * This uses exiftool to access the EXIF data. * * Usage: $0 offset Photos... * * offset is a time offset in the form ±DD:HH:MM:SS * * $Id: resetdate.c,v 1.5 2012/10/11 01:01:43 grog Exp $ */ #include #include #include #include #include #include #include #include #include #include void usage (char *me) { printf ("Usage:\n\n" "%s [-n] offset Photos...\n" " -n means \"show but don't do\"\n" "offset is a time offset in the form [-][[DD]:HH]:MM:SS\n" "at least M and S must be specified\n", me ); exit (1); } int main (int argc, char *argv []) { int sign = 1; /* positive until proven otherwise */ char *c; /* * Parameters passed as offset. The meaning depends on the number; see below. */ int P [4]; int fields; /* number of fields in offset */ time_t offset; /* offset to add to time */ int arg; char command [PATH_MAX + 80]; /* command to hand to system() */ #define BUFSIZE 1024 char buf [BUFSIZE + 1]; /* result read from child */ FILE *child; char *filename; /* name of current file */ struct tm create_date; /* creation date of current file */ struct stat filestat; /* and current status */ struct timeval new_times [2]; /* adjusted time */ int dummy = 0; /* 1 to just show instead of doing it */ char *me = argv [0]; /* this could change */ if (argc < 2) usage (me); if (! strcmp (argv [1], "-n")) { dummy = 1; /* just print stuff out */ argv++; /* shift */ argc--; } if (argc < 2) /* we need two args left overr */ usage (me); c = argv [1]; /* offset */ if (*c == '-') /* negative change */ { sign = -1; c++; } else if (*c == '+') /* redundant positive */ c++; else if (! isdigit (*c)) usage (argv [0]); /* * We can have up to 4 fields here. If they're all there, they have the * meanings implied in the parameter list. Otherwise we have to reshuffle. */ fields = sscanf (c, "%d:%d:%d:%d", &P [0], &P [1], &P [2], &P [3]); switch (fields) { default: usage (argv [0]); case 4: /* all present and accounted for */ offset = P [0] * 86400 + P [1] * 3600 + P [2] * 60 + P [3]; /* D H M S */ break; case 3: /* H:M:S */ offset = P [0] * 3600 + P [1] * 60 + P [2]; break; case 2: /* M:S only */ offset = (P [0] * 60 + P [1]); break; } offset *= sign; new_times [1].tv_usec = 0; /* currently no sub-second component */ new_times [2].tv_usec = 0; /* currently no sub-second component */ /* How I hate these date conversion functions! */ create_date.tm_zone = NULL; create_date.tm_isdst = -1; /* try to guess based on date */ for (arg = 2; arg < argc; arg++) /* each photo */ { filename = argv [arg]; if (stat (filename, &filestat) < 0) fprintf (stderr, "Can't stat %s: %s (%d)\n", filename, strerror (errno), errno); else if (! S_ISREG (filestat.st_mode)) fprintf (stderr, "%s is not a file\n", filename); else { sprintf (command, "exiftool -DateTimeOriginal %s", filename); child = popen (command, "r"); if (! child) { fprintf (stderr, "Can't start %s\n", command); exit (1); } buf [0] = '\0'; /* in case we read nothing */ fread (buf, BUFSIZE, 1, child); if (ferror (child)) { fprintf (stderr, "Can't read child: %s (%d)\n", strerror (errno), errno); exit (1); } /* * We now should have a line of the format: * * Create Date : 2011:04:02 09:23:56 * * For now, strip everything up to and including first : */ c = buf; while (*c && (*c != ':')) c++; while (*c && ((*c == ':') || (*c == ' '))) c++; if (! strptime (c, "%Y:%m:%d %T", &create_date)) fprintf (stderr, "Can't parse date '%s' for %s\n", c, filename); else { new_times [0].tv_sec = mktime (&create_date) + offset; /* adjust time */ /* We don't change the subsecond values. */ new_times [0].tv_usec = filestat.st_atim.tv_nsec / 1000; new_times [1].tv_usec = filestat.st_mtim.tv_nsec / 1000; strftime (buf, BUFSIZE, "%Y:%m:%d %T", localtime (&new_times [0].tv_sec)); sprintf (command, "exiftool -overwrite_original_in_place " "-DateTimeOriginal=\"%s\" " "-CreateDate=\"%s\" " "-ModifyDate=\"%s\" %s ", buf, buf, buf, filename ); puts (command); if (! dummy) { system (command); new_times [1].tv_sec = new_times [0].tv_sec; /* for utimes call */ if (utimes (filename, new_times) < 0) /* set modification timestamp too */ fprintf (stderr, "Can't set modification timestamp for %s: %s (%d)\n", filename, strerror (errno), errno ); } } pclose (child); } } return 0; }