From 2df4e0a3707ff3b70398177e9e001070e47ca9c5 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 16 Sep 2021 01:53:55 +0200 Subject: chmod: correctly report changed modes Chmod used to incorrectly report as changed even files for which the mode did not change. This was caused by extra bits in the st_mode, that were not present when parsed from passed argument in the form of octal number. Patch by Wolf . Signed-off-by: Denys Vlasenko --- coreutils/chmod.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/coreutils/chmod.c b/coreutils/chmod.c index e260adab2..8e3e1387e 100644 --- a/coreutils/chmod.c +++ b/coreutils/chmod.c @@ -88,7 +88,8 @@ static int FAST_FUNC fileAction(struct recursive_state *state, if (chmod(fileName, newmode) == 0) { if (OPT_VERBOSE - || (OPT_CHANGED && statbuf->st_mode != newmode) + || (OPT_CHANGED + && (statbuf->st_mode & 07777) != (newmode & 07777)) ) { printf("mode of '%s' changed to %04o (%s)\n", fileName, newmode & 07777, bb_mode_string(newmode)+1); -- cgit v1.2.3-55-g6feb From 4958c18134eb7ad169cdaf22a9ad957ad4f4858c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 17 Sep 2021 00:47:23 +0200 Subject: libbb: code shrink bb_parse_mode function old new delta bb_parse_mode 393 398 +5 static.who_mask 16 8 -8 static.perm_mask 24 12 -12 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/2 up/down: 5/-20) Total: -15 bytes Signed-off-by: Denys Vlasenko --- include/libbb.h | 2 +- libbb/parse_mode.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/libbb.h b/include/libbb.h index 1ec8d2d3b..6727c22b2 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -1607,7 +1607,7 @@ char *bb_ask_noecho_stdin(const char *prompt) FAST_FUNC; int bb_ask_y_confirmation_FILE(FILE *fp) FAST_FUNC; int bb_ask_y_confirmation(void) FAST_FUNC; -/* Returns -1 if input is invalid. current_mode is a base for e.g. "u+rw" */ +/* Returns -1 if input is invalid. cur_mode is a base for e.g. "u+rw" */ int bb_parse_mode(const char* s, unsigned cur_mode) FAST_FUNC; /* diff --git a/libbb/parse_mode.c b/libbb/parse_mode.c index dc65860f6..1d238e1e0 100644 --- a/libbb/parse_mode.c +++ b/libbb/parse_mode.c @@ -16,13 +16,14 @@ int FAST_FUNC bb_parse_mode(const char *s, unsigned current_mode) { - static const mode_t who_mask[] = { +/* should be mode_t really, but in all Unixes these constants fit into uint16 */ + static const uint16_t who_mask[] ALIGN2 = { S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO, /* a */ S_ISUID | S_IRWXU, /* u */ S_ISGID | S_IRWXG, /* g */ S_IRWXO /* o */ }; - static const mode_t perm_mask[] = { + static const uint16_t perm_mask[] ALIGN2 = { S_IRUSR | S_IRGRP | S_IROTH, /* r */ S_IWUSR | S_IWGRP | S_IWOTH, /* w */ S_IXUSR | S_IXGRP | S_IXOTH, /* x */ -- cgit v1.2.3-55-g6feb From 59ac467dc6429a48522ef7fbe40fcd819563e49a Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 17 Sep 2021 01:13:58 +0200 Subject: libbb: eliminate a static data array in bb_mode_string() function old new delta print_stat 861 869 +8 header_verbose_list_ar 73 77 +4 display_single 975 979 +4 header_verbose_list 237 239 +2 bb_mode_string 124 115 -9 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 4/1 up/down: 18/-9) Total: 9 bytes text data bss dec hex filename 1043136 559 5052 1048747 1000ab busybox_old 1043153 559 5020 1048732 10009c busybox_unstripped Signed-off-by: Denys Vlasenko --- archival/ar.c | 3 ++- archival/libarchive/header_verbose_list.c | 5 +++-- coreutils/chmod.c | 3 ++- coreutils/ls.c | 3 ++- coreutils/stat.c | 6 ++++-- include/libbb.h | 2 +- libbb/mode_string.c | 6 ++---- 7 files changed, 16 insertions(+), 12 deletions(-) diff --git a/archival/ar.c b/archival/ar.c index 71f949e79..320cbae72 100644 --- a/archival/ar.c +++ b/archival/ar.c @@ -196,9 +196,10 @@ static int write_ar_archive(archive_handle_t *handle) static void FAST_FUNC header_verbose_list_ar(const file_header_t *file_header) { - const char *mode = bb_mode_string(file_header->mode); + char mode[12]; char *mtime; + bb_mode_string(mode, file_header->mode); mtime = ctime(&file_header->mtime); mtime[16] = ' '; memmove(&mtime[17], &mtime[20], 4); diff --git a/archival/libarchive/header_verbose_list.c b/archival/libarchive/header_verbose_list.c index be5140f8b..a575a08a0 100644 --- a/archival/libarchive/header_verbose_list.c +++ b/archival/libarchive/header_verbose_list.c @@ -9,6 +9,7 @@ void FAST_FUNC header_verbose_list(const file_header_t *file_header) { struct tm tm_time; struct tm *ptm = &tm_time; //localtime(&file_header->mtime); + char modestr[12]; #if ENABLE_FEATURE_TAR_UNAME_GNAME char uid[sizeof(int)*3 + 2]; @@ -29,7 +30,7 @@ void FAST_FUNC header_verbose_list(const file_header_t *file_header) group = utoa(file_header->gid); } printf("%s %s/%s %9"OFF_FMT"u %4u-%02u-%02u %02u:%02u:%02u %s", - bb_mode_string(file_header->mode), + bb_mode_string(modestr, file_header->mode), user, group, file_header->size, @@ -46,7 +47,7 @@ void FAST_FUNC header_verbose_list(const file_header_t *file_header) localtime_r(&file_header->mtime, ptm); printf("%s %u/%u %9"OFF_FMT"u %4u-%02u-%02u %02u:%02u:%02u %s", - bb_mode_string(file_header->mode), + bb_mode_string(modestr, file_header->mode), (unsigned)file_header->uid, (unsigned)file_header->gid, file_header->size, diff --git a/coreutils/chmod.c b/coreutils/chmod.c index 8e3e1387e..5832cc51b 100644 --- a/coreutils/chmod.c +++ b/coreutils/chmod.c @@ -91,8 +91,9 @@ static int FAST_FUNC fileAction(struct recursive_state *state, || (OPT_CHANGED && (statbuf->st_mode & 07777) != (newmode & 07777)) ) { + char modestr[12]; printf("mode of '%s' changed to %04o (%s)\n", fileName, - newmode & 07777, bb_mode_string(newmode)+1); + newmode & 07777, bb_mode_string(modestr, newmode)+1); } return TRUE; } diff --git a/coreutils/ls.c b/coreutils/ls.c index 9e8561606..9a1264e65 100644 --- a/coreutils/ls.c +++ b/coreutils/ls.c @@ -503,7 +503,8 @@ static NOINLINE unsigned display_single(const struct dnode *dn) column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1)); if (opt & OPT_l) { /* long listing: show mode */ - column += printf("%-10s ", (char *) bb_mode_string(dn->dn_mode)); + char modestr[12]; + column += printf("%-10s ", (char *) bb_mode_string(modestr, dn->dn_mode)); /* long listing: show number of links */ column += printf("%4lu ", (long) dn->dn_nlink); /* long listing: show user/group */ diff --git a/coreutils/stat.c b/coreutils/stat.c index a8393468e..2c2909e7e 100644 --- a/coreutils/stat.c +++ b/coreutils/stat.c @@ -339,7 +339,8 @@ static void FAST_FUNC print_stat(char *pformat, const char m, strcat(pformat, "lo"); printf(pformat, (unsigned long) (statbuf->st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO))); } else if (m == 'A') { - printfs(pformat, bb_mode_string(statbuf->st_mode)); + char modestr[12]; + printfs(pformat, bb_mode_string(modestr, statbuf->st_mode)); } else if (m == 'f') { strcat(pformat, "lx"); printf(pformat, (unsigned long) statbuf->st_mode); @@ -702,6 +703,7 @@ static bool do_stat(const char *filename, const char *format) bb_putchar('\n'); # endif } else { + char modestr[12]; char *linkname = NULL; struct passwd *pw_ent; struct group *gw_ent; @@ -736,7 +738,7 @@ static bool do_stat(const char *filename, const char *format) bb_putchar('\n'); printf("Access: (%04lo/%10.10s) Uid: (%5lu/%8s) Gid: (%5lu/%8s)\n", (unsigned long) (statbuf.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)), - bb_mode_string(statbuf.st_mode), + bb_mode_string(modestr, statbuf.st_mode), (unsigned long) statbuf.st_uid, (pw_ent != NULL) ? pw_ent->pw_name : "UNKNOWN", (unsigned long) statbuf.st_gid, diff --git a/include/libbb.h b/include/libbb.h index 6727c22b2..b0312e5d4 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -442,7 +442,7 @@ void *xmmap_anon(size_t size) FAST_FUNC; //TODO: supply a pointer to char[11] buffer (avoid statics)? -extern const char *bb_mode_string(mode_t mode) FAST_FUNC; +extern char *bb_mode_string(char buf[12], mode_t mode) FAST_FUNC; extern int is_directory(const char *name, int followLinks) FAST_FUNC; enum { /* cp.c, mv.c, install.c depend on these values. CAREFUL when changing them! */ FILEUTILS_PRESERVE_STATUS = 1 << 0, /* -p */ diff --git a/libbb/mode_string.c b/libbb/mode_string.c index 9a286f3ff..2b829016f 100644 --- a/libbb/mode_string.c +++ b/libbb/mode_string.c @@ -43,9 +43,8 @@ static const char type_chars[16] ALIGN1 = "?pc?d?b?-?l?s???"; /***************************************** 0123456789abcdef */ static const char mode_chars[7] ALIGN1 = "rwxSTst"; -const char* FAST_FUNC bb_mode_string(mode_t mode) +char* FAST_FUNC bb_mode_string(char buf[12], mode_t mode) { - static char buf[12]; char *p = buf; int i, j, k; @@ -83,9 +82,8 @@ static const char type_chars[16] ALIGN1 = "?pc?d?b?-?l?s???"; /********************************** 0123456789abcdef */ static const char mode_chars[7] ALIGN1 = "rwxSTst"; -const char* FAST_FUNC bb_mode_string(mode_t mode) +char* FAST_FUNC bb_mode_string(char buf[12], mode_t mode) { - static char buf[12]; char *p = buf; int i, j, k, m; -- cgit v1.2.3-55-g6feb From 3a65435eaa845d45bbea176701726f27a88e8498 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Fri, 17 Sep 2021 10:16:06 +0100 Subject: libbb: ensure mode_string is NUL terminated If the mode_string array is no longer static we can't rely on it being NUL terminated. function old new delta bb_mode_string 115 118 +3 Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- libbb/mode_string.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/libbb/mode_string.c b/libbb/mode_string.c index 2b829016f..8c12b4684 100644 --- a/libbb/mode_string.c +++ b/libbb/mode_string.c @@ -66,10 +66,7 @@ char* FAST_FUNC bb_mode_string(char buf[12], mode_t mode) i += 4; } while (i < 12); - /* Note: We don't bother with nul termination because bss initialization - * should have taken care of that for us. If the user scribbled in buf - * memory, they deserve whatever happens. But we'll at least assert. */ - assert(buf[10] == 0); + buf[10] = '\0'; return buf; } @@ -79,7 +76,7 @@ char* FAST_FUNC bb_mode_string(char buf[12], mode_t mode) /* The previous version used "0pcCd?bB-?l?s???". However, the '0', 'C', * and 'B' types don't appear to be available on linux. So I removed them. */ static const char type_chars[16] ALIGN1 = "?pc?d?b?-?l?s???"; -/********************************** 0123456789abcdef */ +/***************************************** 0123456789abcdef */ static const char mode_chars[7] ALIGN1 = "rwxSTst"; char* FAST_FUNC bb_mode_string(char buf[12], mode_t mode) @@ -107,10 +104,7 @@ char* FAST_FUNC bb_mode_string(char buf[12], mode_t mode) } } while (i < 3); - /* Note: We don't bother with nul termination because bss initialization - * should have taken care of that for us. If the user scribbled in buf - * memory, they deserve whatever happens. But we'll at least assert. */ - assert(buf[10] == 0); + buf[10] = '\0'; return buf; } -- cgit v1.2.3-55-g6feb From 6279aec03d4677424408a515a57aa83664b81311 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 17 Sep 2021 17:10:38 +0200 Subject: libbb: clarify what bb_mode_string() generates Signed-off-by: Denys Vlasenko --- coreutils/ls.c | 2 +- include/libbb.h | 5 ++--- libbb/mode_string.c | 20 +++++++++----------- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/coreutils/ls.c b/coreutils/ls.c index 9a1264e65..48f5eb482 100644 --- a/coreutils/ls.c +++ b/coreutils/ls.c @@ -504,7 +504,7 @@ static NOINLINE unsigned display_single(const struct dnode *dn) if (opt & OPT_l) { /* long listing: show mode */ char modestr[12]; - column += printf("%-10s ", (char *) bb_mode_string(modestr, dn->dn_mode)); + column += printf("%-10s ", bb_mode_string(modestr, dn->dn_mode)); /* long listing: show number of links */ column += printf("%4lu ", (long) dn->dn_nlink); /* long listing: show user/group */ diff --git a/include/libbb.h b/include/libbb.h index b0312e5d4..dfcaa05ec 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -440,9 +440,8 @@ void *xmmap_anon(size_t size) FAST_FUNC; # define cached_pagesize(var) (var) #endif - -//TODO: supply a pointer to char[11] buffer (avoid statics)? -extern char *bb_mode_string(char buf[12], mode_t mode) FAST_FUNC; +/* Generate ls-style "mode string" like "-rwsr-xr-x" or "drwxrwxrwt" */ +extern char *bb_mode_string(char buf[11], mode_t mode) FAST_FUNC; extern int is_directory(const char *name, int followLinks) FAST_FUNC; enum { /* cp.c, mv.c, install.c depend on these values. CAREFUL when changing them! */ FILEUTILS_PRESERVE_STATUS = 1 << 0, /* -p */ diff --git a/libbb/mode_string.c b/libbb/mode_string.c index 8c12b4684..52abe66f7 100644 --- a/libbb/mode_string.c +++ b/libbb/mode_string.c @@ -16,16 +16,18 @@ #error permission bitflag value assumption(s) violated! #endif +/* Generate ls-style "mode string" like "-rwsr-xr-x" or "drwxrwxrwt" */ + #if ( S_IFSOCK!= 0140000 ) || ( S_IFLNK != 0120000 ) \ || ( S_IFREG != 0100000 ) || ( S_IFBLK != 0060000 ) \ || ( S_IFDIR != 0040000 ) || ( S_IFCHR != 0020000 ) \ || ( S_IFIFO != 0010000 ) -#warning mode type bitflag value assumption(s) violated! falling back to larger version +# warning mode type bitflag value assumption(s) violated! falling back to larger version -#if (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX) == 07777 -#undef mode_t -#define mode_t unsigned short -#endif +# if (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX) == 07777 +# undef mode_t +# define mode_t unsigned short +# endif static const mode_t mode_flags[] ALIGN4 = { S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID, @@ -33,17 +35,13 @@ static const mode_t mode_flags[] ALIGN4 = { S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX }; -/* The static const char arrays below are duplicated for the two cases - * because moving them ahead of the mode_flags declaration cause a text - * size increase with the gcc version I'm using. */ - /* The previous version used "0pcCd?bB-?l?s???". However, the '0', 'C', * and 'B' types don't appear to be available on linux. So I removed them. */ static const char type_chars[16] ALIGN1 = "?pc?d?b?-?l?s???"; /***************************************** 0123456789abcdef */ static const char mode_chars[7] ALIGN1 = "rwxSTst"; -char* FAST_FUNC bb_mode_string(char buf[12], mode_t mode) +char* FAST_FUNC bb_mode_string(char buf[11], mode_t mode) { char *p = buf; @@ -79,7 +77,7 @@ static const char type_chars[16] ALIGN1 = "?pc?d?b?-?l?s???"; /***************************************** 0123456789abcdef */ static const char mode_chars[7] ALIGN1 = "rwxSTst"; -char* FAST_FUNC bb_mode_string(char buf[12], mode_t mode) +char* FAST_FUNC bb_mode_string(char buf[11], mode_t mode) { char *p = buf; -- cgit v1.2.3-55-g6feb From 6d2463ac01dd88cc4359ebbeae9cd757ce037c2b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 17 Sep 2021 17:32:30 +0200 Subject: libbb/lineedit: do not escape %^=+}]:, escape ~? in tab completion function old new delta .rodata 104185 104180 -5 Signed-off-by: Denys Vlasenko --- libbb/lineedit.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/libbb/lineedit.c b/libbb/lineedit.c index 68d19e127..e8d721e61 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -1133,7 +1133,16 @@ static void showfiles(void) static const char *is_special_char(char c) { - return strchr(" `\"#$%^&*()=+{}[]:;'|\\<>", c); + // {: It's mandatory to escape { only if entire name is "{" + // (otherwise it's not special. Example: file named "{ " + // can be escaped simply as "{\ "; "{a" or "a{" need no escaping), + // or if shell supports brace expansion + // (ash doesn't, hush optionally does). + // (): unlike {, shell treats () specially even in contexts + // where they clearly are not valid (e.g. "echo )" is an error). + // #: needs escaping to not start a shell comment. + return strchr(" `'\"\\#$~?*[{()&;|<>", c); + // Used to also have %^=+}]: but not necessary to escape? } static char *quote_special_chars(char *found) -- cgit v1.2.3-55-g6feb From f27a6a94a7fb172a6768bc450dbdec68f15bc78f Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Sat, 18 Sep 2021 08:40:44 +0100 Subject: libbb: code shrink parse_datestr (again) Commit 9fe1548bb (date,touch: allow timezone offsets in dates) mentioned the similarity between '@' format dates and those with timezone offsets. It didn't notice that as a result there's common code which can be shared. function old new delta parse_datestr 730 687 -43 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-43) Total: -43 bytes Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- libbb/time.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/libbb/time.c b/libbb/time.c index 41a69c754..f09ef5d52 100644 --- a/libbb/time.c +++ b/libbb/time.c @@ -13,6 +13,7 @@ int FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm) { char end = '\0'; + time_t t; #if ENABLE_DESKTOP /* * strptime is BIG: ~1k in uclibc, ~10k in glibc @@ -29,10 +30,10 @@ int FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm) "%b %d %T %Y" "\0" /* month_name d HH:MM:SS YYYY */ "%Y-%m-%d %R" "\0" /* yyyy-mm-dd HH:MM */ "%Y-%m-%d %T" "\0" /* yyyy-mm-dd HH:MM:SS */ -#if ENABLE_FEATURE_TIMEZONE +# if ENABLE_FEATURE_TIMEZONE "%Y-%m-%d %R %z" "\0" /* yyyy-mm-dd HH:MM TZ */ "%Y-%m-%d %T %z" "\0" /* yyyy-mm-dd HH:MM:SS TZ */ -#endif +# endif "%Y-%m-%d %H" "\0" /* yyyy-mm-dd HH */ "%Y-%m-%d" "\0" /* yyyy-mm-dd */ /* extra NUL */; @@ -45,11 +46,8 @@ int FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm) while (*fmt) { endp = strptime(date_str, fmt, ptm); if (endp && *endp == '\0') { -#if ENABLE_FEATURE_TIMEZONE +# if ENABLE_FEATURE_TIMEZONE if (strchr(fmt, 'z')) { - time_t t; - struct tm *utm; - /* we have timezone offset: obtain Unix time_t */ ptm->tm_sec -= ptm->tm_gmtoff; ptm->tm_isdst = 0; @@ -57,13 +55,9 @@ int FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm) if (t == (time_t)-1) break; /* convert Unix time_t to struct tm in user's locale */ - utm = localtime(&t); - if (!utm) - break; - *ptm = *utm; - return 0; + goto localise; } -#endif +# endif return 1; } *ptm = save; @@ -141,13 +135,14 @@ int FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm) } else #endif /* ENABLE_DESKTOP */ if (date_str[0] == '@') { - time_t t; if (sizeof(t) <= sizeof(long)) t = bb_strtol(date_str + 1, NULL, 10); else /* time_t is 64 bits but longs are smaller */ t = bb_strtoll(date_str + 1, NULL, 10); if (!errno) { - struct tm *lt = localtime(&t); + struct tm *lt; + IF_FEATURE_TIMEZONE(localise:) + lt = localtime(&t); if (lt) { *ptm = *lt; return 0; -- cgit v1.2.3-55-g6feb From 56f0e886db0543a27f369d7f95eb9da2fb3d069c Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Tue, 21 Sep 2021 08:42:58 +0100 Subject: cal: implement -m Some people prefer the week to start on Monday. Add the '-m' option to support this. function old new delta cal_main 926 966 +40 day_array 316 337 +21 packed_usage 34151 34158 +7 .rodata 99224 99225 +1 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 4/0 up/down: 69/0) Total: 69 bytes Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- util-linux/cal.c | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/util-linux/cal.c b/util-linux/cal.c index 006bc817b..6ba6ebf98 100644 --- a/util-linux/cal.c +++ b/util-linux/cal.c @@ -27,10 +27,11 @@ /* http://www.opengroup.org/onlinepubs/007904975/utilities/cal.html */ //usage:#define cal_trivial_usage -//usage: "[-jy] [[MONTH] YEAR]" +//usage: "[-jmy] [[MONTH] YEAR]" //usage:#define cal_full_usage "\n\n" //usage: "Display a calendar\n" //usage: "\n -j Use julian dates" +//usage: "\n -m Week starts on Monday" //usage: "\n -y Display the entire year" #include "libbb.h" @@ -38,6 +39,8 @@ /* We often use "unsigned" instead of "int", it's easier to div on most CPUs */ +#define SUNDAY 0 +#define MONDAY 1 #define THURSDAY 4 /* for reformation */ #define SATURDAY 6 /* 1 Jan 1 was a Saturday */ @@ -81,7 +84,7 @@ static int leap_year(unsigned yr) ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr)) static void center(char *, unsigned, unsigned); -static void day_array(unsigned, unsigned, unsigned *); +static void day_array(unsigned, unsigned, unsigned, unsigned *); static void trim_trailing_spaces_and_print(char *); static void blank_string(char *buf, size_t buflen); @@ -93,12 +96,18 @@ static char *build_row(char *p, unsigned *dp); #define J_WEEK_LEN (WEEK_LEN + 7) #define HEAD_SEP 2 /* spaces between day headings */ +enum { + OPT_JULIAN = (1 << 0), + OPT_MONDAY = (1 << 1), + OPT_YEAR = (1 << 2), +}; + int cal_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int cal_main(int argc UNUSED_PARAM, char **argv) { struct tm zero_tm; time_t now; - unsigned month, year, flags, i; + unsigned month, year, flags, i, weekstart; char *month_names[12]; /* normal heading: */ /* "Su Mo Tu We Th Fr Sa" */ @@ -110,10 +119,11 @@ int cal_main(int argc UNUSED_PARAM, char **argv) init_unicode(); - flags = getopt32(argv, "jy"); - /* This sets julian = flags & 1: */ - option_mask32 &= 1; + flags = getopt32(argv, "jmy"); + /* This sets julian = flags & OPT_JULIAN: */ + option_mask32 &= OPT_JULIAN; month = 0; + weekstart = (flags & OPT_MONDAY) ? MONDAY : SUNDAY; argv += optind; if (!argv[0]) { @@ -122,7 +132,7 @@ int cal_main(int argc UNUSED_PARAM, char **argv) time(&now); ptm = localtime(&now); year = ptm->tm_year + 1900; - if (!(flags & 2)) { /* no -y */ + if (!(flags & OPT_YEAR)) { /* no -y */ month = ptm->tm_mon + 1; } } else { @@ -130,7 +140,7 @@ int cal_main(int argc UNUSED_PARAM, char **argv) if (argv[2]) { bb_show_usage(); } - if (!(flags & 2)) { /* no -y */ + if (!(flags & OPT_YEAR)) { /* no -y */ month = xatou_range(*argv, 1, 12); } argv++; @@ -148,7 +158,7 @@ int cal_main(int argc UNUSED_PARAM, char **argv) month_names[i] = xstrdup(buf); if (i < 7) { - zero_tm.tm_wday = i; + zero_tm.tm_wday = (i + weekstart) % 7; /* abbreviated weekday name according to locale */ strftime(buf, sizeof(buf), "%a", &zero_tm); #if ENABLE_UNICODE_SUPPORT @@ -173,7 +183,7 @@ int cal_main(int argc UNUSED_PARAM, char **argv) unsigned *dp = days; char lineout[30]; - day_array(month, year, dp); + day_array(month, year, weekstart, dp); len = sprintf(lineout, "%s %u", month_names[month - 1], year); printf("%*s%s\n%s\n", ((7*julian + WEEK_LEN) - len) / 2, "", @@ -197,7 +207,7 @@ int cal_main(int argc UNUSED_PARAM, char **argv) ); puts("\n"); /* two \n's */ for (i = 0; i < 12; i++) { - day_array(i + 1, year, days[i]); + day_array(i + 1, year, weekstart, days[i]); } blank_string(lineout, sizeof(lineout)); week_len = WEEK_LEN + julian * (J_WEEK_LEN - WEEK_LEN); @@ -233,7 +243,8 @@ int cal_main(int argc UNUSED_PARAM, char **argv) * out end to end. You would have 42 numbers or spaces. This routine * builds that array for any month from Jan. 1 through Dec. 9999. */ -static void day_array(unsigned month, unsigned year, unsigned *days) +static void day_array(unsigned month, unsigned year, unsigned weekstart, + unsigned *days) { unsigned long temp; unsigned i; @@ -249,7 +260,7 @@ static void day_array(unsigned month, unsigned year, unsigned *days) size_t oday = 0; do { - days[oday+2] = sep1752[oday] + j_offset; + days[oday+2-weekstart] = sep1752[oday] + j_offset; } while (++oday < sizeof(sep1752)); return; @@ -280,6 +291,7 @@ static void day_array(unsigned month, unsigned year, unsigned *days) } else { dw = (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7); } + dw = (dw - weekstart + 7) % 7; if (!julian) { day = 1; -- cgit v1.2.3-55-g6feb