mail.sh: eliminate dependency on Git 1.8.5 or later
[girocco/readme.git] / src / strftime.c
blobc639a659c0f5852ace38bace5d3776db80e89662
1 /*
3 strftime.c -- provide strftime functionality on the command line
4 Copyright (C) 2016 Kyle J. McKay. All rights reserved.
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 #define VERSION \
23 "strftime version 1.1.0\n" \
24 "Copyright (C) 2016 Kyle J. McKay <mackyle at gmail dot com>\n" \
25 "License GPLv2+: GNU GPL version 2 or later.\n" \
26 "<http://gnu.org/licenses/gpl2.html>\n" \
27 "This is free software: you are free to change and redistribute it.\n" \
28 "There is NO WARRANTY, to the extent permitted by law.\n"
30 #define USAGE \
31 "Usage: strftime [option...] [\"strftime(3) format\" [<epochsecs> [<offset>]]]\n" \
32 " --help/-h show this help\n" \
33 " --version/-V show version/license info\n" \
34 " --locale/-l use default locale rather than \"C\" locale\n" \
35 " --adjust/-a n add n (which may be negative) to <epochsecs> before formatting\n" \
36 " -- terminate options, next arg is format even if it starts with -\n" \
37 " <epochsecs> if omitted or empty string ('') use current time\n" \
38 " <offset> must be [+|-]HH[MM[SS]] same meaning as strftime(3) '%z' value\n" \
39 "If \"strftime(3) format\" is omitted (or '') then \"%a, %e %b %Y %T %z\" is used.\n" \
40 "If <offset> is omitted default timezone (taking TZ into account) will be used.\n" \
41 "If <offset> is NOT omitted then a strftime(3) '%Z' value will format as either\n" \
42 "\"unknown\" or \"UTC\" (for a zero offset). Note that except for the time zone\n" \
43 "name, `date` and `strftime '%a %b %e %T %Z %Y' $(date +'%s %z')` should match.\n" \
44 "If --locale is NOT used the default format strips leading spaces from %e value.\n"
46 #define _XOPEN_SOURCE
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <locale.h>
50 #include <string.h>
51 #include <time.h>
53 #define DEFAULT_FORMAT "%a, %e %b %Y %T %z"
54 #define MAXLENGTH 4096
56 static char buff[MAXLENGTH];
57 static char tzval[20]; /* TZ=unknown+HH:MM:SS\0 */
59 int main(int argc, char *argv[])
61 int arg = 1;
62 char *bp = buff;
63 time_t t = time(NULL);
64 const char *fmt = DEFAULT_FORMAT;
65 int defaultformat = 1;
66 int dosetlocale = 0;
67 struct tm localvals;
68 long adjust = 0;
70 while (arg < argc && *argv[arg] == '-') {
71 if (!strcmp(argv[arg], "--help") || !strcmp(argv[arg], "-h")) {
72 printf("%s", USAGE);
73 return 0;
75 if (!strcmp(argv[arg], "--version") || !strcmp(argv[arg], "-V")) {
76 printf("%s", VERSION);
77 return 0;
79 if (!strcmp(argv[arg], "--locale") || !strcmp(argv[arg], "-l")) {
80 dosetlocale = 1;
81 ++arg;
82 continue;
84 if (!strcmp(argv[arg], "--adjust") || !strcmp(argv[arg], "-a")) {
85 char *end;
87 if (++arg >= argc || !*argv[arg]) {
88 fprintf(stderr, "strftime: missing --adjust value "
89 "(see strftime -h)\n");
90 return 1;
92 adjust = strtol(argv[arg], &end, 10);
93 if (*end) {
94 fprintf(stderr, "strftime: invalid number: %s\n",
95 argv[arg]);
96 return 2;
98 ++arg;
99 continue;
101 if (!strcmp(argv[arg], "--")) {
102 ++arg;
103 break;
105 fprintf(stderr, "strftime: invalid option: %s (see strftime -h)\n",
106 argv[arg]);
107 return 1;
109 if (argc - arg > 3) {
110 fprintf(stderr, "strftime: invalid arguments (see strftime -h)\n");
111 return 1;
113 if (argc - arg >= 1) {
114 if (*argv[arg]) {
115 fmt = argv[arg];
116 defaultformat = 0;
118 if (argc - arg >= 2) {
119 if (*argv[arg+1]) {
120 char *end;
121 long l = strtol(argv[arg+1], &end, 10);
123 if (*end) {
124 fprintf(stderr, "strftime: invalid number: %s\n",
125 argv[arg+1]);
126 return 2;
128 t = (time_t)l;
130 if (argc - arg >= 3) {
131 const char *o = argv[arg+2];
132 size_t l = strlen(o);
133 char tzsign = '-';
134 char *d = tzval;
136 if (*o == '+' || *o == '-') {
137 tzsign = (*o == '+') ? '-' : '+';
138 ++o;
139 --l;
141 if (l < 2 || l > 6 || (l & 0x1) || l != strspn(o, "0123456789")) {
142 fprintf(stderr, "strftime: invalid offset: %s\n",
143 argv[arg+2]);
144 return 2;
146 if (l == strspn(o, "0")) {
147 memcpy(d, "TZ=UTC", 6);
148 d += 6;
149 } else {
150 memcpy(d, "TZ=unknown", 10);
151 d += 10;
152 *d++ = tzsign;
153 *d++ = *o++;
154 *d++ = *o++;
155 if (*o) {
156 *d++ = ':';
157 *d++ = *o++;
158 *d++ = *o++;
160 if (*o) {
161 *d++ = ':';
162 *d++ = *o++;
163 *d++ = *o++;
166 *d = '\0';
167 putenv(tzval);
171 if (dosetlocale) {
172 setlocale(LC_ALL, "");
174 tzset();
175 t += (time_t)adjust;
176 localvals = *localtime(&t);
177 if (!strftime(buff, sizeof(buff), fmt, &localvals)) {
178 fprintf(stderr, "strftime: format string too long\n");
179 return 3;
181 if (defaultformat && !dosetlocale && buff[3] == ',' && buff[5] == ' ') {
182 memmove(buff+1, buff, 4);
183 ++bp;
185 printf("%s\n", bp);
186 return 0;