Code:
// timetest.c - free to copy and modify per: GNU General Public License
// https://www.gnu.org/licenses/licenses.en.html
// to compile with gcc
// gcc timetest.c -o timetest
// to compile with cc (Note HP-UX default K & R compile will NOT work)
// cc timetest.c -o timetest
// copy timetest to where it can be seen and executed by the correct set of users Ex:
// cp timetest /path/to/files/
// chmod timetest and directories as needed to allow access + execute
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
// ignore this, it is just for a quick compile on windows, some linux, too
#ifdef __CYGWIN__
#define _XOPEN_SOURCE
extern char *strptime( char *, const char *, struct tm *);
#endif
// *************************************************************************************
// IMPORTANT NOTE:
// The current POSIX (opengroup.org) specification for lpstat does not mention the lpstat -o output
// format for dates of events, nor does it appear to require dates to be output at all.
//
// So, as a guess, your system does what the UNIX/HP-UX command ls does with dates of varying ages:
// "young" file ls -l output:
// -rwxr-xr-x 1 Owner None 494 Sep 12 09:18 arr.c
// "old" file ls -l output:
// -rwxr-xr-x 1 Owner None 2233 Jul 28 2018 unscr.c
//
// The DATES ARE DIFFERENT:
// hours:minutes are dropped, replaced by year.
// lpstat is free to do the same. This code will not deal with old entries.
// The changeover in date formats happens at 6 months age AFAIK. So if you have ancient lp requests,
// they break this code.
// *****************************************************************************************
// timetest.c jmc 2/22/2019 19:17:23 MST
// *****************************************************************
// Function: check age of date string, return 0 if okay, 1 otherwise
// assumes the year value, handles issues in brand new year
// returns 2 on fatal error.
//
// example usage ----------------------------------------
// /usr/bin/lpstat -o |
// while read data
// do
// /path/to/testime $data
// status=$?
// if [ $status -eq 2 ]; then
// exit 1 # fatal error, quit shell script
// fi
// if [ $status -eq 0 ]; then
// echo "$data"
// fi
// done
// ---------------------------------------------
//
// example input data="653e-8022 prod priority 1 Feb 22 08:42"
// the 5th 6th 7th values: Feb 22 08:42, first assume this year (year the code was run)
//
// items you can et by editing a few line -------------------------------------------------
// the variable:
// allowed_secs
// needs to be set
// There are 86400 seconds in one day
#define SECS_PER_DAY 86400
// so we need a number of days , change this value to what is needed, we start with 3 days:
#define DAYS_ALLOWED 3
// the variables min_words and max_words allow you to skip over lines
// with too few words or too many words
// (note it has to be one more):
// Take the value you want, add one to it.
// Ex: You want 13 so enter 14.
// This is here because lpstat -o is somewhat freeform about what it writes to stdout.
#define MAX_ALLOWED_WORDS 11
#define MIN_ALLOWED_WORDS 8
// *******************************************************************
int allowed_secs=SECS_PER_DAY * DAYS_ALLOWED; // number of epoch seconds old
int min_words=MIN_ALLOWED_WORDS;
int max_words=MAX_ALLOWED_WORDS;
void barf(const int, const char *);
int parse(char *, char *, char *, char *);
char *trim(char *);
// fatal error exit for bad data - i.e., not valid data times
void barf(const int line, const char *input)
{
fprintf(stderr, "Content: %s\n", (input==NULL) ? "NULL" : input);
fprintf(stderr, "On line %d:\n Fatal Date/time format error %s\n", line, strerror(errno));
exit(2);
}
// *****************************************************
// parse takes command line data,
// guesses a year of the event
// figures out current epoch seconds based on guessing a year
// compare event date in epoch seconds with limit
// return 0 if all is okay, return 1 not okay, exit when we have unexpected problems
// *****************************************************
int parse(char *mon, char *day, char *HM, char *year)
{
int tmp_yr=0;
struct tm tstruct;
struct tm *tm=&tstruct; // tm is now the name of the struct w want to use
char working[80]={0x0};
time_t now=time(NULL);
time_t limit=now - allowed_secs; // oldest allowed epoch time we set above
time_t then=0; // epoch seconds from derived date
int use_prev_yr=(year[0]!=0); // tells us if we are running a second time
tm=localtime(&now); //assume no error return here
if(use_prev_yr != 0) // first time in the function,
// use the current year since year is blank
// we get here because the original guessed year put us in the future
// example: run on Jan 1 2019 with December 30th 2018 actual event date
// so we get epoch seconds for December 2019 - in future. Wrong year assumed.
// guess again....
{
tm->tm_year--; // use previous year, we guessed wrong the first time
}
tmp_yr=tm->tm_year + 1900; // set variable to guess year
sprintf(year, "%d", tmp_yr); // move value of guess to string
// set string "working" to values from command line
sprintf(working, "%s %s %s %s", mon, day, HM, year);
memset(tm, 0x0, sizeof(struct tm)); // clear tm from previous changes
if (strptime(working, "%b %d %H:%M %Y", tm)==NULL) //next get tm for guessed year/date
{
errno=EINVAL;
barf(__LINE__, working); // strptime failed
}
then=mktime(tm); // get our hopefully "older" value in seconds
if (then > now) // correct problem with assuming we are in the
// same year as the entry
{
if( use_prev_yr) // should not happen, but need to block infinite recursion
{
errno=EINVAL;
barf(__LINE__, "Invalid previous year setting");
}
else
{
return parse( mon, day, HM, year); // recursion: rerun so one year will be substracted
}
}
// we got here and now know
// dates were valid, as far as we know anyway, so:
// then == epoch time of event
// and now == current epoch time
// and limit == oldest allowed epoch time for event
if( then < limit) // too old
{
return 1; // reject event
}
return 0; // event is okay
}
// chop off too long values
char *trim(char *p)
{
if(strlen(p)>9) // try to preserve some junk text without flooding the screen
p[9]=0x0;
return p;
}
// driver
int main(int argc, char **argv)
{
char year[8]={0x0}; // year set to "empty"
char errline[128]={0x0};
// garbage checks -------------------------------------------
// 1. too many or too few words in a line
if(argc < min_words || argc > max_words ) // consider this record an airball(errant text)
exit(1); // do not print this record
// 2. no valid data because a string is too long or someone goofed
// Month is 3, day is 2, time (HH:MM) is 5
if( strnlen(argv[5],6) > 3 ||
strnlen(argv[6],6) > 2 ||
strnlen(argv[7],6) > 5 )
{
// we likely have junk data, so barf
sprintf(errline, "Garbage data(truncated): %s %s %s",
trim(argv[5]),
trim(argv[6]),
trim(argv[7])
);
errno=EINVAL;
barf(__LINE__, errline);
}
// end garbage checks ------------------------------------------
// set return code to 1 or zero (possibly 2 on major error) based on hopefully good data
// return 1 == entry too old
// return 0 == entry okay
return parse( argv[5], argv[6], argv[7], year );
}
// EOF 2/22/2019 23:09:00 MST