Serenity Operating System
at master 118 lines 3.7 kB view raw
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}