Index: bin/ls/print.c =================================================================== --- bin/ls/print.c +++ bin/ls/print.c @@ -47,12 +47,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #ifdef COLORLS #include #include @@ -105,6 +107,12 @@ } colors[C_NUMCOLORS]; #endif +#define MAX_ABMON_WIDTH 5 +/* static list of month */ +static char ab_months[12][MAX_ABMON_WIDTH * 2 * MB_LEN_MAX]; +static wchar_t wab_months[12][MAX_ABMON_WIDTH * 2 * MB_LEN_MAX]; +static size_t max_month_width = 0; + void printscol(const DISPLAY *dp) { @@ -138,6 +146,68 @@ return rc; } +static void +populate_abbreviated_month(void) +{ + int i, width; + size_t len, j, n; + size_t wab_months_width[12]; + + for (i = 0; i < 12; i++) { + wab_months_width[i] = 0; + len = mbstowcs(wab_months[i], nl_langinfo(ABMON_1 + i), + sizeof(wab_months[i])); + if (len == (size_t)-1) { + max_month_width = -1; + return; + } + /* + * count visible columns and pad if needed with spaces until + * MAX_ABMON_WIDTH + */ + for (j = 0, wab_months_width[i] = 0; + wab_months_width[i] < MAX_ABMON_WIDTH && j < len; j++) { + /* skip invisible columns */ + width = wcwidth(wab_months[i][j]); + /* if invalid we truncate to the last valid */ + if (width == -1) { + j--; + break; + } + /* + * check that double width wide char are not making the + * width larger than expected + */ + if (wab_months_width[i] + width > MAX_ABMON_WIDTH) { + j--; + break; + } + wab_months_width[i] += width; + } + /* NULL terminate to simplify padding later */ + wab_months[i][j] = L'\0'; + if (max_month_width < wab_months_width[i]) + max_month_width = wab_months_width[i]; + } + + /* + * for each month, terminate the widechar version at discovered max + * visible column + */ + for (i = 0; i < 12; i++) { + /* pad with spaces */ + if ((n = max_month_width - wab_months_width[i]) > 0) { + wcslcat(wab_months[i], L" "/* MAX_ABMON_WIDTH */, + max_month_width + 1); + } + if (wcstombs(ab_months[i], wab_months[i], + sizeof(ab_months[i])) == (size_t)-1) { + max_month_width = -1; + return; + } + } +} + /* * print name in current style */ @@ -425,6 +495,26 @@ xo_emit("{:device/%#*jx} ", (u_int)width, (uintmax_t)dev); } +static size_t +ls_strftime(char *str, size_t len, const char *fmt, const struct tm *tm) +{ + char *posb, nfmt[BUFSIZ]; + const char *format = fmt; + size_t ret; + + if ((posb = strstr(fmt, "%b")) != NULL) { + if (max_month_width == 0) + populate_abbreviated_month(); + } + if (max_month_width > 0 && posb != NULL) { + snprintf(nfmt, sizeof(nfmt), "%.*s%s%s", (int)(posb - fmt), + fmt, ab_months[tm->tm_mon], posb + 2); + format = nfmt; + } + ret = strftime(str, len, format, tm); + return (ret); +} + static void printtime(const char *field, time_t ftime) { @@ -451,7 +541,7 @@ else /* mmm dd yyyy || dd mmm yyyy */ format = d_first ? "%e %b %Y" : "%b %e %Y"; - strftime(longstring, sizeof(longstring), format, localtime(&ftime)); + ls_strftime(longstring, sizeof(longstring), format, localtime(&ftime)); snprintf(fmt, sizeof(fmt), "{d:%s/%%hs} ", field); xo_attr("value", "%ld", (long) ftime);