Download raw body.
cal: add option to highlight the current day
On Sat, Jul 04, 2026 at 07:25:20AM -0600, Theo de Raadt wrote:
> Calling tgetent() late is bizzare, and it made you do this:
>
> - if (pledge("stdio", NULL) == -1)
> + if (pledge("stdio rpath tty", NULL) == -1)
> err(1, "pledge");
>
> In a word: Nope.
>
>
I've had this patch for a while. I didn't post it here because I
thought no one would be interested. Now I've updated it with the latest
sources.
When you pipe the output to a file it does not send the highlight code.
It also corrects separation between rows of months with cal -y.
Index: cal.c
===================================================================
RCS file: /cvs/src/usr.bin/cal/cal.c,v
diff -u -p -u -p -r1.36 cal.c
--- cal.c 2 Jul 2026 20:40:53 -0000 1.36
+++ cal.c 5 Jul 2026 10:37:44 -0000
@@ -128,6 +128,8 @@ int julian;
int mflag = 0;
int wflag = 0;
+struct tm *local_time = NULL;
+
void ascii_day(char *, int);
void center(const char *, int, int);
void day_array(int, int, int *);
@@ -135,6 +137,7 @@ int day_in_week(int, int, int);
int day_in_year(int, int, int);
int week(int, int, int);
int isoweek(int, int, int);
+int is_today(int, int, int);
void j_yearly(int);
void monthly(int, int);
void trim_trailing_spaces(char *);
@@ -145,7 +148,6 @@ int parsemonth(const char *);
int
main(int argc, char *argv[])
{
- struct tm *local_time;
time_t now;
int ch, month, year, yflag;
const char *errstr;
@@ -153,6 +155,9 @@ main(int argc, char *argv[])
if (pledge("stdio", NULL) == -1)
err(1, "pledge");
+ (void)time(&now);
+ local_time = localtime(&now);
+
yflag = year = 0;
while ((ch = getopt(argc, argv, "jmwy")) != -1)
switch(ch) {
@@ -202,8 +207,6 @@ main(int argc, char *argv[])
if (yflag)
errx(1, "specifying a month conflicts with -y");
month = parsemonth(*argv);
- (void)time(&now);
- local_time = localtime(&now);
year = local_time->tm_year + 1900;
} else {
year = strtonum(*argv, 1, 9999, &errstr);
@@ -212,8 +215,6 @@ main(int argc, char *argv[])
}
break;
case 0:
- (void)time(&now);
- local_time = localtime(&now);
year = local_time->tm_year + 1900;
if (!yflag)
month = local_time->tm_mon + 1;
@@ -295,32 +296,39 @@ isoweek(int day, int month, int year)
return n/7 + 1;
}
+
void
monthly(int month, int year)
{
- int col, row, len, days[MAXDAYS], firstday;
- char *p, lineout[30];
+ int col, today, row, len, days[MAXDAYS], firstday, day_width;
+ char day[J_DAY_LEN], lineout[30];
day_array(month, year, days);
(void)snprintf(lineout, sizeof(lineout), "%s %d",
month_names[month - 1], year);
+
len = strlen(lineout);
(void)printf("%*s%s\n%s\n",
((julian ? J_WEEK_LEN : WEEK_LEN) - len) / 2, "",
lineout, day_headings);
for (row = 0; row < 6; row++) {
firstday = SPACE;
- for (col = 0, p = lineout; col < 7; col++,
- p += julian ? J_DAY_LEN : DAY_LEN) {
- if (firstday == SPACE && days[row * 7 + col] != SPACE)
- firstday = days[row * 7 + col];
- ascii_day(p, days[row * 7 + col]);
+ for (col = 0; col < 7; col++) {
+ today = days[row * 7 + col];
+ day_width = (julian ? J_DAY_LEN : DAY_LEN);
+ if (firstday == SPACE && today != SPACE)
+ firstday = today;
+ ascii_day(day, today);
+ if (is_today(today, month, year))
+ printf("\033[7m");
+ day[day_width] = '\0';
+ printf("%s", day);
+ if (is_today(today, month, year))
+ printf("\033[0m");
+ printf(" ");
}
- *p = '\0';
- trim_trailing_spaces(lineout);
- (void)printf("%-20s", lineout);
if (wflag && firstday != SPACE)
- printf(" [%2d]", week(firstday, month, year));
+ printf("[%2d]", week(firstday, month, year));
printf("\n");
}
}
@@ -328,7 +336,7 @@ monthly(int month, int year)
void
j_yearly(int year)
{
- int col, *dp, i, month, row, which_cal;
+ int col, *dp, i, hl_offset, month, row, which_cal;
int days[12][MAXDAYS];
char *p, lineout[80];
@@ -340,21 +348,36 @@ j_yearly(int year)
(void)memset(lineout, ' ', sizeof(lineout) - 1);
lineout[sizeof(lineout) - 1] = '\0';
for (month = 0; month < 12; month += 2) {
+ if (month >= 6 && month <= 10)
+ (void)printf("\n");
center(month_names[month], J_WEEK_LEN, J_HEAD_SEP);
center(month_names[month + 1], J_WEEK_LEN, 0);
(void)printf("\n%s%*s%s\n", day_headings,
J_HEAD_SEP, "", day_headings);
for (row = 0; row < 6; row++) {
+ hl_offset = -1;
for (which_cal = 0; which_cal < 2; which_cal++) {
p = lineout + which_cal * (J_WEEK_LEN + 2);
dp = &days[month + which_cal][row * 7];
- for (col = 0; col < 7; col++, p += J_DAY_LEN)
+ for (col = 0; col < 7; col++, p += J_DAY_LEN) {
+ if (is_today(*dp, month +
+ which_cal + 1, year))
+ hl_offset = p - lineout;
ascii_day(p, *dp++);
+ p[J_DAY_LEN - 1] = ' ';
+ }
}
*p = '\0';
trim_trailing_spaces(lineout);
- (void)printf("%s\n", lineout);
+ if (hl_offset != -1 &&
+ hl_offset < (int)strlen(lineout)) {
+ (void)printf("%.*s", hl_offset, lineout);
+ (void)printf("\033[7m%.3s\033[0m",
+ lineout + hl_offset);
+ (void)printf("%s\n", lineout + hl_offset + 3);
+ } else
+ (void)printf("%s\n", lineout);
}
}
(void)printf("\n");
@@ -366,6 +389,7 @@ yearly(int year)
int col, *dp, i, month, row, which_cal, week_len, wn, firstday;
int days[12][MAXDAYS];
char *p, lineout[81];
+ int hl_offset;
week_len = WEEK_LEN;
if (wflag)
@@ -378,6 +402,8 @@ yearly(int year)
(void)memset(lineout, ' ', sizeof(lineout) - 1);
lineout[sizeof(lineout) - 1] = '\0';
for (month = 0; month < 12; month += 3) {
+ if (month > 3)
+ (void)printf("\n");
center(month_names[month], week_len, HEAD_SEP);
center(month_names[month + 1], week_len, HEAD_SEP);
center(month_names[month + 2], week_len, 0);
@@ -386,6 +412,7 @@ yearly(int year)
HEAD_SEP + (wflag ? WEEKNUMBER_LEN : 0), "", day_headings);
for (row = 0; row < 6; row++) {
+ hl_offset = -1;
for (which_cal = 0; which_cal < 3; which_cal++) {
p = lineout + which_cal * (week_len + 2);
@@ -394,7 +421,10 @@ yearly(int year)
for (col = 0; col < 7; col++, p += DAY_LEN) {
if (firstday == SPACE && *dp != SPACE)
firstday = *dp;
+ if (is_today(*dp, month + which_cal + 1, year))
+ hl_offset = p - lineout;
ascii_day(p, *dp++);
+ p[DAY_LEN - 1] = ' ';
}
if (wflag && firstday != SPACE) {
wn = week(firstday,
@@ -407,7 +437,14 @@ yearly(int year)
}
*p = '\0';
trim_trailing_spaces(lineout);
- (void)printf("%s\n", lineout);
+ if (hl_offset != -1 &&
+ hl_offset < (int)strlen(lineout)) {
+ (void)printf("%.*s", hl_offset, lineout);
+ (void)printf("\033[7m%.2s\033[0m", lineout +
+ hl_offset);
+ (void)printf("%s\n", lineout + hl_offset + 2);
+ } else
+ (void)printf("%s\n", lineout);
}
}
(void)printf("\n");
@@ -472,45 +509,46 @@ day_in_week(int day, int month, int year
return (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7);
return (THURSDAY);
}
-
void
ascii_day(char *p, int day)
{
- int display, val;
- static const char *aday[] = {
- "",
- " 1", " 2", " 3", " 4", " 5", " 6", " 7",
- " 8", " 9", "10", "11", "12", "13", "14",
- "15", "16", "17", "18", "19", "20", "21",
- "22", "23", "24", "25", "26", "27", "28",
- "29", "30", "31",
- };
-
- if (day == SPACE) {
- memset(p, ' ', julian ? J_DAY_LEN : DAY_LEN);
- return;
- }
- if (julian) {
- val = day / 100;
- if (val) {
- day %= 100;
- *p++ = val + '0';
- display = 1;
- } else {
- *p++ = ' ';
- display = 0;
- }
- val = day / 10;
- if (val || display)
- *p++ = val + '0';
- else
- *p++ = ' ';
- *p++ = day % 10 + '0';
- } else {
- *p++ = aday[day][0];
- *p++ = aday[day][1];
- }
- *p = ' ';
+ int display, val;
+ static const char *aday[] = {
+ "",
+ " 1", " 2", " 3", " 4", " 5", " 6", " 7",
+ " 8", " 9", "10", "11", "12", "13", "14",
+ "15", "16", "17", "18", "19", "20", "21",
+ "22", "23", "24", "25", "26", "27", "28",
+ "29", "30", "31",
+ };
+
+ if (day == SPACE) {
+ int text_width = (julian ? J_DAY_LEN : DAY_LEN) - 1;
+ memset(p, ' ', text_width);
+ p[text_width] = '\0';
+ return;
+ }
+ if (julian) {
+ val = day / 100;
+ if (val) {
+ day %= 100;
+ *p++ = val + '0';
+ display = 1;
+ } else {
+ *p++ = ' ';
+ display = 0;
+ }
+ val = day / 10;
+ if (val || display)
+ *p++ = val + '0';
+ else
+ *p++ = ' ';
+ *p++ = day % 10 + '0';
+ } else {
+ *p++ = aday[day][0];
+ *p++ = aday[day][1];
+ }
+ *p = '\0';
}
void
@@ -560,4 +598,18 @@ parsemonth(const char *s)
if (v <= 0 || v > 12)
errx(1, "invalid month: use 1-12 or a name");
return (v);
+}
+
+int
+is_today(int day, int month, int year)
+{
+ if (!isatty(STDOUT_FILENO))
+ return (0);
+
+ if (day <= 0 || local_time == NULL)
+ return (0);
+
+ return (year == local_time->tm_year + 1900 &&
+ month == local_time->tm_mon + 1 &&
+ day == (julian ? local_time->tm_yday + 1 : local_time->tm_mday));
}
--
Walter
cal: add option to highlight the current day