/*--------------------------------------------------------------------------- fdate.c Greg Roelofs This program prints the modification time for a file in "YYYYMMDDhhmmss" or "YYYYMMDDhhmm.ss" (touch(1)) or Unix (seconds since 1970) format, with options to print the access time, status-change time or system time (clock) instead. To compile: cc -O -o fdate fdate.c or: gcc -Wall -O -o fdate fdate.c Revision History: 1.00 15 Mar 1993 created 1.10 15 Apr 1993 added -s option for system date 1.20 9 Feb 1999 modified default output format for Y2K 1.30 2 Jan 2000 added -t option for touch(1) output format 1.31 24 Jan 2000 merged missing 1.20 changes; updated comments 1.40 23 Sep 2002 added macro(s) for large file support 1.41 23 Sep 2004 fixed some compiler warnings 1.50 23 Jan 2005 added support for new-style touch(1) output 1.60 29 Aug 2005 added -C option to convert Unix integer timestamp 1.70 5 Feb 2010 added -w option to print start-of-week day and -i/-I options for ISO output formats (YYYY-MM-DD hh:mm:ss) ---------------------------------------------------------------------------*/ /* GRR 20020923: see http://www.suse.de/~aj/linux_lfs.html for details */ #define _FILE_OFFSET_BITS 64 /* required */ /* #define _LARGEFILE_SOURCE */ /* not required for stat() */ // GRR 20100205: use reentrant (thread-safe) time functions if possible #define ASSUME_SUSv2 #include #include #include /* memset(), memcpy() */ #include /* stat(), struct stat */ #include /* time_t, dev_t, etc. */ //#include /* gmtime(), localtime(), strftime(), */ #include /* gmtime(), localtime(), strftime(), */ #define VERSION "1.70 of 5 February 2010" enum _timetype { MODIFICATION, ACCESS, STATUS, SYSTEM, CONVERT, START_OF_WEEK }; int main(int argc, char *argv[]) { char ascnumtime[32], *outfmt; int c, c1, error=0; int gflag=0, iflag=0, Iflag=0, lflag=0, tflag=0, Tflag=0, uflag=0; int timetype=MODIFICATION; long wcurday=0L; /* shorthand: default to "today" */ time_t numtime; /*--------------------------------------------------------------------------- Parse command line for '-' options. ---------------------------------------------------------------------------*/ outfmt = "%Y%m%d%H%M%S"; /* default: monotonic, pure-integer format */ if (argc <= 1) --argc; else while (--argc > 0 && (*++argv)[0] == '-') while ((c = *++(argv[0]))) /* argv[0] not program name any more */ switch (c) { case 'a': timetype = ACCESS; break; case 'c': timetype = STATUS; break; case 'C': timetype = CONVERT; break; case 'g': ++gflag; break; case 'i': ++iflag; outfmt = "%Y-%m-%d %H:%M:%S %Z"; // near-ISO 8601 break; case 'I': ++Iflag; #ifdef __GLIBC__ outfmt = "%Y-%m-%dT%H:%M:%S%z"; // like "date -Isec" #else outfmt = "%Y-%m-%dT%H:%M:%S"; // ISO except UTC offset #endif break; case 'l': ++lflag; break; case 's': timetype = SYSTEM; break; case 't': ++tflag; outfmt = "%Y%m%d%H%M.%S"; /* new-style touch(1) */ break; case 'T': ++Tflag; outfmt = "%m%d%H%M%Y.%S"; /* old-style touch(1) */ break; case 'u': ++uflag; break; case 'w': timetype = START_OF_WEEK; outfmt = "%Y%m%d"; // just YYYYMMDD, same as in if ((c1 = argv[0][1])) { // possibly -wYYYYMMDD ? if ((c1 >= '0') && (c1 <= '9')) { wcurday = atol(argv[0]+1); // ptr to digit str argv[0][1] = '\0'; // overwrite digit } } else if (argc > 1) { // possibly -w YYYYMMDD ? --argc; ++argv; if (argv[0][0] != '-') { c1 = argv[0][0]; if ((c1 >= '0') && (c1 <= '9')) { wcurday = atol(argv[0]); // ptr to digit str argv[0][1] = '\0'; // overwrite digit } // or trail. NULL } } // if neither -wYYYYMMDD nor -w YYYYMMDD, use default // if out of "reasonable" range, also use default if (wcurday < 19000101L || wcurday > 99991231L) wcurday = 0L; break; default: ++error; break; } /* end switch, while, while, if */ /*--------------------------------------------------------------------------- Print usage and exit if any errors encountered. ---------------------------------------------------------------------------*/ if (error || (argc == 0 && timetype != SYSTEM && timetype != START_OF_WEEK)) { fprintf(stderr, "\ fdate %s, by Greg Roelofs.\n\n\ Print file date(s) or system date in YYYYMMDDhhmmss (default), YYYYMMDDhhmm.ss,\n\ MMDDhhmmYYYY.ss, or Unix integer format.\n\n\ usage: fdate [-acgltu] file [ file ... ]\n\ fdate -s [-gtu]\n\ fdate -w[YYYYMMDD]\n\ fdate -C [-gt] unixtime\n\ -a print time of last access\n\ -c print time of last status change\n\ -C convert Unix integer format to human-readable form\n\ -g use GMT (UTC) instead of local time for non-Unix output formats\n\ -i use pseudo-ISO-8601 output format (YYYY-MM-DD hh:mm:ss)\n\ -I use ISO-8601 output format (YYYY-MM-DDThh:mm:ss[TZOFF])\n\ -l long format: print filename(s) as well\n\ -s print system time\n\ -t use new-style \"touch\" output format (YYYYMMDDhhmm.ss)\n\ -T use old-style \"touch\" output format (MMDDhhmmYYYY.ss)\n\ -u use Unix integer time format (always GMT/UTC)\n\ -w print start-of-week (Sunday) date for today or specified day\n", VERSION); return 1; } /*--------------------------------------------------------------------------- Get and print system time. ---------------------------------------------------------------------------*/ if (timetype == SYSTEM || timetype == CONVERT) { if (lflag) { fprintf(stderr, "fdate: -l option ignored with -%c\n", (timetype == SYSTEM)? 's' : 'C'); lflag = 0; ++error; } if (timetype == SYSTEM) numtime = time((time_t *)NULL); else numtime = atol(argv[0]); if (uflag) sprintf(ascnumtime, "%lu", (unsigned long)numtime); else { strftime(ascnumtime, 32, outfmt, gflag? gmtime(&numtime) : localtime(&numtime)); } printf("%s\n", ascnumtime); return error? 1 : 0; } /*--------------------------------------------------------------------------- Get current system time or specified date and work backward to Sunday. ---------------------------------------------------------------------------*/ if (timetype == START_OF_WEEK) { struct tm *ptm, tm; memset(&tm, 0, sizeof(struct tm)); if (wcurday == 0L) { numtime = time((time_t *)NULL); #ifdef ASSUME_SUSv2 ptm = gflag? gmtime_r(&numtime, &tm) : localtime_r(&numtime, &tm); #else ptm = gflag? gmtime(&numtime) : localtime(&numtime); memcpy(&tm, ptm, sizeof(struct tm)); #endif } else { // user supplied "today" value (date only, YYYYMMDD format) tm.tm_year = (int)(wcurday / 10000L); wcurday -= (tm.tm_year * 10000L); // now MMDD only tm.tm_year -= 1900; tm.tm_mon = (int)(wcurday / 100L); wcurday -= (tm.tm_mon * 100L); // now DD only if (tm.tm_mon > 0) --tm.tm_mon; // range is 0 to 11, not 1 to 12 tm.tm_mday = (int)wcurday; tm.tm_hour = 12; // noon, for simplicity numtime = mktime(&tm); if (numtime == (time_t)(-1)) { fprintf(stderr, "fdate: mktime() unable to convert parsed -w " "argument %04d-%02d-%02d\n", tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday); ++error; } } numtime -= (tm.tm_wday * 86400); // sometime on Sunday now if (uflag) sprintf(ascnumtime, "%lu", (unsigned long)numtime); else { strftime(ascnumtime, 32, outfmt, gflag? gmtime(&numtime) : localtime(&numtime)); } printf("%s\n", ascnumtime); return error? 1 : 0; } /*--------------------------------------------------------------------------- Loop through files on command line, printing timestamps for each. ---------------------------------------------------------------------------*/ while (argc-- > 0) { struct stat statbuf; if (stat(*argv, &statbuf)) { fprintf(stderr, "fdate: file not found: %s\n", *argv++); ++error; continue; } switch (timetype) { case ACCESS: numtime = statbuf.st_atime; break; case STATUS: numtime = statbuf.st_ctime; break; default: numtime = statbuf.st_mtime; break; } /* convert time to string, one way or other */ if (uflag) sprintf(ascnumtime, "%lu", (unsigned long)numtime); else { strftime(ascnumtime, 32, outfmt, gflag? gmtime(&numtime) : localtime(&numtime)); } /* FIXME: do we want to include "touch -t"/"touch" for -t/-T, resp.? */ printf("%s %s\n", ascnumtime, lflag? *argv : ""); ++argv; } return error? 1 : 0; } /* end main() */