logo

utils-std

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

seq.c (2798B)


  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 <ctype.h> // isdigit
  6. #include <errno.h> // errno
  7. #include <math.h> // fabs
  8. #include <stdbool.h> // bool, true, false
  9. #include <stdio.h> // fprintf
  10. #include <stdlib.h> // exit, strtod
  11. #include <string.h> // strerror
  12. #include <unistd.h> // getopt, optarg, optind
  13. const char *sep = "\n";
  14. const char *term = "\n";
  15. int len = 0;
  16. bool opt_w = false;
  17. double g_last = 1.0;
  18. static void
  19. seq(double i, double step, double last)
  20. {
  21. printf("%0*g", len, i);
  22. if(i < last)
  23. {
  24. while((i + step) <= last)
  25. printf("%s%0*g", sep, len, i += step);
  26. }
  27. else if(i > last)
  28. {
  29. while((i - step) >= last)
  30. printf("%s%0*g", sep, len, i -= step);
  31. }
  32. printf("%s", term);
  33. if(opt_w && i != g_last)
  34. fprintf(stderr, "seq: error: Failed to guess last, got: %g; expected: %g\n", i, g_last);
  35. }
  36. static double
  37. get_num(char *str)
  38. {
  39. errno = 0;
  40. char *endptr = NULL;
  41. double num = strtod(str, &endptr);
  42. if(errno != 0)
  43. {
  44. fprintf(stderr, "seq: error: strtod(\"%s\"): %s\n", str, strerror(errno));
  45. exit(1);
  46. }
  47. if(!(endptr == NULL || endptr[0] == 0))
  48. {
  49. fprintf(stderr,
  50. "seq: error: strtod(\"%s\"), invalid remaining characters found '%s'\n",
  51. str,
  52. endptr);
  53. exit(1);
  54. }
  55. return num;
  56. }
  57. static void
  58. usage(void)
  59. {
  60. fprintf(stderr, "usage: seq [-w] [-s separator] [first [step]] last\n");
  61. }
  62. int
  63. main(int argc, char *argv[])
  64. {
  65. int c;
  66. while((c = getopt(argc, argv, ":ws:t:")) != -1)
  67. {
  68. switch(c)
  69. {
  70. case 'w':
  71. opt_w = true;
  72. break;
  73. case 's':
  74. sep = optarg;
  75. break;
  76. case 't':
  77. term = optarg;
  78. break;
  79. case ':':
  80. fprintf(stderr, "seq: error: Option -%c requires an operand\n", optopt);
  81. usage();
  82. return 1;
  83. case '?':
  84. if(isdigit(optopt))
  85. {
  86. fprintf(stderr,
  87. "seq: warning: Pass -- if the first non-option argument starts with a dash(-)\n");
  88. optind--;
  89. goto getopt_end;
  90. }
  91. usage();
  92. return 1;
  93. }
  94. }
  95. getopt_end:
  96. argc -= optind;
  97. argv += optind;
  98. double first = 1.0;
  99. double step = 1.0;
  100. double last = 1.0;
  101. switch(argc)
  102. {
  103. case 1:
  104. last = get_num(argv[0]);
  105. break;
  106. case 2:
  107. first = get_num(argv[0]);
  108. last = get_num(argv[1]);
  109. break;
  110. case 3:
  111. first = get_num(argv[0]);
  112. step = fabs(get_num(argv[1]));
  113. last = get_num(argv[2]);
  114. break;
  115. default:
  116. usage();
  117. return 1;
  118. }
  119. if(opt_w)
  120. {
  121. double f_mod = fmod(first, step);
  122. double l_mod = fmod(last, step);
  123. g_last = last - l_mod + f_mod;
  124. int f_len = snprintf(NULL, 0, "%g", first);
  125. int l_len = snprintf(NULL, 0, "%g", g_last);
  126. len = f_len > l_len ? f_len : l_len;
  127. }
  128. seq(first, step, last);
  129. return 0;
  130. }