logo

utils-std

Collection of commonly available Unix tools git clone https://anongit.hacktivis.me/git/utils-std.git/

seq.c (3401B)


  1. // utils-std: Collection of commonly available Unix tools
  2. // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
  3. // SPDX-License-Identifier: MPL-2.0
  4. #define _POSIX_C_SOURCE 200809L
  5. #include "../config.h"
  6. #include "../libutils/getopt_nolong.h"
  7. #include <ctype.h> // isdigit
  8. #include <errno.h> // errno
  9. #include <math.h> // fabs
  10. #include <stdbool.h> // bool, true, false
  11. #include <stdio.h> // fprintf
  12. #include <stdlib.h> // exit, strtod
  13. #include <string.h> // strerror
  14. #include <unistd.h> // getopt, optarg, optind
  15. #ifdef HAS_GETOPT_LONG
  16. #include <getopt.h>
  17. #endif
  18. const char *argv0 = "seq";
  19. const char *sep = "\n";
  20. const char *term = "\n";
  21. int len = 0;
  22. bool opt_w = false;
  23. double g_last = 1.0;
  24. static void
  25. seq(double i, double step, double last)
  26. {
  27. printf("%0*g", len, i);
  28. if(i < last)
  29. {
  30. while((i + step) <= last)
  31. printf("%s%0*g", sep, len, i += step);
  32. }
  33. else if(i > last)
  34. {
  35. while((i - step) >= last)
  36. printf("%s%0*g", sep, len, i -= step);
  37. }
  38. printf("%s", term);
  39. if(opt_w && i != g_last)
  40. fprintf(stderr, "seq: error: Failed to guess last, got: %g; expected: %g\n", i, g_last);
  41. }
  42. static double
  43. get_num(char *str)
  44. {
  45. errno = 0;
  46. char *endptr = NULL;
  47. double num = strtod(str, &endptr);
  48. if(errno != 0)
  49. {
  50. fprintf(stderr, "seq: error: strtod(\"%s\"): %s\n", str, strerror(errno));
  51. exit(1);
  52. }
  53. if(!(endptr == NULL || endptr[0] == 0))
  54. {
  55. fprintf(stderr,
  56. "seq: error: strtod(\"%s\"), invalid remaining characters found '%s'\n",
  57. str,
  58. endptr);
  59. exit(1);
  60. }
  61. return num;
  62. }
  63. static void
  64. usage(void)
  65. {
  66. fprintf(stderr, "usage: seq [-w] [-s separator] [first [step]] last\n");
  67. }
  68. int
  69. main(int argc, char *argv[])
  70. {
  71. #ifdef HAS_GETOPT_LONG
  72. // Strictly for GNUisms compatibility so no long-only options
  73. // clang-format off
  74. static struct option opts[] = {
  75. {"equal-width", no_argument, NULL, 'w'},
  76. {"separator", required_argument, NULL, 's'},
  77. {0, 0, 0, 0},
  78. };
  79. // clang-format on
  80. // Need + as first character to get POSIX-style option parsing
  81. for(int c = -1; (c = getopt_long(argc, argv, "+:ws:t:", opts, NULL)) != -1;)
  82. #else
  83. for(int c = -1; (c = getopt_nolong(argc, argv, ":ws:t:")) != -1;)
  84. #endif
  85. {
  86. switch(c)
  87. {
  88. case 'w':
  89. opt_w = true;
  90. break;
  91. case 's':
  92. sep = optarg;
  93. break;
  94. case 't':
  95. term = optarg;
  96. break;
  97. case ':':
  98. fprintf(stderr, "seq: error: Option -%c requires an operand\n", optopt);
  99. usage();
  100. return 1;
  101. case '?':
  102. if(!got_long_opt)
  103. if(isdigit(optopt))
  104. {
  105. fprintf(stderr,
  106. "seq: warning: Pass -- if the first non-option argument starts with a dash(-)\n");
  107. optind--;
  108. goto getopt_end;
  109. }
  110. usage();
  111. return 1;
  112. }
  113. }
  114. getopt_end:
  115. argc -= optind;
  116. argv += optind;
  117. double first = 1.0;
  118. double step = 1.0;
  119. double last = 1.0;
  120. switch(argc)
  121. {
  122. case 1:
  123. last = get_num(argv[0]);
  124. break;
  125. case 2:
  126. first = get_num(argv[0]);
  127. last = get_num(argv[1]);
  128. break;
  129. case 3:
  130. first = get_num(argv[0]);
  131. step = fabs(get_num(argv[1]));
  132. last = get_num(argv[2]);
  133. break;
  134. default:
  135. usage();
  136. return 1;
  137. }
  138. if(opt_w)
  139. {
  140. double f_mod = fmod(first, step);
  141. double l_mod = fmod(last, step);
  142. g_last = last - l_mod + f_mod;
  143. int f_len = snprintf(NULL, 0, "%g", first);
  144. int l_len = snprintf(NULL, 0, "%g", g_last);
  145. len = f_len > l_len ? f_len : l_len;
  146. }
  147. seq(first, step, last);
  148. return 0;
  149. }