Serenity Operating System
at portability 161 lines 5.1 kB view raw
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/StdLibExtras.h> 28#include <LibCore/ArgsParser.h> 29 30#include <errno.h> 31#include <stdio.h> 32#include <stdlib.h> 33 34int head(const String& filename, bool print_filename, int line_count, int char_count); 35 36int main(int argc, char** argv) 37{ 38 if (pledge("stdio rpath", nullptr) < 0) { 39 perror("pledge"); 40 return 1; 41 } 42 43 int line_count = 0; 44 int char_count = 0; 45 bool never_print_filenames = false; 46 bool always_print_filenames = false; 47 Vector<const char*> files; 48 49 Core::ArgsParser args_parser; 50 args_parser.add_option(line_count, "Number of lines to print (default 10)", "lines", 'n', "number"); 51 args_parser.add_option(char_count, "Number of characters to print", "characters", 'c', "number"); 52 args_parser.add_option(never_print_filenames, "Never print file names", "quiet", 'q'); 53 args_parser.add_option(always_print_filenames, "Always print file names", "verbose", 'v'); 54 args_parser.add_positional_argument(files, "File to process", "file", Core::ArgsParser::Required::No); 55 args_parser.parse(argc, argv); 56 57 if (line_count == 0 && char_count == 0) { 58 line_count = 10; 59 } 60 61 bool print_filenames = files.size() > 1; 62 if (always_print_filenames) 63 print_filenames = true; 64 else if (never_print_filenames) 65 print_filenames = false; 66 67 if (files.is_empty()) { 68 return head("", print_filenames, line_count, char_count); 69 } 70 71 int rc = 0; 72 73 for (auto& file : files) { 74 if (head(file, print_filenames, line_count, char_count) != 0) { 75 rc = 1; 76 } 77 } 78 79 return rc; 80} 81 82int head(const String& filename, bool print_filename, int line_count, int char_count) 83{ 84 bool is_stdin = false; 85 FILE* fp = nullptr; 86 87 if (filename == "" || filename == "-") { 88 fp = stdin; 89 is_stdin = true; 90 } else { 91 fp = fopen(filename.characters(), "r"); 92 if (!fp) { 93 fprintf(stderr, "can't open %s for reading: %s\n", filename.characters(), strerror(errno)); 94 return 1; 95 } 96 } 97 98 if (print_filename) { 99 if (is_stdin) { 100 puts("==> standard input <=="); 101 } else { 102 printf("==> %s <==\n", filename.characters()); 103 } 104 } 105 106 if (line_count) { 107 for (int line = 0; line < line_count; ++line) { 108 char buffer[BUFSIZ]; 109 auto* str = fgets(buffer, sizeof(buffer), fp); 110 if (!str) 111 break; 112 113 // specifically use fputs rather than puts, because fputs doesn't add 114 // its own newline. 115 fputs(str, stdout); 116 } 117 } else if (char_count) { 118 char buffer[BUFSIZ]; 119 120 while (char_count) { 121 int nread = fread(buffer, 1, min(BUFSIZ, char_count), fp); 122 if (nread > 0) { 123 int ncomplete = 0; 124 125 while (ncomplete < nread) { 126 int nwrote = fwrite(&buffer[ncomplete], 1, nread - ncomplete, stdout); 127 if (nwrote > 0) 128 ncomplete += nwrote; 129 130 if (feof(stdout)) { 131 fprintf(stderr, "unexpected eof writing to stdout\n"); 132 return 1; 133 } 134 135 if (ferror(stdout)) { 136 fprintf(stderr, "error writing to stdout\n"); 137 return 1; 138 } 139 } 140 } 141 142 char_count -= nread; 143 144 if (feof(fp)) 145 break; 146 147 if (ferror(fp)) { 148 fprintf(stderr, "error reading input\n"); 149 break; 150 } 151 } 152 } 153 154 fclose(fp); 155 156 if (print_filename) { 157 puts(""); 158 } 159 160 return 0; 161}