Files
alexvoste 1a9fd27a31 push
2026-05-07 02:22:25 +03:00

179 lines
4.5 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <sys/cervus.h>
#include <cervus_util.h>
#define A_INVERT "\x1b[7m"
#define A_BOLD "\x1b[1m"
#define A_RESET "\x1b[0m"
static const char *MNAME[12] = {
"January","February","March","April","May","June",
"July","August","September","October","November","December"
};
static int MDAYS[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
static int is_leap(int y)
{
return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0);
}
static int days_in_month(int y, int m)
{
if (m == 2 && is_leap(y)) return 29;
return MDAYS[m - 1];
}
static int first_dow(int y, int m)
{
int64_t days = 0;
for (int yr = 1970; yr < y; yr++) days += is_leap(yr) ? 366 : 365;
int md[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
md[1] = is_leap(y) ? 29 : 28;
for (int mo = 0; mo < m - 1; mo++) days += md[mo];
return (int)((days + 4) % 7);
}
static void get_today(int *out_y, int *out_m, int *out_d)
{
*out_y = 0; *out_m = 0; *out_d = 0;
cervus_timespec_t ts;
if (cervus_clock_gettime(CLOCK_REALTIME, &ts) != 0 || ts.tv_sec <= 0)
return;
int64_t t = ts.tv_sec;
int64_t days = t / 86400;
int y = 1970;
while (1) {
int dy = is_leap(y) ? 366 : 365;
if (days < dy) break;
days -= dy;
y++;
}
int md[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
md[1] = is_leap(y) ? 29 : 28;
int mo = 0;
while (mo < 12) { if (days < md[mo]) break; days -= md[mo]; mo++; }
*out_y = y;
*out_m = mo + 1;
*out_d = (int)(days + 1);
}
static void print_header(int y, int m, int today_y, int today_m)
{
char title[64];
snprintf(title, sizeof(title), "%s %d", MNAME[m - 1], y);
int tlen = (int)strlen(title);
int pad = (20 - tlen) / 2;
for (int i = 0; i < pad; i++) putchar(' ');
if (y == today_y && m == today_m) {
fputs(A_INVERT A_BOLD, stdout);
fputs(title, stdout);
fputs(A_RESET, stdout);
} else {
fputs(A_BOLD, stdout);
fputs(title, stdout);
fputs(A_RESET, stdout);
}
putchar('\n');
fputs(C_GRAY " Su Mo Tu We Th Fr Sa" A_RESET "\n", stdout);
}
static void print_month(int y, int m, int today_y, int today_m, int today_d)
{
print_header(y, m, today_y, today_m);
int mdays = days_in_month(y, m);
int dow = first_dow(y, m);
for (int i = 0; i < dow; i++) fputs(" ", stdout);
for (int d = 1; d <= mdays; d++) {
int is_today = (y == today_y && m == today_m && d == today_d);
if (is_today) {
fprintf(stdout, " " A_INVERT A_BOLD "%2d" A_RESET, d);
} else {
fprintf(stdout, " %2d", d);
}
if (++dow == 7) {
putchar('\n');
dow = 0;
}
}
if (dow != 0) putchar('\n');
}
static void print_help(void)
{
fputs(
"Usage: cal [OPTION] [[MONTH] YEAR]\n"
"Display a calendar.\n"
"\n"
" (no args) current month\n"
" YEAR all 12 months of YEAR\n"
" MONTH YEAR specific month (MONTH = 1-12)\n"
" --help display this help and exit\n"
"\n"
"Today's day is highlighted with " A_INVERT "inverted colours" A_RESET ".\n"
"The current month header is also " A_INVERT "inverted" A_RESET ".\n",
stdout
);
}
int main(int argc, char **argv)
{
int today_y, today_m, today_d;
get_today(&today_y, &today_m, &today_d);
int year = today_y ? today_y : 2025;
int month = today_m ? today_m : 1;
const char *args[2] = {NULL, NULL};
int real_argc = 0;
int flag_help = 0;
for (int i = 1; i < argc; i++) {
if (is_shell_flag(argv[i])) continue;
if (strcmp(argv[i], "--help") == 0) { flag_help = 1; continue; }
if (real_argc < 2) args[real_argc] = argv[i];
real_argc++;
}
if (flag_help) { print_help(); return 0; }
if (real_argc == 2) {
month = atoi(args[0]);
year = atoi(args[1]);
} else if (real_argc == 1) {
year = atoi(args[0]);
putchar('\n');
fprintf(stdout, " " A_BOLD "%d" A_RESET "\n\n", year);
for (int m = 1; m <= 12; m++) {
print_month(year, m, today_y, today_m, today_d);
putchar('\n');
}
return 0;
}
if (month < 1 || month > 12) {
fputs("cal: invalid month (must be 1-12)\n", stderr);
return 1;
}
putchar('\n');
print_month(year, month, today_y, today_m, today_d);
putchar('\n');
return 0;
}