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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
Description: 9libs: Sync with plan9port.
This patch is imported from the upstream's VCS.
Author: Kris Maglione <kris@suckless.org>
Origin: upstream, http://hg.suckless.org/wmii/raw-rev/d1296b42f38a
--- a/include/fmt.h
+++ b/include/fmt.h
@@ -27,10 +27,22 @@
void *farg; /* to make flush a closure */
int nfmt; /* num chars formatted so far */
va_list args; /* args passed to dofmt */
- int r; /* % format Rune */
+ Rune r; /* % format Rune */
int width;
int prec;
unsigned long flags;
+ char *decimal; /* decimal point; cannot be "" */
+
+ /* For %'d */
+ char *thousands; /* separator for thousands */
+
+ /*
+ * Each char is an integer indicating #digits before next separator. Values:
+ * \xFF: no more grouping (or \x7F; defined to be CHAR_MAX in POSIX)
+ * \x00: repeat previous indefinitely
+ * \x**: count that many
+ */
+ char *grouping; /* descriptor of separator placement */
};
enum{
@@ -40,7 +52,8 @@
FmtSharp = FmtPrec << 1,
FmtSpace = FmtSharp << 1,
FmtSign = FmtSpace << 1,
- FmtZero = FmtSign << 1,
+ FmtApost = FmtSign << 1,
+ FmtZero = FmtApost << 1,
FmtUnsigned = FmtZero << 1,
FmtShort = FmtUnsigned << 1,
FmtLong = FmtShort << 1,
@@ -121,6 +134,7 @@
int fmtfdflush(Fmt*);
int fmtfdinit(Fmt*, int fd, char *buf, int size);
int fmtinstall(int, int (*f)(Fmt*));
+void fmtlocaleinit(Fmt*, char *decimal, char *thousands, char *grouping);
int fmtprint(Fmt*, const char*, ...);
int fmtrune(Fmt*, int);
int fmtrunestrcpy(Fmt*, Rune*);
--- a/include/utf.h
+++ b/include/utf.h
@@ -1,14 +1,15 @@
#ifndef _UTF_H_
#define _UTF_H_ 1
-typedef unsigned short Rune; /* 16 bits */
+typedef unsigned int Rune; /* 32 bits */
enum
{
- UTFmax = 3, /* maximum bytes per rune */
+ UTFmax = 4, /* maximum bytes per rune */
Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */
Runeself = 0x80, /* rune and UTF sequences are the same (<) */
- Runeerror = 0xFFFD, /* decoding error in UTF */
+ Runeerror = 0xFFFD, /* decoding error in UTF */
+ Runemax = 0x10FFFF /* maximum rune value */
};
/* Edit .+1,/^$/ | cfn $PLAN9/src/lib9/utf/?*.c | grep -v static |grep -v __ */
@@ -19,7 +20,7 @@
int isspacerune(Rune);
int istitlerune(Rune);
int isupperrune(Rune);
-int runelen(Rune);
+int runelen(long);
int runenlen(const Rune*, int);
Rune* runestrcat(Rune*, const Rune*);
Rune* runestrchr(const Rune*, Rune);
--- /dev/null
+++ b/lib/libfmt/fmtlocale.c
@@ -0,0 +1,55 @@
+/* Copyright (c) 2004 Google Inc.; see LICENSE */
+
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+/*
+ * Fill in the internationalization stuff in the State structure.
+ * For nil arguments, provide the sensible defaults:
+ * decimal is a period
+ * thousands separator is a comma
+ * thousands are marked every three digits
+ */
+void
+fmtlocaleinit(Fmt *f, char *decimal, char *thousands, char *grouping)
+{
+ if(decimal == nil || decimal[0] == '\0')
+ decimal = ".";
+ if(thousands == nil)
+ thousands = ",";
+ if(grouping == nil)
+ grouping = "\3";
+ f->decimal = decimal;
+ f->thousands = thousands;
+ f->grouping = grouping;
+}
+
+/*
+ * We are about to emit a digit in e.g. %'d. If that digit would
+ * overflow a thousands (e.g.) grouping, tell the caller to emit
+ * the thousands separator. Always advance the digit counter
+ * and pointer into the grouping descriptor.
+ */
+int
+__needsep(int *ndig, char **grouping)
+{
+ int group;
+
+ (*ndig)++;
+ group = *(unsigned char*)*grouping;
+ /* CHAR_MAX means no further grouping. \0 means we got the empty string */
+ if(group == 0xFF || group == 0x7f || group == 0x00)
+ return 0;
+ if(*ndig > group){
+ /* if we're at end of string, continue with this grouping; else advance */
+ if((*grouping)[1] != '\0')
+ (*grouping)++;
+ *ndig = 1;
+ return 1;
+ }
+ return 0;
+}
+
|