Serenity Operating System
1/*
2 * Copyright (c) 2020, Nico Weber <thakis@chromium.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/DeprecatedString.h>
8#include <AK/Format.h>
9#include <AK/StdLibExtras.h>
10#include <AK/Vector.h>
11#include <LibCore/ArgsParser.h>
12#include <LibCore/System.h>
13#include <LibMain/Main.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17
18char const* const g_usage = R"(Usage:
19 seq [-h|--help]
20 seq LAST
21 seq FIRST LAST
22 seq FIRST INCREMENT LAST
23)";
24
25static void print_usage(FILE* stream)
26{
27 fputs(g_usage, stream);
28}
29
30static double get_double(char const* name, StringView d_string, size_t* number_of_decimals)
31{
32 auto d = d_string.to_double();
33 if (!d.has_value()) {
34 warnln("{}: invalid argument \"{}\"", name, d_string);
35 print_usage(stderr);
36 exit(1);
37 }
38
39 if (auto dot = d_string.find('.'); dot.has_value())
40 *number_of_decimals = d_string.length() - *dot - 1;
41 else
42 *number_of_decimals = 0;
43
44 return *d;
45}
46
47ErrorOr<int> serenity_main(Main::Arguments arguments)
48{
49 TRY(Core::System::pledge("stdio"));
50 TRY(Core::System::unveil(nullptr, nullptr));
51
52 StringView separator = "\n"sv;
53 StringView terminator = ""sv;
54 Vector<StringView> parameters;
55
56 Core::ArgsParser args_parser;
57 args_parser.add_option(separator, "Characters to print after each number (default: \\n)", "separator", 's', "separator");
58 args_parser.add_option(terminator, "Characters to print at the end of the sequence", "terminator", 't', "terminator");
59 args_parser.add_positional_argument(parameters, "1 to 3 parameters, interpreted as LAST, FIRST LAST, or FIRST INCREMENT LAST", "parameters");
60 args_parser.parse(arguments);
61
62 double start = 1;
63 double step = 1;
64 double end = 1;
65 size_t number_of_start_decimals = 0;
66 size_t number_of_step_decimals = 0;
67 size_t number_of_end_decimals = 0;
68 switch (parameters.size()) {
69 case 1:
70 end = get_double(arguments.argv[0], parameters[0], &number_of_end_decimals);
71 break;
72 case 2:
73 start = get_double(arguments.argv[0], parameters[0], &number_of_start_decimals);
74 end = get_double(arguments.argv[0], parameters[1], &number_of_end_decimals);
75 break;
76 case 3:
77 start = get_double(arguments.argv[0], parameters[0], &number_of_start_decimals);
78 step = get_double(arguments.argv[0], parameters[1], &number_of_step_decimals);
79 end = get_double(arguments.argv[0], parameters[2], &number_of_end_decimals);
80 break;
81 default:
82 warnln("{}: unexpected number of arguments", arguments.argv[0]);
83 print_usage(stderr);
84 return 1;
85 }
86
87 if (step == 0) {
88 warnln("{}: increment must not be 0", arguments.argv[0]);
89 return 1;
90 }
91
92 if (__builtin_isnan(start) || __builtin_isnan(step) || __builtin_isnan(end)) {
93 warnln("{}: start, step, and end must not be NaN", arguments.argv[0]);
94 return 1;
95 }
96
97 size_t number_of_decimals = max(number_of_start_decimals, max(number_of_step_decimals, number_of_end_decimals));
98
99 int n = static_cast<int>((end - start) / step);
100 double d = start;
101 for (int i = 0; i <= n; ++i) {
102 char buf[40];
103 snprintf(buf, sizeof(buf), "%f", d);
104 if (char* dot = strchr(buf, '.')) {
105 if (number_of_decimals == 0)
106 *dot = '\0';
107 else if ((dot - buf) + 1 + number_of_decimals < (int)sizeof(buf))
108 dot[1 + number_of_decimals] = '\0';
109 }
110 out("{}{}", buf, separator);
111 d += step;
112 }
113
114 if (!terminator.is_empty())
115 out(terminator);
116
117 return 0;
118}