Serenity Operating System
at master 98 lines 3.4 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/DeprecatedString.h> 8#include <AK/Vector.h> 9#include <LibCore/ArgsParser.h> 10#include <LibMain/Main.h> 11#include <errno.h> 12#include <stdio.h> 13#include <string.h> 14 15enum NumberStyle { 16 NumberAllLines, 17 NumberNonEmptyLines, 18 NumberNoLines, 19}; 20 21ErrorOr<int> serenity_main(Main::Arguments arguments) 22{ 23 NumberStyle number_style = NumberNonEmptyLines; 24 int increment = 1; 25 StringView separator = " "sv; 26 int start_number = 1; 27 int number_width = 6; 28 Vector<DeprecatedString> files; 29 30 Core::ArgsParser args_parser; 31 32 Core::ArgsParser::Option number_style_option { 33 Core::ArgsParser::OptionArgumentMode::Required, 34 "Line numbering style: 't' for non-empty lines, 'a' for all lines, 'n' for no lines", 35 "body-numbering", 36 'b', 37 "style", 38 [&number_style](StringView s) { 39 if (s == "t"sv) 40 number_style = NumberNonEmptyLines; 41 else if (s == "a"sv) 42 number_style = NumberAllLines; 43 else if (s == "n"sv) 44 number_style = NumberNoLines; 45 else 46 return false; 47 48 return true; 49 } 50 }; 51 52 args_parser.add_option(move(number_style_option)); 53 args_parser.add_option(increment, "Line count increment", "increment", 'i', "number"); 54 args_parser.add_option(separator, "Separator between line numbers and lines", "separator", 's', "string"); 55 args_parser.add_option(start_number, "Initial line number", "startnum", 'v', "number"); 56 args_parser.add_option(number_width, "Number width", "width", 'w', "number"); 57 args_parser.add_positional_argument(files, "Files to process", "file", Core::ArgsParser::Required::No); 58 args_parser.parse(arguments); 59 60 Vector<FILE*> file_pointers; 61 if (!files.is_empty()) { 62 for (auto& file : files) { 63 FILE* file_pointer = fopen(file.characters(), "r"); 64 if (!file_pointer) { 65 warnln("Failed to open {}: {}", file, strerror(errno)); 66 continue; 67 } 68 file_pointers.append(file_pointer); 69 } 70 } else { 71 file_pointers.append(stdin); 72 } 73 74 for (auto& file_pointer : file_pointers) { 75 int line_number = start_number - increment; // so the line number can start at 1 when added below 76 int previous_character = 0; 77 int next_character = 0; 78 while ((next_character = fgetc(file_pointer)) != EOF) { 79 if (previous_character == 0 || previous_character == '\n') { 80 if (next_character == '\n' && number_style != NumberAllLines) { 81 // Skip printing line count on empty lines. 82 outln(); 83 continue; 84 } 85 if (number_style != NumberNoLines) 86 out("{1:{0}}{2}", number_width, (line_number += increment), separator); 87 else 88 out("{1:{0}}", number_width, ""); 89 } 90 putchar(next_character); 91 previous_character = next_character; 92 } 93 fclose(file_pointer); 94 if (previous_character != '\n') 95 outln(); // for cases where files have no trailing newline 96 } 97 return 0; 98}