fork(2) download
  1. // -------------------------------------------------------------------------------
  2. // DATES ver 3.5 <mig_f1@hotmail.com>
  3. // a sample program in ANSI C performing calculations between two Gregorian dates:
  4. // difference in days and in (d,m,y) form, weekday & leap years indicators
  5. // jdn conversions, +/- days arithmetic, monthly calendars display
  6. // it can also print yearly calendars in text files, naming them: "calYEAR.txt"
  7. // by passing the year as a command line argument ( 1752-9999 )
  8. // -------------------------------------------------------------------------------
  9. // migf1, Athens 2011 * use at your own risk * free for whatever use * give credit
  10. // -------------------------------------------------------------------------------
  11. // external alogorithms:
  12. // http://w...content-available-to-author-only...t.com/KB/datetime/DateDurationCalculation1.aspx
  13. // http://w...content-available-to-author-only...c.ch/cal_stud/jdn.htm#comp
  14.  
  15. #include <stdio.h>
  16. #include <time.h>
  17. #include <string.h>
  18. #include <errno.h>
  19. #include <ctype.h>
  20. #include <stdlib.h>
  21.  
  22. #define MAX_INBUF 255+1 // for our input buffer
  23. #define INVALID -1 // just a flag for invalid d,m,y
  24.  
  25. #define validday(d) ( (d) > 0 && (d) < 32 ) // 1st validity check for days
  26. #define validmonth(m) ( (m) > 0 && (m) < 13 ) // 1st validity check for months
  27. #define validyear(y) ( (y) > 1751 && (y) < 10000 ) // 1st validity check for years
  28.  
  29. #define isleap(y) ( !((y) % 4) && ( (y) % 100 || !((y) % 400) ) )
  30.  
  31. #define myabs(x) ( (x) < 0 ? -(x) : (x) ) // absolute value
  32.  
  33. typedef enum bool { FALSE=0, TRUE } bool; // our custom boolean type
  34. enum { JAN=1, FEB }; // just two month constants
  35. enum { ID1=1, ID2 }; // to identify 1st and 2nd dates
  36.  
  37. typedef struct Date { // our date structure
  38. long int d; // 1-31
  39. long int m; // 1-12
  40. long int y; // 1752-9999
  41. } Date;
  42.  
  43. // ------------------------------------------------------------------------------------
  44. // Read s from stdin until either len chars have been typed or ENTER has been hit,
  45. // and null-terminate s (if ENTER was there, it is replaced).
  46. // Return the null-terminated s
  47. //
  48. char *s_get(char *s, size_t len)
  49. {
  50. char *cp;
  51. for (cp=s; (*cp=getc(stdin)) != '\n' && (cp-s) < len-1; cp++ )
  52. ; // for-loop with empty body
  53. *cp = '\0'; // null-terminate last character
  54.  
  55. return s;
  56. }
  57.  
  58. // ------------------------------------------------------------------------------------
  59. // Trim leading & trailing blanks from string s, pad it with '\0's and return it
  60. // (or NULL on error)
  61. //
  62. char *s_trim(char *s)
  63. {
  64. if ( !s || !*s ) // error, early exit
  65. return NULL;
  66.  
  67. char *cp1; // for parsing the whole s
  68. char *cp2; // for shifting & padding
  69.  
  70. // trim leading & shift left remaining
  71. for (cp1=s; isblank((int)*cp1); cp1++ ) // skip leading blanks, via cp1
  72. ;
  73. for (cp2=s; *cp1; cp1++, cp2++) // shift-left remaining chars, via cp2
  74. *cp2 = *cp1;
  75. *cp2-- = '\0'; // mark end of left trimmed s
  76.  
  77. // replace trailing blanks with '\0's
  78. while ( cp2 > s && isblank((int)*cp2) )
  79. *cp2-- = '\0'; // pad with '\0'
  80.  
  81. return s;
  82. }
  83.  
  84. // ------------------------------------------------------------------------------------
  85. // remove from string s any char contained in string del (return the modified string s)
  86. char *s_strip(char *s, const char *del)
  87. {
  88. if ( !s || !*s)
  89. return NULL;
  90.  
  91. char *cp1; // for parsing the whole s
  92. char *cp2; // for keeping desired *cp1's
  93.  
  94. for (cp1=s, cp2=s; *cp1; cp1++ )
  95. if ( !strchr(del, *cp1) ) // *cp1 is NOT contained in del
  96. *cp2++ = *cp1; // copy it to start of s, via cp2
  97. *cp2 = 0; // null terminate the trimmed s
  98.  
  99. return s;
  100. }
  101.  
  102. // ------------------------------------------------------------------------------------
  103. char *s_ncopy( char *dst, const char *src, int n )
  104. {
  105. char *ret = dst;
  106.  
  107. while ( (dst-ret) < n-1 && (*dst=*src) != '\0' )
  108. dst++, src++;
  109.  
  110. if ( *dst )
  111. *dst = 0;
  112.  
  113. return ret;
  114. }
  115.  
  116. // ------------------------------------------------------------------------------------
  117. // Convert s to lowercase and return it (or NULL on error)
  118. //
  119. char *s_tolower( char *s )
  120. {
  121. if ( !s ) // error, early exit
  122. return NULL;
  123.  
  124. char *ret;
  125. for ( ret=s; (*s=tolower(*s)); s++ )
  126. ;
  127. return ret;
  128. }
  129.  
  130. // ------------------------------------------------------------------------------------
  131. // break a string up to maxtokens tokens and store them in *tokens[]
  132. // (uses " " as the delimeter string)
  133. // returns the number of tokens, or 0 on failure
  134. // ------------------------------------------------------------------------------------
  135.  
  136. int s_tokenize(char *s, char *tokens[], int maxtokens, char *delimiters)
  137. {
  138. if ( !s || !*s || !tokens || !maxtokens || !delimiters || !*delimiters )
  139. return 0;
  140.  
  141. register int i=0;
  142.  
  143. tokens[0] = strtok(s, delimiters);
  144. if (tokens[0] == NULL)
  145. return 0;
  146. for (i=1; i < maxtokens && (tokens[i]=strtok(NULL, delimiters)) != NULL; i++);
  147.  
  148. return i;
  149. }
  150.  
  151. // ------------------------------------------------------------------------------------
  152. // Convert a Gregorian date to a Julian Day count
  153. // IMPORTANT: accurate ONLY for dates after Oct 15, 1582 (Gregorian Calendar)
  154. // Algorithm by Henry F. Fliegel & Thomas C. Van Flandern:
  155. // http://w...content-available-to-author-only...c.ch/cal_stud/jdn.htm#comp
  156. //
  157. long date_2jdn( Date date )
  158. {
  159. return
  160. ( 1461 * ( date.y + 4800 + ( date.m - 14 ) / 12 ) ) / 4 +
  161. ( 367 * ( date.m - 2 - 12 * ( ( date.m - 14 ) / 12 ) ) ) / 12 -
  162. ( 3 * ( ( date.y + 4900 + ( date.m - 14 ) / 12 ) / 100 ) ) / 4 +
  163. date.d - 32075;
  164.  
  165. }
  166.  
  167. // ----------------------------------------------------------------------------------
  168. // Convert a Julian Day count to a Gregorian date (d,m,y)
  169. // IMPORTANT: accurate ONLY for dates after Oct 15, 1582 (Gregorian Calendar)
  170. // Algorithm by Henry F. Fliegel & Thomas C. Van Flandern:
  171. // http://w...content-available-to-author-only...c.ch/cal_stud/jdn.htm#comp
  172. //
  173. Date *jdn_2date( Date *date, long jd )
  174. {
  175. long l = jd + 68569;
  176. long n = ( 4 * l ) / 146097;
  177. l = l - ( 146097 * n + 3 ) / 4;
  178. long i = ( 4000 * ( l + 1 ) ) / 1461001;
  179. l = l - ( 1461 * i ) / 4 + 31;
  180. long j = ( 80 * l ) / 2447;
  181. date->d = l - ( 2447 * j ) / 80;
  182. l = j / 11;
  183. date->m = j + 2 - ( 12 * l );
  184. date->y = 100 * ( n - 49 ) + i + l;
  185.  
  186. return date;
  187. }
  188.  
  189. // ------------------------------------------------------------------------------------
  190. // Compare date1 against date2, chronologically.
  191. // Return: 1 if date1 > date2, 0 if date1 == date2, -1 if date1 < date2
  192. int date_compare( Date date1, Date date2 )
  193. {
  194. long int jdn1 = date_2jdn( date1 ); // convert date1 to julian day number
  195. long int jdn2 = date_2jdn( date2 ); // convert date2 to julian day number
  196.  
  197. if (jdn1 > jdn2)
  198. return 1;
  199. if (jdn1 == jdn2)
  200. return 0;
  201.  
  202. return -1;
  203. }
  204.  
  205. // ------------------------------------------------------------------------------------
  206. // Check if date.d exceeds the max allowed days in month date.m of year date.y
  207. // Return TRUE if does not, FALSE otherwise
  208. // for example: 29/2/2011, 31/4/anyyear are both FALSE
  209. //
  210. bool date_dayinbounds( Date date, int mdays[] )
  211. {
  212. if ( date.d > mdays[ date.m - 1 ] )
  213. {
  214. if ( date.d == 29 && date.m == FEB && isleap( date.y ) )
  215. return TRUE;
  216. return FALSE;
  217. }
  218.  
  219. return TRUE;
  220. }
  221. // ------------------------------------------------------------------------------------
  222. // Check if date lyes between calstart and calend (inclusive)
  223. // Return TRUE if it does, FALSE otherwise
  224. //
  225. bool date_inbounds( Date date, Date calstart, Date calend)
  226. {
  227. return date_compare(date, calstart) >= 0 && date_compare(date, calend) <= 0;
  228. }
  229.  
  230. // ------------------------------------------------------------------------------------
  231. // Convert Gregorian date to weekday (valid from Sep 14, 1752 to Dec 31, 9999)
  232. // Return 0 to 6 (Mon to Sun)
  233. //
  234. int date_2weekday( Date date )
  235. {
  236. return date_2jdn(date) % 7; // return jdn % 7
  237. }
  238.  
  239. // ------------------------------------------------------------------------------------
  240. // Calc the difference between date1 and date2 and RETURN it expressed as # of days.
  241. // IMPORTANT:,
  242. // the difference is also calc'ed as days, months, years and passed into datediff
  243. // Algorithm by Mohammed Ali Babu
  244. // http://w...content-available-to-author-only...t.com/KB/datetime/DateDurationCalculation1.aspx
  245. //
  246. long date_diff( Date *datediff, Date date1, Date date2, int mdays[] )
  247. {
  248. if ( !datediff )
  249. return -1;
  250.  
  251. long int jdn1 = date_2jdn( date1 ); // calc jd of date1
  252. long int jdn2 = date_2jdn( date2 ); // calc jd of date2
  253. Date *dp2 = (jdn2 > jdn1) ? &date2 : &date1; // dp2 points to latest date
  254. Date *dp1 = (dp2 == &date1) ? &date2 : &date1; // dp1 points to earliest date
  255.  
  256. /*
  257. * the following alogorithm is published by Mohammed Ali Babu at:
  258. * http://w...content-available-to-author-only...t.com/KB/datetime/DateDurationCalculation1.aspx
  259. */
  260.  
  261. // first calc the difference of the day part
  262. int increment = 0;
  263. if ( dp1->d > dp2->d )
  264. increment = mdays[ dp1->m - 1 ];
  265.  
  266. if (increment == -1)
  267. {
  268. if ( isleap( dp1->y ) )
  269. increment = 29;
  270. else
  271. increment = 28;
  272. }
  273.  
  274. if (increment != 0)
  275. {
  276. datediff->d = (dp2->d + increment) - dp1->d;
  277. increment = 1;
  278. }
  279. else
  280. datediff->d = dp2->d - dp1->d;
  281.  
  282. // then calc the difference of the month part
  283. if ( (dp1->m + increment) > dp2->m )
  284. {
  285. datediff->m = (dp2->m + 12) - (dp1->m + increment);
  286. increment = 1;
  287. }
  288. else {
  289. datediff->m = dp2->m - (dp1->m + increment);
  290. increment = 0;
  291. }
  292.  
  293. // and last calculate the difference of the year part
  294. datediff->y = dp2->y - (dp1->y + increment);
  295.  
  296.  
  297. return myabs( jdn2-jdn1 );
  298. }
  299.  
  300. // ------------------------------------------------------------------------------------
  301. // Add ndays to basedate and Return the result into date (ndays can be negative)
  302. // (calstart and calend are used for boundary checking)
  303. //
  304. Date *date_plusdays( Date *date, Date basedate, long ndays, Date calstart, Date calend )
  305. {
  306. long jstart = date_2jdn( calstart ); // julian day of our calendar start date
  307. long jend = date_2jdn( calend ); // julian day of our calendar end date
  308. long jd = date_2jdn( basedate ); // julian day of basedate
  309.  
  310. if ( jd+ndays > jend ) // fix overflow (calend)
  311. return jdn_2date( date, jend );
  312.  
  313. if ( jd+ndays < jstart ) // fix underflow (calstart)
  314. return jdn_2date( date, jstart );
  315.  
  316. return jdn_2date( date, jd+ndays );
  317. }
  318.  
  319. // ------------------------------------------------------------------------------------
  320. // Convert string inbuf into a valid signed long int, store it in date->d
  321. // Return FALSE on failure
  322. //
  323. bool date_getoperation( Date *date, char *inbuf )
  324. {
  325. if ( !inbuf || !*inbuf || (*inbuf != '+' && *inbuf != '-') )
  326. return FALSE;
  327.  
  328. char *tail; // in strtol() for err checking
  329.  
  330. errno = 0;
  331. date->m = date->y = INVALID;
  332.  
  333. s_strip(inbuf, " \t\v"); // strip off blanks from inbuf
  334.  
  335. date->d = strtol(inbuf, &tail, 10); // convert inbuf to long
  336. if ( *tail != '\0' || errno == ERANGE )
  337. {
  338. puts("\t*** error: invalid operation");
  339. date->d = INVALID;
  340. return FALSE;
  341. }
  342.  
  343. return TRUE;
  344. }
  345.  
  346. // ------------------------------------------------------------------------------------
  347. // If string inbuf contains any of our recognized mnemonics, date is set accordingly
  348. // and the function returns TRUE (otherwise it returns FALSE).
  349. //
  350. bool date_getmnemonic( Date *date, char *inbuf, const Date calstart, const Date calend )
  351. {
  352. if ( !inbuf || !*inbuf ) // inbuf is either non-existant or empty
  353. return FALSE; // early exit
  354.  
  355. time_t today = time( NULL ); // get system time
  356. struct tm *tmtoday = localtime( &today );// convert it to a tm structure
  357. long jstart = date_2jdn( calstart ); // julian day of our calendar start date
  358. long jend = date_2jdn( calend ); // julian day of our calendar end date
  359. long jtoday;
  360. Date caltoday;
  361.  
  362. caltoday.d = (long) tmtoday->tm_mday;
  363. caltoday.m = (long) tmtoday->tm_mon + 1;
  364. caltoday.y = (long) tmtoday->tm_year + 1900;
  365. jtoday = date_2jdn( caltoday );
  366.  
  367. if ( !strcmp( inbuf, "start") ) {
  368. jdn_2date( date, jstart );
  369. return TRUE;
  370. }
  371. if ( !strcmp( inbuf, "yesterday") ) {
  372. // jdn_2date( date, jtoday-1 ); // faster but no boundary checking
  373. date_plusdays( date, caltoday, -1, calstart, calend);
  374. return TRUE;
  375. }
  376. if ( !strcmp( inbuf, "today") ) {
  377. jdn_2date( date, jtoday );
  378. return TRUE;
  379. }
  380. if ( !strcmp( inbuf, "tomorrow") ) {
  381. // jdn_2date( date, jtoday+1 ); // faster but no boundary checking
  382. date_plusdays( date, caltoday, +1, calstart, calend);
  383. return TRUE;
  384. }
  385. if ( !strcmp( inbuf, "end") ) {
  386. jdn_2date( date, jend );
  387. return TRUE;
  388. }
  389.  
  390. return FALSE;
  391. }
  392.  
  393. // ------------------------------------------------------------------------------------
  394. // Read a Gregorian date as a string form stdin, validate it & return it inside *date
  395. // IMPORTANT:
  396. //
  397. // The function returns TRUE if the input string is a valid operation (i.e "-200" )
  398. // instead of a valid date string (i.e. "1/1/2000" ). In that case, date->d
  399. // is assigned the numeric value of the operation ( -200 for the above example )
  400. // while date->m and date->y are assined the value -1 ( INVALID ).
  401. //
  402. // Only the second date is allowed to accept an operation string, so we use
  403. // the parameter 'id' in order to know whether we're dealing with date1 or
  404. // date2 in here.
  405. //
  406. // Since in case of a valid operation in the input, the returned date does NOT
  407. // hold a valid date, further processing needs to be done in the main() function,
  408. // to ensure that date2 is properly converted to a valid date, equalling to:
  409. // date1 + date2.d, before attempting to do anything else with it (e.g. printing
  410. // it)
  411. //
  412. //
  413. bool date_askuser( int id, char *prompt, Date *date,
  414. int max_inbuf, int mdays[], Date calstart, Date calend )
  415. {
  416. if ( !prompt || !date) // inbuf does not exist or it is empty
  417. return FALSE;
  418.  
  419. char inbuf[ max_inbuf ]; // for reading the user input
  420. char *stokens[3]; // to read d, m, y as strings
  421. char *tail; // in strtol() for err checking
  422. bool stop = TRUE; // for controlling the main loop
  423. bool operated = FALSE; // got a date or an operation?
  424.  
  425. do
  426. {
  427. stop = TRUE; // reset boolean flag to TRUE
  428. operated = FALSE; // reset boolean flag to FALSE
  429.  
  430. // prompt and get user input
  431. printf( prompt ); // ask for input
  432. fflush(stdin); // clear input buffer (stdin)
  433. s_get(inbuf, max_inbuf); // read input as string in inbuf
  434.  
  435. // check if user typed just an ENTER
  436. if ( !*inbuf )
  437. continue; // stop = TRUE
  438.  
  439. s_trim( s_tolower(inbuf) ); // trim leading & trailng blanks
  440.  
  441. // check if user typed an operation (+/-) followed by a number
  442. if ( id == ID1 && (*inbuf =='+' || *inbuf == '-') ) {
  443. puts("\t*** error: operations only allowed in the 2nd date");
  444. stop = FALSE;
  445. continue;
  446. }
  447. if ( date_getoperation(date, inbuf) ) {
  448. operated = TRUE;
  449. continue; // stop = TRUE
  450. }
  451.  
  452. // check if user typed any of our mnemonic strings
  453. if ( date_getmnemonic( date, inbuf, calstart, calend ) ) {
  454. stop = TRUE;
  455. continue;
  456. }
  457.  
  458. // split inbuf in up to 3 strings (tokens) assuming d, m, y
  459. if ( s_tokenize( inbuf, stokens, 3, " /,.;:\t") != 3 ) {
  460. puts("\t*** error: invalid date");
  461. stop = FALSE;
  462. continue;
  463. }
  464.  
  465. // demand from user to type a day between 1-31
  466. errno = 0; // reset golbal var errno
  467. date->d = strtol(stokens[0], &tail, 10);// convert str day to long
  468. if ( !validday(date->d) || *tail != '\0' || errno == ERANGE )
  469. {
  470. puts("\t*** error: valid days are 1-31");
  471. date->d = INVALID;
  472. stop = FALSE;
  473. continue;
  474. }
  475.  
  476. // demand from user to type a month between 1-12
  477. errno = 0; // reset golbal var errno
  478. date->m = strtol(stokens[1], &tail, 10);// convert str month to long
  479. if ( *tail != '\0' || errno == ERANGE || !validmonth(date->m) )
  480. {
  481. puts("\t*** error: valid months are 1-12");
  482. date->m = INVALID;
  483. stop = FALSE;
  484. continue;
  485. }
  486.  
  487. // demand from user to type a year between 1752-9999
  488. errno = 0; // reset golbal errno
  489. date->y = strtol(stokens[2], &tail, 10);// convert str year to long
  490. if ( *tail != '\0' || errno == ERANGE || !validyear(date->y) )
  491. {
  492. puts("\t*** error: valid years are 1752-9999");
  493. date->y = INVALID;
  494. stop = FALSE;
  495. continue;
  496. }
  497.  
  498. /* now we have a complete date (d,m,y) but this
  499. * does not gurantee us that it is a valid one
  500. * (e.g. 29/2/2000 is valid, but 29/2/2001 is not)
  501. * (also, e.g. 31/4 is invalid, an so on)
  502. */
  503.  
  504. // ensure day lyes inside the month boundary of the typed date
  505. if ( !date_dayinbounds(*date, mdays) )
  506. {
  507. date->d = date->m = date->y = INVALID;
  508. puts("\t*** error: invalid day");
  509. stop = FALSE;
  510. continue;
  511. }
  512.  
  513. // ensure date is between 14/9/1752 and 31/12/9999
  514. if ( !date_inbounds(*date, calstart, calend) )
  515. {
  516. date->d = date->m = date->y = INVALID;
  517. puts("\t*** error: valid dates are 14/9/1752 - 31/12/9999");
  518. stop = FALSE;
  519. continue;
  520. }
  521.  
  522. } while( !stop || !*inbuf );
  523.  
  524. return operated;
  525. }
  526.  
  527. // ------------------------------------------------------------------------------------
  528. void print_help( void )
  529. {
  530. //printf("\n\n%70s\n", "<mig_f1@hotmail.com>\n");
  531. puts("A sample program performing various calculations between two dates in \nthe time interval: 14/9/1752 to 31/12/9999 (results never exceed it).\n\nEach date input is checked in real time against both syntax & logical\nerrors. It can be in one of the following formats...\n-\td/m/y\t\t: other separators: :,.; and tab\n-\ta signed number\t: e.g. -200 (subtract 200 days from 1st Date)\n-\tstart\t\t: equals to 14/9/1752\n-\tyesterday\t: equals to yesterday's date\n-\ttoday\t\t: equals to today's date\n-\ttomorrow\t: equals to tomorrow's date\n-\tend\t\t: equals to 31/12/9999\n\nThe calculated results include...\n-\tdifference expressed in days\n-\tdifference expressed in days, months, years\n-\tweekday indication\n-\tJulian Day Number indication\n-\tleap year indication\n-\tmonthly calendar of both dates (or 1st only, if same with 2nd)");
  532. puts("\nBONUS:\tPass a year (1752 - 9999) in the command line, to create a text\n\tfile called \"calYEAR.txt\", containing the yearly calendar.");
  533.  
  534. return;
  535. }
  536.  
  537. // ------------------------------------------------------------------------------------
  538. // Print date in the form: MONTH DAY YEAR, Weekday (jdn) (is leap year ?)
  539. //
  540. void print_date(Date date, int id, char *mnames[], char *dnames[] )
  541. {
  542. if ( !validday( date.d ) || !validmonth( date.m ) || !validyear( date.y ) )
  543. return;
  544.  
  545. printf( "\t%d. %s %02ld %04ld, %-9s (jdn: %ld) %s\n",
  546. id,
  547. mnames[ (int)(date.m)-1 ],
  548. date.d, date.y, dnames[ date_2weekday(date) ],
  549. date_2jdn( date ),
  550. isleap( date.y ) ? "\t(leap year)" : ""
  551. );
  552.  
  553. return;
  554. }
  555.  
  556. // ------------------------------------------------------------------------------------
  557. // Print the difference between date1 and date 2 expressed both in number of days
  558. // and in the form: days, months, years
  559. //
  560. void print_diffs(long int ndays, Date date, char *mnames[], char *dnames[] )
  561. {
  562. printf("\tDiff:\t%ld day(s)\n", ndays);
  563. printf( "\t\t%ld years(s), %02ld months(s) and %02ld days(s)\n",
  564. date.y, date.m, date.d );
  565.  
  566. return;
  567. }
  568.  
  569. // ------------------------------------------------------------------------------------
  570. // Print the monthly calendar of date
  571. //
  572. void fprint_calmonth(Date date, int mdays[], char *mnames[], char *dnames[], FILE *fp )
  573. {
  574. register int i=0, j=0;
  575. Date mon1date = { // temp date for 1/date.m/date.y
  576. .d = 1, .m = date.m, .y = date.y
  577. };
  578. int monthdays = mdays[date.m - 1]; // calc # days of date.m
  579. if ( isleap(date.y) && date.m == FEB ) // +1 if leap February
  580. monthdays++;
  581.  
  582. // print month & year
  583. fprintf(fp, "\n%s %ld\t", mnames[date.m - 1], date.y );
  584.  
  585. // print header (day names)
  586. for (i=0; i<7; i++) {
  587. fprintf(fp, "%3s ", dnames[i]);
  588. }
  589. fputc('\n', fp);
  590.  
  591. // now print the day numbers
  592.  
  593. // start with blanks, when needed
  594. fprintf(fp, "\t\t");
  595. for (i=0; i < date_2weekday( mon1date ); i++)
  596. fprintf(fp, "%3s ", "");
  597.  
  598. // start printing the day numbers (i is carried from previous loop)
  599. for ( j=0; j < monthdays; j++, i++ )
  600. {
  601. if ( i % 7 == 0)
  602. fprintf(fp, "\n\t\t");
  603. fprintf(fp, "%3d ", j+1);
  604. }
  605. fputc('\n', fp);
  606.  
  607. return;
  608. }
  609.  
  610. // ------------------------------------------------------------------------------------
  611. // Print the yearly calendar of date
  612. //
  613. void fprint_calyear( Date date, int mdays[], char *mnames[], char *dnames[], FILE *fp )
  614. {
  615. register int i;
  616.  
  617. for (i=0; i<12; i++) {
  618. date.m = i+1;
  619. fprint_calmonth( date, mdays, mnames, dnames, fp );
  620. }
  621.  
  622. return;
  623. }
  624. // ------------------------------------------------------------------------------------
  625. // Check for any command line arguments, if any and handle them accordingly.
  626. // Currently we are only accepting 1 argument, representing a year for which we
  627. // create a text file, holding the yearly calendar. Years earlier than 1752 or
  628. // later than 9999 are automatically fixed to be equal with the lower or upper
  629. // bound, respectively.
  630. //
  631. void do_cmlargs(char *argv[], Date calstart, Date calend,
  632. int mdays[], char *mnames[], char *dnames[] )
  633. {
  634. Date ydate; // for 1/1/argv[1]
  635. Date *pdate = NULL; // temp pointer
  636. FILE *fp; // file pointer
  637. char fname[12+1] = "";
  638.  
  639. s_strip( argv[1], " \t\v"); // strip off blanks
  640.  
  641. // construct a temporary date corresponding to: 1/1/argv[1]
  642. ydate.d=1; ydate.m=1; ydate.y = strtol( argv[1], NULL, 10 );
  643.  
  644. // fix year if it either underflows or overflows (1752 - 9999)
  645. pdate = &ydate;
  646. if ( date_compare(ydate, calstart) < 0 )
  647. pdate = &calstart;
  648. else if ( date_compare(ydate, calend) > 0 )
  649. pdate = &calend;
  650.  
  651. // construct filename
  652. sprintf(fname, "cal%ld.txt", pdate->y);
  653.  
  654. // open file and write the yearly calendar
  655. fp = fopen(fname, "w");
  656. if ( !fp ) { // file open failed
  657. printf("\t*** write error in file: %s\n", fname);
  658. }
  659. else {
  660. fprint_calyear(*pdate, mdays, mnames, dnames, fp);
  661. printf( "\t*** yearly calendar of %ld printed in %s\n",
  662. pdate->y, fname );
  663. fclose( fp );
  664. }
  665. printf("press ENTER to continue...");
  666. fflush(stdin); getchar();
  667.  
  668. return;
  669. }
  670. // ------------------------------------------------------------------------------------
  671. int main( int argc, char *argv[] )
  672. {
  673. char *dnames[7] = { // day names
  674. "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"
  675. };
  676. char *mnames[12] = { // month names
  677. "JAN","FEB","MAR","APR","MAY","JUN","JUL","AUG","SEP","OCT","NOV","DEC"
  678. };
  679. // month lengths in days
  680. int mdays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  681.  
  682. Date calstart = { .d=14, .m=9, .y=1752 }; // our calendar starting date
  683. Date calend = { .d=31, .m=12, .y=9999 }; // our calendar ending date
  684.  
  685. Date date1, date2; // dates to be read from user
  686. Date diffdate; // for holding diff as (d,m,y)
  687. long ndays; // difference in days
  688. bool operated = FALSE; // input was date or operation?
  689. char inbuf[MAX_INBUF] = ""; // our own input buffer
  690.  
  691. // check & execute any command line arguments
  692. if ( argv[1] )
  693. do_cmlargs( argv, calstart, calend, mdays, mnames, dnames );
  694.  
  695. // start the main loop of the program
  696. do
  697. {
  698. print_help();
  699.  
  700. // read date1 and date2
  701. date_askuser( ID1, "\nFirst date (d/m/y): ", &date1,
  702. MAX_INBUF, mdays, calstart, calend );
  703. operated = date_askuser(
  704. ID2, "Second date (d/m/y): ", &date2,
  705. MAX_INBUF, mdays, calstart, calend );
  706.  
  707. // if date2 was an operation, store the resulting date into date2
  708. if ( operated )
  709. date_plusdays( &date2, date1, date2.d, calstart, calend);
  710.  
  711. // calc difference between date1 and date2
  712. ndays = date_diff( &diffdate, date1, date2, mdays );
  713.  
  714. // print dates and their difference
  715. putchar('\n');
  716. puts("\t--------------------------------------------------");
  717. print_date( date1, ID1, mnames, dnames );
  718. print_date( date2, ID2, mnames, dnames );
  719. puts("\t--------------------------------------------------");
  720. print_diffs( ndays, diffdate, mnames, dnames );
  721. puts("\t--------------------------------------------------");
  722.  
  723. // display month calendars
  724. fprint_calmonth( date1, mdays, mnames, dnames, stdout );
  725. if ( date1.y != date2.y || (date1.y == date2.y && date1.m != date2.m) )
  726. fprint_calmonth(date2, mdays, mnames, dnames, stdout);
  727.  
  728. printf("\ntry again (/n): ");
  729. fflush(stdin);
  730. } while ( *s_tolower( s_get(inbuf, MAX_INBUF) ) != 'n' );
  731.  
  732. exit(0);
  733. }
  734.  
Not running #stdin #stdout 0s 0KB
stdin
Standard input is empty
stdout
Standard output is empty