1/* -*- c -*- */ 2#if !defined INCLUDED_yuck_h_ 3#define INCLUDED_yuck_h_ 4 5#include <stddef.h> 6 7#define YUCK_OPTARG_NONE ((void*)0x1U) 8 9enum yuck_cmds_e { 10 /* value used when no command was specified */ 11 STRPTIME_CMD_NONE = 0U, 12 13 /* actual commands */ 14 15 /* convenience identifiers */ 16 YUCK_NOCMD = STRPTIME_CMD_NONE, 17 YUCK_NCMDS = STRPTIME_CMD_NONE 18}; 19 20 21 22typedef struct yuck_s yuck_t; 23 24 25 26/* generic struct */ 27struct yuck_s { 28 enum yuck_cmds_e cmd; 29 30 /* left-over arguments, 31 * the command string is never a part of this */ 32 size_t nargs; 33 char **args; 34 35 /* slots common to all commands */ 36 37 /* help is handled automatically */ 38 /* version is handled automatically */ 39 unsigned int time_flag; 40 unsigned int quiet_flag; 41 char *format_arg; 42 size_t input_format_nargs; char **input_format_args; 43 unsigned int backslash_escapes_flag; 44 unsigned int sed_mode_flag; 45 unsigned int locale_flag; 46}; 47 48 49static __attribute__((nonnull(1))) int 50yuck_parse(yuck_t*, int argc, char *argv[]); 51static __attribute__((nonnull(1))) void yuck_free(yuck_t*); 52 53static __attribute__((nonnull(1))) void yuck_auto_help(const yuck_t*); 54static __attribute__((nonnull(1))) void yuck_auto_usage(const yuck_t*); 55static __attribute__((nonnull(1))) void yuck_auto_version(const yuck_t*); 56 57/* some hooks */ 58#if defined yuck_post_help 59static __attribute__((nonnull(1))) void yuck_post_help(const yuck_t*); 60#endif /* yuck_post_help */ 61 62#if defined yuck_post_usage 63static __attribute__((nonnull(1))) void yuck_post_usage(const yuck_t*); 64#endif /* yuck_post_usage */ 65 66#if defined yuck_post_version 67static __attribute__((nonnull(1))) void yuck_post_version(const yuck_t*); 68#endif /* yuck_post_version */ 69 70#endif /* INCLUDED_yuck_h_ */ 71/* -*- c -*- */ 72#if defined HAVE_CONFIG_H 73# include "config.h" 74#endif /* HAVE_CONFIG_H */ 75#if defined HAVE_VERSION_H 76# include "version.h" 77#endif /* HAVE_VERSION_H */ 78#include <stdlib.h> 79#include <stdbool.h> 80#include <string.h> 81#include <stdio.h> 82#include <limits.h> 83 84#if defined __INTEL_COMPILER 85# pragma warning (push) 86# pragma warning (disable:177) 87# pragma warning (disable:111) 88# pragma warning (disable:3280) 89#elif defined __GNUC__ 90# if __GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6 91# pragma GCC diagnostic push 92# endif /* GCC version */ 93# pragma GCC diagnostic ignored "-Wunused-label" 94# pragma GCC diagnostic ignored "-Wunused-variable" 95# pragma GCC diagnostic ignored "-Wunused-function" 96# pragma GCC diagnostic ignored "-Wshadow" 97#endif /* __INTEL_COMPILER */ 98 99 100static inline bool 101yuck_streqp(const char *s1, const char *s2) 102{ 103 return !strcmp(s1, s2); 104} 105 106/* for multi-args */ 107static inline char** 108yuck_append(char **array, size_t n, char *val) 109{ 110 if (!(n % 16U)) { 111 /* resize */ 112 void *tmp = realloc(array, (n + 16U) * sizeof(*array)); 113 if (tmp == NULL) { 114 free(array); 115 return NULL; 116 } 117 /* otherwise make it persistent */ 118 array = tmp; 119 } 120 array[n] = val; 121 return array; 122} 123 124static enum yuck_cmds_e yuck_parse_cmd(const char *cmd) 125{ 126 if (0) { 127 ; 128 } else { 129 /* error here? */ 130 fprintf(stderr, "strptime: invalid command `%s'\n\ 131Try `--help' for a list of commands.\n", cmd); 132 } 133 return (enum yuck_cmds_e)-1; 134} 135 136 137static int yuck_parse(yuck_t tgt[static 1U], int argc, char *argv[]) 138{ 139 char *op; 140 int i; 141 142 /* we'll have at most this many args */ 143 memset(tgt, 0, sizeof(*tgt)); 144 if ((tgt->args = calloc(argc, sizeof(*tgt->args))) == NULL) { 145 return -1; 146 } 147 for (i = 1; i < argc && tgt->nargs < (size_t)-1; i++) { 148 op = argv[i]; 149 150 switch (*op) { 151 case '-': 152 /* could be an option */ 153 switch (*++op) { 154 default: 155 /* could be glued into one */ 156 for (; *op; op++) { 157 goto shortopt; back_from_shortopt:; 158 } 159 break; 160 case '-': 161 if (*++op == '\0') { 162 i++; 163 goto dashdash; back_from_dashdash:; 164 break; 165 } 166 goto longopt; back_from_longopt:; 167 break; 168 case '\0': 169 goto plain_dash; 170 } 171 break; 172 default: 173 plain_dash: 174 goto arg; back_from_arg:; 175 break; 176 } 177 } 178 if (i < argc) { 179 op = argv[i]; 180 181 if (*op++ == '-' && *op++ == '-' && !*op) { 182 /* another dashdash, filter out */ 183 i++; 184 } 185 } 186 /* has to be here as the max_pargs condition might drive us here */ 187 dashdash: 188 { 189 /* dashdash loop, pile everything on tgt->args 190 * don't check for subcommands either, this is in accordance to 191 * the git tool which won't accept commands after -- */ 192 for (; i < argc; i++) { 193 tgt->args[tgt->nargs++] = argv[i]; 194 } 195 } 196 return 0; 197 198 longopt: 199 { 200 /* split into option and arg part */ 201 char *arg; 202 203 if ((arg = strchr(op, '=')) != NULL) { 204 /* \nul this one out */ 205 *arg++ = '\0'; 206 } 207 208 switch (tgt->cmd) { 209 default: 210 goto STRPTIME_CMD_NONE_longopt; back_from_STRPTIME_CMD_NONE_longopt:; 211 break; 212 } 213 goto back_from_longopt; 214 215 216 STRPTIME_CMD_NONE_longopt: 217 { 218 if (0) { 219 ; 220 } else if (yuck_streqp(op, "help")) { 221 /* invoke auto action and exit */ 222 yuck_auto_help(tgt); 223 goto success; 224 } else if (yuck_streqp(op, "version")) { 225 /* invoke auto action and exit */ 226 yuck_auto_version(tgt); 227 goto success; 228 } else if (yuck_streqp(op, "time")) { 229 tgt->time_flag++; goto xtra_chk; 230 } else if (yuck_streqp(op, "quiet")) { 231 tgt->quiet_flag++; goto xtra_chk; 232 } else if (yuck_streqp(op, "format")) { 233 tgt->format_arg = arg ?: argv[++i]; 234 } else if (yuck_streqp(op, "input-format")) { 235 tgt->input_format_args = 236 yuck_append( 237 tgt->input_format_args, tgt->input_format_nargs++, 238 arg ?: argv[++i]); 239 if (tgt->input_format_args == NULL) { 240 return -1; 241 }; 242 } else if (yuck_streqp(op, "backslash-escapes")) { 243 tgt->backslash_escapes_flag++; goto xtra_chk; 244 } else if (yuck_streqp(op, "sed-mode")) { 245 tgt->sed_mode_flag++; goto xtra_chk; 246 } else if (yuck_streqp(op, "locale")) { 247 tgt->locale_flag++; goto xtra_chk; 248 } else { 249 /* grml */ 250 fprintf(stderr, "strptime: unrecognized option `--%s'\n", op); 251 goto failure; 252 xtra_chk: 253 if (arg != NULL) { 254 fprintf(stderr, "strptime: option `--%s' doesn't allow an argument\n", op); 255 goto failure; 256 } 257 } 258 if (i >= argc) { 259 fprintf(stderr, "strptime: option `--%s' requires an argument\n", op); 260 goto failure; 261 } 262 goto back_from_STRPTIME_CMD_NONE_longopt; 263 } 264 265 } 266 267 shortopt: 268 { 269 char *arg = op + 1U; 270 271 switch (tgt->cmd) { 272 default: 273 goto STRPTIME_CMD_NONE_shortopt; back_from_STRPTIME_CMD_NONE_shortopt:; 274 break; 275 } 276 goto back_from_shortopt; 277 278 279 STRPTIME_CMD_NONE_shortopt: 280 { 281 switch (*op) { 282 case '0': 283 case '1': 284 case '2': 285 case '3': 286 case '4': 287 case '5': 288 case '6': 289 case '7': 290 case '8': 291 case '9': 292 if (op[-1] == '-') { 293 /* literal treatment of numeral */ 294 goto arg; 295 } 296 /*@fallthrough@*/ 297 default: 298 ; 299 ; 300 fprintf(stderr, "strptime: invalid option -%c\n", *op); 301 goto failure; 302 303 304 305 306 case 'h': 307 /* invoke auto action and exit */ 308 yuck_auto_help(tgt); 309 goto success; 310 break; 311 case 'V': 312 /* invoke auto action and exit */ 313 yuck_auto_version(tgt); 314 goto success; 315 break; 316 case 't': 317 tgt->time_flag++; 318 break; 319 case 'q': 320 tgt->quiet_flag++; 321 break; 322 case 'f': 323 tgt->format_arg = *arg 324 ? (op += strlen(arg), arg) 325 : argv[++i]; 326 break; 327 case 'i': 328 tgt->input_format_args = 329 yuck_append( 330 tgt->input_format_args, 331 tgt->input_format_nargs++, 332 *arg ? (op += strlen(arg), arg) : argv[++i]); 333 if (tgt->input_format_args == NULL) { 334 return -1; 335 }; 336 break; 337 case 'e': 338 tgt->backslash_escapes_flag++; 339 break; 340 case 'S': 341 tgt->sed_mode_flag++; 342 break; 343 case 'l': 344 tgt->locale_flag++; 345 break; 346 } 347 if (i >= argc) { 348 fprintf(stderr, "strptime: option `--%s' requires an argument\n", op); 349 goto failure; 350 } 351 goto back_from_STRPTIME_CMD_NONE_shortopt; 352 } 353 354 } 355 356 arg: 357 { 358 if (tgt->cmd || YUCK_NCMDS == 0U) { 359 tgt->args[tgt->nargs++] = argv[i]; 360 } else { 361 /* ah, might be an arg then */ 362 if ((tgt->cmd = yuck_parse_cmd(op)) > YUCK_NCMDS) { 363 return -1; 364 } 365 } 366 goto back_from_arg; 367 } 368 369 failure: 370 { 371 exit(EXIT_FAILURE); 372 } 373 374 success: 375 { 376 exit(EXIT_SUCCESS); 377 } 378} 379 380static void yuck_free(yuck_t tgt[static 1U]) 381{ 382 if (tgt->args != NULL) { 383 /* free despite const qualifier */ 384 free(tgt->args); 385 } 386 /* free mulargs */ 387 switch (tgt->cmd) { 388 void *ptr; 389 default: 390 break; 391 case STRPTIME_CMD_NONE: 392; 393; 394; 395; 396; 397 ptr = tgt->input_format_args; 398 if (ptr != NULL) { 399 free(ptr); 400 } 401; 402; 403; 404; 405 break; 406 } 407 return; 408} 409 410static void yuck_auto_usage(const yuck_t src[static 1U]) 411{ 412 switch (src->cmd) { 413 default: 414 YUCK_NOCMD: 415 puts("Usage: strptime [OPTION]... [INPUT]...\n\ 416\n\ 417Parse input from stdin according to one of the given formats FORMATs.\n\ 418The format string specifiers are the same as for strptime(3).\n\ 419"); 420 break; 421 422 } 423 424#if defined yuck_post_usage 425 yuck_post_usage(src); 426#endif /* yuck_post_usage */ 427 return; 428} 429 430static void yuck_auto_help(const yuck_t src[static 1U]) 431{ 432 yuck_auto_usage(src); 433 434 435 /* leave a not about common options */ 436 if (src->cmd == YUCK_NOCMD) { 437 ; 438 } 439 440 switch (src->cmd) { 441 default: 442 case STRPTIME_CMD_NONE: 443 puts("\ 444 -h, --help Print help and exit\n\ 445 -V, --version Print version and exit\n\ 446 -t, --time also display time in the output, default is to\n\ 447 display the date\n\ 448 -q, --quiet Suppress message about date/time and duration\n\ 449 parser errors.\n\ 450 -f, --format=STRING Output format. This can either be a specifier\n\ 451 string (similar to strftime()'s FMT) or the name\n\ 452 of a calendar.\n\ 453 -i, --input-format=STRING...\n\ 454 Input format, can be used multiple times.\n\ 455 Each date/time will be passed to the input\n\ 456 format parsers in the order they are given, if a\n\ 457 date/time can be read successfully with a given\n\ 458 input format specifier string, that value will\n\ 459 be used.\n\ 460 -e, --backslash-escapes\n\ 461 Enable interpretation of backslash escapes in the\n\ 462 output and input format specifier strings.\n\ 463 -S, --sed-mode Copy parts from the input before and after a\n\ 464 matching date/time.\n\ 465 Note that all occurrences of date/times within a\n\ 466 line will be processed.\n\ 467 -l, --locale Make internal strptime(3) and strftime(3) behave\n\ 468 in a locale dependent way, default is to pretend\n\ 469 LC_ALL=C is in place.\n\ 470"); 471 break; 472 473 } 474 475#if defined yuck_post_help 476 yuck_post_help(src); 477#endif /* yuck_post_help */ 478 479#if defined PACKAGE_BUGREPORT 480 puts("\n\ 481Report bugs to " PACKAGE_BUGREPORT); 482#endif /* PACKAGE_BUGREPORT */ 483 return; 484} 485 486static void yuck_auto_version(const yuck_t src[static 1U]) 487{ 488 switch (src->cmd) { 489 default: 490#if 0 491 492#elif defined package_string 493 puts(package_string); 494#elif defined package_version 495 printf("strptime %s\n", package_version); 496#elif defined PACKAGE_STRING 497 puts(PACKAGE_STRING); 498#elif defined PACKAGE_VERSION 499 puts("strptime " PACKAGE_VERSION); 500#elif defined VERSION 501 puts("strptime " VERSION); 502#else /* !PACKAGE_VERSION, !VERSION */ 503 puts("strptime unknown version"); 504#endif /* PACKAGE_VERSION */ 505 break; 506 } 507 508#if defined yuck_post_version 509 yuck_post_version(src); 510#endif /* yuck_post_version */ 511 return; 512} 513 514#if defined __INTEL_COMPILER 515# pragma warning (pop) 516#elif defined __GNUC__ 517# if __GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6 518# pragma GCC diagnostic pop 519# endif /* GCC version */ 520#endif /* __INTEL_COMPILER */ 521