From: Walter Alejandro Iglesias Subject: Re: cal: add option to highlight the current day To: Johannes Thyssen Tishman , tech@openbsd.org, landry@openbsd.org, deraadt@openbsd.org Date: Sun, 5 Jul 2026 12:38:13 +0200 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