Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <AK/String.h>
28#include <AK/Vector.h>
29#include <LibCore/ArgsParser.h>
30
31#include <stdio.h>
32#include <string.h>
33
34enum NumberStyle {
35 NumberAllLines,
36 NumberNonEmptyLines,
37 NumberNoLines,
38};
39
40int main(int argc, char** argv)
41{
42 NumberStyle number_style = NumberNonEmptyLines;
43 int increment = 1;
44 const char* separator = " ";
45 int start_number = 1;
46 int number_width = 6;
47 Vector<const char*> files;
48
49 Core::ArgsParser args_parser;
50
51 Core::ArgsParser::Option number_style_option {
52 true,
53 "Line numbering style: 't' for non-empty lines, 'a' for all lines, 'n' for no lines",
54 "body-numbering",
55 'b',
56 "style",
57 [&number_style](const char* s) {
58 if (!strcmp(s, "t"))
59 number_style = NumberNonEmptyLines;
60 else if (!strcmp(s, "a"))
61 number_style = NumberAllLines;
62 else if (!strcmp(s, "n"))
63 number_style = NumberNoLines;
64 else
65 return false;
66
67 return true;
68 }
69 };
70
71 args_parser.add_option(move(number_style_option));
72 args_parser.add_option(increment, "Line count increment", "increment", 'i', "number");
73 args_parser.add_option(separator, "Separator between line numbers and lines", "separator", 's', "string");
74 args_parser.add_option(start_number, "Initial line number", "startnum", 'v', "number");
75 args_parser.add_option(number_width, "Number width", "width", 'w', "number");
76 args_parser.add_positional_argument(files, "Files to process", "file", Core::ArgsParser::Required::No);
77 args_parser.parse(argc, argv);
78
79 Vector<FILE*> file_pointers;
80 if (!files.is_empty()) {
81 for (auto& file : files) {
82 FILE* file_pointer = fopen(file, "r");
83 if (!file_pointer) {
84 fprintf(stderr, "unable to open %s\n", file);
85 continue;
86 }
87 file_pointers.append(file_pointer);
88 }
89 } else {
90 file_pointers.append(stdin);
91 }
92
93 for (auto& file_pointer : file_pointers) {
94 int line_number = start_number - increment; // so the line number can start at 1 when added below
95 int previous_character = 0;
96 int next_character = 0;
97 while ((next_character = fgetc(file_pointer)) != EOF) {
98 if (previous_character == 0 || previous_character == '\n') {
99 if (next_character == '\n' && number_style != NumberAllLines) {
100 // Skip printing line count on empty lines.
101 printf("\n");
102 continue;
103 }
104 if (number_style != NumberNoLines)
105 printf("%*d%s", number_width, (line_number += increment), separator);
106 else
107 printf("%*s", number_width, "");
108 }
109 putchar(next_character);
110 previous_character = next_character;
111 }
112 fclose(file_pointer);
113 if (previous_character != '\n')
114 printf("\n"); // for cases where files have no trailing newline
115 }
116 return 0;
117}