alt_printf 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +2 -0
- data/README.md +14 -0
- data/Rakefile +53 -0
- data/alt_printf.gemspec +24 -0
- data/ext/alt_printf/alt_printf.c +222 -0
- data/ext/alt_printf/altprintf.c +244 -0
- data/ext/alt_printf/altprintf.h +42 -0
- data/ext/alt_printf/extconf.rb +5 -0
- data/ext/alt_printf/extconf_dev.rb +9 -0
- data/ext/alt_printf/list.c +81 -0
- data/ext/alt_printf/list.h +24 -0
- data/ext/alt_printf/log.h +11 -0
- data/ext/alt_printf/strbuf.c +142 -0
- data/ext/alt_printf/strbuf.h +28 -0
- data/ext/alt_printf/syntax.h +30 -0
- data/lib/alt_printf/alt_printf.so +0 -0
- data/lib/alt_printf/version.rb +3 -0
- data/lib/alt_printf.rb +4 -0
- metadata +89 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ca3bfbd5642eb9ee9d75ed799b3cb18708d44c2d1f57eabcf8cf9795cc54e82c
|
4
|
+
data.tar.gz: e7d66a6c4770522c112d677de2c3d88df98ddb2a0089ddd784b10063493a21cb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fe5d82f1920848129f2bf490bb49754efd1de8b46dd7c468cc700b6a5e9d76a5b130706bcbfff1d87faa000f8a933247886bff4c2c2fdba17d5f398f7b822dac
|
7
|
+
data.tar.gz: 5fe9d231104bcfc0d5fda40924c3f55630c3981427f09ab63985432123f314250b858f2bb7dd946ae58161702abd832e2e81f4a4f06a8b58ab7f56b86a80fcfd
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# AltPrintf
|
2
|
+
|
3
|
+
AltPrintf is a gem that wraps `altprintf`.
|
4
|
+
|
5
|
+
It exposes the following module functions
|
6
|
+
|
7
|
+
+ `#fmt(format_string, *args, **kwargs)`
|
8
|
+
+ `#fmtm(passes, format_string, *args, **kwargs)`
|
9
|
+
|
10
|
+
In addition to the syntax of vanilla altprintf, the following additional
|
11
|
+
argument is accepted:
|
12
|
+
|
13
|
+
+ `<val>` - access the value of the given hash with key `:"#{val}"` rather than
|
14
|
+
try to read the next argument.
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rake/extensiontask'
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
require 'securerandom'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
SRC_DIR = File.join(__dir__, '../src')
|
7
|
+
|
8
|
+
Rake::ExtensionTask.new('alt_printf') do |ext|
|
9
|
+
ext.lib_dir = 'lib/alt_printf'
|
10
|
+
ext.config_script = 'extconf_dev.rb'
|
11
|
+
end
|
12
|
+
|
13
|
+
task :build_gem do
|
14
|
+
tmp_dir = "/tmp/alt_printf_#{SecureRandom.hex(8)}"
|
15
|
+
puts "cloning to #{tmp_dir}"
|
16
|
+
FileUtils.mkdir(tmp_dir)
|
17
|
+
|
18
|
+
Dir[
|
19
|
+
'Gemfile',
|
20
|
+
'README.md',
|
21
|
+
'Rakefile',
|
22
|
+
'alt_printf.gemspec',
|
23
|
+
'{ext,lib}/**/*'
|
24
|
+
].each do |f|
|
25
|
+
next if File.directory?(f)
|
26
|
+
|
27
|
+
dir = File.join(tmp_dir, File.dirname(f))
|
28
|
+
FileUtils.mkdir_p(dir)
|
29
|
+
FileUtils.cp(f, File.join(dir, File.basename(f)))
|
30
|
+
end
|
31
|
+
|
32
|
+
FileUtils.cp_r(
|
33
|
+
%w[altprintf list log strbuf syntax].map do |w|
|
34
|
+
%w[h c].map { |e| File.join(SRC_DIR, "#{w}.#{e}") }
|
35
|
+
.select { |f| File.exist?(f) }
|
36
|
+
end.flatten,
|
37
|
+
File.join(tmp_dir, 'ext/alt_printf/')
|
38
|
+
)
|
39
|
+
|
40
|
+
FileUtils.cd(tmp_dir)
|
41
|
+
|
42
|
+
sh "rake build"
|
43
|
+
|
44
|
+
FileUtils.cp(Dir['pkg/*'], File.join(__dir__, 'pkg'))
|
45
|
+
FileUtils.cd(__dir__)
|
46
|
+
FileUtils.rm_rf(tmp_dir)
|
47
|
+
end
|
48
|
+
|
49
|
+
task publish: :build_gem do
|
50
|
+
load File.join(__dir__, 'alt_printf.gemspec')
|
51
|
+
p AltPrintf
|
52
|
+
sh "gem push pkg/#{AltPrintf::SPEC.name}-#{AltPrintf::SPEC.version}.gem"
|
53
|
+
end
|
data/alt_printf.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'date'
|
2
|
+
require_relative 'lib/alt_printf/version'
|
3
|
+
|
4
|
+
AltPrintf::SPEC = Gem::Specification.new do |s|
|
5
|
+
s.name = 'alt_printf'
|
6
|
+
s.version = AltPrintf::VERSION.join('.')
|
7
|
+
s.date = Date.today.strftime('%Y-%m-%d')
|
8
|
+
s.summary = 'A powerful printf-like template language'
|
9
|
+
s.authors = ['Stone Tickle']
|
10
|
+
s.email = 'lattis@mochiro.moe'
|
11
|
+
s.homepage = 'https://github.com/alt_printf/gem'
|
12
|
+
s.license = 'MIT'
|
13
|
+
|
14
|
+
s.files = Dir['{**/*}']
|
15
|
+
|
16
|
+
s.platform = Gem::Platform::RUBY
|
17
|
+
s.extensions = Dir["ext/**/extconf.rb"]
|
18
|
+
s.require_paths = ['lib']
|
19
|
+
|
20
|
+
s.required_ruby_version = '>= 2.6.3'
|
21
|
+
|
22
|
+
s.add_development_dependency 'rake-compiler', '~> 1.0'
|
23
|
+
s.add_development_dependency 'rake', '~> 12.3'
|
24
|
+
end unless AltPrintf.const_defined?(:'SPEC')
|
@@ -0,0 +1,222 @@
|
|
1
|
+
#include <stdio.h>
|
2
|
+
#include <locale.h>
|
3
|
+
#include <wchar.h>
|
4
|
+
#include <ruby.h>
|
5
|
+
#include <ruby/encoding.h>
|
6
|
+
#include "extconf.h"
|
7
|
+
#include "list.h"
|
8
|
+
#include "altprintf.h"
|
9
|
+
#include "log.h"
|
10
|
+
|
11
|
+
#define CHECKARG \
|
12
|
+
if (!use_hash) { \
|
13
|
+
if (*argi >= argc) goto no_more_args; \
|
14
|
+
entry = rb_ary_entry(*argv, *argi); \
|
15
|
+
}
|
16
|
+
|
17
|
+
#define MODNAME "AltPrintf"
|
18
|
+
|
19
|
+
rb_encoding *enc;
|
20
|
+
|
21
|
+
wchar_t *rbstowcs(VALUE str) {
|
22
|
+
const char *cstr;
|
23
|
+
wchar_t *wstr;
|
24
|
+
size_t len;
|
25
|
+
|
26
|
+
cstr = StringValueCStr(str);
|
27
|
+
|
28
|
+
len = mbsrtowcs(NULL, &cstr, 0, NULL);
|
29
|
+
wstr = calloc(len + 1, sizeof(wchar_t));
|
30
|
+
len = mbsrtowcs(wstr, &cstr, len, NULL);
|
31
|
+
|
32
|
+
LOG("rbs to wcs, len: %d, cstr: '%s'\n", len, cstr);
|
33
|
+
LOG("wide string: '%ls'\n", wstr);
|
34
|
+
|
35
|
+
return wstr;
|
36
|
+
}
|
37
|
+
|
38
|
+
VALUE wcstorbs(const wchar_t *wstr) {
|
39
|
+
size_t len;
|
40
|
+
char *cstr;
|
41
|
+
VALUE str;
|
42
|
+
|
43
|
+
len = wcsrtombs(NULL, &wstr, 0, NULL);
|
44
|
+
cstr = calloc(len, sizeof(wchar_t));
|
45
|
+
wcsrtombs(cstr, &wstr, len, NULL);
|
46
|
+
|
47
|
+
LOG("wcs to rbs, len: %d, cstr: '%s'\n", len, cstr);
|
48
|
+
|
49
|
+
str = rb_external_str_new_with_enc(cstr, len, enc);
|
50
|
+
free(cstr);
|
51
|
+
|
52
|
+
return str;
|
53
|
+
}
|
54
|
+
|
55
|
+
struct list_elem *rb_altprintf_make_list(const wchar_t *fmt, VALUE *argv, long *argi, VALUE *hash) {
|
56
|
+
struct list_elem *le_cur;
|
57
|
+
struct list_elem *le_start;
|
58
|
+
struct list_elem *le_prev;
|
59
|
+
/* create a dummy element as the head */
|
60
|
+
le_start = le_prev = list_elem_create();
|
61
|
+
|
62
|
+
VALUE entry, symbol;
|
63
|
+
|
64
|
+
long int *tmp_int;
|
65
|
+
wint_t *tmp_char;
|
66
|
+
double *tmp_double;
|
67
|
+
const wchar_t *tmp_str;
|
68
|
+
|
69
|
+
char *cstr;
|
70
|
+
size_t len;
|
71
|
+
|
72
|
+
int mode = 0;
|
73
|
+
long argc = rb_array_len(*argv);
|
74
|
+
int use_hash = 0;
|
75
|
+
|
76
|
+
const wchar_t *end = &fmt[wcslen(fmt)];
|
77
|
+
|
78
|
+
for (;fmt<end;fmt++) {
|
79
|
+
LOG("checking char '%lc', lvl: '%d'\n", (wint_t)(*fmt), mode);
|
80
|
+
if (mode == 0) {
|
81
|
+
switch(*fmt) {
|
82
|
+
case FS_START: mode = 1;
|
83
|
+
break;
|
84
|
+
}
|
85
|
+
} else {
|
86
|
+
switch (*fmt) {
|
87
|
+
case FS_A_CHARARG:
|
88
|
+
fmt++;
|
89
|
+
break;
|
90
|
+
case FS_A_RBHASHSTART:
|
91
|
+
tmp_str = fmt + 1;
|
92
|
+
|
93
|
+
use_hash = -1;
|
94
|
+
while (fmt < end && *fmt != FS_A_RBHASHEND) {
|
95
|
+
fmt++;
|
96
|
+
use_hash++;
|
97
|
+
}
|
98
|
+
|
99
|
+
len = wcsnrtombs(NULL, &fmt, use_hash, 0, NULL);
|
100
|
+
cstr = calloc(len + 1, sizeof(char));
|
101
|
+
wcsnrtombs(cstr, &tmp_str, use_hash, len, NULL);
|
102
|
+
LOG("cstr: '%s'\n", cstr);
|
103
|
+
|
104
|
+
symbol = rb_check_symbol_cstr(cstr, len, enc);
|
105
|
+
entry = rb_hash_lookup2(*hash, symbol, Qnil);
|
106
|
+
free(cstr);
|
107
|
+
use_hash = 1;
|
108
|
+
|
109
|
+
break;
|
110
|
+
case FS_A_STRINGSTART:
|
111
|
+
while (fmt < end && *fmt != FS_A_STRINGEND) fmt++;
|
112
|
+
break;
|
113
|
+
case FS_T_STRING:
|
114
|
+
CHECKARG;
|
115
|
+
|
116
|
+
tmp_str = rbstowcs(entry);
|
117
|
+
le_cur = list_elem_ini(tmp_str, String);
|
118
|
+
goto match;
|
119
|
+
case FS_T_MUL:
|
120
|
+
case FS_T_TERN:
|
121
|
+
case FS_T_ALIGN:
|
122
|
+
case FS_T_INT:
|
123
|
+
CHECKARG;
|
124
|
+
|
125
|
+
tmp_int = malloc(sizeof(long int));
|
126
|
+
*tmp_int = FIX2LONG(entry);
|
127
|
+
LOG("got int %ld\n", *tmp_int);
|
128
|
+
le_cur = list_elem_ini(tmp_int, Int);
|
129
|
+
goto match;
|
130
|
+
case FS_T_CHAR:
|
131
|
+
CHECKARG;
|
132
|
+
|
133
|
+
tmp_char = malloc(sizeof(wint_t));
|
134
|
+
tmp_str = rbstowcs(entry);
|
135
|
+
*tmp_char = btowc(tmp_str[0]);
|
136
|
+
le_cur = list_elem_ini(tmp_char, Char);
|
137
|
+
goto match;
|
138
|
+
case FS_T_DOUBLE:
|
139
|
+
CHECKARG;
|
140
|
+
|
141
|
+
tmp_double = malloc(sizeof(double));
|
142
|
+
*tmp_double = RFLOAT_VALUE(entry);
|
143
|
+
le_cur = list_elem_ini(tmp_double, Double);
|
144
|
+
goto match;
|
145
|
+
match: le_prev->next = le_cur;
|
146
|
+
le_prev = le_cur;
|
147
|
+
mode = 0;
|
148
|
+
(*argi)++;
|
149
|
+
break;
|
150
|
+
case FS_START:
|
151
|
+
mode = 0;
|
152
|
+
break;
|
153
|
+
}
|
154
|
+
}
|
155
|
+
}
|
156
|
+
|
157
|
+
no_more_args:
|
158
|
+
if (le_start->next == NULL) return le_start;
|
159
|
+
|
160
|
+
/* set cur to the 2nd element and destroy the first one */
|
161
|
+
le_cur = le_start->next;
|
162
|
+
le_start->next = NULL;
|
163
|
+
list_elem_destroy(le_start);
|
164
|
+
|
165
|
+
return le_cur;
|
166
|
+
}
|
167
|
+
|
168
|
+
VALUE rb_alt_printf(long passes, size_t argc, VALUE *argv, VALUE self) {
|
169
|
+
VALUE fmt, args, hash, final;
|
170
|
+
wchar_t *wfmt;
|
171
|
+
wchar_t *formatted;
|
172
|
+
struct list_elem *ap;
|
173
|
+
|
174
|
+
rb_scan_args(argc, argv, "1*:", &fmt, &args, &hash);
|
175
|
+
|
176
|
+
if (passes == 0) return fmt;
|
177
|
+
|
178
|
+
wfmt = rbstowcs(fmt);
|
179
|
+
|
180
|
+
long argi = 0;
|
181
|
+
while (passes > 0) {
|
182
|
+
ap = rb_altprintf_make_list(wfmt, &args, &argi, &hash);
|
183
|
+
|
184
|
+
formatted = altsprintf(wfmt, ap);
|
185
|
+
LOG("formatted result: '%ls'\n", formatted);
|
186
|
+
|
187
|
+
free(wfmt);
|
188
|
+
list_elem_destroy(ap);
|
189
|
+
|
190
|
+
wfmt = formatted;
|
191
|
+
passes--;
|
192
|
+
}
|
193
|
+
|
194
|
+
final = wcstorbs(formatted);
|
195
|
+
|
196
|
+
free(formatted);
|
197
|
+
|
198
|
+
return final;
|
199
|
+
}
|
200
|
+
|
201
|
+
VALUE rb_alt_printf_single_pass(size_t argc, VALUE *argv, VALUE self) {
|
202
|
+
VALUE fmt, args, hash, final;
|
203
|
+
|
204
|
+
return rb_alt_printf(1, argc, argv, self);
|
205
|
+
}
|
206
|
+
|
207
|
+
VALUE rb_alt_printf_multi_pass(size_t argc, VALUE *argv, VALUE self) {
|
208
|
+
long passes;
|
209
|
+
|
210
|
+
passes = FIX2LONG(argv[0]);
|
211
|
+
|
212
|
+
LOG("passes: %ld\n", passes);
|
213
|
+
return rb_alt_printf(passes, argc - 1, &argv[1], self);
|
214
|
+
}
|
215
|
+
|
216
|
+
void Init_alt_printf()
|
217
|
+
{
|
218
|
+
enc = rb_enc_find("UTF-8");
|
219
|
+
VALUE mod = rb_define_module(MODNAME);
|
220
|
+
rb_define_module_function(mod, "fmt", rb_alt_printf_single_pass, -1);
|
221
|
+
rb_define_module_function(mod, "fmtm", rb_alt_printf_multi_pass, -1);
|
222
|
+
}
|
@@ -0,0 +1,244 @@
|
|
1
|
+
#include "syntax.h"
|
2
|
+
#include "altprintf.h"
|
3
|
+
#include "log.h"
|
4
|
+
|
5
|
+
void default_format(struct format *f) {
|
6
|
+
struct width width = {.prec = -1, .pad = 0};
|
7
|
+
f->stringarg_start = NULL;
|
8
|
+
f->stringarg_end = NULL;
|
9
|
+
f->chararg = L':';
|
10
|
+
f->padchar = L' ';
|
11
|
+
f->align = Right;
|
12
|
+
f->width = width;
|
13
|
+
f->le = NULL;
|
14
|
+
}
|
15
|
+
|
16
|
+
void format_mul(struct strbuf *sb, struct format *f)
|
17
|
+
{
|
18
|
+
long int *i = f->le->data;
|
19
|
+
strbuf_pad(sb, f->chararg, *i);
|
20
|
+
}
|
21
|
+
void format_tern(struct strbuf *sb, struct format *f)
|
22
|
+
{
|
23
|
+
long int *b = f->le->data;
|
24
|
+
int first_half = 1;
|
25
|
+
wchar_t sep = f->chararg;
|
26
|
+
wchar_t *p = f->stringarg_start;
|
27
|
+
for (;p<=f->stringarg_end;p++) {
|
28
|
+
LOG("*p: %lc, first half? %d, bool: %ld, sep: %lc\n", (wint_t)*p, first_half, *b, (wint_t)sep);
|
29
|
+
if (*p == sep) first_half = 0;
|
30
|
+
else if (*b && first_half) strbuf_append_char(sb, p);
|
31
|
+
else if (!*b && !first_half) strbuf_append_char(sb, p);
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
void format_string(struct strbuf *sb, struct format *f)
|
36
|
+
{
|
37
|
+
int prec = f->width.prec == -1 ? 100000000 : f->width.prec;
|
38
|
+
strbuf_append_str(sb, f->le->data, prec);
|
39
|
+
}
|
40
|
+
void format_char(struct strbuf *sb, struct format *f)
|
41
|
+
{
|
42
|
+
strbuf_append_char(sb, f->le->data);
|
43
|
+
}
|
44
|
+
void format_int(struct strbuf *sb, struct format *f)
|
45
|
+
{
|
46
|
+
strbuf_append_int(sb, f->le->data);
|
47
|
+
}
|
48
|
+
void format_double(struct strbuf *sb, struct format *f)
|
49
|
+
{
|
50
|
+
int prec = f->width.prec == -1 ? 3 : f->width.prec;
|
51
|
+
strbuf_append_double(sb, f->le->data, prec);
|
52
|
+
}
|
53
|
+
|
54
|
+
void format(struct strbuf *sb, struct format *f, void (*to_s)(struct strbuf *, struct format *))
|
55
|
+
{
|
56
|
+
struct strbuf *tmp = strbuf_new();
|
57
|
+
to_s(tmp, f);
|
58
|
+
|
59
|
+
if (tmp->len == 0) return;
|
60
|
+
|
61
|
+
int pad = f->width.pad - tmp->width;
|
62
|
+
|
63
|
+
if (pad > 0) {
|
64
|
+
LOG("padding: %d\n", pad);
|
65
|
+
switch(f->align) {
|
66
|
+
case Right:
|
67
|
+
strbuf_append_strbuf(sb, tmp);
|
68
|
+
strbuf_pad(sb, f->padchar, pad);
|
69
|
+
break;
|
70
|
+
case Left:
|
71
|
+
strbuf_pad(sb, f->padchar, pad);
|
72
|
+
strbuf_append_strbuf(sb, tmp);
|
73
|
+
break;
|
74
|
+
case Center:
|
75
|
+
strbuf_pad(sb, f->padchar, pad/2);
|
76
|
+
strbuf_append_strbuf(sb, tmp);
|
77
|
+
strbuf_pad(sb, f->padchar, pad/2 + pad%2);
|
78
|
+
break;
|
79
|
+
}
|
80
|
+
} else {
|
81
|
+
strbuf_append_strbuf(sb, tmp);
|
82
|
+
}
|
83
|
+
|
84
|
+
strbuf_destroy(tmp);
|
85
|
+
}
|
86
|
+
|
87
|
+
wchar_t *altsprintf(wchar_t *fmt, struct list_elem *le) {
|
88
|
+
int lvl = 0;
|
89
|
+
int split = 0;
|
90
|
+
struct strbuf *sbs[] = {strbuf_new(), strbuf_new()};
|
91
|
+
struct strbuf *sb = sbs[0];
|
92
|
+
wchar_t *end = &fmt[wcslen(fmt)];
|
93
|
+
wchar_t *jump;
|
94
|
+
|
95
|
+
void (*append_func)(struct strbuf *, struct format *);
|
96
|
+
struct format f;
|
97
|
+
long int *number_p = NULL;
|
98
|
+
long int *width;
|
99
|
+
wint_t split_pad = L' ';
|
100
|
+
|
101
|
+
for (;fmt<end;fmt++) {
|
102
|
+
LOG("checking char '%lc', lvl: '%d'\n", (wint_t)(*fmt), lvl);
|
103
|
+
switch (lvl) {
|
104
|
+
case 0:
|
105
|
+
switch(*fmt) {
|
106
|
+
case FS_START:
|
107
|
+
default_format(&f);
|
108
|
+
lvl = 1;
|
109
|
+
break;
|
110
|
+
case FS_ESC:
|
111
|
+
lvl = 3;
|
112
|
+
break;
|
113
|
+
default:
|
114
|
+
strbuf_append(sb, *fmt);
|
115
|
+
break;
|
116
|
+
}; break;
|
117
|
+
case 1:
|
118
|
+
switch(*fmt) {
|
119
|
+
/* special arguments */
|
120
|
+
case FS_A_STRINGSTART:
|
121
|
+
f.stringarg_start = fmt + 1;
|
122
|
+
lvl = 2;
|
123
|
+
break;
|
124
|
+
case FS_A_CHARARG:
|
125
|
+
f.chararg = *(fmt+1);
|
126
|
+
fmt += 1;
|
127
|
+
break;
|
128
|
+
|
129
|
+
/* standard arguments */
|
130
|
+
case FS_A_LALIGN:
|
131
|
+
f.align = Left;
|
132
|
+
break;
|
133
|
+
case FS_A_SPAD:
|
134
|
+
f.padchar = FS_A_SPAD;
|
135
|
+
break;
|
136
|
+
case 0:
|
137
|
+
f.padchar = '0';
|
138
|
+
break;
|
139
|
+
case '.':
|
140
|
+
number_p = &f.width.prec;
|
141
|
+
fmt++;
|
142
|
+
case '1': case '2': case '3': case '4': case '5':
|
143
|
+
case '6': case '7': case '8': case '9':
|
144
|
+
if (number_p == NULL) number_p = &f.width.pad;
|
145
|
+
*number_p = wcstol(fmt, &jump, 10);
|
146
|
+
fmt = (jump-1);
|
147
|
+
break;
|
148
|
+
|
149
|
+
/* align operator */
|
150
|
+
case FS_T_ALIGN:
|
151
|
+
if (le != NULL && le->type != Null) {
|
152
|
+
width = le->data;
|
153
|
+
le = le->next;
|
154
|
+
} else {
|
155
|
+
goto no_more_args;
|
156
|
+
}
|
157
|
+
|
158
|
+
split = 1;
|
159
|
+
split_pad = f.chararg;
|
160
|
+
sb = sbs[1];
|
161
|
+
lvl = 0;
|
162
|
+
break;
|
163
|
+
|
164
|
+
/* types */
|
165
|
+
case FS_T_STRING:
|
166
|
+
append_func = format_string;
|
167
|
+
goto match;
|
168
|
+
case FS_T_TERN:
|
169
|
+
append_func = format_tern;
|
170
|
+
goto match;
|
171
|
+
case FS_T_INT:
|
172
|
+
append_func = format_int;
|
173
|
+
goto match;
|
174
|
+
case FS_T_MUL:
|
175
|
+
append_func = format_mul;
|
176
|
+
goto match;
|
177
|
+
case FS_T_CHAR:
|
178
|
+
append_func = format_char;
|
179
|
+
goto match;
|
180
|
+
case FS_T_DOUBLE:
|
181
|
+
append_func = format_double;
|
182
|
+
match:
|
183
|
+
if (le != NULL && le->type != Null) {
|
184
|
+
f.le = le;
|
185
|
+
format(sb, &f, append_func);
|
186
|
+
le = le->next;
|
187
|
+
}
|
188
|
+
lvl = 0;
|
189
|
+
break;
|
190
|
+
case FS_START:
|
191
|
+
strbuf_append(sb, FS_START);
|
192
|
+
lvl = 0;
|
193
|
+
break;
|
194
|
+
}; break;
|
195
|
+
case 2:
|
196
|
+
if (*fmt == FS_A_STRINGEND) {
|
197
|
+
f.stringarg_end = fmt - 1;
|
198
|
+
lvl = 1;
|
199
|
+
}; break;
|
200
|
+
case 3:
|
201
|
+
switch(*fmt) {
|
202
|
+
case FS_ESC_NL:
|
203
|
+
strbuf_append(sb, '\n');
|
204
|
+
break;
|
205
|
+
case FS_ESC_ESC:
|
206
|
+
strbuf_append(sb, '\e');
|
207
|
+
break;
|
208
|
+
default:
|
209
|
+
strbuf_append(sb, *fmt);
|
210
|
+
break;
|
211
|
+
};
|
212
|
+
lvl = 0;
|
213
|
+
break;
|
214
|
+
}
|
215
|
+
}
|
216
|
+
|
217
|
+
wchar_t *str;
|
218
|
+
no_more_args:
|
219
|
+
if (split) {
|
220
|
+
LOG("splitting string\n");
|
221
|
+
lvl = *width - (sbs[0]->width + sbs[1]->width);
|
222
|
+
if (lvl >= 0) {
|
223
|
+
LOG("padding center\n");
|
224
|
+
strbuf_pad(sbs[0], split_pad, lvl);
|
225
|
+
strbuf_append_strbuf(sbs[0], sbs[1]);
|
226
|
+
} else if (sbs[0]->width > *width) {
|
227
|
+
LOG("the first half is longer than the requested with\n");
|
228
|
+
strbuf_destroy(sbs[1]);
|
229
|
+
sbs[1] = sbs[0];
|
230
|
+
sbs[0] = strbuf_new();
|
231
|
+
strbuf_appendw_strbuf(sbs[0], sbs[1], *width);
|
232
|
+
} else {
|
233
|
+
LOG("just shave some off the last half\n");
|
234
|
+
strbuf_appendw_strbuf(sbs[0], sbs[1], *width - sbs[0]->width);
|
235
|
+
}
|
236
|
+
}
|
237
|
+
|
238
|
+
str = strbuf_cstr(sbs[0]);
|
239
|
+
|
240
|
+
strbuf_destroy(sbs[0]);
|
241
|
+
strbuf_destroy(sbs[1]);
|
242
|
+
|
243
|
+
return str;
|
244
|
+
}
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#ifndef ALTPRINTF_H
|
2
|
+
#define ALTPRINTF_H
|
3
|
+
|
4
|
+
#ifndef _XOPEN_SOURCE
|
5
|
+
#define _XOPEN_SOURCE
|
6
|
+
#endif
|
7
|
+
|
8
|
+
#include <stdio.h>
|
9
|
+
#include <stdlib.h>
|
10
|
+
#include <stdarg.h>
|
11
|
+
#include <string.h>
|
12
|
+
#include <math.h>
|
13
|
+
#include <limits.h>
|
14
|
+
#include <wchar.h>
|
15
|
+
#include "strbuf.h"
|
16
|
+
#include "list.h"
|
17
|
+
#include "syntax.h"
|
18
|
+
|
19
|
+
wchar_t *altsprintf(wchar_t *fmt, struct list_elem *le);
|
20
|
+
|
21
|
+
enum align {
|
22
|
+
Left,
|
23
|
+
Right,
|
24
|
+
Center
|
25
|
+
};
|
26
|
+
|
27
|
+
struct width {
|
28
|
+
long int prec;
|
29
|
+
long int pad;
|
30
|
+
};
|
31
|
+
|
32
|
+
struct format {
|
33
|
+
wchar_t *stringarg_start;
|
34
|
+
wchar_t *stringarg_end;
|
35
|
+
wint_t chararg;
|
36
|
+
wint_t padchar;
|
37
|
+
enum align align;
|
38
|
+
struct width width;
|
39
|
+
struct list_elem *le;
|
40
|
+
};
|
41
|
+
|
42
|
+
#endif
|
@@ -0,0 +1,9 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'mkmf'
|
3
|
+
|
4
|
+
base_dir = File.join(__dir__, '../../../')
|
5
|
+
find_header('altprintf.h', File.join(base_dir, 'src'))
|
6
|
+
$objs = Dir[File.join(base_dir, 'target/release/*.o')] + ['alt_printf.o']
|
7
|
+
|
8
|
+
create_header
|
9
|
+
create_makefile('alt_printf')
|
@@ -0,0 +1,81 @@
|
|
1
|
+
#include "list.h"
|
2
|
+
#include "log.h"
|
3
|
+
|
4
|
+
struct list_elem *list_elem_create() {
|
5
|
+
void *null = NULL;
|
6
|
+
struct list_elem *le = malloc(sizeof(struct list_elem));
|
7
|
+
LOG("allocatted %zu %p\n", sizeof(struct list_elem), le);
|
8
|
+
le->data = null;
|
9
|
+
le->type = Null;
|
10
|
+
le->next = null;
|
11
|
+
le->heap = 1;
|
12
|
+
|
13
|
+
return le;
|
14
|
+
}
|
15
|
+
|
16
|
+
void list_elem_destroy(struct list_elem *le) {
|
17
|
+
if (le->heap && le->data != NULL) {
|
18
|
+
LOG("freeing %p\n", le->data);
|
19
|
+
free(le->data);
|
20
|
+
}
|
21
|
+
if (le->next != NULL) { list_elem_destroy(le->next); }
|
22
|
+
|
23
|
+
LOG("freeing %p\n", le);
|
24
|
+
free(le);
|
25
|
+
}
|
26
|
+
|
27
|
+
struct list_elem *list_elem_ini(void *data, enum type t) {
|
28
|
+
struct list_elem *le = list_elem_create();
|
29
|
+
le->data = data;
|
30
|
+
le->type = t;
|
31
|
+
return le;
|
32
|
+
}
|
33
|
+
|
34
|
+
struct list_elem *list_elem_ini_str(wchar_t *str) {
|
35
|
+
struct list_elem *le = list_elem_create();
|
36
|
+
le->data = str;
|
37
|
+
le->type = String;
|
38
|
+
return le;
|
39
|
+
}
|
40
|
+
|
41
|
+
struct list_elem *list_elem_ini_int(long int *i) {
|
42
|
+
struct list_elem *le = list_elem_create();
|
43
|
+
le->data = i;
|
44
|
+
le->type = Int;
|
45
|
+
return le;
|
46
|
+
}
|
47
|
+
|
48
|
+
struct list_elem *list_elem_ini_dub(double *d) {
|
49
|
+
struct list_elem *le = list_elem_create();
|
50
|
+
le->data = d;
|
51
|
+
le->type = Double;
|
52
|
+
return le;
|
53
|
+
}
|
54
|
+
|
55
|
+
void list_elem_inspect(struct list_elem *le) {
|
56
|
+
switch (le->type) {
|
57
|
+
case Int:
|
58
|
+
printf("->Int %ld\n", *(long int *)le->data);
|
59
|
+
break;
|
60
|
+
case String:
|
61
|
+
printf("->String '%ls'\n", (wchar_t *)le->data);
|
62
|
+
break;
|
63
|
+
case Char:
|
64
|
+
printf("->Char %lc\n", *(wint_t *)le->data);
|
65
|
+
break;
|
66
|
+
case Double:
|
67
|
+
printf("->Double %f\n", *(double *)le->data);
|
68
|
+
break;
|
69
|
+
case Null:
|
70
|
+
printf("->Null\n");
|
71
|
+
break;
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
void list_elem_inspect_all(struct list_elem *le) {
|
76
|
+
while(1) {
|
77
|
+
list_elem_inspect(le);
|
78
|
+
if (le->next == NULL) { break; }
|
79
|
+
le = le->next;
|
80
|
+
}
|
81
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#ifndef LIST_H
|
2
|
+
#define LIST_H
|
3
|
+
#include <stdio.h>
|
4
|
+
#include <stdlib.h>
|
5
|
+
#include <wchar.h>
|
6
|
+
|
7
|
+
enum type { Int, String, Char, Double, Null };
|
8
|
+
|
9
|
+
struct list_elem {
|
10
|
+
void *data;
|
11
|
+
enum type type;
|
12
|
+
struct list_elem *next;
|
13
|
+
int heap;
|
14
|
+
};
|
15
|
+
|
16
|
+
struct list_elem *list_elem_create();
|
17
|
+
void list_elem_destroy(struct list_elem *le);
|
18
|
+
struct list_elem *list_elem_ini(void *data, enum type t);
|
19
|
+
struct list_elem *list_elem_ini_int(long int *i);
|
20
|
+
struct list_elem *list_elem_ini_str(wchar_t *str);
|
21
|
+
struct list_elem *list_elem_ini_dub(double *d);
|
22
|
+
void list_elem_inspect(struct list_elem *le);
|
23
|
+
void list_elem_inspect_all(struct list_elem *le);
|
24
|
+
#endif
|
@@ -0,0 +1,11 @@
|
|
1
|
+
#ifndef LOG_H
|
2
|
+
#define LOG_H
|
3
|
+
|
4
|
+
#ifdef DEBUG
|
5
|
+
#define LOG(...) printf("%s[%d] - ", __FILE__, __LINE__);printf(__VA_ARGS__);
|
6
|
+
#define FLOG(msg, ...) fprintf(stderr, "%s[%d] - "msg, __FILE__, __LINE__ __VA_OPT__(,) __VA_ARGS__)
|
7
|
+
#else
|
8
|
+
#define LOG(msg, ...)
|
9
|
+
#define FLOG(msg, ...)
|
10
|
+
#endif
|
11
|
+
#endif
|
@@ -0,0 +1,142 @@
|
|
1
|
+
#define _XOPEN_SOURCE
|
2
|
+
#include <locale.h>
|
3
|
+
#include <limits.h>
|
4
|
+
#include "strbuf.h"
|
5
|
+
#include "log.h"
|
6
|
+
|
7
|
+
#define STRBUF_INI_SIZE 5
|
8
|
+
#define STRBUF_GROW_STEP 100
|
9
|
+
|
10
|
+
extern struct lconv *locale_info;
|
11
|
+
|
12
|
+
struct strbuf *strbuf_new() {
|
13
|
+
struct strbuf *sb = malloc(sizeof(struct strbuf));
|
14
|
+
|
15
|
+
if (NULL == sb) {
|
16
|
+
LOG("can't alloc memory for new strbuf", NULL);
|
17
|
+
exit(1);
|
18
|
+
}
|
19
|
+
|
20
|
+
sb->start = sb->end = calloc(STRBUF_INI_SIZE, sizeof(wchar_t));
|
21
|
+
|
22
|
+
if (NULL == sb->start) {
|
23
|
+
LOG("can't alloc memory for new strbuf string", NULL);
|
24
|
+
exit(1);
|
25
|
+
}
|
26
|
+
|
27
|
+
sb->len = 0;
|
28
|
+
sb->cap = STRBUF_INI_SIZE;
|
29
|
+
sb->width = 0;
|
30
|
+
return sb;
|
31
|
+
}
|
32
|
+
|
33
|
+
void strbuf_destroy(struct strbuf *sb) {
|
34
|
+
free(sb->start);
|
35
|
+
free(sb);
|
36
|
+
}
|
37
|
+
|
38
|
+
void strbuf_append(struct strbuf *sb, wchar_t c)
|
39
|
+
{
|
40
|
+
wchar_t *ns;
|
41
|
+
|
42
|
+
if (sb->cap < sb->len + 2) {
|
43
|
+
ns = calloc(sb->cap + STRBUF_GROW_STEP, sizeof(wchar_t));
|
44
|
+
|
45
|
+
if (ns == NULL) {
|
46
|
+
LOG("can't increase size of strbuf to %d\n", sb->cap + STRBUF_GROW_STEP);
|
47
|
+
exit(1);
|
48
|
+
}
|
49
|
+
|
50
|
+
wcscpy(ns, sb->start);
|
51
|
+
free(sb->start);
|
52
|
+
|
53
|
+
sb->start = ns;
|
54
|
+
sb->cap += STRBUF_GROW_STEP;
|
55
|
+
}
|
56
|
+
|
57
|
+
sb->start[sb->len] = c;
|
58
|
+
sb->start[sb->len+1] = L'\0';
|
59
|
+
LOG("string so far: %ls\n", sb->start);
|
60
|
+
sb->end = &sb->start[sb->len];
|
61
|
+
sb->len++;
|
62
|
+
int w = wcwidth(c);
|
63
|
+
if (w >= 0) sb->width += w;
|
64
|
+
}
|
65
|
+
|
66
|
+
void strbuf_append_strbuf(struct strbuf *sb, void *sbuf)
|
67
|
+
{
|
68
|
+
wchar_t *pos;
|
69
|
+
struct strbuf *frm = sbuf;
|
70
|
+
LOG("frm->start: %p | frm->end: %p\n", frm->start, frm->end);
|
71
|
+
|
72
|
+
for (pos = frm->start;pos<=frm->end;pos++) {
|
73
|
+
strbuf_append(sb, *pos);
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
77
|
+
void strbuf_appendw_strbuf(struct strbuf *sb, void *sbuf, long w)
|
78
|
+
{
|
79
|
+
wchar_t *pos;
|
80
|
+
long ws = 0;
|
81
|
+
struct strbuf *frm = sbuf;
|
82
|
+
LOG("frm->start: %p | frm->end: %p\n", frm->start, frm->end);
|
83
|
+
|
84
|
+
for (pos = frm->start;pos<=frm->end;pos++) {
|
85
|
+
ws += wcwidth(*pos);
|
86
|
+
LOG("new width would be: %ld, requested width: %ld\n", ws, w);
|
87
|
+
if (ws > w) break;
|
88
|
+
strbuf_append(sb, *pos);
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
void strbuf_append_char(struct strbuf *sb, void *chr)
|
93
|
+
{
|
94
|
+
wint_t *c = chr;
|
95
|
+
strbuf_append(sb, *c);
|
96
|
+
}
|
97
|
+
|
98
|
+
void strbuf_append_str(struct strbuf *sb, void *str, int maxwidth)
|
99
|
+
{
|
100
|
+
wchar_t *s = str;
|
101
|
+
wchar_t *end = &s[wcslen(s)];
|
102
|
+
int width = 0;
|
103
|
+
|
104
|
+
for (;s<end;s++) {
|
105
|
+
width += wcwidth(*s);
|
106
|
+
if (width > maxwidth) return;
|
107
|
+
strbuf_append(sb, *s);
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
void strbuf_append_int(struct strbuf *sb, void *in)
|
112
|
+
{
|
113
|
+
long int *i = in;
|
114
|
+
wchar_t wcs[50];
|
115
|
+
swprintf(wcs, 50, L"%ld", *i);
|
116
|
+
strbuf_append_str(sb, wcs, 50);
|
117
|
+
}
|
118
|
+
|
119
|
+
void strbuf_append_double(struct strbuf *sb, void *dub, int prec)
|
120
|
+
{
|
121
|
+
double *d = dub;
|
122
|
+
wchar_t wcs[50];
|
123
|
+
wchar_t format[30];
|
124
|
+
swprintf(format, 30, L"%%.%ldf", prec);
|
125
|
+
LOG("format: %ls\n", format);
|
126
|
+
swprintf(wcs, 50, format, *d);
|
127
|
+
strbuf_append_str(sb, wcs, 50);
|
128
|
+
}
|
129
|
+
|
130
|
+
void strbuf_pad(struct strbuf *sb, wchar_t pc, int amnt)
|
131
|
+
{
|
132
|
+
for (;amnt>0;amnt--) strbuf_append(sb, pc);
|
133
|
+
}
|
134
|
+
|
135
|
+
wchar_t *strbuf_cstr(struct strbuf *sb)
|
136
|
+
{
|
137
|
+
wchar_t *str;
|
138
|
+
str = calloc(sb->len + 1, sizeof(wchar_t));
|
139
|
+
wcscpy(str, sb->start);
|
140
|
+
|
141
|
+
return str;
|
142
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#ifndef STRBUF_H
|
2
|
+
#define STRBUF_H
|
3
|
+
#include <wchar.h>
|
4
|
+
#include <stdio.h>
|
5
|
+
#include <stdlib.h>
|
6
|
+
#include <string.h>
|
7
|
+
#include <math.h>
|
8
|
+
|
9
|
+
struct strbuf *strbuf_new();
|
10
|
+
void strbuf_destroy(struct strbuf *sb);
|
11
|
+
void strbuf_append(struct strbuf *sb, wchar_t);
|
12
|
+
void strbuf_append_char(struct strbuf *sb, void *);
|
13
|
+
void strbuf_append_str(struct strbuf *, void *, int);
|
14
|
+
void strbuf_append_int(struct strbuf *sb, void *);
|
15
|
+
void strbuf_append_double(struct strbuf *, void *, int);
|
16
|
+
void strbuf_append_strbuf(struct strbuf *, void *);
|
17
|
+
void strbuf_appendw_strbuf(struct strbuf *, void *, long);
|
18
|
+
void strbuf_pad(struct strbuf *, wchar_t, int);
|
19
|
+
wchar_t *strbuf_cstr(struct strbuf *);
|
20
|
+
|
21
|
+
struct strbuf {
|
22
|
+
wchar_t *start;
|
23
|
+
wchar_t *end;
|
24
|
+
size_t len;
|
25
|
+
size_t width;
|
26
|
+
size_t cap;
|
27
|
+
};
|
28
|
+
#endif
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#ifndef SYNTAX_H
|
2
|
+
#define SYNTAX_H
|
3
|
+
|
4
|
+
#define FS_START '%'
|
5
|
+
#define FS_DOUBLE_SEP '.'
|
6
|
+
|
7
|
+
#define FS_ESC '\\'
|
8
|
+
#define FS_ESC_NL 'n'
|
9
|
+
#define FS_ESC_ESC 'e'
|
10
|
+
|
11
|
+
#define FS_T_STRING 's'
|
12
|
+
#define FS_T_CHAR 'c'
|
13
|
+
#define FS_T_DOUBLE 'f'
|
14
|
+
#define FS_T_INT 'd'
|
15
|
+
#define FS_T_MUL '*'
|
16
|
+
#define FS_T_TERN '?'
|
17
|
+
#define FS_T_ALIGN '='
|
18
|
+
|
19
|
+
#define FS_A_STRINGSTART '('
|
20
|
+
#define FS_A_STRINGEND ')'
|
21
|
+
#define FS_A_CHARARG '~'
|
22
|
+
#define FS_A_LALIGN '-'
|
23
|
+
#define FS_A_SPAD ' '
|
24
|
+
|
25
|
+
#define FS_A_RBHASHSTART '<'
|
26
|
+
#define FS_A_RBHASHEND '>'
|
27
|
+
|
28
|
+
#define FS_D_PREC 100
|
29
|
+
|
30
|
+
#endif
|
Binary file
|
data/lib/alt_printf.rb
ADDED
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: alt_printf
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Stone Tickle
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-08-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake-compiler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '12.3'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '12.3'
|
41
|
+
description:
|
42
|
+
email: lattis@mochiro.moe
|
43
|
+
executables: []
|
44
|
+
extensions:
|
45
|
+
- ext/alt_printf/extconf.rb
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- Gemfile
|
49
|
+
- README.md
|
50
|
+
- Rakefile
|
51
|
+
- alt_printf.gemspec
|
52
|
+
- ext/alt_printf/alt_printf.c
|
53
|
+
- ext/alt_printf/altprintf.c
|
54
|
+
- ext/alt_printf/altprintf.h
|
55
|
+
- ext/alt_printf/extconf.rb
|
56
|
+
- ext/alt_printf/extconf_dev.rb
|
57
|
+
- ext/alt_printf/list.c
|
58
|
+
- ext/alt_printf/list.h
|
59
|
+
- ext/alt_printf/log.h
|
60
|
+
- ext/alt_printf/strbuf.c
|
61
|
+
- ext/alt_printf/strbuf.h
|
62
|
+
- ext/alt_printf/syntax.h
|
63
|
+
- lib/alt_printf.rb
|
64
|
+
- lib/alt_printf/alt_printf.so
|
65
|
+
- lib/alt_printf/version.rb
|
66
|
+
homepage: https://github.com/alt_printf/gem
|
67
|
+
licenses:
|
68
|
+
- MIT
|
69
|
+
metadata: {}
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: 2.6.3
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubygems_version: 3.0.3
|
86
|
+
signing_key:
|
87
|
+
specification_version: 4
|
88
|
+
summary: A powerful printf-like template language
|
89
|
+
test_files: []
|