1 /*** strptime.c -- a shell interface to strptime(3)
2  *
3  * Copyright (C) 2011-2016 Sebastian Freundt
4  *
5  * Author:  Sebastian Freundt <freundt@ga-group.nl>
6  *
7  * This file is part of dateutils.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * 3. Neither the name of the author nor the names of any contributors
21  *    may be used to endorse or promote products derived from this
22  *    software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27  * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
31  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
32  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
33  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
34  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  *
36  ***/
37 
38 #if defined HAVE_CONFIG_H
39 # include "config.h"
40 #endif	/* HAVE_CONFIG_H */
41 #include <stdint.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 #include <string.h>
45 #include <stdio.h>
46 #include <sys/time.h>
47 #include <time.h>
48 #include <locale.h>
49 
50 #include "dt-io.h"
51 #include "prchunk.h"
52 
53 const char *prog = "strptime";
54 
55 
56 static int
pars_line(struct tm * tm,const char * const * fmt,size_t nfmt,const char * line)57 pars_line(struct tm *tm, const char *const *fmt, size_t nfmt, const char *line)
58 {
59 	for (size_t i = 0; i < nfmt; i++) {
60 		if (fmt[i] && strptime(line, fmt[i], tm) != NULL) {
61 			return 0;
62 		}
63 	}
64 	return -1;
65 }
66 
67 static void
prnt_line(const char * ofmt,struct tm * tm)68 prnt_line(const char *ofmt, struct tm *tm)
69 {
70 	char res[256];
71 	strftime(res, sizeof(res), ofmt, tm);
72 	fputs(res, stdout);
73 	return;
74 }
75 
76 static inline __attribute__((pure, const)) struct tm
__tm_initialiser(void)77 __tm_initialiser(void)
78 {
79 #if defined HAVE_SLOPPY_STRUCTS_INIT
80 	static const struct tm res = {};
81 #else
82 	static const struct tm res;
83 #endif	/* HAVE_SLOPPY_STRUCTS_INIT */
84 	return res;
85 }
86 
87 static void
proc_line(const char * ln,const char * const * fmt,size_t nfmt,const char * ofmt,int quietp)88 proc_line(
89 	const char *ln, const char *const *fmt, size_t nfmt,
90 	const char *ofmt,
91 	int quietp)
92 {
93 	struct tm tm = __tm_initialiser();
94 
95 	if (pars_line(&tm, fmt, nfmt, ln) < 0) {
96 		if (!quietp) {
97 			dt_io_warn_strpdt(ln);
98 		}
99 	} else {
100 		prnt_line(ofmt, &tm);
101 	}
102 	return;
103 }
104 
105 static void
proc_lines(const char * const * fmt,size_t nfmt,const char * ofmt,int quietp)106 proc_lines(const char *const *fmt, size_t nfmt, const char *ofmt, int quietp)
107 {
108 	size_t lno = 0;
109 	void *pctx;
110 
111 	/* using the prchunk reader now */
112 	if ((pctx = init_prchunk(STDIN_FILENO)) == NULL) {
113 		serror("Error: could not open stdin");
114 		return;
115 	}
116 	while (prchunk_fill(pctx) >= 0) {
117 		for (char *line; prchunk_haslinep(pctx); lno++) {
118 			(void)prchunk_getline(pctx, &line);
119 			/* check if line matches */
120 			proc_line(line, fmt, nfmt, ofmt, quietp);
121 		}
122 	}
123 	/* get rid of resources */
124 	free_prchunk(pctx);
125 	return;
126 }
127 
128 
129 #include "strptime.yucc"
130 
131 int
main(int argc,char * argv[])132 main(int argc, char *argv[])
133 {
134 	static char dflt_fmt[] = "%Y-%m-%d\n\0H:%M:%S %Z\n";
135 	yuck_t argi[1U];
136 	char *outfmt = dflt_fmt;
137 	char **infmt;
138 	size_t ninfmt;
139 	char **input;
140 	size_t ninput;
141 	int quietp;
142 	int res = 0;
143 
144 	if (yuck_parse(argi, argc, argv)) {
145 		res = 1;
146 		goto out;
147 	}
148 
149 	if (argi->format_arg) {
150 		outfmt = argi->format_arg;
151 		/* unescape sequences, maybe */
152 		if (argi->backslash_escapes_flag) {
153 			dt_io_unescape(outfmt);
154 		}
155 	} else if (argi->time_flag) {
156 		outfmt[8] = ' ';
157 		outfmt[9] = '%';
158 	}
159 
160 	if (!argi->input_format_nargs) {
161 		infmt = argi->args;
162 		ninfmt = argi->nargs;
163 		input = NULL;
164 		ninput = 0;
165 	} else {
166 		infmt = argi->input_format_args;
167 		ninfmt = argi->input_format_nargs;
168 		input = argi->args;
169 		ninput = argi->nargs;
170 	}
171 	/* get quiet predicate */
172 	quietp = argi->quiet_flag;
173 
174 	/* set locale specific/independent behaviour */
175 	with (const char *loc) {
176 		if (!argi->locale_flag) {
177 			loc = "C";
178 			/* we need to null out TZ for UTC */
179 			setenv("TZ", "", 1);
180 		} else {
181 			loc = "";
182 		}
183 		/* actually set our findings in stone */
184 		setlocale(LC_TIME, loc);
185 		tzset();
186 	}
187 
188 	/* get lines one by one, apply format string and print date/time */
189 	if (ninput == 0) {
190 		/* read from stdin */
191 		proc_lines((const char*const*)infmt, ninfmt, outfmt, quietp);
192 	} else {
193 		const char *const *cinfmt = (const char*const*)infmt;
194 		for (size_t i = 0; i < ninput; i++) {
195 			proc_line(input[i], cinfmt, ninfmt, outfmt, quietp);
196 		}
197 	}
198 
199 out:
200 	yuck_free(argi);
201 	return res;
202 }
203 
204 /* strptime.c ends here */
205