ufold

Прелама сваки UTF-8 улазни ред да стане у задату дужину
git clone https://git.sr.ht/~strahinja/ufold
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

ufold.c (6998B)


      1 /* This program is licensed under the terms of GNU GPL v3 or (at your option)
      2  * any later version. Copyright (C) 2021-2025  Страхиња Радић.
      3  * See the file LICENSE for exact copyright and license details. */
      4 
      5 #include <errno.h>
      6 #include <stdarg.h>
      7 #include <stdio.h>
      8 #include <stdint.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 
     12 #include "defs.h"
     13 #include "utf8.h"
     14 #include "version.h"
     15 
     16 #define COPYRIGHT                                                            \
     17 	("  This program is licensed under the terms of GNU GPL v3"          \
     18 	 " or (at your option)\n"                                            \
     19 	 "  any later version. Copyright (C) 2021-2025  Strahinya Radich.\n" \
     20 	 "  See the file LICENSE for exact copyright and license "           \
     21 	 "details.")
     22 
     23 const u32 U32_SPACE = (const u32)0x00000020;
     24 const u32 U32_TAB   = (const u32)0x00000009;
     25 
     26 char filename[MAXPATH];
     27 
     28 int
     29 usage(void)
     30 {
     31 	printf("Usage:\t%s -h | --help | -V | --full-version | -v | --version\n"
     32 	       "\t%s [-s | --spaces] [-w | --width=<cols>] [file]\n",
     33 		PROGRAMNAME, PROGRAMNAME);
     34 	return 0;
     35 }
     36 
     37 int
     38 error(const int code, const char* fmt, ...)
     39 {
     40 	char buf[MAXBUF];
     41 	va_list args;
     42 	va_start(args, fmt);
     43 	vsnprintf(buf, sizeof(buf), fmt, args);
     44 	va_end(args);
     45 	fprintf(stderr, "%s:%s: %s\n", PROGRAMNAME,
     46 		*filename ? filename : "(stdin)", buf);
     47 	return code;
     48 }
     49 
     50 int
     51 version(const int full)
     52 {
     53 	printf("%s %s, built on %s\n", PROGRAMNAME, VERSION, DATE);
     54 	if (full)
     55 		puts(COPYRIGHT);
     56 	return 0;
     57 }
     58 
     59 int
     60 starts_with(const char* s, const char* with)
     61 {
     62 	while (*s && *s == *with)
     63 	{
     64 		if (*s != *with)
     65 			return 0;
     66 		s++;
     67 		with++;
     68 	}
     69 	return *with == 0;
     70 }
     71 
     72 void
     73 fold_line_spaces(const u8* line, const int width)
     74 {
     75 	u32* ptoken	= NULL;
     76 	const u8* pline = line;
     77 	u32 uch		= 0;
     78 	u32 token[MAXINPUTBUF];
     79 	u8 u8ch[7];
     80 	size_t u8ch_len = 0;
     81 	size_t delta	= 0;
     82 	size_t len	= 0;
     83 	size_t col	= 0;
     84 
     85 	if (!line)
     86 		return;
     87 
     88 	/*
     89 	 * Idea:
     90 	 * 1. store characters in token until line is exhausted
     91 	 * 2. when ' ' or end of line is encountered:
     92 	 *    2,1, if token length > width, see if current column is 0:
     93 	 *         2.1.1. if it isn't, output newline and go to 2.1.2.
     94 	 *         2.1.2. (if it is) output chunks of length "width" until the
     95 	 *                end of token, then reset token
     96 	 *    2.2. if token length <= width, see if token length + current
     97 	 *         column exceed width
     98 	 *         2.2.1. if they do, output newline and go to 2.2.2.
     99 	 *         2.2.2. (if they don't) output token
    100 	 * 3. go to 1. unless line is exhausted
    101 	 */
    102 
    103 	*token = 0;
    104 	ptoken = token;
    105 	/* 1. */
    106 	while (1)
    107 	{
    108 		u8_char_to_u32(&uch, pline, &len);
    109 		if (!len)
    110 			return;
    111 		*ptoken++ = uch;
    112 		pline += len;
    113 
    114 		/* 2. */
    115 		if (uch == U32_SPACE || !uch)
    116 		{
    117 			if (ptoken - token > width)
    118 			{
    119 				/* 2.1. */
    120 				if (col > 0)
    121 				{
    122 					/* 2.1.1. */
    123 					printf("\n");
    124 					col = 0;
    125 				}
    126 
    127 				/* 2.1.2. */
    128 				*ptoken = 0;
    129 				ptoken	= token;
    130 				while (*ptoken)
    131 				{
    132 					u8ch_len
    133 						= u32_char_to_u8(u8ch, *ptoken);
    134 					if (!u8ch_len)
    135 						return;
    136 					u8ch[u8ch_len] = 0;
    137 
    138 					if (*ptoken == U32_TAB)
    139 						delta = 8 - (col % 8);
    140 					else
    141 						delta = 1;
    142 
    143 					if (col + delta > width)
    144 					{
    145 						printf("\n");
    146 						col = 0;
    147 						if (*ptoken == U32_TAB)
    148 							delta = 8;
    149 						else
    150 							delta = 1;
    151 					}
    152 					printf("%s", u8ch);
    153 					col += delta;
    154 					ptoken += u8ch_len;
    155 				}
    156 				*token = 0;
    157 				ptoken = token;
    158 			}
    159 			else
    160 			{
    161 				/* 2.2. */
    162 				if (ptoken - token + col > width)
    163 				{
    164 					/* 2.2.1. */
    165 					printf("\n");
    166 					col = 0;
    167 				}
    168 
    169 				/* 2.2.2. */
    170 				*ptoken = 0;
    171 				ptoken	= token;
    172 				while (*ptoken)
    173 				{
    174 					u8ch_len
    175 						= u32_char_to_u8(u8ch, *ptoken);
    176 					if (!u8ch_len)
    177 						return;
    178 					u8ch[u8ch_len] = 0;
    179 
    180 					if (*ptoken == U32_TAB)
    181 						delta = 8 - (col % 8);
    182 					else
    183 						delta = 1;
    184 
    185 					printf("%s", u8ch);
    186 					col += delta;
    187 					ptoken++;
    188 				}
    189 				*token = 0;
    190 				ptoken = token;
    191 			}
    192 		}
    193 
    194 		/* 3. */
    195 		if (!uch)
    196 			break;
    197 	}
    198 }
    199 
    200 void
    201 fold_line_normal(const u8* line, const int width)
    202 {
    203 	if (!line)
    204 		return;
    205 
    206 	const u8* pline	      = line;
    207 	const u8* pline_inner = NULL;
    208 	u32 uch;
    209 	u8 u8_char[7];
    210 	u8* pu8_char = NULL;
    211 	size_t uch_len;
    212 	size_t delta = 0;
    213 	size_t col   = 0;
    214 
    215 	while (*pline)
    216 	{
    217 		/* convert only to get byte length */
    218 		u8_char_to_u32(&uch, pline, &uch_len);
    219 		if (!uch_len)
    220 			return;
    221 
    222 		pline_inner = pline;
    223 		pu8_char    = u8_char;
    224 		while (*pline_inner && pline_inner < pline + uch_len)
    225 			*pu8_char++ = *pline_inner++;
    226 		*pu8_char = 0;
    227 
    228 		if (uch == U32_TAB)
    229 			delta = 8 - (col % 8);
    230 		else
    231 			delta = 1;
    232 
    233 		if (col + delta > width)
    234 		{
    235 			printf("\n");
    236 			col = 0;
    237 			if (uch == U32_TAB)
    238 				delta = 8;
    239 			else
    240 				delta = 1;
    241 		}
    242 
    243 		printf("%s", u8_char);
    244 		col += delta;
    245 		pline += uch_len;
    246 	}
    247 }
    248 
    249 int
    250 main(int argc, char** argv)
    251 {
    252 	int argn     = 1;
    253 	char* carg   = NULL;
    254 	char** earg  = NULL;
    255 	int spaces   = 0;
    256 	int in_width = 0;
    257 	long width   = 80;
    258 	char width_buf[MAXBUF];
    259 
    260 	*filename = 0;
    261 	while (argn < argc)
    262 	{
    263 		carg = *(argv + argn);
    264 		if (!strcmp(carg, "-s") || !strcmp(carg, "--spaces"))
    265 			spaces = 1;
    266 		else if (!strcmp(carg, "-h") || !strcmp(carg, "--help"))
    267 			return usage();
    268 		else if (!strcmp(carg, "-V") || !strcmp(carg, "--full-version"))
    269 			return version(1);
    270 		else if (!strcmp(carg, "-v") || !strcmp(carg, "--version"))
    271 			return version(0);
    272 		else if (!strcmp(carg, "-w"))
    273 			in_width = 1;
    274 		else if (starts_with(carg, "--width="))
    275 		{
    276 			char* pcarg = carg + strlen("--width=");
    277 			strncpy(width_buf, pcarg, MAXBUF);
    278 			errno = 0;
    279 			earg  = &pcarg;
    280 			width = strtol(width_buf, earg, 10);
    281 			if (errno == ERANGE || errno == EINVAL
    282 				|| (**earg && *pcarg))
    283 				return error(EINVAL,
    284 					"Invalid numerical value "
    285 					"for --width: %s",
    286 					pcarg);
    287 			in_width = 0;
    288 		}
    289 		else if (in_width)
    290 		{
    291 			errno = 0;
    292 			earg  = &carg;
    293 			width = strtol(carg, earg, 10);
    294 			if (errno == ERANGE || errno == EINVAL
    295 				|| (**earg && *carg))
    296 				return error(EINVAL,
    297 					"Invalid numerical value "
    298 					"for -w: %s",
    299 					carg);
    300 			in_width = 0;
    301 		}
    302 		else if (starts_with(carg, "--width"))
    303 			return error(EINVAL,
    304 				"Parameter --width requires an "
    305 				"argument");
    306 		else if (*carg == '-' && *(carg + 1) != 0)
    307 			return usage();
    308 		else
    309 			strncpy(filename, carg, MAXPATH);
    310 		argn++;
    311 	}
    312 
    313 	if (width < 1)
    314 		return error(EINVAL, "Width too small");
    315 
    316 	if (!*filename)
    317 	{
    318 		filename[0] = '-';
    319 		filename[1] = 0;
    320 	}
    321 
    322 	FILE* input = NULL;
    323 	if (!*filename || (*filename == '-' && !*(filename + 1)))
    324 		input = stdin;
    325 	else
    326 		input = fopen(filename, "rt");
    327 	if (!input)
    328 		return error(errno, "Cannot open file");
    329 
    330 	char line[MAXINPUTBUF];
    331 	while (!feof(input))
    332 	{
    333 		if (!fgets(line, MAXINPUTBUF, input))
    334 		{
    335 			if (!feof(input))
    336 				return error(errno, "Error reading file");
    337 			else
    338 				break;
    339 		}
    340 		char* eol = strchr(line, '\n');
    341 		if (eol)
    342 			*eol = 0;
    343 		if (spaces)
    344 			fold_line_spaces((const u8*)line, width);
    345 		else
    346 			fold_line_normal((const u8*)line, width);
    347 		printf("\n");
    348 	}
    349 
    350 	fclose(input);
    351 	return 0;
    352 }