~q3cpma/misc-tools

ref: 313cd570e4b81c5f6749356698ce617c65181b61 misc-tools/natsort.c -rw-r--r-- 2.1 KiB
313cd570q3cpma README nit 10 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#include <ctype.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "array.h"
#include "misc.h"
#include "utf8.h"


/* Natural string comparison: case insensitive and compare digit sequences
   inside the strings as numbers */
static int strnatcmp(const void *a, const void *b)
{
	const char *s1 = *(const char **)a, *s2 = *(const char **)b;
	uint32_t c1 = 0, c2 = 0;

	do
	{
		if (isdigit(*s1) && isdigit(*s2))
		{
			char *end1, *end2;
			const uint64_t n1 = strtoull(s1, &end1, 10);
			const uint64_t n2 = strtoull(s2, &end2, 10);
			if (n1 != n2)
			{
				return n1 > n2 ?  1 :
					   n1 < n2 ? -1 : 0;
			}
			s1 = end1;
			s2 = end2;
			continue;
		}
		else
		{
			c1 = codep_tolower(xutf8_decode_step(&s1));
			c2 = codep_tolower(xutf8_decode_step(&s2));
		}
	}
	while (c1 == c2 && c1);

	return c1 > c2 ?  1 :
		   c1 < c2 ? -1 : 0;
}

NORETURN void usage(int exit_status)
{
	printf(
		"NAME\n"
		"    %s - Sort input lines naturally\n"
		"\n"
		"SYNOPSIS\n"
		"    %s [OPTIONS]\n"
		"\n"
		"DESCRIPTION\n"
		"    Sort input lines case insensitively while comparing numbers (as in '[0-9]+')\n"
		"    by their value.\n"
		"    Only supports UTF-8 locales.\n"
		"\n"
		"OPTIONS\n"
		"    -h\n"
		"        Print this help message and exit.\n"
		"\n"
		"    -v\n"
		"        Print the version and exit.\n"
		"\n",
		PROG_NAME, PROG_NAME);
	exit(exit_status);
}


int main(int argc, char **argv)
{
	signals_nointerrupt();

	for (int opt; (opt = getopt(argc, argv, "hv")) != -1;)
	{
		switch (opt)
		{
			case 'h':
				usage(EXIT_SUCCESS);
				break;

			case 'v':
			    puts(PROG_VERSION);
				exit(EXIT_SUCCESS);

			default:
				exit(EXIT_FAILURE);
		}
	}
	if (optind != argc)
	{
		usage(EXIT_FAILURE);
	}

	size_t dummy = 0;
	char **lines, *line = NULL;
	GARRAY_INIT(lines);

	for (ssize_t read; (read = xgetline(&line, &dummy, stdin)) != -1;
		dummy = 0, line = NULL)
	{
		GARRAY_APPEND(lines, line);
	}
	free(line);

	qsort(lines, GARRAY_SIZE(lines), sizeof(char *), strnatcmp);

	for (size_t i = 0; i < GARRAY_SIZE(lines); ++i)
	{
		puts(lines[i]);
	}

	PTR_GARRAY_FREE(lines, free);
	return EXIT_SUCCESS;
}