reflow.c (4622B)
1 /* This program is licensed under the terms of GNU GPL v3 or (at your option) 2 * any later version. Copyright (C) 2021-2026 Страхиња Радић. 3 * See the file LICENSE for exact copyright and license details. */ 4 5 #include <assert.h> 6 #include <errno.h> 7 #include <stdarg.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 12 #include "defs.h" 13 #include "version.h" 14 15 #define COPYRIGHT \ 16 (" This program is licensed under the terms of GNU GPL v3" \ 17 " or (at your option)\n" \ 18 " any later version. Copyright (C) 2021-2026 Strahinya Radich.\n" \ 19 " See the file LICENSE for exact copyright and license " \ 20 "details.") 21 22 char filename[MAXPATH]; 23 24 int 25 usage(void) 26 { 27 printf("Usage:\t%s -h | --help | -V | --full-version | -v | --version\n" 28 "\t%s [file]\n", 29 PROGRAMNAME, PROGRAMNAME); 30 return 0; 31 } 32 33 int 34 error(const int code, const char* format, ...) 35 { 36 char buf[MAXBUF]; 37 va_list args; 38 assert(format != NULL); 39 va_start(args, format); 40 vsnprintf(buf, sizeof(buf), format, args); 41 va_end(args); 42 fprintf(stderr, "%s:%s: %s\n", PROGRAMNAME, 43 *filename ? filename : "(stdin)", buf); 44 return code; 45 } 46 47 int 48 version(const int full) 49 { 50 printf("%s %s, built on %s\n", PROGRAMNAME, VERSION, DATE); 51 if (full) 52 puts(COPYRIGHT); 53 return 0; 54 } 55 56 int 57 is_whitespace(const char ch) 58 { 59 return (ch == ' ') || (ch == '\t'); 60 } 61 62 int 63 main(int argc, char** argv) 64 { 65 FILE* input = NULL; 66 char* buffer = NULL; 67 char* carg = NULL; 68 char* pbuffer = NULL; 69 char* line = NULL; 70 char* pline = NULL; 71 char* eol = NULL; 72 char* tempb = NULL; 73 ssize_t buffer_size = 0; 74 ssize_t line_len = 0; 75 ssize_t prev_size = 0; 76 int argn = 1; 77 int empty_line; 78 int initial_whitespace; 79 80 *filename = 0; 81 while (argn < argc) 82 { 83 carg = *(argv + argn); 84 if (!strcmp(carg, "-h") || !strcmp(carg, "--help")) 85 return usage(); 86 else if (!strcmp(carg, "-V") || !strcmp(carg, "--full-version")) 87 return version(1); 88 else if (!strcmp(carg, "-v") || !strcmp(carg, "--version")) 89 return version(0); 90 else if (!*filename) 91 { 92 if (!memccpy(filename, carg, 0, MAXPATH)) 93 return error(1, 94 "Overflow when setting filename"); 95 } 96 else 97 return usage(); 98 argn++; 99 } 100 101 if (!*filename || (*filename == '-' && !*(filename + 1))) 102 input = stdin; 103 else 104 input = fopen(filename, "rt"); 105 if (!input) 106 return error(errno, "Cannot open file"); 107 108 line = malloc(MAXBUF); 109 if (!line) 110 exit(error(ENOMEM, "Memory allocation failed")); 111 prev_size = MAXBUF; 112 113 while (!feof(input)) 114 { 115 if (!fgets(line, MAXBUF, input)) 116 { 117 if (!feof(input)) 118 return error(errno, "Error reading file"); 119 else 120 break; 121 } 122 123 eol = strchr(line, '\n'); 124 if (eol) 125 line_len = eol - line + 1; 126 else 127 line_len = strlen(line); 128 129 buffer_size += line_len; 130 if (!buffer) 131 { 132 buffer = malloc(buffer_size + 1); 133 if (!buffer) 134 exit(error(ENOMEM, "Memory allocation failed")); 135 pbuffer = buffer; 136 } 137 else 138 { 139 ssize_t pbuffer_start = pbuffer - buffer; 140 tempb = realloc(buffer, buffer_size + 1); 141 if (!tempb) 142 exit(error(ENOMEM, "Memory allocation failed")); 143 buffer = tempb; 144 pbuffer = buffer + pbuffer_start; 145 } 146 pline = line; 147 initial_whitespace = is_whitespace(*pline); 148 while (*pline) 149 { 150 if (initial_whitespace) 151 { 152 if (!is_whitespace(*pline)) 153 { 154 initial_whitespace = 0; 155 *pbuffer++ = *pline; 156 } 157 } 158 else if (!((pline + 1 - line) < line_len 159 && is_whitespace(*pline) 160 && is_whitespace(pline[1]))) 161 *pbuffer++ = *pline; 162 pline++; 163 } 164 } 165 if (pbuffer) 166 *pbuffer = 0; 167 else 168 exit(0); 169 170 empty_line = 0; 171 pbuffer = buffer; 172 tempb = NULL; 173 while (*pbuffer) 174 { 175 eol = strchr(pbuffer, '\n'); 176 if (eol) 177 line_len = eol - pbuffer; 178 else 179 line_len = strlen(pbuffer); 180 if (empty_line && line_len == 0) 181 { 182 pbuffer++; 183 continue; 184 } 185 empty_line = line_len == 0; 186 if (!line || line_len + 1 > prev_size) 187 { 188 tempb = realloc(line, line_len + 1); 189 if (!tempb) 190 exit(error(ENOMEM, "Memory allocation failed")); 191 prev_size = line_len + 1; 192 line = tempb; 193 } 194 strncpy(line, pbuffer, line_len); 195 *(line + line_len) = 0; 196 printf("%s", line); 197 if (pbuffer[line_len + 1] == '\n' && !empty_line) 198 printf("\n\n"); 199 else if (!empty_line && !is_whitespace(pbuffer[line_len - 1]) 200 && !is_whitespace(pbuffer[line_len + 1]) 201 && pbuffer[line_len - 1] != '\n' 202 && pbuffer[line_len + 1] != '\n') 203 printf(" "); 204 pbuffer += line_len + 1; 205 } 206 puts(""); 207 free(buffer); 208 free(line); 209 210 return 0; 211 }