1a2438132SYegappan Lakshmanan /* vi:set ts=8 sts=4 sw=4 noet:
2a2438132SYegappan Lakshmanan *
3a2438132SYegappan Lakshmanan * VIM - Vi IMproved by Bram Moolenaar
4a2438132SYegappan Lakshmanan *
5a2438132SYegappan Lakshmanan * Do ":help uganda" in Vim to read copying and usage conditions.
6a2438132SYegappan Lakshmanan * Do ":help credits" in Vim to see a list of people who contributed.
7a2438132SYegappan Lakshmanan * See README.txt for an overview of the Vim source code.
8a2438132SYegappan Lakshmanan */
9a2438132SYegappan Lakshmanan
10a2438132SYegappan Lakshmanan /*
11a2438132SYegappan Lakshmanan * strings.c: string manipulation functions
12a2438132SYegappan Lakshmanan */
13a2438132SYegappan Lakshmanan
148ee52affSYegappan Lakshmanan #define USING_FLOAT_STUFF
15a2438132SYegappan Lakshmanan #include "vim.h"
16a2438132SYegappan Lakshmanan
17a2438132SYegappan Lakshmanan /*
18a2438132SYegappan Lakshmanan * Copy "string" into newly allocated memory.
19a2438132SYegappan Lakshmanan */
20a2438132SYegappan Lakshmanan char_u *
vim_strsave(char_u * string)21a2438132SYegappan Lakshmanan vim_strsave(char_u *string)
22a2438132SYegappan Lakshmanan {
23a2438132SYegappan Lakshmanan char_u *p;
24a2438132SYegappan Lakshmanan size_t len;
25a2438132SYegappan Lakshmanan
26a2438132SYegappan Lakshmanan len = STRLEN(string) + 1;
27a2438132SYegappan Lakshmanan p = alloc(len);
28a2438132SYegappan Lakshmanan if (p != NULL)
29a2438132SYegappan Lakshmanan mch_memmove(p, string, len);
30a2438132SYegappan Lakshmanan return p;
31a2438132SYegappan Lakshmanan }
32a2438132SYegappan Lakshmanan
33a2438132SYegappan Lakshmanan /*
34a2438132SYegappan Lakshmanan * Copy up to "len" bytes of "string" into newly allocated memory and
35a2438132SYegappan Lakshmanan * terminate with a NUL.
36a2438132SYegappan Lakshmanan * The allocated memory always has size "len + 1", also when "string" is
37a2438132SYegappan Lakshmanan * shorter.
38a2438132SYegappan Lakshmanan */
39a2438132SYegappan Lakshmanan char_u *
vim_strnsave(char_u * string,size_t len)40a2438132SYegappan Lakshmanan vim_strnsave(char_u *string, size_t len)
41a2438132SYegappan Lakshmanan {
42a2438132SYegappan Lakshmanan char_u *p;
43a2438132SYegappan Lakshmanan
44a2438132SYegappan Lakshmanan p = alloc(len + 1);
45a2438132SYegappan Lakshmanan if (p != NULL)
46a2438132SYegappan Lakshmanan {
47a2438132SYegappan Lakshmanan STRNCPY(p, string, len);
48a2438132SYegappan Lakshmanan p[len] = NUL;
49a2438132SYegappan Lakshmanan }
50a2438132SYegappan Lakshmanan return p;
51a2438132SYegappan Lakshmanan }
52a2438132SYegappan Lakshmanan
53a2438132SYegappan Lakshmanan /*
54a2438132SYegappan Lakshmanan * Same as vim_strsave(), but any characters found in esc_chars are preceded
55a2438132SYegappan Lakshmanan * by a backslash.
56a2438132SYegappan Lakshmanan */
57a2438132SYegappan Lakshmanan char_u *
vim_strsave_escaped(char_u * string,char_u * esc_chars)58a2438132SYegappan Lakshmanan vim_strsave_escaped(char_u *string, char_u *esc_chars)
59a2438132SYegappan Lakshmanan {
60a2438132SYegappan Lakshmanan return vim_strsave_escaped_ext(string, esc_chars, '\\', FALSE);
61a2438132SYegappan Lakshmanan }
62a2438132SYegappan Lakshmanan
63a2438132SYegappan Lakshmanan /*
64a2438132SYegappan Lakshmanan * Same as vim_strsave_escaped(), but when "bsl" is TRUE also escape
65a2438132SYegappan Lakshmanan * characters where rem_backslash() would remove the backslash.
66a2438132SYegappan Lakshmanan * Escape the characters with "cc".
67a2438132SYegappan Lakshmanan */
68a2438132SYegappan Lakshmanan char_u *
vim_strsave_escaped_ext(char_u * string,char_u * esc_chars,int cc,int bsl)69a2438132SYegappan Lakshmanan vim_strsave_escaped_ext(
70a2438132SYegappan Lakshmanan char_u *string,
71a2438132SYegappan Lakshmanan char_u *esc_chars,
72a2438132SYegappan Lakshmanan int cc,
73a2438132SYegappan Lakshmanan int bsl)
74a2438132SYegappan Lakshmanan {
75a2438132SYegappan Lakshmanan char_u *p;
76a2438132SYegappan Lakshmanan char_u *p2;
77a2438132SYegappan Lakshmanan char_u *escaped_string;
78a2438132SYegappan Lakshmanan unsigned length;
79a2438132SYegappan Lakshmanan int l;
80a2438132SYegappan Lakshmanan
818ee52affSYegappan Lakshmanan // First count the number of backslashes required.
828ee52affSYegappan Lakshmanan // Then allocate the memory and insert them.
83a2438132SYegappan Lakshmanan length = 1; // count the trailing NUL
84a2438132SYegappan Lakshmanan for (p = string; *p; p++)
85a2438132SYegappan Lakshmanan {
86a2438132SYegappan Lakshmanan if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
87a2438132SYegappan Lakshmanan {
88a2438132SYegappan Lakshmanan length += l; // count a multibyte char
89a2438132SYegappan Lakshmanan p += l - 1;
90a2438132SYegappan Lakshmanan continue;
91a2438132SYegappan Lakshmanan }
92a2438132SYegappan Lakshmanan if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p)))
93a2438132SYegappan Lakshmanan ++length; // count a backslash
94a2438132SYegappan Lakshmanan ++length; // count an ordinary char
95a2438132SYegappan Lakshmanan }
96a2438132SYegappan Lakshmanan escaped_string = alloc(length);
97a2438132SYegappan Lakshmanan if (escaped_string != NULL)
98a2438132SYegappan Lakshmanan {
99a2438132SYegappan Lakshmanan p2 = escaped_string;
100a2438132SYegappan Lakshmanan for (p = string; *p; p++)
101a2438132SYegappan Lakshmanan {
102a2438132SYegappan Lakshmanan if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
103a2438132SYegappan Lakshmanan {
104a2438132SYegappan Lakshmanan mch_memmove(p2, p, (size_t)l);
105a2438132SYegappan Lakshmanan p2 += l;
106a2438132SYegappan Lakshmanan p += l - 1; // skip multibyte char
107a2438132SYegappan Lakshmanan continue;
108a2438132SYegappan Lakshmanan }
109a2438132SYegappan Lakshmanan if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p)))
110a2438132SYegappan Lakshmanan *p2++ = cc;
111a2438132SYegappan Lakshmanan *p2++ = *p;
112a2438132SYegappan Lakshmanan }
113a2438132SYegappan Lakshmanan *p2 = NUL;
114a2438132SYegappan Lakshmanan }
115a2438132SYegappan Lakshmanan return escaped_string;
116a2438132SYegappan Lakshmanan }
117a2438132SYegappan Lakshmanan
118a2438132SYegappan Lakshmanan /*
119a2438132SYegappan Lakshmanan * Return TRUE when 'shell' has "csh" in the tail.
120a2438132SYegappan Lakshmanan */
121a2438132SYegappan Lakshmanan int
csh_like_shell(void)122a2438132SYegappan Lakshmanan csh_like_shell(void)
123a2438132SYegappan Lakshmanan {
124a2438132SYegappan Lakshmanan return (strstr((char *)gettail(p_sh), "csh") != NULL);
125a2438132SYegappan Lakshmanan }
126a2438132SYegappan Lakshmanan
127a2438132SYegappan Lakshmanan /*
1286e823511SJason Cox * Return TRUE when 'shell' has "fish" in the tail.
1296e823511SJason Cox */
130de05ae71SDominique Pelle static int
fish_like_shell(void)1316e823511SJason Cox fish_like_shell(void)
1326e823511SJason Cox {
1336e823511SJason Cox return (strstr((char *)gettail(p_sh), "fish") != NULL);
1346e823511SJason Cox }
1356e823511SJason Cox
1366e823511SJason Cox /*
137a2438132SYegappan Lakshmanan * Escape "string" for use as a shell argument with system().
138a2438132SYegappan Lakshmanan * This uses single quotes, except when we know we need to use double quotes
139a2438132SYegappan Lakshmanan * (MS-DOS and MS-Windows not using PowerShell and without 'shellslash' set).
140a2438132SYegappan Lakshmanan * PowerShell also uses a novel escaping for enclosed single quotes - double
141a2438132SYegappan Lakshmanan * them up.
142a2438132SYegappan Lakshmanan * Escape a newline, depending on the 'shell' option.
143a2438132SYegappan Lakshmanan * When "do_special" is TRUE also replace "!", "%", "#" and things starting
144a2438132SYegappan Lakshmanan * with "<" like "<cfile>".
145a2438132SYegappan Lakshmanan * When "do_newline" is FALSE do not escape newline unless it is csh shell.
146a2438132SYegappan Lakshmanan * Returns the result in allocated memory, NULL if we have run out.
147a2438132SYegappan Lakshmanan */
148a2438132SYegappan Lakshmanan char_u *
vim_strsave_shellescape(char_u * string,int do_special,int do_newline)149a2438132SYegappan Lakshmanan vim_strsave_shellescape(char_u *string, int do_special, int do_newline)
150a2438132SYegappan Lakshmanan {
151a2438132SYegappan Lakshmanan unsigned length;
152a2438132SYegappan Lakshmanan char_u *p;
153a2438132SYegappan Lakshmanan char_u *d;
154a2438132SYegappan Lakshmanan char_u *escaped_string;
155a2438132SYegappan Lakshmanan int l;
156a2438132SYegappan Lakshmanan int csh_like;
1576e823511SJason Cox int fish_like;
158a2438132SYegappan Lakshmanan char_u *shname;
159a2438132SYegappan Lakshmanan int powershell;
160a2438132SYegappan Lakshmanan # ifdef MSWIN
161a2438132SYegappan Lakshmanan int double_quotes;
162a2438132SYegappan Lakshmanan # endif
163a2438132SYegappan Lakshmanan
164a2438132SYegappan Lakshmanan // Only csh and similar shells expand '!' within single quotes. For sh and
165a2438132SYegappan Lakshmanan // the like we must not put a backslash before it, it will be taken
166a2438132SYegappan Lakshmanan // literally. If do_special is set the '!' will be escaped twice.
167a2438132SYegappan Lakshmanan // Csh also needs to have "\n" escaped twice when do_special is set.
168a2438132SYegappan Lakshmanan csh_like = csh_like_shell();
169a2438132SYegappan Lakshmanan
1706e823511SJason Cox // Fish shell uses '\' as an escape character within single quotes, so '\'
1716e823511SJason Cox // itself must be escaped to get a literal '\'.
1726e823511SJason Cox fish_like = fish_like_shell();
1736e823511SJason Cox
174a2438132SYegappan Lakshmanan // PowerShell uses it's own version for quoting single quotes
175a2438132SYegappan Lakshmanan shname = gettail(p_sh);
176a2438132SYegappan Lakshmanan powershell = strstr((char *)shname, "pwsh") != NULL;
177a2438132SYegappan Lakshmanan # ifdef MSWIN
178a2438132SYegappan Lakshmanan powershell = powershell || strstr((char *)shname, "powershell") != NULL;
179a2438132SYegappan Lakshmanan // PowerShell only accepts single quotes so override shellslash.
180a2438132SYegappan Lakshmanan double_quotes = !powershell && !p_ssl;
181a2438132SYegappan Lakshmanan # endif
182a2438132SYegappan Lakshmanan
183a2438132SYegappan Lakshmanan // First count the number of extra bytes required.
184a2438132SYegappan Lakshmanan length = (unsigned)STRLEN(string) + 3; // two quotes and a trailing NUL
185a2438132SYegappan Lakshmanan for (p = string; *p != NUL; MB_PTR_ADV(p))
186a2438132SYegappan Lakshmanan {
187a2438132SYegappan Lakshmanan # ifdef MSWIN
188a2438132SYegappan Lakshmanan if (double_quotes)
189a2438132SYegappan Lakshmanan {
190a2438132SYegappan Lakshmanan if (*p == '"')
191a2438132SYegappan Lakshmanan ++length; // " -> ""
192a2438132SYegappan Lakshmanan }
193a2438132SYegappan Lakshmanan else
194a2438132SYegappan Lakshmanan # endif
195a2438132SYegappan Lakshmanan if (*p == '\'')
196a2438132SYegappan Lakshmanan {
197a2438132SYegappan Lakshmanan if (powershell)
198a2438132SYegappan Lakshmanan length +=2; // ' => ''
199a2438132SYegappan Lakshmanan else
200a2438132SYegappan Lakshmanan length += 3; // ' => '\''
201a2438132SYegappan Lakshmanan }
202a2438132SYegappan Lakshmanan if ((*p == '\n' && (csh_like || do_newline))
203a2438132SYegappan Lakshmanan || (*p == '!' && (csh_like || do_special)))
204a2438132SYegappan Lakshmanan {
205a2438132SYegappan Lakshmanan ++length; // insert backslash
206a2438132SYegappan Lakshmanan if (csh_like && do_special)
207a2438132SYegappan Lakshmanan ++length; // insert backslash
208a2438132SYegappan Lakshmanan }
209a2438132SYegappan Lakshmanan if (do_special && find_cmdline_var(p, &l) >= 0)
210a2438132SYegappan Lakshmanan {
211a2438132SYegappan Lakshmanan ++length; // insert backslash
212a2438132SYegappan Lakshmanan p += l - 1;
213a2438132SYegappan Lakshmanan }
2146e823511SJason Cox if (*p == '\\' && fish_like)
2156e823511SJason Cox ++length; // insert backslash
216a2438132SYegappan Lakshmanan }
217a2438132SYegappan Lakshmanan
218a2438132SYegappan Lakshmanan // Allocate memory for the result and fill it.
219a2438132SYegappan Lakshmanan escaped_string = alloc(length);
220a2438132SYegappan Lakshmanan if (escaped_string != NULL)
221a2438132SYegappan Lakshmanan {
222a2438132SYegappan Lakshmanan d = escaped_string;
223a2438132SYegappan Lakshmanan
224a2438132SYegappan Lakshmanan // add opening quote
225a2438132SYegappan Lakshmanan # ifdef MSWIN
226a2438132SYegappan Lakshmanan if (double_quotes)
227a2438132SYegappan Lakshmanan *d++ = '"';
228a2438132SYegappan Lakshmanan else
229a2438132SYegappan Lakshmanan # endif
230a2438132SYegappan Lakshmanan *d++ = '\'';
231a2438132SYegappan Lakshmanan
232a2438132SYegappan Lakshmanan for (p = string; *p != NUL; )
233a2438132SYegappan Lakshmanan {
234a2438132SYegappan Lakshmanan # ifdef MSWIN
235a2438132SYegappan Lakshmanan if (double_quotes)
236a2438132SYegappan Lakshmanan {
237a2438132SYegappan Lakshmanan if (*p == '"')
238a2438132SYegappan Lakshmanan {
239a2438132SYegappan Lakshmanan *d++ = '"';
240a2438132SYegappan Lakshmanan *d++ = '"';
241a2438132SYegappan Lakshmanan ++p;
242a2438132SYegappan Lakshmanan continue;
243a2438132SYegappan Lakshmanan }
244a2438132SYegappan Lakshmanan }
245a2438132SYegappan Lakshmanan else
246a2438132SYegappan Lakshmanan # endif
247a2438132SYegappan Lakshmanan if (*p == '\'')
248a2438132SYegappan Lakshmanan {
249a2438132SYegappan Lakshmanan if (powershell)
250a2438132SYegappan Lakshmanan {
251a2438132SYegappan Lakshmanan *d++ = '\'';
252a2438132SYegappan Lakshmanan *d++ = '\'';
253a2438132SYegappan Lakshmanan }
254a2438132SYegappan Lakshmanan else
255a2438132SYegappan Lakshmanan {
256a2438132SYegappan Lakshmanan *d++ = '\'';
257a2438132SYegappan Lakshmanan *d++ = '\\';
258a2438132SYegappan Lakshmanan *d++ = '\'';
259a2438132SYegappan Lakshmanan *d++ = '\'';
260a2438132SYegappan Lakshmanan }
261a2438132SYegappan Lakshmanan ++p;
262a2438132SYegappan Lakshmanan continue;
263a2438132SYegappan Lakshmanan }
264a2438132SYegappan Lakshmanan if ((*p == '\n' && (csh_like || do_newline))
265a2438132SYegappan Lakshmanan || (*p == '!' && (csh_like || do_special)))
266a2438132SYegappan Lakshmanan {
267a2438132SYegappan Lakshmanan *d++ = '\\';
268a2438132SYegappan Lakshmanan if (csh_like && do_special)
269a2438132SYegappan Lakshmanan *d++ = '\\';
270a2438132SYegappan Lakshmanan *d++ = *p++;
271a2438132SYegappan Lakshmanan continue;
272a2438132SYegappan Lakshmanan }
273a2438132SYegappan Lakshmanan if (do_special && find_cmdline_var(p, &l) >= 0)
274a2438132SYegappan Lakshmanan {
275a2438132SYegappan Lakshmanan *d++ = '\\'; // insert backslash
276a2438132SYegappan Lakshmanan while (--l >= 0) // copy the var
277a2438132SYegappan Lakshmanan *d++ = *p++;
278a2438132SYegappan Lakshmanan continue;
279a2438132SYegappan Lakshmanan }
2806e823511SJason Cox if (*p == '\\' && fish_like)
2816e823511SJason Cox {
2826e823511SJason Cox *d++ = '\\';
2836e823511SJason Cox *d++ = *p++;
28466315974SBram Moolenaar continue;
2856e823511SJason Cox }
286a2438132SYegappan Lakshmanan
287a2438132SYegappan Lakshmanan MB_COPY_CHAR(p, d);
288a2438132SYegappan Lakshmanan }
289a2438132SYegappan Lakshmanan
290a2438132SYegappan Lakshmanan // add terminating quote and finish with a NUL
291a2438132SYegappan Lakshmanan # ifdef MSWIN
292a2438132SYegappan Lakshmanan if (double_quotes)
293a2438132SYegappan Lakshmanan *d++ = '"';
294a2438132SYegappan Lakshmanan else
295a2438132SYegappan Lakshmanan # endif
296a2438132SYegappan Lakshmanan *d++ = '\'';
297a2438132SYegappan Lakshmanan *d = NUL;
298a2438132SYegappan Lakshmanan }
299a2438132SYegappan Lakshmanan
300a2438132SYegappan Lakshmanan return escaped_string;
301a2438132SYegappan Lakshmanan }
302a2438132SYegappan Lakshmanan
303a2438132SYegappan Lakshmanan /*
304a2438132SYegappan Lakshmanan * Like vim_strsave(), but make all characters uppercase.
305a2438132SYegappan Lakshmanan * This uses ASCII lower-to-upper case translation, language independent.
306a2438132SYegappan Lakshmanan */
307a2438132SYegappan Lakshmanan char_u *
vim_strsave_up(char_u * string)308a2438132SYegappan Lakshmanan vim_strsave_up(char_u *string)
309a2438132SYegappan Lakshmanan {
310a2438132SYegappan Lakshmanan char_u *p1;
311a2438132SYegappan Lakshmanan
312a2438132SYegappan Lakshmanan p1 = vim_strsave(string);
313a2438132SYegappan Lakshmanan vim_strup(p1);
314a2438132SYegappan Lakshmanan return p1;
315a2438132SYegappan Lakshmanan }
316a2438132SYegappan Lakshmanan
317a2438132SYegappan Lakshmanan /*
318a2438132SYegappan Lakshmanan * Like vim_strnsave(), but make all characters uppercase.
319a2438132SYegappan Lakshmanan * This uses ASCII lower-to-upper case translation, language independent.
320a2438132SYegappan Lakshmanan */
321a2438132SYegappan Lakshmanan char_u *
vim_strnsave_up(char_u * string,size_t len)322a2438132SYegappan Lakshmanan vim_strnsave_up(char_u *string, size_t len)
323a2438132SYegappan Lakshmanan {
324a2438132SYegappan Lakshmanan char_u *p1;
325a2438132SYegappan Lakshmanan
326a2438132SYegappan Lakshmanan p1 = vim_strnsave(string, len);
327a2438132SYegappan Lakshmanan vim_strup(p1);
328a2438132SYegappan Lakshmanan return p1;
329a2438132SYegappan Lakshmanan }
330a2438132SYegappan Lakshmanan
331a2438132SYegappan Lakshmanan /*
332a2438132SYegappan Lakshmanan * ASCII lower-to-upper case translation, language independent.
333a2438132SYegappan Lakshmanan */
334a2438132SYegappan Lakshmanan void
vim_strup(char_u * p)335a2438132SYegappan Lakshmanan vim_strup(
336a2438132SYegappan Lakshmanan char_u *p)
337a2438132SYegappan Lakshmanan {
338a2438132SYegappan Lakshmanan char_u *p2;
339a2438132SYegappan Lakshmanan int c;
340a2438132SYegappan Lakshmanan
341a2438132SYegappan Lakshmanan if (p != NULL)
342a2438132SYegappan Lakshmanan {
343a2438132SYegappan Lakshmanan p2 = p;
344a2438132SYegappan Lakshmanan while ((c = *p2) != NUL)
345a2438132SYegappan Lakshmanan #ifdef EBCDIC
346a2438132SYegappan Lakshmanan *p2++ = isalpha(c) ? toupper(c) : c;
347a2438132SYegappan Lakshmanan #else
348a2438132SYegappan Lakshmanan *p2++ = (c < 'a' || c > 'z') ? c : (c - 0x20);
349a2438132SYegappan Lakshmanan #endif
350a2438132SYegappan Lakshmanan }
351a2438132SYegappan Lakshmanan }
352a2438132SYegappan Lakshmanan
353a2438132SYegappan Lakshmanan #if defined(FEAT_EVAL) || defined(FEAT_SPELL) || defined(PROTO)
354a2438132SYegappan Lakshmanan /*
355a2438132SYegappan Lakshmanan * Make string "s" all upper-case and return it in allocated memory.
356a2438132SYegappan Lakshmanan * Handles multi-byte characters as well as possible.
357a2438132SYegappan Lakshmanan * Returns NULL when out of memory.
358a2438132SYegappan Lakshmanan */
359a2438132SYegappan Lakshmanan static char_u *
strup_save(char_u * orig)360a2438132SYegappan Lakshmanan strup_save(char_u *orig)
361a2438132SYegappan Lakshmanan {
362a2438132SYegappan Lakshmanan char_u *p;
363a2438132SYegappan Lakshmanan char_u *res;
364a2438132SYegappan Lakshmanan
365a2438132SYegappan Lakshmanan res = p = vim_strsave(orig);
366a2438132SYegappan Lakshmanan
367a2438132SYegappan Lakshmanan if (res != NULL)
368a2438132SYegappan Lakshmanan while (*p != NUL)
369a2438132SYegappan Lakshmanan {
370a2438132SYegappan Lakshmanan int l;
371a2438132SYegappan Lakshmanan
372a2438132SYegappan Lakshmanan if (enc_utf8)
373a2438132SYegappan Lakshmanan {
374a2438132SYegappan Lakshmanan int c, uc;
375a2438132SYegappan Lakshmanan int newl;
376a2438132SYegappan Lakshmanan char_u *s;
377a2438132SYegappan Lakshmanan
378a2438132SYegappan Lakshmanan c = utf_ptr2char(p);
379a2438132SYegappan Lakshmanan l = utf_ptr2len(p);
380a2438132SYegappan Lakshmanan if (c == 0)
381a2438132SYegappan Lakshmanan {
382a2438132SYegappan Lakshmanan // overlong sequence, use only the first byte
383a2438132SYegappan Lakshmanan c = *p;
384a2438132SYegappan Lakshmanan l = 1;
385a2438132SYegappan Lakshmanan }
386a2438132SYegappan Lakshmanan uc = utf_toupper(c);
387a2438132SYegappan Lakshmanan
388a2438132SYegappan Lakshmanan // Reallocate string when byte count changes. This is rare,
389a2438132SYegappan Lakshmanan // thus it's OK to do another malloc()/free().
390a2438132SYegappan Lakshmanan newl = utf_char2len(uc);
391a2438132SYegappan Lakshmanan if (newl != l)
392a2438132SYegappan Lakshmanan {
393a2438132SYegappan Lakshmanan s = alloc(STRLEN(res) + 1 + newl - l);
394a2438132SYegappan Lakshmanan if (s == NULL)
395a2438132SYegappan Lakshmanan {
396a2438132SYegappan Lakshmanan vim_free(res);
397a2438132SYegappan Lakshmanan return NULL;
398a2438132SYegappan Lakshmanan }
399a2438132SYegappan Lakshmanan mch_memmove(s, res, p - res);
400a2438132SYegappan Lakshmanan STRCPY(s + (p - res) + newl, p + l);
401a2438132SYegappan Lakshmanan p = s + (p - res);
402a2438132SYegappan Lakshmanan vim_free(res);
403a2438132SYegappan Lakshmanan res = s;
404a2438132SYegappan Lakshmanan }
405a2438132SYegappan Lakshmanan
406a2438132SYegappan Lakshmanan utf_char2bytes(uc, p);
407a2438132SYegappan Lakshmanan p += newl;
408a2438132SYegappan Lakshmanan }
409a2438132SYegappan Lakshmanan else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
410a2438132SYegappan Lakshmanan p += l; // skip multi-byte character
411a2438132SYegappan Lakshmanan else
412a2438132SYegappan Lakshmanan {
413a2438132SYegappan Lakshmanan *p = TOUPPER_LOC(*p); // note that toupper() can be a macro
414a2438132SYegappan Lakshmanan p++;
415a2438132SYegappan Lakshmanan }
416a2438132SYegappan Lakshmanan }
417a2438132SYegappan Lakshmanan
418a2438132SYegappan Lakshmanan return res;
419a2438132SYegappan Lakshmanan }
420a2438132SYegappan Lakshmanan
421a2438132SYegappan Lakshmanan /*
422a2438132SYegappan Lakshmanan * Make string "s" all lower-case and return it in allocated memory.
423a2438132SYegappan Lakshmanan * Handles multi-byte characters as well as possible.
424a2438132SYegappan Lakshmanan * Returns NULL when out of memory.
425a2438132SYegappan Lakshmanan */
426a2438132SYegappan Lakshmanan char_u *
strlow_save(char_u * orig)427a2438132SYegappan Lakshmanan strlow_save(char_u *orig)
428a2438132SYegappan Lakshmanan {
429a2438132SYegappan Lakshmanan char_u *p;
430a2438132SYegappan Lakshmanan char_u *res;
431a2438132SYegappan Lakshmanan
432a2438132SYegappan Lakshmanan res = p = vim_strsave(orig);
433a2438132SYegappan Lakshmanan
434a2438132SYegappan Lakshmanan if (res != NULL)
435a2438132SYegappan Lakshmanan while (*p != NUL)
436a2438132SYegappan Lakshmanan {
437a2438132SYegappan Lakshmanan int l;
438a2438132SYegappan Lakshmanan
439a2438132SYegappan Lakshmanan if (enc_utf8)
440a2438132SYegappan Lakshmanan {
441a2438132SYegappan Lakshmanan int c, lc;
442a2438132SYegappan Lakshmanan int newl;
443a2438132SYegappan Lakshmanan char_u *s;
444a2438132SYegappan Lakshmanan
445a2438132SYegappan Lakshmanan c = utf_ptr2char(p);
446a2438132SYegappan Lakshmanan l = utf_ptr2len(p);
447a2438132SYegappan Lakshmanan if (c == 0)
448a2438132SYegappan Lakshmanan {
449a2438132SYegappan Lakshmanan // overlong sequence, use only the first byte
450a2438132SYegappan Lakshmanan c = *p;
451a2438132SYegappan Lakshmanan l = 1;
452a2438132SYegappan Lakshmanan }
453a2438132SYegappan Lakshmanan lc = utf_tolower(c);
454a2438132SYegappan Lakshmanan
455a2438132SYegappan Lakshmanan // Reallocate string when byte count changes. This is rare,
456a2438132SYegappan Lakshmanan // thus it's OK to do another malloc()/free().
457a2438132SYegappan Lakshmanan newl = utf_char2len(lc);
458a2438132SYegappan Lakshmanan if (newl != l)
459a2438132SYegappan Lakshmanan {
460a2438132SYegappan Lakshmanan s = alloc(STRLEN(res) + 1 + newl - l);
461a2438132SYegappan Lakshmanan if (s == NULL)
462a2438132SYegappan Lakshmanan {
463a2438132SYegappan Lakshmanan vim_free(res);
464a2438132SYegappan Lakshmanan return NULL;
465a2438132SYegappan Lakshmanan }
466a2438132SYegappan Lakshmanan mch_memmove(s, res, p - res);
467a2438132SYegappan Lakshmanan STRCPY(s + (p - res) + newl, p + l);
468a2438132SYegappan Lakshmanan p = s + (p - res);
469a2438132SYegappan Lakshmanan vim_free(res);
470a2438132SYegappan Lakshmanan res = s;
471a2438132SYegappan Lakshmanan }
472a2438132SYegappan Lakshmanan
473a2438132SYegappan Lakshmanan utf_char2bytes(lc, p);
474a2438132SYegappan Lakshmanan p += newl;
475a2438132SYegappan Lakshmanan }
476a2438132SYegappan Lakshmanan else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
477a2438132SYegappan Lakshmanan p += l; // skip multi-byte character
478a2438132SYegappan Lakshmanan else
479a2438132SYegappan Lakshmanan {
480a2438132SYegappan Lakshmanan *p = TOLOWER_LOC(*p); // note that tolower() can be a macro
481a2438132SYegappan Lakshmanan p++;
482a2438132SYegappan Lakshmanan }
483a2438132SYegappan Lakshmanan }
484a2438132SYegappan Lakshmanan
485a2438132SYegappan Lakshmanan return res;
486a2438132SYegappan Lakshmanan }
487a2438132SYegappan Lakshmanan #endif
488a2438132SYegappan Lakshmanan
489a2438132SYegappan Lakshmanan /*
490a2438132SYegappan Lakshmanan * delete spaces at the end of a string
491a2438132SYegappan Lakshmanan */
492a2438132SYegappan Lakshmanan void
del_trailing_spaces(char_u * ptr)493a2438132SYegappan Lakshmanan del_trailing_spaces(char_u *ptr)
494a2438132SYegappan Lakshmanan {
495a2438132SYegappan Lakshmanan char_u *q;
496a2438132SYegappan Lakshmanan
497a2438132SYegappan Lakshmanan q = ptr + STRLEN(ptr);
498a2438132SYegappan Lakshmanan while (--q > ptr && VIM_ISWHITE(q[0]) && q[-1] != '\\' && q[-1] != Ctrl_V)
499a2438132SYegappan Lakshmanan *q = NUL;
500a2438132SYegappan Lakshmanan }
501a2438132SYegappan Lakshmanan
502a2438132SYegappan Lakshmanan /*
503a2438132SYegappan Lakshmanan * Like strncpy(), but always terminate the result with one NUL.
504a2438132SYegappan Lakshmanan * "to" must be "len + 1" long!
505a2438132SYegappan Lakshmanan */
506a2438132SYegappan Lakshmanan void
vim_strncpy(char_u * to,char_u * from,size_t len)507a2438132SYegappan Lakshmanan vim_strncpy(char_u *to, char_u *from, size_t len)
508a2438132SYegappan Lakshmanan {
509a2438132SYegappan Lakshmanan STRNCPY(to, from, len);
510a2438132SYegappan Lakshmanan to[len] = NUL;
511a2438132SYegappan Lakshmanan }
512a2438132SYegappan Lakshmanan
513a2438132SYegappan Lakshmanan /*
514a2438132SYegappan Lakshmanan * Like strcat(), but make sure the result fits in "tosize" bytes and is
515a2438132SYegappan Lakshmanan * always NUL terminated. "from" and "to" may overlap.
516a2438132SYegappan Lakshmanan */
517a2438132SYegappan Lakshmanan void
vim_strcat(char_u * to,char_u * from,size_t tosize)518a2438132SYegappan Lakshmanan vim_strcat(char_u *to, char_u *from, size_t tosize)
519a2438132SYegappan Lakshmanan {
520a2438132SYegappan Lakshmanan size_t tolen = STRLEN(to);
521a2438132SYegappan Lakshmanan size_t fromlen = STRLEN(from);
522a2438132SYegappan Lakshmanan
523a2438132SYegappan Lakshmanan if (tolen + fromlen + 1 > tosize)
524a2438132SYegappan Lakshmanan {
525a2438132SYegappan Lakshmanan mch_memmove(to + tolen, from, tosize - tolen - 1);
526a2438132SYegappan Lakshmanan to[tosize - 1] = NUL;
527a2438132SYegappan Lakshmanan }
528a2438132SYegappan Lakshmanan else
529a2438132SYegappan Lakshmanan mch_memmove(to + tolen, from, fromlen + 1);
530a2438132SYegappan Lakshmanan }
531a2438132SYegappan Lakshmanan
532a2438132SYegappan Lakshmanan #if (!defined(HAVE_STRCASECMP) && !defined(HAVE_STRICMP)) || defined(PROTO)
533a2438132SYegappan Lakshmanan /*
534a2438132SYegappan Lakshmanan * Compare two strings, ignoring case, using current locale.
535a2438132SYegappan Lakshmanan * Doesn't work for multi-byte characters.
536a2438132SYegappan Lakshmanan * return 0 for match, < 0 for smaller, > 0 for bigger
537a2438132SYegappan Lakshmanan */
538a2438132SYegappan Lakshmanan int
vim_stricmp(char * s1,char * s2)539a2438132SYegappan Lakshmanan vim_stricmp(char *s1, char *s2)
540a2438132SYegappan Lakshmanan {
541a2438132SYegappan Lakshmanan int i;
542a2438132SYegappan Lakshmanan
543a2438132SYegappan Lakshmanan for (;;)
544a2438132SYegappan Lakshmanan {
545a2438132SYegappan Lakshmanan i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2);
546a2438132SYegappan Lakshmanan if (i != 0)
547a2438132SYegappan Lakshmanan return i; // this character different
548a2438132SYegappan Lakshmanan if (*s1 == NUL)
549a2438132SYegappan Lakshmanan break; // strings match until NUL
550a2438132SYegappan Lakshmanan ++s1;
551a2438132SYegappan Lakshmanan ++s2;
552a2438132SYegappan Lakshmanan }
553a2438132SYegappan Lakshmanan return 0; // strings match
554a2438132SYegappan Lakshmanan }
555a2438132SYegappan Lakshmanan #endif
556a2438132SYegappan Lakshmanan
557a2438132SYegappan Lakshmanan #if (!defined(HAVE_STRNCASECMP) && !defined(HAVE_STRNICMP)) || defined(PROTO)
558a2438132SYegappan Lakshmanan /*
559a2438132SYegappan Lakshmanan * Compare two strings, for length "len", ignoring case, using current locale.
560a2438132SYegappan Lakshmanan * Doesn't work for multi-byte characters.
561a2438132SYegappan Lakshmanan * return 0 for match, < 0 for smaller, > 0 for bigger
562a2438132SYegappan Lakshmanan */
563a2438132SYegappan Lakshmanan int
vim_strnicmp(char * s1,char * s2,size_t len)564a2438132SYegappan Lakshmanan vim_strnicmp(char *s1, char *s2, size_t len)
565a2438132SYegappan Lakshmanan {
566a2438132SYegappan Lakshmanan int i;
567a2438132SYegappan Lakshmanan
568a2438132SYegappan Lakshmanan while (len > 0)
569a2438132SYegappan Lakshmanan {
570a2438132SYegappan Lakshmanan i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2);
571a2438132SYegappan Lakshmanan if (i != 0)
572a2438132SYegappan Lakshmanan return i; // this character different
573a2438132SYegappan Lakshmanan if (*s1 == NUL)
574a2438132SYegappan Lakshmanan break; // strings match until NUL
575a2438132SYegappan Lakshmanan ++s1;
576a2438132SYegappan Lakshmanan ++s2;
577a2438132SYegappan Lakshmanan --len;
578a2438132SYegappan Lakshmanan }
579a2438132SYegappan Lakshmanan return 0; // strings match
580a2438132SYegappan Lakshmanan }
581a2438132SYegappan Lakshmanan #endif
582a2438132SYegappan Lakshmanan
583a2438132SYegappan Lakshmanan /*
584a2438132SYegappan Lakshmanan * Search for first occurrence of "c" in "string".
585a2438132SYegappan Lakshmanan * Version of strchr() that handles unsigned char strings with characters from
586a2438132SYegappan Lakshmanan * 128 to 255 correctly. It also doesn't return a pointer to the NUL at the
587a2438132SYegappan Lakshmanan * end of the string.
588a2438132SYegappan Lakshmanan */
589a2438132SYegappan Lakshmanan char_u *
vim_strchr(char_u * string,int c)590a2438132SYegappan Lakshmanan vim_strchr(char_u *string, int c)
591a2438132SYegappan Lakshmanan {
592a2438132SYegappan Lakshmanan char_u *p;
593a2438132SYegappan Lakshmanan int b;
594a2438132SYegappan Lakshmanan
595a2438132SYegappan Lakshmanan p = string;
596a2438132SYegappan Lakshmanan if (enc_utf8 && c >= 0x80)
597a2438132SYegappan Lakshmanan {
598a2438132SYegappan Lakshmanan while (*p != NUL)
599a2438132SYegappan Lakshmanan {
600a2438132SYegappan Lakshmanan int l = utfc_ptr2len(p);
601a2438132SYegappan Lakshmanan
602a2438132SYegappan Lakshmanan // Avoid matching an illegal byte here.
603a2438132SYegappan Lakshmanan if (utf_ptr2char(p) == c && l > 1)
604a2438132SYegappan Lakshmanan return p;
605a2438132SYegappan Lakshmanan p += l;
606a2438132SYegappan Lakshmanan }
607a2438132SYegappan Lakshmanan return NULL;
608a2438132SYegappan Lakshmanan }
609a2438132SYegappan Lakshmanan if (enc_dbcs != 0 && c > 255)
610a2438132SYegappan Lakshmanan {
611a2438132SYegappan Lakshmanan int n2 = c & 0xff;
612a2438132SYegappan Lakshmanan
613a2438132SYegappan Lakshmanan c = ((unsigned)c >> 8) & 0xff;
614a2438132SYegappan Lakshmanan while ((b = *p) != NUL)
615a2438132SYegappan Lakshmanan {
616a2438132SYegappan Lakshmanan if (b == c && p[1] == n2)
617a2438132SYegappan Lakshmanan return p;
618a2438132SYegappan Lakshmanan p += (*mb_ptr2len)(p);
619a2438132SYegappan Lakshmanan }
620a2438132SYegappan Lakshmanan return NULL;
621a2438132SYegappan Lakshmanan }
622a2438132SYegappan Lakshmanan if (has_mbyte)
623a2438132SYegappan Lakshmanan {
624a2438132SYegappan Lakshmanan while ((b = *p) != NUL)
625a2438132SYegappan Lakshmanan {
626a2438132SYegappan Lakshmanan if (b == c)
627a2438132SYegappan Lakshmanan return p;
628a2438132SYegappan Lakshmanan p += (*mb_ptr2len)(p);
629a2438132SYegappan Lakshmanan }
630a2438132SYegappan Lakshmanan return NULL;
631a2438132SYegappan Lakshmanan }
632a2438132SYegappan Lakshmanan while ((b = *p) != NUL)
633a2438132SYegappan Lakshmanan {
634a2438132SYegappan Lakshmanan if (b == c)
635a2438132SYegappan Lakshmanan return p;
636a2438132SYegappan Lakshmanan ++p;
637a2438132SYegappan Lakshmanan }
638a2438132SYegappan Lakshmanan return NULL;
639a2438132SYegappan Lakshmanan }
640a2438132SYegappan Lakshmanan
641a2438132SYegappan Lakshmanan /*
642a2438132SYegappan Lakshmanan * Version of strchr() that only works for bytes and handles unsigned char
643a2438132SYegappan Lakshmanan * strings with characters above 128 correctly. It also doesn't return a
644a2438132SYegappan Lakshmanan * pointer to the NUL at the end of the string.
645a2438132SYegappan Lakshmanan */
646a2438132SYegappan Lakshmanan char_u *
vim_strbyte(char_u * string,int c)647a2438132SYegappan Lakshmanan vim_strbyte(char_u *string, int c)
648a2438132SYegappan Lakshmanan {
649a2438132SYegappan Lakshmanan char_u *p = string;
650a2438132SYegappan Lakshmanan
651a2438132SYegappan Lakshmanan while (*p != NUL)
652a2438132SYegappan Lakshmanan {
653a2438132SYegappan Lakshmanan if (*p == c)
654a2438132SYegappan Lakshmanan return p;
655a2438132SYegappan Lakshmanan ++p;
656a2438132SYegappan Lakshmanan }
657a2438132SYegappan Lakshmanan return NULL;
658a2438132SYegappan Lakshmanan }
659a2438132SYegappan Lakshmanan
660a2438132SYegappan Lakshmanan /*
661a2438132SYegappan Lakshmanan * Search for last occurrence of "c" in "string".
662a2438132SYegappan Lakshmanan * Version of strrchr() that handles unsigned char strings with characters from
663a2438132SYegappan Lakshmanan * 128 to 255 correctly. It also doesn't return a pointer to the NUL at the
664a2438132SYegappan Lakshmanan * end of the string.
665a2438132SYegappan Lakshmanan * Return NULL if not found.
666a2438132SYegappan Lakshmanan * Does not handle multi-byte char for "c"!
667a2438132SYegappan Lakshmanan */
668a2438132SYegappan Lakshmanan char_u *
vim_strrchr(char_u * string,int c)669a2438132SYegappan Lakshmanan vim_strrchr(char_u *string, int c)
670a2438132SYegappan Lakshmanan {
671a2438132SYegappan Lakshmanan char_u *retval = NULL;
672a2438132SYegappan Lakshmanan char_u *p = string;
673a2438132SYegappan Lakshmanan
674a2438132SYegappan Lakshmanan while (*p)
675a2438132SYegappan Lakshmanan {
676a2438132SYegappan Lakshmanan if (*p == c)
677a2438132SYegappan Lakshmanan retval = p;
678a2438132SYegappan Lakshmanan MB_PTR_ADV(p);
679a2438132SYegappan Lakshmanan }
680a2438132SYegappan Lakshmanan return retval;
681a2438132SYegappan Lakshmanan }
682a2438132SYegappan Lakshmanan
683a2438132SYegappan Lakshmanan /*
684a2438132SYegappan Lakshmanan * Vim's version of strpbrk(), in case it's missing.
685a2438132SYegappan Lakshmanan * Don't generate a prototype for this, causes problems when it's not used.
686a2438132SYegappan Lakshmanan */
687a2438132SYegappan Lakshmanan #ifndef PROTO
688a2438132SYegappan Lakshmanan # ifndef HAVE_STRPBRK
689a2438132SYegappan Lakshmanan # ifdef vim_strpbrk
690a2438132SYegappan Lakshmanan # undef vim_strpbrk
691a2438132SYegappan Lakshmanan # endif
692a2438132SYegappan Lakshmanan char_u *
vim_strpbrk(char_u * s,char_u * charset)693a2438132SYegappan Lakshmanan vim_strpbrk(char_u *s, char_u *charset)
694a2438132SYegappan Lakshmanan {
695a2438132SYegappan Lakshmanan while (*s)
696a2438132SYegappan Lakshmanan {
697a2438132SYegappan Lakshmanan if (vim_strchr(charset, *s) != NULL)
698a2438132SYegappan Lakshmanan return s;
699a2438132SYegappan Lakshmanan MB_PTR_ADV(s);
700a2438132SYegappan Lakshmanan }
701a2438132SYegappan Lakshmanan return NULL;
702a2438132SYegappan Lakshmanan }
703a2438132SYegappan Lakshmanan # endif
704a2438132SYegappan Lakshmanan #endif
705a2438132SYegappan Lakshmanan
706a2438132SYegappan Lakshmanan /*
707a2438132SYegappan Lakshmanan * Sort an array of strings.
708a2438132SYegappan Lakshmanan */
709a2438132SYegappan Lakshmanan static int sort_compare(const void *s1, const void *s2);
710a2438132SYegappan Lakshmanan
711a2438132SYegappan Lakshmanan static int
sort_compare(const void * s1,const void * s2)712a2438132SYegappan Lakshmanan sort_compare(const void *s1, const void *s2)
713a2438132SYegappan Lakshmanan {
714a2438132SYegappan Lakshmanan return STRCMP(*(char **)s1, *(char **)s2);
715a2438132SYegappan Lakshmanan }
716a2438132SYegappan Lakshmanan
717a2438132SYegappan Lakshmanan void
sort_strings(char_u ** files,int count)718a2438132SYegappan Lakshmanan sort_strings(
719a2438132SYegappan Lakshmanan char_u **files,
720a2438132SYegappan Lakshmanan int count)
721a2438132SYegappan Lakshmanan {
722a2438132SYegappan Lakshmanan qsort((void *)files, (size_t)count, sizeof(char_u *), sort_compare);
723a2438132SYegappan Lakshmanan }
724a2438132SYegappan Lakshmanan
725a2438132SYegappan Lakshmanan #if defined(FEAT_QUICKFIX) || defined(FEAT_SPELL) || defined(PROTO)
726a2438132SYegappan Lakshmanan /*
727a2438132SYegappan Lakshmanan * Return TRUE if string "s" contains a non-ASCII character (128 or higher).
728a2438132SYegappan Lakshmanan * When "s" is NULL FALSE is returned.
729a2438132SYegappan Lakshmanan */
730a2438132SYegappan Lakshmanan int
has_non_ascii(char_u * s)731a2438132SYegappan Lakshmanan has_non_ascii(char_u *s)
732a2438132SYegappan Lakshmanan {
733a2438132SYegappan Lakshmanan char_u *p;
734a2438132SYegappan Lakshmanan
735a2438132SYegappan Lakshmanan if (s != NULL)
736a2438132SYegappan Lakshmanan for (p = s; *p != NUL; ++p)
737a2438132SYegappan Lakshmanan if (*p >= 128)
738a2438132SYegappan Lakshmanan return TRUE;
739a2438132SYegappan Lakshmanan return FALSE;
740a2438132SYegappan Lakshmanan }
741a2438132SYegappan Lakshmanan #endif
742a2438132SYegappan Lakshmanan
743a2438132SYegappan Lakshmanan /*
744a2438132SYegappan Lakshmanan * Concatenate two strings and return the result in allocated memory.
745a2438132SYegappan Lakshmanan * Returns NULL when out of memory.
746a2438132SYegappan Lakshmanan */
747a2438132SYegappan Lakshmanan char_u *
concat_str(char_u * str1,char_u * str2)748a2438132SYegappan Lakshmanan concat_str(char_u *str1, char_u *str2)
749a2438132SYegappan Lakshmanan {
750a2438132SYegappan Lakshmanan char_u *dest;
751a2438132SYegappan Lakshmanan size_t l = str1 == NULL ? 0 : STRLEN(str1);
752a2438132SYegappan Lakshmanan
753a2438132SYegappan Lakshmanan dest = alloc(l + (str2 == NULL ? 0 : STRLEN(str2)) + 1L);
754a2438132SYegappan Lakshmanan if (dest != NULL)
755a2438132SYegappan Lakshmanan {
756a2438132SYegappan Lakshmanan if (str1 == NULL)
757a2438132SYegappan Lakshmanan *dest = NUL;
758a2438132SYegappan Lakshmanan else
759a2438132SYegappan Lakshmanan STRCPY(dest, str1);
760a2438132SYegappan Lakshmanan if (str2 != NULL)
761a2438132SYegappan Lakshmanan STRCPY(dest + l, str2);
762a2438132SYegappan Lakshmanan }
763a2438132SYegappan Lakshmanan return dest;
764a2438132SYegappan Lakshmanan }
765a2438132SYegappan Lakshmanan
766a2438132SYegappan Lakshmanan #if defined(FEAT_EVAL) || defined(PROTO)
767a2438132SYegappan Lakshmanan
768a2438132SYegappan Lakshmanan /*
769a2438132SYegappan Lakshmanan * Return string "str" in ' quotes, doubling ' characters.
770a2438132SYegappan Lakshmanan * If "str" is NULL an empty string is assumed.
771a2438132SYegappan Lakshmanan * If "function" is TRUE make it function('string').
772a2438132SYegappan Lakshmanan */
773a2438132SYegappan Lakshmanan char_u *
string_quote(char_u * str,int function)774a2438132SYegappan Lakshmanan string_quote(char_u *str, int function)
775a2438132SYegappan Lakshmanan {
776a2438132SYegappan Lakshmanan unsigned len;
777a2438132SYegappan Lakshmanan char_u *p, *r, *s;
778a2438132SYegappan Lakshmanan
779a2438132SYegappan Lakshmanan len = (function ? 13 : 3);
780a2438132SYegappan Lakshmanan if (str != NULL)
781a2438132SYegappan Lakshmanan {
782a2438132SYegappan Lakshmanan len += (unsigned)STRLEN(str);
783a2438132SYegappan Lakshmanan for (p = str; *p != NUL; MB_PTR_ADV(p))
784a2438132SYegappan Lakshmanan if (*p == '\'')
785a2438132SYegappan Lakshmanan ++len;
786a2438132SYegappan Lakshmanan }
787a2438132SYegappan Lakshmanan s = r = alloc(len);
788a2438132SYegappan Lakshmanan if (r != NULL)
789a2438132SYegappan Lakshmanan {
790a2438132SYegappan Lakshmanan if (function)
791a2438132SYegappan Lakshmanan {
792a2438132SYegappan Lakshmanan STRCPY(r, "function('");
793a2438132SYegappan Lakshmanan r += 10;
794a2438132SYegappan Lakshmanan }
795a2438132SYegappan Lakshmanan else
796a2438132SYegappan Lakshmanan *r++ = '\'';
797a2438132SYegappan Lakshmanan if (str != NULL)
798a2438132SYegappan Lakshmanan for (p = str; *p != NUL; )
799a2438132SYegappan Lakshmanan {
800a2438132SYegappan Lakshmanan if (*p == '\'')
801a2438132SYegappan Lakshmanan *r++ = '\'';
802a2438132SYegappan Lakshmanan MB_COPY_CHAR(p, r);
803a2438132SYegappan Lakshmanan }
804a2438132SYegappan Lakshmanan *r++ = '\'';
805a2438132SYegappan Lakshmanan if (function)
806a2438132SYegappan Lakshmanan *r++ = ')';
807a2438132SYegappan Lakshmanan *r++ = NUL;
808a2438132SYegappan Lakshmanan }
809a2438132SYegappan Lakshmanan return s;
810a2438132SYegappan Lakshmanan }
811a2438132SYegappan Lakshmanan
812a2438132SYegappan Lakshmanan static void
byteidx(typval_T * argvars,typval_T * rettv,int comp UNUSED)813a2438132SYegappan Lakshmanan byteidx(typval_T *argvars, typval_T *rettv, int comp UNUSED)
814a2438132SYegappan Lakshmanan {
815a2438132SYegappan Lakshmanan char_u *t;
816a2438132SYegappan Lakshmanan char_u *str;
817a2438132SYegappan Lakshmanan varnumber_T idx;
818a2438132SYegappan Lakshmanan
8191a71d31bSYegappan Lakshmanan rettv->vval.v_number = -1;
8201a71d31bSYegappan Lakshmanan
8211a71d31bSYegappan Lakshmanan if (in_vim9script()
8221a71d31bSYegappan Lakshmanan && (check_for_string_arg(argvars, 0) == FAIL
8231a71d31bSYegappan Lakshmanan || check_for_number_arg(argvars, 1) == FAIL))
8241a71d31bSYegappan Lakshmanan return;
8251a71d31bSYegappan Lakshmanan
826a2438132SYegappan Lakshmanan str = tv_get_string_chk(&argvars[0]);
827a2438132SYegappan Lakshmanan idx = tv_get_number_chk(&argvars[1], NULL);
828a2438132SYegappan Lakshmanan if (str == NULL || idx < 0)
829a2438132SYegappan Lakshmanan return;
830a2438132SYegappan Lakshmanan
831a2438132SYegappan Lakshmanan t = str;
832a2438132SYegappan Lakshmanan for ( ; idx > 0; idx--)
833a2438132SYegappan Lakshmanan {
834a2438132SYegappan Lakshmanan if (*t == NUL) // EOL reached
835a2438132SYegappan Lakshmanan return;
836a2438132SYegappan Lakshmanan if (enc_utf8 && comp)
837a2438132SYegappan Lakshmanan t += utf_ptr2len(t);
838a2438132SYegappan Lakshmanan else
839a2438132SYegappan Lakshmanan t += (*mb_ptr2len)(t);
840a2438132SYegappan Lakshmanan }
841a2438132SYegappan Lakshmanan rettv->vval.v_number = (varnumber_T)(t - str);
842a2438132SYegappan Lakshmanan }
843a2438132SYegappan Lakshmanan
844a2438132SYegappan Lakshmanan /*
845a2438132SYegappan Lakshmanan * "byteidx()" function
846a2438132SYegappan Lakshmanan */
847a2438132SYegappan Lakshmanan void
f_byteidx(typval_T * argvars,typval_T * rettv)848a2438132SYegappan Lakshmanan f_byteidx(typval_T *argvars, typval_T *rettv)
849a2438132SYegappan Lakshmanan {
850a2438132SYegappan Lakshmanan byteidx(argvars, rettv, FALSE);
851a2438132SYegappan Lakshmanan }
852a2438132SYegappan Lakshmanan
853a2438132SYegappan Lakshmanan /*
854a2438132SYegappan Lakshmanan * "byteidxcomp()" function
855a2438132SYegappan Lakshmanan */
856a2438132SYegappan Lakshmanan void
f_byteidxcomp(typval_T * argvars,typval_T * rettv)857a2438132SYegappan Lakshmanan f_byteidxcomp(typval_T *argvars, typval_T *rettv)
858a2438132SYegappan Lakshmanan {
859a2438132SYegappan Lakshmanan byteidx(argvars, rettv, TRUE);
860a2438132SYegappan Lakshmanan }
861a2438132SYegappan Lakshmanan
862a2438132SYegappan Lakshmanan /*
863a2438132SYegappan Lakshmanan * "charidx()" function
864a2438132SYegappan Lakshmanan */
865a2438132SYegappan Lakshmanan void
f_charidx(typval_T * argvars,typval_T * rettv)866a2438132SYegappan Lakshmanan f_charidx(typval_T *argvars, typval_T *rettv)
867a2438132SYegappan Lakshmanan {
868a2438132SYegappan Lakshmanan char_u *str;
869a2438132SYegappan Lakshmanan varnumber_T idx;
870a2438132SYegappan Lakshmanan varnumber_T countcc = FALSE;
871a2438132SYegappan Lakshmanan char_u *p;
872a2438132SYegappan Lakshmanan int len;
873a2438132SYegappan Lakshmanan int (*ptr2len)(char_u *);
874a2438132SYegappan Lakshmanan
875a2438132SYegappan Lakshmanan rettv->vval.v_number = -1;
876a2438132SYegappan Lakshmanan
8774490ec4eSYegappan Lakshmanan if (in_vim9script()
8784490ec4eSYegappan Lakshmanan && (check_for_string_arg(argvars, 0) == FAIL
8794490ec4eSYegappan Lakshmanan || check_for_number_arg(argvars, 1) == FAIL
8804490ec4eSYegappan Lakshmanan || check_for_opt_bool_arg(argvars, 2) == FAIL))
8814490ec4eSYegappan Lakshmanan return;
8824490ec4eSYegappan Lakshmanan
883a2438132SYegappan Lakshmanan if (argvars[0].v_type != VAR_STRING || argvars[1].v_type != VAR_NUMBER
884a2438132SYegappan Lakshmanan || (argvars[2].v_type != VAR_UNKNOWN
885a2438132SYegappan Lakshmanan && argvars[2].v_type != VAR_NUMBER
886a2438132SYegappan Lakshmanan && argvars[2].v_type != VAR_BOOL))
887a2438132SYegappan Lakshmanan {
888a2438132SYegappan Lakshmanan emsg(_(e_invarg));
889a2438132SYegappan Lakshmanan return;
890a2438132SYegappan Lakshmanan }
891a2438132SYegappan Lakshmanan
892a2438132SYegappan Lakshmanan str = tv_get_string_chk(&argvars[0]);
893a2438132SYegappan Lakshmanan idx = tv_get_number_chk(&argvars[1], NULL);
894a2438132SYegappan Lakshmanan if (str == NULL || idx < 0)
895a2438132SYegappan Lakshmanan return;
896a2438132SYegappan Lakshmanan
897a2438132SYegappan Lakshmanan if (argvars[2].v_type != VAR_UNKNOWN)
898a2438132SYegappan Lakshmanan countcc = tv_get_bool(&argvars[2]);
899a2438132SYegappan Lakshmanan if (countcc < 0 || countcc > 1)
900a2438132SYegappan Lakshmanan {
901a2438132SYegappan Lakshmanan semsg(_(e_using_number_as_bool_nr), countcc);
902a2438132SYegappan Lakshmanan return;
903a2438132SYegappan Lakshmanan }
904a2438132SYegappan Lakshmanan
905a2438132SYegappan Lakshmanan if (enc_utf8 && countcc)
906a2438132SYegappan Lakshmanan ptr2len = utf_ptr2len;
907a2438132SYegappan Lakshmanan else
908a2438132SYegappan Lakshmanan ptr2len = mb_ptr2len;
909a2438132SYegappan Lakshmanan
910a2438132SYegappan Lakshmanan for (p = str, len = 0; p <= str + idx; len++)
911a2438132SYegappan Lakshmanan {
912a2438132SYegappan Lakshmanan if (*p == NUL)
913a2438132SYegappan Lakshmanan return;
914a2438132SYegappan Lakshmanan p += ptr2len(p);
915a2438132SYegappan Lakshmanan }
916a2438132SYegappan Lakshmanan
917a2438132SYegappan Lakshmanan rettv->vval.v_number = len > 0 ? len - 1 : 0;
918a2438132SYegappan Lakshmanan }
919a2438132SYegappan Lakshmanan
920a2438132SYegappan Lakshmanan /*
921a2438132SYegappan Lakshmanan * "str2list()" function
922a2438132SYegappan Lakshmanan */
923a2438132SYegappan Lakshmanan void
f_str2list(typval_T * argvars,typval_T * rettv)924a2438132SYegappan Lakshmanan f_str2list(typval_T *argvars, typval_T *rettv)
925a2438132SYegappan Lakshmanan {
926a2438132SYegappan Lakshmanan char_u *p;
927a2438132SYegappan Lakshmanan int utf8 = FALSE;
928a2438132SYegappan Lakshmanan
929a2438132SYegappan Lakshmanan if (rettv_list_alloc(rettv) == FAIL)
930a2438132SYegappan Lakshmanan return;
931a2438132SYegappan Lakshmanan
932a9a7c0c6SYegappan Lakshmanan if (in_vim9script()
933a9a7c0c6SYegappan Lakshmanan && (check_for_string_arg(argvars, 0) == FAIL
93483494b4aSYegappan Lakshmanan || check_for_opt_bool_arg(argvars, 1) == FAIL))
935a9a7c0c6SYegappan Lakshmanan return;
936a9a7c0c6SYegappan Lakshmanan
937a2438132SYegappan Lakshmanan if (argvars[1].v_type != VAR_UNKNOWN)
938a2438132SYegappan Lakshmanan utf8 = (int)tv_get_bool_chk(&argvars[1], NULL);
939a2438132SYegappan Lakshmanan
940a2438132SYegappan Lakshmanan p = tv_get_string(&argvars[0]);
941a2438132SYegappan Lakshmanan
942a2438132SYegappan Lakshmanan if (has_mbyte || utf8)
943a2438132SYegappan Lakshmanan {
944a2438132SYegappan Lakshmanan int (*ptr2len)(char_u *);
945a2438132SYegappan Lakshmanan int (*ptr2char)(char_u *);
946a2438132SYegappan Lakshmanan
947a2438132SYegappan Lakshmanan if (utf8 || enc_utf8)
948a2438132SYegappan Lakshmanan {
949a2438132SYegappan Lakshmanan ptr2len = utf_ptr2len;
950a2438132SYegappan Lakshmanan ptr2char = utf_ptr2char;
951a2438132SYegappan Lakshmanan }
952a2438132SYegappan Lakshmanan else
953a2438132SYegappan Lakshmanan {
954a2438132SYegappan Lakshmanan ptr2len = mb_ptr2len;
955a2438132SYegappan Lakshmanan ptr2char = mb_ptr2char;
956a2438132SYegappan Lakshmanan }
957a2438132SYegappan Lakshmanan
958a2438132SYegappan Lakshmanan for ( ; *p != NUL; p += (*ptr2len)(p))
959a2438132SYegappan Lakshmanan list_append_number(rettv->vval.v_list, (*ptr2char)(p));
960a2438132SYegappan Lakshmanan }
961a2438132SYegappan Lakshmanan else
962a2438132SYegappan Lakshmanan for ( ; *p != NUL; ++p)
963a2438132SYegappan Lakshmanan list_append_number(rettv->vval.v_list, *p);
964a2438132SYegappan Lakshmanan }
965a2438132SYegappan Lakshmanan
966a2438132SYegappan Lakshmanan /*
967a2438132SYegappan Lakshmanan * "str2nr()" function
968a2438132SYegappan Lakshmanan */
969a2438132SYegappan Lakshmanan void
f_str2nr(typval_T * argvars,typval_T * rettv)970a2438132SYegappan Lakshmanan f_str2nr(typval_T *argvars, typval_T *rettv)
971a2438132SYegappan Lakshmanan {
972a2438132SYegappan Lakshmanan int base = 10;
973a2438132SYegappan Lakshmanan char_u *p;
974a2438132SYegappan Lakshmanan varnumber_T n;
975a2438132SYegappan Lakshmanan int what = 0;
976a2438132SYegappan Lakshmanan int isneg;
977a2438132SYegappan Lakshmanan
9784490ec4eSYegappan Lakshmanan if (in_vim9script()
9794490ec4eSYegappan Lakshmanan && (check_for_string_arg(argvars, 0) == FAIL
9804490ec4eSYegappan Lakshmanan || check_for_opt_number_arg(argvars, 1) == FAIL
9814490ec4eSYegappan Lakshmanan || (argvars[1].v_type != VAR_UNKNOWN
9824490ec4eSYegappan Lakshmanan && check_for_opt_bool_arg(argvars, 2) == FAIL)))
9834490ec4eSYegappan Lakshmanan return;
9844490ec4eSYegappan Lakshmanan
985a2438132SYegappan Lakshmanan if (argvars[1].v_type != VAR_UNKNOWN)
986a2438132SYegappan Lakshmanan {
987a2438132SYegappan Lakshmanan base = (int)tv_get_number(&argvars[1]);
988a2438132SYegappan Lakshmanan if (base != 2 && base != 8 && base != 10 && base != 16)
989a2438132SYegappan Lakshmanan {
990a2438132SYegappan Lakshmanan emsg(_(e_invarg));
991a2438132SYegappan Lakshmanan return;
992a2438132SYegappan Lakshmanan }
993a2438132SYegappan Lakshmanan if (argvars[2].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[2]))
994a2438132SYegappan Lakshmanan what |= STR2NR_QUOTE;
995a2438132SYegappan Lakshmanan }
996a2438132SYegappan Lakshmanan
997a2438132SYegappan Lakshmanan p = skipwhite(tv_get_string_strict(&argvars[0]));
998a2438132SYegappan Lakshmanan isneg = (*p == '-');
999a2438132SYegappan Lakshmanan if (*p == '+' || *p == '-')
1000a2438132SYegappan Lakshmanan p = skipwhite(p + 1);
1001a2438132SYegappan Lakshmanan switch (base)
1002a2438132SYegappan Lakshmanan {
1003a2438132SYegappan Lakshmanan case 2: what |= STR2NR_BIN + STR2NR_FORCE; break;
1004a2438132SYegappan Lakshmanan case 8: what |= STR2NR_OCT + STR2NR_OOCT + STR2NR_FORCE; break;
1005a2438132SYegappan Lakshmanan case 16: what |= STR2NR_HEX + STR2NR_FORCE; break;
1006a2438132SYegappan Lakshmanan }
1007a2438132SYegappan Lakshmanan vim_str2nr(p, NULL, NULL, what, &n, NULL, 0, FALSE);
1008a2438132SYegappan Lakshmanan // Text after the number is silently ignored.
1009a2438132SYegappan Lakshmanan if (isneg)
1010a2438132SYegappan Lakshmanan rettv->vval.v_number = -n;
1011a2438132SYegappan Lakshmanan else
1012a2438132SYegappan Lakshmanan rettv->vval.v_number = n;
1013a2438132SYegappan Lakshmanan
1014a2438132SYegappan Lakshmanan }
1015a2438132SYegappan Lakshmanan
1016a2438132SYegappan Lakshmanan /*
1017a2438132SYegappan Lakshmanan * "strgetchar()" function
1018a2438132SYegappan Lakshmanan */
1019a2438132SYegappan Lakshmanan void
f_strgetchar(typval_T * argvars,typval_T * rettv)1020a2438132SYegappan Lakshmanan f_strgetchar(typval_T *argvars, typval_T *rettv)
1021a2438132SYegappan Lakshmanan {
1022a2438132SYegappan Lakshmanan char_u *str;
1023a2438132SYegappan Lakshmanan int len;
1024a2438132SYegappan Lakshmanan int error = FALSE;
1025a2438132SYegappan Lakshmanan int charidx;
1026a2438132SYegappan Lakshmanan int byteidx = 0;
1027a2438132SYegappan Lakshmanan
1028a2438132SYegappan Lakshmanan rettv->vval.v_number = -1;
10291a71d31bSYegappan Lakshmanan
10301a71d31bSYegappan Lakshmanan if (in_vim9script()
10311a71d31bSYegappan Lakshmanan && (check_for_string_arg(argvars, 0) == FAIL
10321a71d31bSYegappan Lakshmanan || check_for_number_arg(argvars, 1) == FAIL))
10331a71d31bSYegappan Lakshmanan return;
10341a71d31bSYegappan Lakshmanan
1035a2438132SYegappan Lakshmanan str = tv_get_string_chk(&argvars[0]);
1036a2438132SYegappan Lakshmanan if (str == NULL)
1037a2438132SYegappan Lakshmanan return;
1038a2438132SYegappan Lakshmanan len = (int)STRLEN(str);
1039a2438132SYegappan Lakshmanan charidx = (int)tv_get_number_chk(&argvars[1], &error);
1040a2438132SYegappan Lakshmanan if (error)
1041a2438132SYegappan Lakshmanan return;
1042a2438132SYegappan Lakshmanan
1043a2438132SYegappan Lakshmanan while (charidx >= 0 && byteidx < len)
1044a2438132SYegappan Lakshmanan {
1045a2438132SYegappan Lakshmanan if (charidx == 0)
1046a2438132SYegappan Lakshmanan {
1047a2438132SYegappan Lakshmanan rettv->vval.v_number = mb_ptr2char(str + byteidx);
1048a2438132SYegappan Lakshmanan break;
1049a2438132SYegappan Lakshmanan }
1050a2438132SYegappan Lakshmanan --charidx;
1051a2438132SYegappan Lakshmanan byteidx += MB_CPTR2LEN(str + byteidx);
1052a2438132SYegappan Lakshmanan }
1053a2438132SYegappan Lakshmanan }
1054a2438132SYegappan Lakshmanan
1055a2438132SYegappan Lakshmanan /*
1056a2438132SYegappan Lakshmanan * "stridx()" function
1057a2438132SYegappan Lakshmanan */
1058a2438132SYegappan Lakshmanan void
f_stridx(typval_T * argvars,typval_T * rettv)1059a2438132SYegappan Lakshmanan f_stridx(typval_T *argvars, typval_T *rettv)
1060a2438132SYegappan Lakshmanan {
1061a2438132SYegappan Lakshmanan char_u buf[NUMBUFLEN];
1062a2438132SYegappan Lakshmanan char_u *needle;
1063a2438132SYegappan Lakshmanan char_u *haystack;
1064a2438132SYegappan Lakshmanan char_u *save_haystack;
1065a2438132SYegappan Lakshmanan char_u *pos;
1066a2438132SYegappan Lakshmanan int start_idx;
1067a2438132SYegappan Lakshmanan
10684490ec4eSYegappan Lakshmanan if (in_vim9script()
10694490ec4eSYegappan Lakshmanan && (check_for_string_arg(argvars, 0) == FAIL
10704490ec4eSYegappan Lakshmanan || check_for_string_arg(argvars, 1) == FAIL
10714490ec4eSYegappan Lakshmanan || check_for_opt_number_arg(argvars, 2) == FAIL))
10724490ec4eSYegappan Lakshmanan return;
10734490ec4eSYegappan Lakshmanan
1074a2438132SYegappan Lakshmanan needle = tv_get_string_chk(&argvars[1]);
1075a2438132SYegappan Lakshmanan save_haystack = haystack = tv_get_string_buf_chk(&argvars[0], buf);
1076a2438132SYegappan Lakshmanan rettv->vval.v_number = -1;
1077a2438132SYegappan Lakshmanan if (needle == NULL || haystack == NULL)
1078a2438132SYegappan Lakshmanan return; // type error; errmsg already given
1079a2438132SYegappan Lakshmanan
1080a2438132SYegappan Lakshmanan if (argvars[2].v_type != VAR_UNKNOWN)
1081a2438132SYegappan Lakshmanan {
1082a2438132SYegappan Lakshmanan int error = FALSE;
1083a2438132SYegappan Lakshmanan
1084a2438132SYegappan Lakshmanan start_idx = (int)tv_get_number_chk(&argvars[2], &error);
1085a2438132SYegappan Lakshmanan if (error || start_idx >= (int)STRLEN(haystack))
1086a2438132SYegappan Lakshmanan return;
1087a2438132SYegappan Lakshmanan if (start_idx >= 0)
1088a2438132SYegappan Lakshmanan haystack += start_idx;
1089a2438132SYegappan Lakshmanan }
1090a2438132SYegappan Lakshmanan
1091a2438132SYegappan Lakshmanan pos = (char_u *)strstr((char *)haystack, (char *)needle);
1092a2438132SYegappan Lakshmanan if (pos != NULL)
1093a2438132SYegappan Lakshmanan rettv->vval.v_number = (varnumber_T)(pos - save_haystack);
1094a2438132SYegappan Lakshmanan }
1095a2438132SYegappan Lakshmanan
1096a2438132SYegappan Lakshmanan /*
1097a2438132SYegappan Lakshmanan * "string()" function
1098a2438132SYegappan Lakshmanan */
1099a2438132SYegappan Lakshmanan void
f_string(typval_T * argvars,typval_T * rettv)1100a2438132SYegappan Lakshmanan f_string(typval_T *argvars, typval_T *rettv)
1101a2438132SYegappan Lakshmanan {
1102a2438132SYegappan Lakshmanan char_u *tofree;
1103a2438132SYegappan Lakshmanan char_u numbuf[NUMBUFLEN];
1104a2438132SYegappan Lakshmanan
1105a2438132SYegappan Lakshmanan rettv->v_type = VAR_STRING;
1106a2438132SYegappan Lakshmanan rettv->vval.v_string = tv2string(&argvars[0], &tofree, numbuf,
1107a2438132SYegappan Lakshmanan get_copyID());
1108a2438132SYegappan Lakshmanan // Make a copy if we have a value but it's not in allocated memory.
1109a2438132SYegappan Lakshmanan if (rettv->vval.v_string != NULL && tofree == NULL)
1110a2438132SYegappan Lakshmanan rettv->vval.v_string = vim_strsave(rettv->vval.v_string);
1111a2438132SYegappan Lakshmanan }
1112a2438132SYegappan Lakshmanan
1113a2438132SYegappan Lakshmanan /*
1114a2438132SYegappan Lakshmanan * "strlen()" function
1115a2438132SYegappan Lakshmanan */
1116a2438132SYegappan Lakshmanan void
f_strlen(typval_T * argvars,typval_T * rettv)1117a2438132SYegappan Lakshmanan f_strlen(typval_T *argvars, typval_T *rettv)
1118a2438132SYegappan Lakshmanan {
11194490ec4eSYegappan Lakshmanan if (in_vim9script()
11204490ec4eSYegappan Lakshmanan && check_for_string_or_number_arg(argvars, 0) == FAIL)
11214490ec4eSYegappan Lakshmanan return;
11224490ec4eSYegappan Lakshmanan
1123a2438132SYegappan Lakshmanan rettv->vval.v_number = (varnumber_T)(STRLEN(
1124a2438132SYegappan Lakshmanan tv_get_string(&argvars[0])));
1125a2438132SYegappan Lakshmanan }
1126a2438132SYegappan Lakshmanan
1127a2438132SYegappan Lakshmanan static void
strchar_common(typval_T * argvars,typval_T * rettv,int skipcc)1128a2438132SYegappan Lakshmanan strchar_common(typval_T *argvars, typval_T *rettv, int skipcc)
1129a2438132SYegappan Lakshmanan {
1130a2438132SYegappan Lakshmanan char_u *s = tv_get_string(&argvars[0]);
1131a2438132SYegappan Lakshmanan varnumber_T len = 0;
1132a2438132SYegappan Lakshmanan int (*func_mb_ptr2char_adv)(char_u **pp);
1133a2438132SYegappan Lakshmanan
1134a2438132SYegappan Lakshmanan func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv;
1135a2438132SYegappan Lakshmanan while (*s != NUL)
1136a2438132SYegappan Lakshmanan {
1137a2438132SYegappan Lakshmanan func_mb_ptr2char_adv(&s);
1138a2438132SYegappan Lakshmanan ++len;
1139a2438132SYegappan Lakshmanan }
1140a2438132SYegappan Lakshmanan rettv->vval.v_number = len;
1141a2438132SYegappan Lakshmanan }
1142a2438132SYegappan Lakshmanan
1143a2438132SYegappan Lakshmanan /*
1144a2438132SYegappan Lakshmanan * "strcharlen()" function
1145a2438132SYegappan Lakshmanan */
1146a2438132SYegappan Lakshmanan void
f_strcharlen(typval_T * argvars,typval_T * rettv)1147a2438132SYegappan Lakshmanan f_strcharlen(typval_T *argvars, typval_T *rettv)
1148a2438132SYegappan Lakshmanan {
11494490ec4eSYegappan Lakshmanan if (in_vim9script()
11504490ec4eSYegappan Lakshmanan && check_for_string_or_number_arg(argvars, 0) == FAIL)
11514490ec4eSYegappan Lakshmanan return;
11524490ec4eSYegappan Lakshmanan
1153a2438132SYegappan Lakshmanan strchar_common(argvars, rettv, TRUE);
1154a2438132SYegappan Lakshmanan }
1155a2438132SYegappan Lakshmanan
1156a2438132SYegappan Lakshmanan /*
1157a2438132SYegappan Lakshmanan * "strchars()" function
1158a2438132SYegappan Lakshmanan */
1159a2438132SYegappan Lakshmanan void
f_strchars(typval_T * argvars,typval_T * rettv)1160a2438132SYegappan Lakshmanan f_strchars(typval_T *argvars, typval_T *rettv)
1161a2438132SYegappan Lakshmanan {
1162a2438132SYegappan Lakshmanan varnumber_T skipcc = FALSE;
1163a2438132SYegappan Lakshmanan
1164a9a7c0c6SYegappan Lakshmanan if (in_vim9script()
1165a9a7c0c6SYegappan Lakshmanan && (check_for_string_arg(argvars, 0) == FAIL
116683494b4aSYegappan Lakshmanan || check_for_opt_bool_arg(argvars, 1) == FAIL))
1167a9a7c0c6SYegappan Lakshmanan return;
1168a9a7c0c6SYegappan Lakshmanan
1169a2438132SYegappan Lakshmanan if (argvars[1].v_type != VAR_UNKNOWN)
1170a2438132SYegappan Lakshmanan skipcc = tv_get_bool(&argvars[1]);
1171a2438132SYegappan Lakshmanan if (skipcc < 0 || skipcc > 1)
1172a2438132SYegappan Lakshmanan semsg(_(e_using_number_as_bool_nr), skipcc);
1173a2438132SYegappan Lakshmanan else
1174a2438132SYegappan Lakshmanan strchar_common(argvars, rettv, skipcc);
1175a2438132SYegappan Lakshmanan }
1176a2438132SYegappan Lakshmanan
1177a2438132SYegappan Lakshmanan /*
1178a2438132SYegappan Lakshmanan * "strdisplaywidth()" function
1179a2438132SYegappan Lakshmanan */
1180a2438132SYegappan Lakshmanan void
f_strdisplaywidth(typval_T * argvars,typval_T * rettv)1181a2438132SYegappan Lakshmanan f_strdisplaywidth(typval_T *argvars, typval_T *rettv)
1182a2438132SYegappan Lakshmanan {
11831a71d31bSYegappan Lakshmanan char_u *s;
1184a2438132SYegappan Lakshmanan int col = 0;
1185a2438132SYegappan Lakshmanan
11861a71d31bSYegappan Lakshmanan rettv->vval.v_number = -1;
11871a71d31bSYegappan Lakshmanan
11881a71d31bSYegappan Lakshmanan if (in_vim9script()
11891a71d31bSYegappan Lakshmanan && (check_for_string_arg(argvars, 0) == FAIL
119083494b4aSYegappan Lakshmanan || check_for_opt_number_arg(argvars, 1) == FAIL))
11911a71d31bSYegappan Lakshmanan return;
11921a71d31bSYegappan Lakshmanan
11931a71d31bSYegappan Lakshmanan s = tv_get_string(&argvars[0]);
1194a2438132SYegappan Lakshmanan if (argvars[1].v_type != VAR_UNKNOWN)
1195a2438132SYegappan Lakshmanan col = (int)tv_get_number(&argvars[1]);
1196a2438132SYegappan Lakshmanan
1197a2438132SYegappan Lakshmanan rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, s) - col);
1198a2438132SYegappan Lakshmanan }
1199a2438132SYegappan Lakshmanan
1200a2438132SYegappan Lakshmanan /*
1201a2438132SYegappan Lakshmanan * "strwidth()" function
1202a2438132SYegappan Lakshmanan */
1203a2438132SYegappan Lakshmanan void
f_strwidth(typval_T * argvars,typval_T * rettv)1204a2438132SYegappan Lakshmanan f_strwidth(typval_T *argvars, typval_T *rettv)
1205a2438132SYegappan Lakshmanan {
12064490ec4eSYegappan Lakshmanan char_u *s;
1207a2438132SYegappan Lakshmanan
12084490ec4eSYegappan Lakshmanan if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
12094490ec4eSYegappan Lakshmanan return;
12104490ec4eSYegappan Lakshmanan
12114490ec4eSYegappan Lakshmanan s = tv_get_string_strict(&argvars[0]);
1212a2438132SYegappan Lakshmanan rettv->vval.v_number = (varnumber_T)(mb_string2cells(s, -1));
1213a2438132SYegappan Lakshmanan }
1214a2438132SYegappan Lakshmanan
1215a2438132SYegappan Lakshmanan /*
1216a2438132SYegappan Lakshmanan * "strcharpart()" function
1217a2438132SYegappan Lakshmanan */
1218a2438132SYegappan Lakshmanan void
f_strcharpart(typval_T * argvars,typval_T * rettv)1219a2438132SYegappan Lakshmanan f_strcharpart(typval_T *argvars, typval_T *rettv)
1220a2438132SYegappan Lakshmanan {
1221a2438132SYegappan Lakshmanan char_u *p;
1222a2438132SYegappan Lakshmanan int nchar;
1223a2438132SYegappan Lakshmanan int nbyte = 0;
1224a2438132SYegappan Lakshmanan int charlen;
1225a2438132SYegappan Lakshmanan int skipcc = FALSE;
1226a2438132SYegappan Lakshmanan int len = 0;
1227a2438132SYegappan Lakshmanan int slen;
1228a2438132SYegappan Lakshmanan int error = FALSE;
1229a2438132SYegappan Lakshmanan
123083494b4aSYegappan Lakshmanan if (in_vim9script()
123183494b4aSYegappan Lakshmanan && (check_for_string_arg(argvars, 0) == FAIL
123283494b4aSYegappan Lakshmanan || check_for_number_arg(argvars, 1) == FAIL
123383494b4aSYegappan Lakshmanan || check_for_opt_number_arg(argvars, 2) == FAIL
123483494b4aSYegappan Lakshmanan || (argvars[2].v_type != VAR_UNKNOWN
123583494b4aSYegappan Lakshmanan && check_for_opt_bool_arg(argvars, 3) == FAIL)))
123683494b4aSYegappan Lakshmanan return;
123783494b4aSYegappan Lakshmanan
1238a2438132SYegappan Lakshmanan p = tv_get_string(&argvars[0]);
1239a2438132SYegappan Lakshmanan slen = (int)STRLEN(p);
1240a2438132SYegappan Lakshmanan
1241a2438132SYegappan Lakshmanan nchar = (int)tv_get_number_chk(&argvars[1], &error);
1242a2438132SYegappan Lakshmanan if (!error)
1243a2438132SYegappan Lakshmanan {
1244a2438132SYegappan Lakshmanan if (argvars[2].v_type != VAR_UNKNOWN
1245a2438132SYegappan Lakshmanan && argvars[3].v_type != VAR_UNKNOWN)
1246a2438132SYegappan Lakshmanan {
1247a2438132SYegappan Lakshmanan skipcc = tv_get_bool(&argvars[3]);
1248a2438132SYegappan Lakshmanan if (skipcc < 0 || skipcc > 1)
1249a2438132SYegappan Lakshmanan {
1250a2438132SYegappan Lakshmanan semsg(_(e_using_number_as_bool_nr), skipcc);
1251a2438132SYegappan Lakshmanan return;
1252a2438132SYegappan Lakshmanan }
1253a2438132SYegappan Lakshmanan }
1254a2438132SYegappan Lakshmanan
1255a2438132SYegappan Lakshmanan if (nchar > 0)
1256a2438132SYegappan Lakshmanan while (nchar > 0 && nbyte < slen)
1257a2438132SYegappan Lakshmanan {
1258a2438132SYegappan Lakshmanan if (skipcc)
1259a2438132SYegappan Lakshmanan nbyte += mb_ptr2len(p + nbyte);
1260a2438132SYegappan Lakshmanan else
1261a2438132SYegappan Lakshmanan nbyte += MB_CPTR2LEN(p + nbyte);
1262a2438132SYegappan Lakshmanan --nchar;
1263a2438132SYegappan Lakshmanan }
1264a2438132SYegappan Lakshmanan else
1265a2438132SYegappan Lakshmanan nbyte = nchar;
1266a2438132SYegappan Lakshmanan if (argvars[2].v_type != VAR_UNKNOWN)
1267a2438132SYegappan Lakshmanan {
1268a2438132SYegappan Lakshmanan charlen = (int)tv_get_number(&argvars[2]);
1269a2438132SYegappan Lakshmanan while (charlen > 0 && nbyte + len < slen)
1270a2438132SYegappan Lakshmanan {
1271a2438132SYegappan Lakshmanan int off = nbyte + len;
1272a2438132SYegappan Lakshmanan
1273a2438132SYegappan Lakshmanan if (off < 0)
1274a2438132SYegappan Lakshmanan len += 1;
1275a2438132SYegappan Lakshmanan else
1276a2438132SYegappan Lakshmanan {
1277a2438132SYegappan Lakshmanan if (skipcc)
1278a2438132SYegappan Lakshmanan len += mb_ptr2len(p + off);
1279a2438132SYegappan Lakshmanan else
1280a2438132SYegappan Lakshmanan len += MB_CPTR2LEN(p + off);
1281a2438132SYegappan Lakshmanan }
1282a2438132SYegappan Lakshmanan --charlen;
1283a2438132SYegappan Lakshmanan }
1284a2438132SYegappan Lakshmanan }
1285a2438132SYegappan Lakshmanan else
1286a2438132SYegappan Lakshmanan len = slen - nbyte; // default: all bytes that are available.
1287a2438132SYegappan Lakshmanan }
1288a2438132SYegappan Lakshmanan
12898ee52affSYegappan Lakshmanan // Only return the overlap between the specified part and the actual
12908ee52affSYegappan Lakshmanan // string.
1291a2438132SYegappan Lakshmanan if (nbyte < 0)
1292a2438132SYegappan Lakshmanan {
1293a2438132SYegappan Lakshmanan len += nbyte;
1294a2438132SYegappan Lakshmanan nbyte = 0;
1295a2438132SYegappan Lakshmanan }
1296a2438132SYegappan Lakshmanan else if (nbyte > slen)
1297a2438132SYegappan Lakshmanan nbyte = slen;
1298a2438132SYegappan Lakshmanan if (len < 0)
1299a2438132SYegappan Lakshmanan len = 0;
1300a2438132SYegappan Lakshmanan else if (nbyte + len > slen)
1301a2438132SYegappan Lakshmanan len = slen - nbyte;
1302a2438132SYegappan Lakshmanan
1303a2438132SYegappan Lakshmanan rettv->v_type = VAR_STRING;
1304a2438132SYegappan Lakshmanan rettv->vval.v_string = vim_strnsave(p + nbyte, len);
1305a2438132SYegappan Lakshmanan }
1306a2438132SYegappan Lakshmanan
1307a2438132SYegappan Lakshmanan /*
1308a2438132SYegappan Lakshmanan * "strpart()" function
1309a2438132SYegappan Lakshmanan */
1310a2438132SYegappan Lakshmanan void
f_strpart(typval_T * argvars,typval_T * rettv)1311a2438132SYegappan Lakshmanan f_strpart(typval_T *argvars, typval_T *rettv)
1312a2438132SYegappan Lakshmanan {
1313a2438132SYegappan Lakshmanan char_u *p;
1314a2438132SYegappan Lakshmanan int n;
1315a2438132SYegappan Lakshmanan int len;
1316a2438132SYegappan Lakshmanan int slen;
1317a2438132SYegappan Lakshmanan int error = FALSE;
1318a2438132SYegappan Lakshmanan
131983494b4aSYegappan Lakshmanan if (in_vim9script()
132083494b4aSYegappan Lakshmanan && (check_for_string_arg(argvars, 0) == FAIL
132183494b4aSYegappan Lakshmanan || check_for_number_arg(argvars, 1) == FAIL
132283494b4aSYegappan Lakshmanan || check_for_opt_number_arg(argvars, 2) == FAIL
132383494b4aSYegappan Lakshmanan || (argvars[2].v_type != VAR_UNKNOWN
132483494b4aSYegappan Lakshmanan && check_for_opt_bool_arg(argvars, 3) == FAIL)))
132583494b4aSYegappan Lakshmanan return;
132683494b4aSYegappan Lakshmanan
1327a2438132SYegappan Lakshmanan p = tv_get_string(&argvars[0]);
1328a2438132SYegappan Lakshmanan slen = (int)STRLEN(p);
1329a2438132SYegappan Lakshmanan
1330a2438132SYegappan Lakshmanan n = (int)tv_get_number_chk(&argvars[1], &error);
1331a2438132SYegappan Lakshmanan if (error)
1332a2438132SYegappan Lakshmanan len = 0;
1333a2438132SYegappan Lakshmanan else if (argvars[2].v_type != VAR_UNKNOWN)
1334a2438132SYegappan Lakshmanan len = (int)tv_get_number(&argvars[2]);
1335a2438132SYegappan Lakshmanan else
1336a2438132SYegappan Lakshmanan len = slen - n; // default len: all bytes that are available.
1337a2438132SYegappan Lakshmanan
1338a2438132SYegappan Lakshmanan // Only return the overlap between the specified part and the actual
1339a2438132SYegappan Lakshmanan // string.
1340a2438132SYegappan Lakshmanan if (n < 0)
1341a2438132SYegappan Lakshmanan {
1342a2438132SYegappan Lakshmanan len += n;
1343a2438132SYegappan Lakshmanan n = 0;
1344a2438132SYegappan Lakshmanan }
1345a2438132SYegappan Lakshmanan else if (n > slen)
1346a2438132SYegappan Lakshmanan n = slen;
1347a2438132SYegappan Lakshmanan if (len < 0)
1348a2438132SYegappan Lakshmanan len = 0;
1349a2438132SYegappan Lakshmanan else if (n + len > slen)
1350a2438132SYegappan Lakshmanan len = slen - n;
1351a2438132SYegappan Lakshmanan
1352a2438132SYegappan Lakshmanan if (argvars[2].v_type != VAR_UNKNOWN && argvars[3].v_type != VAR_UNKNOWN)
1353a2438132SYegappan Lakshmanan {
1354a2438132SYegappan Lakshmanan int off;
1355a2438132SYegappan Lakshmanan
1356a2438132SYegappan Lakshmanan // length in characters
1357a2438132SYegappan Lakshmanan for (off = n; off < slen && len > 0; --len)
1358a2438132SYegappan Lakshmanan off += mb_ptr2len(p + off);
1359a2438132SYegappan Lakshmanan len = off - n;
1360a2438132SYegappan Lakshmanan }
1361a2438132SYegappan Lakshmanan
1362a2438132SYegappan Lakshmanan rettv->v_type = VAR_STRING;
1363a2438132SYegappan Lakshmanan rettv->vval.v_string = vim_strnsave(p + n, len);
1364a2438132SYegappan Lakshmanan }
1365a2438132SYegappan Lakshmanan
1366a2438132SYegappan Lakshmanan /*
1367a2438132SYegappan Lakshmanan * "strridx()" function
1368a2438132SYegappan Lakshmanan */
1369a2438132SYegappan Lakshmanan void
f_strridx(typval_T * argvars,typval_T * rettv)1370a2438132SYegappan Lakshmanan f_strridx(typval_T *argvars, typval_T *rettv)
1371a2438132SYegappan Lakshmanan {
1372a2438132SYegappan Lakshmanan char_u buf[NUMBUFLEN];
1373a2438132SYegappan Lakshmanan char_u *needle;
1374a2438132SYegappan Lakshmanan char_u *haystack;
1375a2438132SYegappan Lakshmanan char_u *rest;
1376a2438132SYegappan Lakshmanan char_u *lastmatch = NULL;
1377a2438132SYegappan Lakshmanan int haystack_len, end_idx;
1378a2438132SYegappan Lakshmanan
13794490ec4eSYegappan Lakshmanan if (in_vim9script()
13804490ec4eSYegappan Lakshmanan && (check_for_string_arg(argvars, 0) == FAIL
13814490ec4eSYegappan Lakshmanan || check_for_string_arg(argvars, 1) == FAIL
13824490ec4eSYegappan Lakshmanan || check_for_opt_number_arg(argvars, 2) == FAIL))
13834490ec4eSYegappan Lakshmanan return;
13844490ec4eSYegappan Lakshmanan
1385a2438132SYegappan Lakshmanan needle = tv_get_string_chk(&argvars[1]);
1386a2438132SYegappan Lakshmanan haystack = tv_get_string_buf_chk(&argvars[0], buf);
1387a2438132SYegappan Lakshmanan
1388a2438132SYegappan Lakshmanan rettv->vval.v_number = -1;
1389a2438132SYegappan Lakshmanan if (needle == NULL || haystack == NULL)
1390a2438132SYegappan Lakshmanan return; // type error; errmsg already given
1391a2438132SYegappan Lakshmanan
1392a2438132SYegappan Lakshmanan haystack_len = (int)STRLEN(haystack);
1393a2438132SYegappan Lakshmanan if (argvars[2].v_type != VAR_UNKNOWN)
1394a2438132SYegappan Lakshmanan {
1395a2438132SYegappan Lakshmanan // Third argument: upper limit for index
1396a2438132SYegappan Lakshmanan end_idx = (int)tv_get_number_chk(&argvars[2], NULL);
1397a2438132SYegappan Lakshmanan if (end_idx < 0)
1398a2438132SYegappan Lakshmanan return; // can never find a match
1399a2438132SYegappan Lakshmanan }
1400a2438132SYegappan Lakshmanan else
1401a2438132SYegappan Lakshmanan end_idx = haystack_len;
1402a2438132SYegappan Lakshmanan
1403a2438132SYegappan Lakshmanan if (*needle == NUL)
1404a2438132SYegappan Lakshmanan {
1405a2438132SYegappan Lakshmanan // Empty string matches past the end.
1406a2438132SYegappan Lakshmanan lastmatch = haystack + end_idx;
1407a2438132SYegappan Lakshmanan }
1408a2438132SYegappan Lakshmanan else
1409a2438132SYegappan Lakshmanan {
1410a2438132SYegappan Lakshmanan for (rest = haystack; *rest != '\0'; ++rest)
1411a2438132SYegappan Lakshmanan {
1412a2438132SYegappan Lakshmanan rest = (char_u *)strstr((char *)rest, (char *)needle);
1413a2438132SYegappan Lakshmanan if (rest == NULL || rest > haystack + end_idx)
1414a2438132SYegappan Lakshmanan break;
1415a2438132SYegappan Lakshmanan lastmatch = rest;
1416a2438132SYegappan Lakshmanan }
1417a2438132SYegappan Lakshmanan }
1418a2438132SYegappan Lakshmanan
1419a2438132SYegappan Lakshmanan if (lastmatch == NULL)
1420a2438132SYegappan Lakshmanan rettv->vval.v_number = -1;
1421a2438132SYegappan Lakshmanan else
1422a2438132SYegappan Lakshmanan rettv->vval.v_number = (varnumber_T)(lastmatch - haystack);
1423a2438132SYegappan Lakshmanan }
1424a2438132SYegappan Lakshmanan
1425a2438132SYegappan Lakshmanan /*
1426a2438132SYegappan Lakshmanan * "strtrans()" function
1427a2438132SYegappan Lakshmanan */
1428a2438132SYegappan Lakshmanan void
f_strtrans(typval_T * argvars,typval_T * rettv)1429a2438132SYegappan Lakshmanan f_strtrans(typval_T *argvars, typval_T *rettv)
1430a2438132SYegappan Lakshmanan {
14314490ec4eSYegappan Lakshmanan if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
14324490ec4eSYegappan Lakshmanan return;
14334490ec4eSYegappan Lakshmanan
1434a2438132SYegappan Lakshmanan rettv->v_type = VAR_STRING;
1435a2438132SYegappan Lakshmanan rettv->vval.v_string = transstr(tv_get_string(&argvars[0]));
1436a2438132SYegappan Lakshmanan }
1437a2438132SYegappan Lakshmanan
1438a2438132SYegappan Lakshmanan /*
1439a2438132SYegappan Lakshmanan * "tolower(string)" function
1440a2438132SYegappan Lakshmanan */
1441a2438132SYegappan Lakshmanan void
f_tolower(typval_T * argvars,typval_T * rettv)1442a2438132SYegappan Lakshmanan f_tolower(typval_T *argvars, typval_T *rettv)
1443a2438132SYegappan Lakshmanan {
14444490ec4eSYegappan Lakshmanan if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
14454490ec4eSYegappan Lakshmanan return;
14464490ec4eSYegappan Lakshmanan
1447a2438132SYegappan Lakshmanan rettv->v_type = VAR_STRING;
1448a2438132SYegappan Lakshmanan rettv->vval.v_string = strlow_save(tv_get_string(&argvars[0]));
1449a2438132SYegappan Lakshmanan }
1450a2438132SYegappan Lakshmanan
1451a2438132SYegappan Lakshmanan /*
1452a2438132SYegappan Lakshmanan * "toupper(string)" function
1453a2438132SYegappan Lakshmanan */
1454a2438132SYegappan Lakshmanan void
f_toupper(typval_T * argvars,typval_T * rettv)1455a2438132SYegappan Lakshmanan f_toupper(typval_T *argvars, typval_T *rettv)
1456a2438132SYegappan Lakshmanan {
14574490ec4eSYegappan Lakshmanan if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
14584490ec4eSYegappan Lakshmanan return;
14594490ec4eSYegappan Lakshmanan
1460a2438132SYegappan Lakshmanan rettv->v_type = VAR_STRING;
1461a2438132SYegappan Lakshmanan rettv->vval.v_string = strup_save(tv_get_string(&argvars[0]));
1462a2438132SYegappan Lakshmanan }
1463a2438132SYegappan Lakshmanan
1464a2438132SYegappan Lakshmanan /*
1465a2438132SYegappan Lakshmanan * "tr(string, fromstr, tostr)" function
1466a2438132SYegappan Lakshmanan */
1467a2438132SYegappan Lakshmanan void
f_tr(typval_T * argvars,typval_T * rettv)1468a2438132SYegappan Lakshmanan f_tr(typval_T *argvars, typval_T *rettv)
1469a2438132SYegappan Lakshmanan {
1470a2438132SYegappan Lakshmanan char_u *in_str;
1471a2438132SYegappan Lakshmanan char_u *fromstr;
1472a2438132SYegappan Lakshmanan char_u *tostr;
1473a2438132SYegappan Lakshmanan char_u *p;
1474a2438132SYegappan Lakshmanan int inlen;
1475a2438132SYegappan Lakshmanan int fromlen;
1476a2438132SYegappan Lakshmanan int tolen;
1477a2438132SYegappan Lakshmanan int idx;
1478a2438132SYegappan Lakshmanan char_u *cpstr;
1479a2438132SYegappan Lakshmanan int cplen;
1480a2438132SYegappan Lakshmanan int first = TRUE;
1481a2438132SYegappan Lakshmanan char_u buf[NUMBUFLEN];
1482a2438132SYegappan Lakshmanan char_u buf2[NUMBUFLEN];
1483a2438132SYegappan Lakshmanan garray_T ga;
1484a2438132SYegappan Lakshmanan
14854490ec4eSYegappan Lakshmanan if (in_vim9script()
14864490ec4eSYegappan Lakshmanan && (check_for_string_arg(argvars, 0) == FAIL
14874490ec4eSYegappan Lakshmanan || check_for_string_arg(argvars, 1) == FAIL
14884490ec4eSYegappan Lakshmanan || check_for_string_arg(argvars, 2) == FAIL))
14894490ec4eSYegappan Lakshmanan return;
14904490ec4eSYegappan Lakshmanan
1491a2438132SYegappan Lakshmanan in_str = tv_get_string(&argvars[0]);
1492a2438132SYegappan Lakshmanan fromstr = tv_get_string_buf_chk(&argvars[1], buf);
1493a2438132SYegappan Lakshmanan tostr = tv_get_string_buf_chk(&argvars[2], buf2);
1494a2438132SYegappan Lakshmanan
1495a2438132SYegappan Lakshmanan // Default return value: empty string.
1496a2438132SYegappan Lakshmanan rettv->v_type = VAR_STRING;
1497a2438132SYegappan Lakshmanan rettv->vval.v_string = NULL;
1498a2438132SYegappan Lakshmanan if (fromstr == NULL || tostr == NULL)
1499a2438132SYegappan Lakshmanan return; // type error; errmsg already given
1500a2438132SYegappan Lakshmanan ga_init2(&ga, (int)sizeof(char), 80);
1501a2438132SYegappan Lakshmanan
1502a2438132SYegappan Lakshmanan if (!has_mbyte)
1503a2438132SYegappan Lakshmanan // not multi-byte: fromstr and tostr must be the same length
1504a2438132SYegappan Lakshmanan if (STRLEN(fromstr) != STRLEN(tostr))
1505a2438132SYegappan Lakshmanan {
1506a2438132SYegappan Lakshmanan error:
1507a2438132SYegappan Lakshmanan semsg(_(e_invarg2), fromstr);
1508a2438132SYegappan Lakshmanan ga_clear(&ga);
1509a2438132SYegappan Lakshmanan return;
1510a2438132SYegappan Lakshmanan }
1511a2438132SYegappan Lakshmanan
1512a2438132SYegappan Lakshmanan // fromstr and tostr have to contain the same number of chars
1513a2438132SYegappan Lakshmanan while (*in_str != NUL)
1514a2438132SYegappan Lakshmanan {
1515a2438132SYegappan Lakshmanan if (has_mbyte)
1516a2438132SYegappan Lakshmanan {
1517a2438132SYegappan Lakshmanan inlen = (*mb_ptr2len)(in_str);
1518a2438132SYegappan Lakshmanan cpstr = in_str;
1519a2438132SYegappan Lakshmanan cplen = inlen;
1520a2438132SYegappan Lakshmanan idx = 0;
1521a2438132SYegappan Lakshmanan for (p = fromstr; *p != NUL; p += fromlen)
1522a2438132SYegappan Lakshmanan {
1523a2438132SYegappan Lakshmanan fromlen = (*mb_ptr2len)(p);
1524a2438132SYegappan Lakshmanan if (fromlen == inlen && STRNCMP(in_str, p, inlen) == 0)
1525a2438132SYegappan Lakshmanan {
1526a2438132SYegappan Lakshmanan for (p = tostr; *p != NUL; p += tolen)
1527a2438132SYegappan Lakshmanan {
1528a2438132SYegappan Lakshmanan tolen = (*mb_ptr2len)(p);
1529a2438132SYegappan Lakshmanan if (idx-- == 0)
1530a2438132SYegappan Lakshmanan {
1531a2438132SYegappan Lakshmanan cplen = tolen;
1532a2438132SYegappan Lakshmanan cpstr = p;
1533a2438132SYegappan Lakshmanan break;
1534a2438132SYegappan Lakshmanan }
1535a2438132SYegappan Lakshmanan }
1536a2438132SYegappan Lakshmanan if (*p == NUL) // tostr is shorter than fromstr
1537a2438132SYegappan Lakshmanan goto error;
1538a2438132SYegappan Lakshmanan break;
1539a2438132SYegappan Lakshmanan }
1540a2438132SYegappan Lakshmanan ++idx;
1541a2438132SYegappan Lakshmanan }
1542a2438132SYegappan Lakshmanan
1543a2438132SYegappan Lakshmanan if (first && cpstr == in_str)
1544a2438132SYegappan Lakshmanan {
1545a2438132SYegappan Lakshmanan // Check that fromstr and tostr have the same number of
1546a2438132SYegappan Lakshmanan // (multi-byte) characters. Done only once when a character
1547a2438132SYegappan Lakshmanan // of in_str doesn't appear in fromstr.
1548a2438132SYegappan Lakshmanan first = FALSE;
1549a2438132SYegappan Lakshmanan for (p = tostr; *p != NUL; p += tolen)
1550a2438132SYegappan Lakshmanan {
1551a2438132SYegappan Lakshmanan tolen = (*mb_ptr2len)(p);
1552a2438132SYegappan Lakshmanan --idx;
1553a2438132SYegappan Lakshmanan }
1554a2438132SYegappan Lakshmanan if (idx != 0)
1555a2438132SYegappan Lakshmanan goto error;
1556a2438132SYegappan Lakshmanan }
1557a2438132SYegappan Lakshmanan
1558a2438132SYegappan Lakshmanan (void)ga_grow(&ga, cplen);
1559a2438132SYegappan Lakshmanan mch_memmove((char *)ga.ga_data + ga.ga_len, cpstr, (size_t)cplen);
1560a2438132SYegappan Lakshmanan ga.ga_len += cplen;
1561a2438132SYegappan Lakshmanan
1562a2438132SYegappan Lakshmanan in_str += inlen;
1563a2438132SYegappan Lakshmanan }
1564a2438132SYegappan Lakshmanan else
1565a2438132SYegappan Lakshmanan {
1566a2438132SYegappan Lakshmanan // When not using multi-byte chars we can do it faster.
1567a2438132SYegappan Lakshmanan p = vim_strchr(fromstr, *in_str);
1568a2438132SYegappan Lakshmanan if (p != NULL)
1569a2438132SYegappan Lakshmanan ga_append(&ga, tostr[p - fromstr]);
1570a2438132SYegappan Lakshmanan else
1571a2438132SYegappan Lakshmanan ga_append(&ga, *in_str);
1572a2438132SYegappan Lakshmanan ++in_str;
1573a2438132SYegappan Lakshmanan }
1574a2438132SYegappan Lakshmanan }
1575a2438132SYegappan Lakshmanan
1576a2438132SYegappan Lakshmanan // add a terminating NUL
1577a2438132SYegappan Lakshmanan (void)ga_grow(&ga, 1);
1578a2438132SYegappan Lakshmanan ga_append(&ga, NUL);
1579a2438132SYegappan Lakshmanan
1580a2438132SYegappan Lakshmanan rettv->vval.v_string = ga.ga_data;
1581a2438132SYegappan Lakshmanan }
1582a2438132SYegappan Lakshmanan
1583a2438132SYegappan Lakshmanan /*
1584a2438132SYegappan Lakshmanan * "trim({expr})" function
1585a2438132SYegappan Lakshmanan */
1586a2438132SYegappan Lakshmanan void
f_trim(typval_T * argvars,typval_T * rettv)1587a2438132SYegappan Lakshmanan f_trim(typval_T *argvars, typval_T *rettv)
1588a2438132SYegappan Lakshmanan {
1589a2438132SYegappan Lakshmanan char_u buf1[NUMBUFLEN];
1590a2438132SYegappan Lakshmanan char_u buf2[NUMBUFLEN];
15914490ec4eSYegappan Lakshmanan char_u *head;
1592a2438132SYegappan Lakshmanan char_u *mask = NULL;
1593a2438132SYegappan Lakshmanan char_u *tail;
1594a2438132SYegappan Lakshmanan char_u *prev;
1595a2438132SYegappan Lakshmanan char_u *p;
1596a2438132SYegappan Lakshmanan int c1;
1597a2438132SYegappan Lakshmanan int dir = 0;
1598a2438132SYegappan Lakshmanan
1599a2438132SYegappan Lakshmanan rettv->v_type = VAR_STRING;
1600a2438132SYegappan Lakshmanan rettv->vval.v_string = NULL;
16014490ec4eSYegappan Lakshmanan
16024490ec4eSYegappan Lakshmanan if (in_vim9script()
16034490ec4eSYegappan Lakshmanan && (check_for_string_arg(argvars, 0) == FAIL
16044490ec4eSYegappan Lakshmanan || check_for_opt_string_arg(argvars, 1) == FAIL
16054490ec4eSYegappan Lakshmanan || (argvars[1].v_type != VAR_UNKNOWN
16064490ec4eSYegappan Lakshmanan && check_for_opt_number_arg(argvars, 2) == FAIL)))
16074490ec4eSYegappan Lakshmanan return;
16084490ec4eSYegappan Lakshmanan
16094490ec4eSYegappan Lakshmanan head = tv_get_string_buf_chk(&argvars[0], buf1);
1610a2438132SYegappan Lakshmanan if (head == NULL)
1611a2438132SYegappan Lakshmanan return;
1612a2438132SYegappan Lakshmanan
1613a2438132SYegappan Lakshmanan if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_STRING)
1614a2438132SYegappan Lakshmanan {
1615a2438132SYegappan Lakshmanan semsg(_(e_invarg2), tv_get_string(&argvars[1]));
1616a2438132SYegappan Lakshmanan return;
1617a2438132SYegappan Lakshmanan }
1618a2438132SYegappan Lakshmanan
1619a2438132SYegappan Lakshmanan if (argvars[1].v_type == VAR_STRING)
1620a2438132SYegappan Lakshmanan {
1621a2438132SYegappan Lakshmanan mask = tv_get_string_buf_chk(&argvars[1], buf2);
1622a2438132SYegappan Lakshmanan
1623a2438132SYegappan Lakshmanan if (argvars[2].v_type != VAR_UNKNOWN)
1624a2438132SYegappan Lakshmanan {
1625a2438132SYegappan Lakshmanan int error = 0;
1626a2438132SYegappan Lakshmanan
1627a2438132SYegappan Lakshmanan // leading or trailing characters to trim
1628a2438132SYegappan Lakshmanan dir = (int)tv_get_number_chk(&argvars[2], &error);
1629a2438132SYegappan Lakshmanan if (error)
1630a2438132SYegappan Lakshmanan return;
1631a2438132SYegappan Lakshmanan if (dir < 0 || dir > 2)
1632a2438132SYegappan Lakshmanan {
1633a2438132SYegappan Lakshmanan semsg(_(e_invarg2), tv_get_string(&argvars[2]));
1634a2438132SYegappan Lakshmanan return;
1635a2438132SYegappan Lakshmanan }
1636a2438132SYegappan Lakshmanan }
1637a2438132SYegappan Lakshmanan }
1638a2438132SYegappan Lakshmanan
1639a2438132SYegappan Lakshmanan if (dir == 0 || dir == 1)
1640a2438132SYegappan Lakshmanan {
1641a2438132SYegappan Lakshmanan // Trim leading characters
1642a2438132SYegappan Lakshmanan while (*head != NUL)
1643a2438132SYegappan Lakshmanan {
1644a2438132SYegappan Lakshmanan c1 = PTR2CHAR(head);
1645a2438132SYegappan Lakshmanan if (mask == NULL)
1646a2438132SYegappan Lakshmanan {
1647a2438132SYegappan Lakshmanan if (c1 > ' ' && c1 != 0xa0)
1648a2438132SYegappan Lakshmanan break;
1649a2438132SYegappan Lakshmanan }
1650a2438132SYegappan Lakshmanan else
1651a2438132SYegappan Lakshmanan {
1652a2438132SYegappan Lakshmanan for (p = mask; *p != NUL; MB_PTR_ADV(p))
1653a2438132SYegappan Lakshmanan if (c1 == PTR2CHAR(p))
1654a2438132SYegappan Lakshmanan break;
1655a2438132SYegappan Lakshmanan if (*p == NUL)
1656a2438132SYegappan Lakshmanan break;
1657a2438132SYegappan Lakshmanan }
1658a2438132SYegappan Lakshmanan MB_PTR_ADV(head);
1659a2438132SYegappan Lakshmanan }
1660a2438132SYegappan Lakshmanan }
1661a2438132SYegappan Lakshmanan
1662a2438132SYegappan Lakshmanan tail = head + STRLEN(head);
1663a2438132SYegappan Lakshmanan if (dir == 0 || dir == 2)
1664a2438132SYegappan Lakshmanan {
1665a2438132SYegappan Lakshmanan // Trim trailing characters
1666a2438132SYegappan Lakshmanan for (; tail > head; tail = prev)
1667a2438132SYegappan Lakshmanan {
1668a2438132SYegappan Lakshmanan prev = tail;
1669a2438132SYegappan Lakshmanan MB_PTR_BACK(head, prev);
1670a2438132SYegappan Lakshmanan c1 = PTR2CHAR(prev);
1671a2438132SYegappan Lakshmanan if (mask == NULL)
1672a2438132SYegappan Lakshmanan {
1673a2438132SYegappan Lakshmanan if (c1 > ' ' && c1 != 0xa0)
1674a2438132SYegappan Lakshmanan break;
1675a2438132SYegappan Lakshmanan }
1676a2438132SYegappan Lakshmanan else
1677a2438132SYegappan Lakshmanan {
1678a2438132SYegappan Lakshmanan for (p = mask; *p != NUL; MB_PTR_ADV(p))
1679a2438132SYegappan Lakshmanan if (c1 == PTR2CHAR(p))
1680a2438132SYegappan Lakshmanan break;
1681a2438132SYegappan Lakshmanan if (*p == NUL)
1682a2438132SYegappan Lakshmanan break;
1683a2438132SYegappan Lakshmanan }
1684a2438132SYegappan Lakshmanan }
1685a2438132SYegappan Lakshmanan }
1686a2438132SYegappan Lakshmanan rettv->vval.v_string = vim_strnsave(head, tail - head);
1687a2438132SYegappan Lakshmanan }
1688a2438132SYegappan Lakshmanan
1689a2438132SYegappan Lakshmanan #endif
16908ee52affSYegappan Lakshmanan
16918ee52affSYegappan Lakshmanan #if defined(FEAT_EVAL)
16928ee52affSYegappan Lakshmanan static char *e_printf = N_("E766: Insufficient arguments for printf()");
16938ee52affSYegappan Lakshmanan
16948ee52affSYegappan Lakshmanan /*
16958ee52affSYegappan Lakshmanan * Get number argument from "idxp" entry in "tvs". First entry is 1.
16968ee52affSYegappan Lakshmanan */
16978ee52affSYegappan Lakshmanan static varnumber_T
tv_nr(typval_T * tvs,int * idxp)16988ee52affSYegappan Lakshmanan tv_nr(typval_T *tvs, int *idxp)
16998ee52affSYegappan Lakshmanan {
17008ee52affSYegappan Lakshmanan int idx = *idxp - 1;
17018ee52affSYegappan Lakshmanan varnumber_T n = 0;
17028ee52affSYegappan Lakshmanan int err = FALSE;
17038ee52affSYegappan Lakshmanan
17048ee52affSYegappan Lakshmanan if (tvs[idx].v_type == VAR_UNKNOWN)
17058ee52affSYegappan Lakshmanan emsg(_(e_printf));
17068ee52affSYegappan Lakshmanan else
17078ee52affSYegappan Lakshmanan {
17088ee52affSYegappan Lakshmanan ++*idxp;
17098ee52affSYegappan Lakshmanan n = tv_get_number_chk(&tvs[idx], &err);
17108ee52affSYegappan Lakshmanan if (err)
17118ee52affSYegappan Lakshmanan n = 0;
17128ee52affSYegappan Lakshmanan }
17138ee52affSYegappan Lakshmanan return n;
17148ee52affSYegappan Lakshmanan }
17158ee52affSYegappan Lakshmanan
17168ee52affSYegappan Lakshmanan /*
17178ee52affSYegappan Lakshmanan * Get string argument from "idxp" entry in "tvs". First entry is 1.
17188ee52affSYegappan Lakshmanan * If "tofree" is NULL tv_get_string_chk() is used. Some types (e.g. List)
17198ee52affSYegappan Lakshmanan * are not converted to a string.
17208ee52affSYegappan Lakshmanan * If "tofree" is not NULL echo_string() is used. All types are converted to
17218ee52affSYegappan Lakshmanan * a string with the same format as ":echo". The caller must free "*tofree".
17228ee52affSYegappan Lakshmanan * Returns NULL for an error.
17238ee52affSYegappan Lakshmanan */
17248ee52affSYegappan Lakshmanan static char *
tv_str(typval_T * tvs,int * idxp,char_u ** tofree)17258ee52affSYegappan Lakshmanan tv_str(typval_T *tvs, int *idxp, char_u **tofree)
17268ee52affSYegappan Lakshmanan {
17278ee52affSYegappan Lakshmanan int idx = *idxp - 1;
17288ee52affSYegappan Lakshmanan char *s = NULL;
17298ee52affSYegappan Lakshmanan static char_u numbuf[NUMBUFLEN];
17308ee52affSYegappan Lakshmanan
17318ee52affSYegappan Lakshmanan if (tvs[idx].v_type == VAR_UNKNOWN)
17328ee52affSYegappan Lakshmanan emsg(_(e_printf));
17338ee52affSYegappan Lakshmanan else
17348ee52affSYegappan Lakshmanan {
17358ee52affSYegappan Lakshmanan ++*idxp;
17368ee52affSYegappan Lakshmanan if (tofree != NULL)
17378ee52affSYegappan Lakshmanan s = (char *)echo_string(&tvs[idx], tofree, numbuf, get_copyID());
17388ee52affSYegappan Lakshmanan else
17398ee52affSYegappan Lakshmanan s = (char *)tv_get_string_chk(&tvs[idx]);
17408ee52affSYegappan Lakshmanan }
17418ee52affSYegappan Lakshmanan return s;
17428ee52affSYegappan Lakshmanan }
17438ee52affSYegappan Lakshmanan
17448ee52affSYegappan Lakshmanan # ifdef FEAT_FLOAT
17458ee52affSYegappan Lakshmanan /*
17468ee52affSYegappan Lakshmanan * Get float argument from "idxp" entry in "tvs". First entry is 1.
17478ee52affSYegappan Lakshmanan */
17488ee52affSYegappan Lakshmanan static double
tv_float(typval_T * tvs,int * idxp)17498ee52affSYegappan Lakshmanan tv_float(typval_T *tvs, int *idxp)
17508ee52affSYegappan Lakshmanan {
17518ee52affSYegappan Lakshmanan int idx = *idxp - 1;
17528ee52affSYegappan Lakshmanan double f = 0;
17538ee52affSYegappan Lakshmanan
17548ee52affSYegappan Lakshmanan if (tvs[idx].v_type == VAR_UNKNOWN)
17558ee52affSYegappan Lakshmanan emsg(_(e_printf));
17568ee52affSYegappan Lakshmanan else
17578ee52affSYegappan Lakshmanan {
17588ee52affSYegappan Lakshmanan ++*idxp;
17598ee52affSYegappan Lakshmanan if (tvs[idx].v_type == VAR_FLOAT)
17608ee52affSYegappan Lakshmanan f = tvs[idx].vval.v_float;
17618ee52affSYegappan Lakshmanan else if (tvs[idx].v_type == VAR_NUMBER)
17628ee52affSYegappan Lakshmanan f = (double)tvs[idx].vval.v_number;
17638ee52affSYegappan Lakshmanan else
17648ee52affSYegappan Lakshmanan emsg(_("E807: Expected Float argument for printf()"));
17658ee52affSYegappan Lakshmanan }
17668ee52affSYegappan Lakshmanan return f;
17678ee52affSYegappan Lakshmanan }
17688ee52affSYegappan Lakshmanan # endif
17698ee52affSYegappan Lakshmanan #endif
17708ee52affSYegappan Lakshmanan
17718ee52affSYegappan Lakshmanan #ifdef FEAT_FLOAT
17728ee52affSYegappan Lakshmanan /*
17738ee52affSYegappan Lakshmanan * Return the representation of infinity for printf() function:
17748ee52affSYegappan Lakshmanan * "-inf", "inf", "+inf", " inf", "-INF", "INF", "+INF" or " INF".
17758ee52affSYegappan Lakshmanan */
17768ee52affSYegappan Lakshmanan static const char *
infinity_str(int positive,char fmt_spec,int force_sign,int space_for_positive)17778ee52affSYegappan Lakshmanan infinity_str(int positive,
17788ee52affSYegappan Lakshmanan char fmt_spec,
17798ee52affSYegappan Lakshmanan int force_sign,
17808ee52affSYegappan Lakshmanan int space_for_positive)
17818ee52affSYegappan Lakshmanan {
17828ee52affSYegappan Lakshmanan static const char *table[] =
17838ee52affSYegappan Lakshmanan {
17848ee52affSYegappan Lakshmanan "-inf", "inf", "+inf", " inf",
17858ee52affSYegappan Lakshmanan "-INF", "INF", "+INF", " INF"
17868ee52affSYegappan Lakshmanan };
17878ee52affSYegappan Lakshmanan int idx = positive * (1 + force_sign + force_sign * space_for_positive);
17888ee52affSYegappan Lakshmanan
17898ee52affSYegappan Lakshmanan if (ASCII_ISUPPER(fmt_spec))
17908ee52affSYegappan Lakshmanan idx += 4;
17918ee52affSYegappan Lakshmanan return table[idx];
17928ee52affSYegappan Lakshmanan }
17938ee52affSYegappan Lakshmanan #endif
17948ee52affSYegappan Lakshmanan
17958ee52affSYegappan Lakshmanan /*
17968ee52affSYegappan Lakshmanan * This code was included to provide a portable vsnprintf() and snprintf().
17978ee52affSYegappan Lakshmanan * Some systems may provide their own, but we always use this one for
17988ee52affSYegappan Lakshmanan * consistency.
17998ee52affSYegappan Lakshmanan *
18008ee52affSYegappan Lakshmanan * This code is based on snprintf.c - a portable implementation of snprintf
18018ee52affSYegappan Lakshmanan * by Mark Martinec <[email protected]>, Version 2.2, 2000-10-06.
18028ee52affSYegappan Lakshmanan * Included with permission. It was heavily modified to fit in Vim.
18038ee52affSYegappan Lakshmanan * The original code, including useful comments, can be found here:
18048ee52affSYegappan Lakshmanan * http://www.ijs.si/software/snprintf/
18058ee52affSYegappan Lakshmanan *
18068ee52affSYegappan Lakshmanan * This snprintf() only supports the following conversion specifiers:
18078ee52affSYegappan Lakshmanan * s, c, d, u, o, x, X, p (and synonyms: i, D, U, O - see below)
18088ee52affSYegappan Lakshmanan * with flags: '-', '+', ' ', '0' and '#'.
18098ee52affSYegappan Lakshmanan * An asterisk is supported for field width as well as precision.
18108ee52affSYegappan Lakshmanan *
18118ee52affSYegappan Lakshmanan * Limited support for floating point was added: 'f', 'F', 'e', 'E', 'g', 'G'.
18128ee52affSYegappan Lakshmanan *
18138ee52affSYegappan Lakshmanan * Length modifiers 'h' (short int) and 'l' (long int) and 'll' (long long int)
18148ee52affSYegappan Lakshmanan * are supported. NOTE: for 'll' the argument is varnumber_T or uvarnumber_T.
18158ee52affSYegappan Lakshmanan *
18168ee52affSYegappan Lakshmanan * The locale is not used, the string is used as a byte string. This is only
18178ee52affSYegappan Lakshmanan * relevant for double-byte encodings where the second byte may be '%'.
18188ee52affSYegappan Lakshmanan *
18198ee52affSYegappan Lakshmanan * It is permitted for "str_m" to be zero, and it is permitted to specify NULL
18208ee52affSYegappan Lakshmanan * pointer for resulting string argument if "str_m" is zero (as per ISO C99).
18218ee52affSYegappan Lakshmanan *
18228ee52affSYegappan Lakshmanan * The return value is the number of characters which would be generated
18238ee52affSYegappan Lakshmanan * for the given input, excluding the trailing NUL. If this value
18248ee52affSYegappan Lakshmanan * is greater or equal to "str_m", not all characters from the result
18258ee52affSYegappan Lakshmanan * have been stored in str, output bytes beyond the ("str_m"-1) -th character
18268ee52affSYegappan Lakshmanan * are discarded. If "str_m" is greater than zero it is guaranteed
18278ee52affSYegappan Lakshmanan * the resulting string will be NUL-terminated.
18288ee52affSYegappan Lakshmanan */
18298ee52affSYegappan Lakshmanan
18308ee52affSYegappan Lakshmanan /*
18318ee52affSYegappan Lakshmanan * When va_list is not supported we only define vim_snprintf().
18328ee52affSYegappan Lakshmanan *
18338ee52affSYegappan Lakshmanan * vim_vsnprintf_typval() can be invoked with either "va_list" or a list of
18348ee52affSYegappan Lakshmanan * "typval_T". When the latter is not used it must be NULL.
18358ee52affSYegappan Lakshmanan */
18368ee52affSYegappan Lakshmanan
18378ee52affSYegappan Lakshmanan // When generating prototypes all of this is skipped, cproto doesn't
18388ee52affSYegappan Lakshmanan // understand this.
18398ee52affSYegappan Lakshmanan #ifndef PROTO
18408ee52affSYegappan Lakshmanan
18418ee52affSYegappan Lakshmanan // Like vim_vsnprintf() but append to the string.
18428ee52affSYegappan Lakshmanan int
vim_snprintf_add(char * str,size_t str_m,const char * fmt,...)18438ee52affSYegappan Lakshmanan vim_snprintf_add(char *str, size_t str_m, const char *fmt, ...)
18448ee52affSYegappan Lakshmanan {
18458ee52affSYegappan Lakshmanan va_list ap;
18468ee52affSYegappan Lakshmanan int str_l;
18478ee52affSYegappan Lakshmanan size_t len = STRLEN(str);
18488ee52affSYegappan Lakshmanan size_t space;
18498ee52affSYegappan Lakshmanan
18508ee52affSYegappan Lakshmanan if (str_m <= len)
18518ee52affSYegappan Lakshmanan space = 0;
18528ee52affSYegappan Lakshmanan else
18538ee52affSYegappan Lakshmanan space = str_m - len;
18548ee52affSYegappan Lakshmanan va_start(ap, fmt);
18558ee52affSYegappan Lakshmanan str_l = vim_vsnprintf(str + len, space, fmt, ap);
18568ee52affSYegappan Lakshmanan va_end(ap);
18578ee52affSYegappan Lakshmanan return str_l;
18588ee52affSYegappan Lakshmanan }
18598ee52affSYegappan Lakshmanan
18608ee52affSYegappan Lakshmanan int
vim_snprintf(char * str,size_t str_m,const char * fmt,...)18618ee52affSYegappan Lakshmanan vim_snprintf(char *str, size_t str_m, const char *fmt, ...)
18628ee52affSYegappan Lakshmanan {
18638ee52affSYegappan Lakshmanan va_list ap;
18648ee52affSYegappan Lakshmanan int str_l;
18658ee52affSYegappan Lakshmanan
18668ee52affSYegappan Lakshmanan va_start(ap, fmt);
18678ee52affSYegappan Lakshmanan str_l = vim_vsnprintf(str, str_m, fmt, ap);
18688ee52affSYegappan Lakshmanan va_end(ap);
18698ee52affSYegappan Lakshmanan return str_l;
18708ee52affSYegappan Lakshmanan }
18718ee52affSYegappan Lakshmanan
18728ee52affSYegappan Lakshmanan int
vim_vsnprintf(char * str,size_t str_m,const char * fmt,va_list ap)18738ee52affSYegappan Lakshmanan vim_vsnprintf(
18748ee52affSYegappan Lakshmanan char *str,
18758ee52affSYegappan Lakshmanan size_t str_m,
18768ee52affSYegappan Lakshmanan const char *fmt,
18778ee52affSYegappan Lakshmanan va_list ap)
18788ee52affSYegappan Lakshmanan {
18798ee52affSYegappan Lakshmanan return vim_vsnprintf_typval(str, str_m, fmt, ap, NULL);
18808ee52affSYegappan Lakshmanan }
18818ee52affSYegappan Lakshmanan
18828ee52affSYegappan Lakshmanan int
vim_vsnprintf_typval(char * str,size_t str_m,const char * fmt,va_list ap,typval_T * tvs)18838ee52affSYegappan Lakshmanan vim_vsnprintf_typval(
18848ee52affSYegappan Lakshmanan char *str,
18858ee52affSYegappan Lakshmanan size_t str_m,
18868ee52affSYegappan Lakshmanan const char *fmt,
18878ee52affSYegappan Lakshmanan va_list ap,
18888ee52affSYegappan Lakshmanan typval_T *tvs)
18898ee52affSYegappan Lakshmanan {
18908ee52affSYegappan Lakshmanan size_t str_l = 0;
18918ee52affSYegappan Lakshmanan const char *p = fmt;
18928ee52affSYegappan Lakshmanan int arg_idx = 1;
18938ee52affSYegappan Lakshmanan
18948ee52affSYegappan Lakshmanan if (p == NULL)
18958ee52affSYegappan Lakshmanan p = "";
18968ee52affSYegappan Lakshmanan while (*p != NUL)
18978ee52affSYegappan Lakshmanan {
18988ee52affSYegappan Lakshmanan if (*p != '%')
18998ee52affSYegappan Lakshmanan {
19008ee52affSYegappan Lakshmanan char *q = strchr(p + 1, '%');
19018ee52affSYegappan Lakshmanan size_t n = (q == NULL) ? STRLEN(p) : (size_t)(q - p);
19028ee52affSYegappan Lakshmanan
19038ee52affSYegappan Lakshmanan // Copy up to the next '%' or NUL without any changes.
19048ee52affSYegappan Lakshmanan if (str_l < str_m)
19058ee52affSYegappan Lakshmanan {
19068ee52affSYegappan Lakshmanan size_t avail = str_m - str_l;
19078ee52affSYegappan Lakshmanan
19088ee52affSYegappan Lakshmanan mch_memmove(str + str_l, p, n > avail ? avail : n);
19098ee52affSYegappan Lakshmanan }
19108ee52affSYegappan Lakshmanan p += n;
19118ee52affSYegappan Lakshmanan str_l += n;
19128ee52affSYegappan Lakshmanan }
19138ee52affSYegappan Lakshmanan else
19148ee52affSYegappan Lakshmanan {
19158ee52affSYegappan Lakshmanan size_t min_field_width = 0, precision = 0;
19168ee52affSYegappan Lakshmanan int zero_padding = 0, precision_specified = 0, justify_left = 0;
19178ee52affSYegappan Lakshmanan int alternate_form = 0, force_sign = 0;
19188ee52affSYegappan Lakshmanan
19198ee52affSYegappan Lakshmanan // If both the ' ' and '+' flags appear, the ' ' flag should be
19208ee52affSYegappan Lakshmanan // ignored.
19218ee52affSYegappan Lakshmanan int space_for_positive = 1;
19228ee52affSYegappan Lakshmanan
19238ee52affSYegappan Lakshmanan // allowed values: \0, h, l, L
19248ee52affSYegappan Lakshmanan char length_modifier = '\0';
19258ee52affSYegappan Lakshmanan
19268ee52affSYegappan Lakshmanan // temporary buffer for simple numeric->string conversion
19278ee52affSYegappan Lakshmanan # if defined(FEAT_FLOAT)
19288ee52affSYegappan Lakshmanan # define TMP_LEN 350 // On my system 1e308 is the biggest number possible.
19298ee52affSYegappan Lakshmanan // That sounds reasonable to use as the maximum
19308ee52affSYegappan Lakshmanan // printable.
19318ee52affSYegappan Lakshmanan # else
19328ee52affSYegappan Lakshmanan # define TMP_LEN 66
19338ee52affSYegappan Lakshmanan # endif
19348ee52affSYegappan Lakshmanan char tmp[TMP_LEN];
19358ee52affSYegappan Lakshmanan
19368ee52affSYegappan Lakshmanan // string address in case of string argument
19378ee52affSYegappan Lakshmanan const char *str_arg = NULL;
19388ee52affSYegappan Lakshmanan
19398ee52affSYegappan Lakshmanan // natural field width of arg without padding and sign
19408ee52affSYegappan Lakshmanan size_t str_arg_l;
19418ee52affSYegappan Lakshmanan
19428ee52affSYegappan Lakshmanan // unsigned char argument value - only defined for c conversion.
19438ee52affSYegappan Lakshmanan // N.B. standard explicitly states the char argument for the c
19448ee52affSYegappan Lakshmanan // conversion is unsigned
19458ee52affSYegappan Lakshmanan unsigned char uchar_arg;
19468ee52affSYegappan Lakshmanan
19478ee52affSYegappan Lakshmanan // number of zeros to be inserted for numeric conversions as
19488ee52affSYegappan Lakshmanan // required by the precision or minimal field width
19498ee52affSYegappan Lakshmanan size_t number_of_zeros_to_pad = 0;
19508ee52affSYegappan Lakshmanan
19518ee52affSYegappan Lakshmanan // index into tmp where zero padding is to be inserted
19528ee52affSYegappan Lakshmanan size_t zero_padding_insertion_ind = 0;
19538ee52affSYegappan Lakshmanan
19548ee52affSYegappan Lakshmanan // current conversion specifier character
19558ee52affSYegappan Lakshmanan char fmt_spec = '\0';
19568ee52affSYegappan Lakshmanan
19578ee52affSYegappan Lakshmanan // buffer for 's' and 'S' specs
19588ee52affSYegappan Lakshmanan char_u *tofree = NULL;
19598ee52affSYegappan Lakshmanan
19608ee52affSYegappan Lakshmanan
19618ee52affSYegappan Lakshmanan p++; // skip '%'
19628ee52affSYegappan Lakshmanan
19638ee52affSYegappan Lakshmanan // parse flags
19648ee52affSYegappan Lakshmanan while (*p == '0' || *p == '-' || *p == '+' || *p == ' '
19658ee52affSYegappan Lakshmanan || *p == '#' || *p == '\'')
19668ee52affSYegappan Lakshmanan {
19678ee52affSYegappan Lakshmanan switch (*p)
19688ee52affSYegappan Lakshmanan {
19698ee52affSYegappan Lakshmanan case '0': zero_padding = 1; break;
19708ee52affSYegappan Lakshmanan case '-': justify_left = 1; break;
19718ee52affSYegappan Lakshmanan case '+': force_sign = 1; space_for_positive = 0; break;
19728ee52affSYegappan Lakshmanan case ' ': force_sign = 1;
19738ee52affSYegappan Lakshmanan // If both the ' ' and '+' flags appear, the ' '
19748ee52affSYegappan Lakshmanan // flag should be ignored
19758ee52affSYegappan Lakshmanan break;
19768ee52affSYegappan Lakshmanan case '#': alternate_form = 1; break;
19778ee52affSYegappan Lakshmanan case '\'': break;
19788ee52affSYegappan Lakshmanan }
19798ee52affSYegappan Lakshmanan p++;
19808ee52affSYegappan Lakshmanan }
19818ee52affSYegappan Lakshmanan // If the '0' and '-' flags both appear, the '0' flag should be
19828ee52affSYegappan Lakshmanan // ignored.
19838ee52affSYegappan Lakshmanan
19848ee52affSYegappan Lakshmanan // parse field width
19858ee52affSYegappan Lakshmanan if (*p == '*')
19868ee52affSYegappan Lakshmanan {
19878ee52affSYegappan Lakshmanan int j;
19888ee52affSYegappan Lakshmanan
19898ee52affSYegappan Lakshmanan p++;
19908ee52affSYegappan Lakshmanan j =
19918ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
19928ee52affSYegappan Lakshmanan tvs != NULL ? tv_nr(tvs, &arg_idx) :
19938ee52affSYegappan Lakshmanan # endif
19948ee52affSYegappan Lakshmanan va_arg(ap, int);
19958ee52affSYegappan Lakshmanan if (j >= 0)
19968ee52affSYegappan Lakshmanan min_field_width = j;
19978ee52affSYegappan Lakshmanan else
19988ee52affSYegappan Lakshmanan {
19998ee52affSYegappan Lakshmanan min_field_width = -j;
20008ee52affSYegappan Lakshmanan justify_left = 1;
20018ee52affSYegappan Lakshmanan }
20028ee52affSYegappan Lakshmanan }
20038ee52affSYegappan Lakshmanan else if (VIM_ISDIGIT((int)(*p)))
20048ee52affSYegappan Lakshmanan {
20058ee52affSYegappan Lakshmanan // size_t could be wider than unsigned int; make sure we treat
20068ee52affSYegappan Lakshmanan // argument like common implementations do
20078ee52affSYegappan Lakshmanan unsigned int uj = *p++ - '0';
20088ee52affSYegappan Lakshmanan
20098ee52affSYegappan Lakshmanan while (VIM_ISDIGIT((int)(*p)))
20108ee52affSYegappan Lakshmanan uj = 10 * uj + (unsigned int)(*p++ - '0');
20118ee52affSYegappan Lakshmanan min_field_width = uj;
20128ee52affSYegappan Lakshmanan }
20138ee52affSYegappan Lakshmanan
20148ee52affSYegappan Lakshmanan // parse precision
20158ee52affSYegappan Lakshmanan if (*p == '.')
20168ee52affSYegappan Lakshmanan {
20178ee52affSYegappan Lakshmanan p++;
20188ee52affSYegappan Lakshmanan precision_specified = 1;
20198ee52affSYegappan Lakshmanan if (*p == '*')
20208ee52affSYegappan Lakshmanan {
20218ee52affSYegappan Lakshmanan int j;
20228ee52affSYegappan Lakshmanan
20238ee52affSYegappan Lakshmanan j =
20248ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
20258ee52affSYegappan Lakshmanan tvs != NULL ? tv_nr(tvs, &arg_idx) :
20268ee52affSYegappan Lakshmanan # endif
20278ee52affSYegappan Lakshmanan va_arg(ap, int);
20288ee52affSYegappan Lakshmanan p++;
20298ee52affSYegappan Lakshmanan if (j >= 0)
20308ee52affSYegappan Lakshmanan precision = j;
20318ee52affSYegappan Lakshmanan else
20328ee52affSYegappan Lakshmanan {
20338ee52affSYegappan Lakshmanan precision_specified = 0;
20348ee52affSYegappan Lakshmanan precision = 0;
20358ee52affSYegappan Lakshmanan }
20368ee52affSYegappan Lakshmanan }
20378ee52affSYegappan Lakshmanan else if (VIM_ISDIGIT((int)(*p)))
20388ee52affSYegappan Lakshmanan {
20398ee52affSYegappan Lakshmanan // size_t could be wider than unsigned int; make sure we
20408ee52affSYegappan Lakshmanan // treat argument like common implementations do
20418ee52affSYegappan Lakshmanan unsigned int uj = *p++ - '0';
20428ee52affSYegappan Lakshmanan
20438ee52affSYegappan Lakshmanan while (VIM_ISDIGIT((int)(*p)))
20448ee52affSYegappan Lakshmanan uj = 10 * uj + (unsigned int)(*p++ - '0');
20458ee52affSYegappan Lakshmanan precision = uj;
20468ee52affSYegappan Lakshmanan }
20478ee52affSYegappan Lakshmanan }
20488ee52affSYegappan Lakshmanan
20498ee52affSYegappan Lakshmanan // parse 'h', 'l' and 'll' length modifiers
20508ee52affSYegappan Lakshmanan if (*p == 'h' || *p == 'l')
20518ee52affSYegappan Lakshmanan {
20528ee52affSYegappan Lakshmanan length_modifier = *p;
20538ee52affSYegappan Lakshmanan p++;
20548ee52affSYegappan Lakshmanan if (length_modifier == 'l' && *p == 'l')
20558ee52affSYegappan Lakshmanan {
20568ee52affSYegappan Lakshmanan // double l = __int64 / varnumber_T
20578ee52affSYegappan Lakshmanan length_modifier = 'L';
20588ee52affSYegappan Lakshmanan p++;
20598ee52affSYegappan Lakshmanan }
20608ee52affSYegappan Lakshmanan }
20618ee52affSYegappan Lakshmanan fmt_spec = *p;
20628ee52affSYegappan Lakshmanan
20638ee52affSYegappan Lakshmanan // common synonyms:
20648ee52affSYegappan Lakshmanan switch (fmt_spec)
20658ee52affSYegappan Lakshmanan {
20668ee52affSYegappan Lakshmanan case 'i': fmt_spec = 'd'; break;
20678ee52affSYegappan Lakshmanan case 'D': fmt_spec = 'd'; length_modifier = 'l'; break;
20688ee52affSYegappan Lakshmanan case 'U': fmt_spec = 'u'; length_modifier = 'l'; break;
20698ee52affSYegappan Lakshmanan case 'O': fmt_spec = 'o'; length_modifier = 'l'; break;
20708ee52affSYegappan Lakshmanan default: break;
20718ee52affSYegappan Lakshmanan }
20728ee52affSYegappan Lakshmanan
20738ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
20748ee52affSYegappan Lakshmanan switch (fmt_spec)
20758ee52affSYegappan Lakshmanan {
20768ee52affSYegappan Lakshmanan case 'd': case 'u': case 'o': case 'x': case 'X':
20778ee52affSYegappan Lakshmanan if (tvs != NULL && length_modifier == '\0')
20788ee52affSYegappan Lakshmanan length_modifier = 'L';
20798ee52affSYegappan Lakshmanan }
20808ee52affSYegappan Lakshmanan # endif
20818ee52affSYegappan Lakshmanan
20828ee52affSYegappan Lakshmanan // get parameter value, do initial processing
20838ee52affSYegappan Lakshmanan switch (fmt_spec)
20848ee52affSYegappan Lakshmanan {
20858ee52affSYegappan Lakshmanan // '%' and 'c' behave similar to 's' regarding flags and field
20868ee52affSYegappan Lakshmanan // widths
20878ee52affSYegappan Lakshmanan case '%':
20888ee52affSYegappan Lakshmanan case 'c':
20898ee52affSYegappan Lakshmanan case 's':
20908ee52affSYegappan Lakshmanan case 'S':
20918ee52affSYegappan Lakshmanan str_arg_l = 1;
20928ee52affSYegappan Lakshmanan switch (fmt_spec)
20938ee52affSYegappan Lakshmanan {
20948ee52affSYegappan Lakshmanan case '%':
20958ee52affSYegappan Lakshmanan str_arg = p;
20968ee52affSYegappan Lakshmanan break;
20978ee52affSYegappan Lakshmanan
20988ee52affSYegappan Lakshmanan case 'c':
20998ee52affSYegappan Lakshmanan {
21008ee52affSYegappan Lakshmanan int j;
21018ee52affSYegappan Lakshmanan
21028ee52affSYegappan Lakshmanan j =
21038ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
21048ee52affSYegappan Lakshmanan tvs != NULL ? tv_nr(tvs, &arg_idx) :
21058ee52affSYegappan Lakshmanan # endif
21068ee52affSYegappan Lakshmanan va_arg(ap, int);
21078ee52affSYegappan Lakshmanan // standard demands unsigned char
21088ee52affSYegappan Lakshmanan uchar_arg = (unsigned char)j;
21098ee52affSYegappan Lakshmanan str_arg = (char *)&uchar_arg;
21108ee52affSYegappan Lakshmanan break;
21118ee52affSYegappan Lakshmanan }
21128ee52affSYegappan Lakshmanan
21138ee52affSYegappan Lakshmanan case 's':
21148ee52affSYegappan Lakshmanan case 'S':
21158ee52affSYegappan Lakshmanan str_arg =
21168ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
21178ee52affSYegappan Lakshmanan tvs != NULL ? tv_str(tvs, &arg_idx, &tofree) :
21188ee52affSYegappan Lakshmanan # endif
21198ee52affSYegappan Lakshmanan va_arg(ap, char *);
21208ee52affSYegappan Lakshmanan if (str_arg == NULL)
21218ee52affSYegappan Lakshmanan {
21228ee52affSYegappan Lakshmanan str_arg = "[NULL]";
21238ee52affSYegappan Lakshmanan str_arg_l = 6;
21248ee52affSYegappan Lakshmanan }
21258ee52affSYegappan Lakshmanan // make sure not to address string beyond the specified
21268ee52affSYegappan Lakshmanan // precision !!!
21278ee52affSYegappan Lakshmanan else if (!precision_specified)
21288ee52affSYegappan Lakshmanan str_arg_l = strlen(str_arg);
21298ee52affSYegappan Lakshmanan // truncate string if necessary as requested by precision
21308ee52affSYegappan Lakshmanan else if (precision == 0)
21318ee52affSYegappan Lakshmanan str_arg_l = 0;
21328ee52affSYegappan Lakshmanan else
21338ee52affSYegappan Lakshmanan {
21348ee52affSYegappan Lakshmanan // Don't put the #if inside memchr(), it can be a
21358ee52affSYegappan Lakshmanan // macro.
21368ee52affSYegappan Lakshmanan // memchr on HP does not like n > 2^31 !!!
21378ee52affSYegappan Lakshmanan char *q = memchr(str_arg, '\0',
21388ee52affSYegappan Lakshmanan precision <= (size_t)0x7fffffffL ? precision
21398ee52affSYegappan Lakshmanan : (size_t)0x7fffffffL);
2140*d85fccdfSpresuku
21418ee52affSYegappan Lakshmanan str_arg_l = (q == NULL) ? precision
21428ee52affSYegappan Lakshmanan : (size_t)(q - str_arg);
21438ee52affSYegappan Lakshmanan }
21448ee52affSYegappan Lakshmanan if (fmt_spec == 'S')
21458ee52affSYegappan Lakshmanan {
2146*d85fccdfSpresuku size_t base_width = min_field_width;
2147*d85fccdfSpresuku size_t pad_cell = 0;
2148*d85fccdfSpresuku
21498ee52affSYegappan Lakshmanan if (precision)
21508ee52affSYegappan Lakshmanan {
21518ee52affSYegappan Lakshmanan char_u *p1;
21528ee52affSYegappan Lakshmanan size_t i = 0;
21538ee52affSYegappan Lakshmanan
21548ee52affSYegappan Lakshmanan for (p1 = (char_u *)str_arg; *p1;
21558ee52affSYegappan Lakshmanan p1 += mb_ptr2len(p1))
21568ee52affSYegappan Lakshmanan {
21578ee52affSYegappan Lakshmanan i += (size_t)mb_ptr2cells(p1);
21588ee52affSYegappan Lakshmanan if (i > precision)
21598ee52affSYegappan Lakshmanan break;
21608ee52affSYegappan Lakshmanan }
2161*d85fccdfSpresuku pad_cell = min_field_width - precision;
2162*d85fccdfSpresuku base_width = str_arg_l = precision =
2163*d85fccdfSpresuku p1 - (char_u *)str_arg;
21648ee52affSYegappan Lakshmanan }
2165*d85fccdfSpresuku if (min_field_width != 0)
2166*d85fccdfSpresuku min_field_width = base_width + pad_cell;
21678ee52affSYegappan Lakshmanan }
21688ee52affSYegappan Lakshmanan break;
21698ee52affSYegappan Lakshmanan
21708ee52affSYegappan Lakshmanan default:
21718ee52affSYegappan Lakshmanan break;
21728ee52affSYegappan Lakshmanan }
21738ee52affSYegappan Lakshmanan break;
21748ee52affSYegappan Lakshmanan
21758ee52affSYegappan Lakshmanan case 'd': case 'u':
21768ee52affSYegappan Lakshmanan case 'b': case 'B':
21778ee52affSYegappan Lakshmanan case 'o':
21788ee52affSYegappan Lakshmanan case 'x': case 'X':
21798ee52affSYegappan Lakshmanan case 'p':
21808ee52affSYegappan Lakshmanan {
21818ee52affSYegappan Lakshmanan // NOTE: the u, b, o, x, X and p conversion specifiers
21828ee52affSYegappan Lakshmanan // imply the value is unsigned; d implies a signed
21838ee52affSYegappan Lakshmanan // value
21848ee52affSYegappan Lakshmanan
21858ee52affSYegappan Lakshmanan // 0 if numeric argument is zero (or if pointer is
21868ee52affSYegappan Lakshmanan // NULL for 'p'), +1 if greater than zero (or nonzero
21878ee52affSYegappan Lakshmanan // for unsigned arguments), -1 if negative (unsigned
21888ee52affSYegappan Lakshmanan // argument is never negative)
21898ee52affSYegappan Lakshmanan int arg_sign = 0;
21908ee52affSYegappan Lakshmanan
21918ee52affSYegappan Lakshmanan // only set for length modifier h, or for no length
21928ee52affSYegappan Lakshmanan // modifiers
21938ee52affSYegappan Lakshmanan int int_arg = 0;
21948ee52affSYegappan Lakshmanan unsigned int uint_arg = 0;
21958ee52affSYegappan Lakshmanan
21968ee52affSYegappan Lakshmanan // only set for length modifier l
21978ee52affSYegappan Lakshmanan long int long_arg = 0;
21988ee52affSYegappan Lakshmanan unsigned long int ulong_arg = 0;
21998ee52affSYegappan Lakshmanan
22008ee52affSYegappan Lakshmanan // only set for length modifier ll
22018ee52affSYegappan Lakshmanan varnumber_T llong_arg = 0;
22028ee52affSYegappan Lakshmanan uvarnumber_T ullong_arg = 0;
22038ee52affSYegappan Lakshmanan
22048ee52affSYegappan Lakshmanan // only set for b conversion
22058ee52affSYegappan Lakshmanan uvarnumber_T bin_arg = 0;
22068ee52affSYegappan Lakshmanan
22078ee52affSYegappan Lakshmanan // pointer argument value -only defined for p
22088ee52affSYegappan Lakshmanan // conversion
22098ee52affSYegappan Lakshmanan void *ptr_arg = NULL;
22108ee52affSYegappan Lakshmanan
22118ee52affSYegappan Lakshmanan if (fmt_spec == 'p')
22128ee52affSYegappan Lakshmanan {
22138ee52affSYegappan Lakshmanan length_modifier = '\0';
22148ee52affSYegappan Lakshmanan ptr_arg =
22158ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
22168ee52affSYegappan Lakshmanan tvs != NULL ? (void *)tv_str(tvs, &arg_idx,
22178ee52affSYegappan Lakshmanan NULL) :
22188ee52affSYegappan Lakshmanan # endif
22198ee52affSYegappan Lakshmanan va_arg(ap, void *);
22208ee52affSYegappan Lakshmanan if (ptr_arg != NULL)
22218ee52affSYegappan Lakshmanan arg_sign = 1;
22228ee52affSYegappan Lakshmanan }
22238ee52affSYegappan Lakshmanan else if (fmt_spec == 'b' || fmt_spec == 'B')
22248ee52affSYegappan Lakshmanan {
22258ee52affSYegappan Lakshmanan bin_arg =
22268ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
22278ee52affSYegappan Lakshmanan tvs != NULL ?
22288ee52affSYegappan Lakshmanan (uvarnumber_T)tv_nr(tvs, &arg_idx) :
22298ee52affSYegappan Lakshmanan # endif
22308ee52affSYegappan Lakshmanan va_arg(ap, uvarnumber_T);
22318ee52affSYegappan Lakshmanan if (bin_arg != 0)
22328ee52affSYegappan Lakshmanan arg_sign = 1;
22338ee52affSYegappan Lakshmanan }
22348ee52affSYegappan Lakshmanan else if (fmt_spec == 'd')
22358ee52affSYegappan Lakshmanan {
22368ee52affSYegappan Lakshmanan // signed
22378ee52affSYegappan Lakshmanan switch (length_modifier)
22388ee52affSYegappan Lakshmanan {
22398ee52affSYegappan Lakshmanan case '\0':
22408ee52affSYegappan Lakshmanan case 'h':
22418ee52affSYegappan Lakshmanan // char and short arguments are passed as int.
22428ee52affSYegappan Lakshmanan int_arg =
22438ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
22448ee52affSYegappan Lakshmanan tvs != NULL ? tv_nr(tvs, &arg_idx) :
22458ee52affSYegappan Lakshmanan # endif
22468ee52affSYegappan Lakshmanan va_arg(ap, int);
22478ee52affSYegappan Lakshmanan if (int_arg > 0)
22488ee52affSYegappan Lakshmanan arg_sign = 1;
22498ee52affSYegappan Lakshmanan else if (int_arg < 0)
22508ee52affSYegappan Lakshmanan arg_sign = -1;
22518ee52affSYegappan Lakshmanan break;
22528ee52affSYegappan Lakshmanan case 'l':
22538ee52affSYegappan Lakshmanan long_arg =
22548ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
22558ee52affSYegappan Lakshmanan tvs != NULL ? tv_nr(tvs, &arg_idx) :
22568ee52affSYegappan Lakshmanan # endif
22578ee52affSYegappan Lakshmanan va_arg(ap, long int);
22588ee52affSYegappan Lakshmanan if (long_arg > 0)
22598ee52affSYegappan Lakshmanan arg_sign = 1;
22608ee52affSYegappan Lakshmanan else if (long_arg < 0)
22618ee52affSYegappan Lakshmanan arg_sign = -1;
22628ee52affSYegappan Lakshmanan break;
22638ee52affSYegappan Lakshmanan case 'L':
22648ee52affSYegappan Lakshmanan llong_arg =
22658ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
22668ee52affSYegappan Lakshmanan tvs != NULL ? tv_nr(tvs, &arg_idx) :
22678ee52affSYegappan Lakshmanan # endif
22688ee52affSYegappan Lakshmanan va_arg(ap, varnumber_T);
22698ee52affSYegappan Lakshmanan if (llong_arg > 0)
22708ee52affSYegappan Lakshmanan arg_sign = 1;
22718ee52affSYegappan Lakshmanan else if (llong_arg < 0)
22728ee52affSYegappan Lakshmanan arg_sign = -1;
22738ee52affSYegappan Lakshmanan break;
22748ee52affSYegappan Lakshmanan }
22758ee52affSYegappan Lakshmanan }
22768ee52affSYegappan Lakshmanan else
22778ee52affSYegappan Lakshmanan {
22788ee52affSYegappan Lakshmanan // unsigned
22798ee52affSYegappan Lakshmanan switch (length_modifier)
22808ee52affSYegappan Lakshmanan {
22818ee52affSYegappan Lakshmanan case '\0':
22828ee52affSYegappan Lakshmanan case 'h':
22838ee52affSYegappan Lakshmanan uint_arg =
22848ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
22858ee52affSYegappan Lakshmanan tvs != NULL ? (unsigned)
22868ee52affSYegappan Lakshmanan tv_nr(tvs, &arg_idx) :
22878ee52affSYegappan Lakshmanan # endif
22888ee52affSYegappan Lakshmanan va_arg(ap, unsigned int);
22898ee52affSYegappan Lakshmanan if (uint_arg != 0)
22908ee52affSYegappan Lakshmanan arg_sign = 1;
22918ee52affSYegappan Lakshmanan break;
22928ee52affSYegappan Lakshmanan case 'l':
22938ee52affSYegappan Lakshmanan ulong_arg =
22948ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
22958ee52affSYegappan Lakshmanan tvs != NULL ? (unsigned long)
22968ee52affSYegappan Lakshmanan tv_nr(tvs, &arg_idx) :
22978ee52affSYegappan Lakshmanan # endif
22988ee52affSYegappan Lakshmanan va_arg(ap, unsigned long int);
22998ee52affSYegappan Lakshmanan if (ulong_arg != 0)
23008ee52affSYegappan Lakshmanan arg_sign = 1;
23018ee52affSYegappan Lakshmanan break;
23028ee52affSYegappan Lakshmanan case 'L':
23038ee52affSYegappan Lakshmanan ullong_arg =
23048ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
23058ee52affSYegappan Lakshmanan tvs != NULL ? (uvarnumber_T)
23068ee52affSYegappan Lakshmanan tv_nr(tvs, &arg_idx) :
23078ee52affSYegappan Lakshmanan # endif
23088ee52affSYegappan Lakshmanan va_arg(ap, uvarnumber_T);
23098ee52affSYegappan Lakshmanan if (ullong_arg != 0)
23108ee52affSYegappan Lakshmanan arg_sign = 1;
23118ee52affSYegappan Lakshmanan break;
23128ee52affSYegappan Lakshmanan }
23138ee52affSYegappan Lakshmanan }
23148ee52affSYegappan Lakshmanan
23158ee52affSYegappan Lakshmanan str_arg = tmp;
23168ee52affSYegappan Lakshmanan str_arg_l = 0;
23178ee52affSYegappan Lakshmanan
23188ee52affSYegappan Lakshmanan // NOTE:
23198ee52affSYegappan Lakshmanan // For d, i, u, o, x, and X conversions, if precision is
23208ee52affSYegappan Lakshmanan // specified, the '0' flag should be ignored. This is so
23218ee52affSYegappan Lakshmanan // with Solaris 2.6, Digital UNIX 4.0, HPUX 10, Linux,
23228ee52affSYegappan Lakshmanan // FreeBSD, NetBSD; but not with Perl.
23238ee52affSYegappan Lakshmanan if (precision_specified)
23248ee52affSYegappan Lakshmanan zero_padding = 0;
23258ee52affSYegappan Lakshmanan if (fmt_spec == 'd')
23268ee52affSYegappan Lakshmanan {
23278ee52affSYegappan Lakshmanan if (force_sign && arg_sign >= 0)
23288ee52affSYegappan Lakshmanan tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
23298ee52affSYegappan Lakshmanan // leave negative numbers for sprintf to handle, to
23308ee52affSYegappan Lakshmanan // avoid handling tricky cases like (short int)-32768
23318ee52affSYegappan Lakshmanan }
23328ee52affSYegappan Lakshmanan else if (alternate_form)
23338ee52affSYegappan Lakshmanan {
23348ee52affSYegappan Lakshmanan if (arg_sign != 0
23358ee52affSYegappan Lakshmanan && (fmt_spec == 'b' || fmt_spec == 'B'
23368ee52affSYegappan Lakshmanan || fmt_spec == 'x' || fmt_spec == 'X') )
23378ee52affSYegappan Lakshmanan {
23388ee52affSYegappan Lakshmanan tmp[str_arg_l++] = '0';
23398ee52affSYegappan Lakshmanan tmp[str_arg_l++] = fmt_spec;
23408ee52affSYegappan Lakshmanan }
23418ee52affSYegappan Lakshmanan // alternate form should have no effect for p
23428ee52affSYegappan Lakshmanan // conversion, but ...
23438ee52affSYegappan Lakshmanan }
23448ee52affSYegappan Lakshmanan
23458ee52affSYegappan Lakshmanan zero_padding_insertion_ind = str_arg_l;
23468ee52affSYegappan Lakshmanan if (!precision_specified)
23478ee52affSYegappan Lakshmanan precision = 1; // default precision is 1
23488ee52affSYegappan Lakshmanan if (precision == 0 && arg_sign == 0)
23498ee52affSYegappan Lakshmanan {
23508ee52affSYegappan Lakshmanan // When zero value is formatted with an explicit
23518ee52affSYegappan Lakshmanan // precision 0, the resulting formatted string is
23528ee52affSYegappan Lakshmanan // empty (d, i, u, b, B, o, x, X, p).
23538ee52affSYegappan Lakshmanan }
23548ee52affSYegappan Lakshmanan else
23558ee52affSYegappan Lakshmanan {
23568ee52affSYegappan Lakshmanan char f[6];
23578ee52affSYegappan Lakshmanan int f_l = 0;
23588ee52affSYegappan Lakshmanan
23598ee52affSYegappan Lakshmanan // construct a simple format string for sprintf
23608ee52affSYegappan Lakshmanan f[f_l++] = '%';
23618ee52affSYegappan Lakshmanan if (!length_modifier)
23628ee52affSYegappan Lakshmanan ;
23638ee52affSYegappan Lakshmanan else if (length_modifier == 'L')
23648ee52affSYegappan Lakshmanan {
23658ee52affSYegappan Lakshmanan # ifdef MSWIN
23668ee52affSYegappan Lakshmanan f[f_l++] = 'I';
23678ee52affSYegappan Lakshmanan f[f_l++] = '6';
23688ee52affSYegappan Lakshmanan f[f_l++] = '4';
23698ee52affSYegappan Lakshmanan # else
23708ee52affSYegappan Lakshmanan f[f_l++] = 'l';
23718ee52affSYegappan Lakshmanan f[f_l++] = 'l';
23728ee52affSYegappan Lakshmanan # endif
23738ee52affSYegappan Lakshmanan }
23748ee52affSYegappan Lakshmanan else
23758ee52affSYegappan Lakshmanan f[f_l++] = length_modifier;
23768ee52affSYegappan Lakshmanan f[f_l++] = fmt_spec;
23778ee52affSYegappan Lakshmanan f[f_l++] = '\0';
23788ee52affSYegappan Lakshmanan
23798ee52affSYegappan Lakshmanan if (fmt_spec == 'p')
23808ee52affSYegappan Lakshmanan str_arg_l += sprintf(tmp + str_arg_l, f, ptr_arg);
23818ee52affSYegappan Lakshmanan else if (fmt_spec == 'b' || fmt_spec == 'B')
23828ee52affSYegappan Lakshmanan {
23838ee52affSYegappan Lakshmanan char b[8 * sizeof(uvarnumber_T)];
23848ee52affSYegappan Lakshmanan size_t b_l = 0;
23858ee52affSYegappan Lakshmanan uvarnumber_T bn = bin_arg;
23868ee52affSYegappan Lakshmanan
23878ee52affSYegappan Lakshmanan do
23888ee52affSYegappan Lakshmanan {
23898ee52affSYegappan Lakshmanan b[sizeof(b) - ++b_l] = '0' + (bn & 0x1);
23908ee52affSYegappan Lakshmanan bn >>= 1;
23918ee52affSYegappan Lakshmanan }
23928ee52affSYegappan Lakshmanan while (bn != 0);
23938ee52affSYegappan Lakshmanan
23948ee52affSYegappan Lakshmanan memcpy(tmp + str_arg_l, b + sizeof(b) - b_l, b_l);
23958ee52affSYegappan Lakshmanan str_arg_l += b_l;
23968ee52affSYegappan Lakshmanan }
23978ee52affSYegappan Lakshmanan else if (fmt_spec == 'd')
23988ee52affSYegappan Lakshmanan {
23998ee52affSYegappan Lakshmanan // signed
24008ee52affSYegappan Lakshmanan switch (length_modifier)
24018ee52affSYegappan Lakshmanan {
24028ee52affSYegappan Lakshmanan case '\0': str_arg_l += sprintf(
24038ee52affSYegappan Lakshmanan tmp + str_arg_l, f,
24048ee52affSYegappan Lakshmanan int_arg);
24058ee52affSYegappan Lakshmanan break;
24068ee52affSYegappan Lakshmanan case 'h': str_arg_l += sprintf(
24078ee52affSYegappan Lakshmanan tmp + str_arg_l, f,
24088ee52affSYegappan Lakshmanan (short)int_arg);
24098ee52affSYegappan Lakshmanan break;
24108ee52affSYegappan Lakshmanan case 'l': str_arg_l += sprintf(
24118ee52affSYegappan Lakshmanan tmp + str_arg_l, f, long_arg);
24128ee52affSYegappan Lakshmanan break;
24138ee52affSYegappan Lakshmanan case 'L': str_arg_l += sprintf(
24148ee52affSYegappan Lakshmanan tmp + str_arg_l, f, llong_arg);
24158ee52affSYegappan Lakshmanan break;
24168ee52affSYegappan Lakshmanan }
24178ee52affSYegappan Lakshmanan }
24188ee52affSYegappan Lakshmanan else
24198ee52affSYegappan Lakshmanan {
24208ee52affSYegappan Lakshmanan // unsigned
24218ee52affSYegappan Lakshmanan switch (length_modifier)
24228ee52affSYegappan Lakshmanan {
24238ee52affSYegappan Lakshmanan case '\0': str_arg_l += sprintf(
24248ee52affSYegappan Lakshmanan tmp + str_arg_l, f,
24258ee52affSYegappan Lakshmanan uint_arg);
24268ee52affSYegappan Lakshmanan break;
24278ee52affSYegappan Lakshmanan case 'h': str_arg_l += sprintf(
24288ee52affSYegappan Lakshmanan tmp + str_arg_l, f,
24298ee52affSYegappan Lakshmanan (unsigned short)uint_arg);
24308ee52affSYegappan Lakshmanan break;
24318ee52affSYegappan Lakshmanan case 'l': str_arg_l += sprintf(
24328ee52affSYegappan Lakshmanan tmp + str_arg_l, f, ulong_arg);
24338ee52affSYegappan Lakshmanan break;
24348ee52affSYegappan Lakshmanan case 'L': str_arg_l += sprintf(
24358ee52affSYegappan Lakshmanan tmp + str_arg_l, f, ullong_arg);
24368ee52affSYegappan Lakshmanan break;
24378ee52affSYegappan Lakshmanan }
24388ee52affSYegappan Lakshmanan }
24398ee52affSYegappan Lakshmanan
24408ee52affSYegappan Lakshmanan // include the optional minus sign and possible
24418ee52affSYegappan Lakshmanan // "0x" in the region before the zero padding
24428ee52affSYegappan Lakshmanan // insertion point
24438ee52affSYegappan Lakshmanan if (zero_padding_insertion_ind < str_arg_l
24448ee52affSYegappan Lakshmanan && tmp[zero_padding_insertion_ind] == '-')
24458ee52affSYegappan Lakshmanan zero_padding_insertion_ind++;
24468ee52affSYegappan Lakshmanan if (zero_padding_insertion_ind + 1 < str_arg_l
24478ee52affSYegappan Lakshmanan && tmp[zero_padding_insertion_ind] == '0'
24488ee52affSYegappan Lakshmanan && (tmp[zero_padding_insertion_ind + 1] == 'x'
24498ee52affSYegappan Lakshmanan || tmp[zero_padding_insertion_ind + 1] == 'X'))
24508ee52affSYegappan Lakshmanan zero_padding_insertion_ind += 2;
24518ee52affSYegappan Lakshmanan }
24528ee52affSYegappan Lakshmanan
24538ee52affSYegappan Lakshmanan {
24548ee52affSYegappan Lakshmanan size_t num_of_digits = str_arg_l
24558ee52affSYegappan Lakshmanan - zero_padding_insertion_ind;
24568ee52affSYegappan Lakshmanan
24578ee52affSYegappan Lakshmanan if (alternate_form && fmt_spec == 'o'
24588ee52affSYegappan Lakshmanan // unless zero is already the first
24598ee52affSYegappan Lakshmanan // character
24608ee52affSYegappan Lakshmanan && !(zero_padding_insertion_ind < str_arg_l
24618ee52affSYegappan Lakshmanan && tmp[zero_padding_insertion_ind] == '0'))
24628ee52affSYegappan Lakshmanan {
24638ee52affSYegappan Lakshmanan // assure leading zero for alternate-form
24648ee52affSYegappan Lakshmanan // octal numbers
24658ee52affSYegappan Lakshmanan if (!precision_specified
24668ee52affSYegappan Lakshmanan || precision < num_of_digits + 1)
24678ee52affSYegappan Lakshmanan {
24688ee52affSYegappan Lakshmanan // precision is increased to force the
24698ee52affSYegappan Lakshmanan // first character to be zero, except if a
24708ee52affSYegappan Lakshmanan // zero value is formatted with an
24718ee52affSYegappan Lakshmanan // explicit precision of zero
24728ee52affSYegappan Lakshmanan precision = num_of_digits + 1;
24738ee52affSYegappan Lakshmanan }
24748ee52affSYegappan Lakshmanan }
24758ee52affSYegappan Lakshmanan // zero padding to specified precision?
24768ee52affSYegappan Lakshmanan if (num_of_digits < precision)
24778ee52affSYegappan Lakshmanan number_of_zeros_to_pad = precision - num_of_digits;
24788ee52affSYegappan Lakshmanan }
24798ee52affSYegappan Lakshmanan // zero padding to specified minimal field width?
24808ee52affSYegappan Lakshmanan if (!justify_left && zero_padding)
24818ee52affSYegappan Lakshmanan {
24828ee52affSYegappan Lakshmanan int n = (int)(min_field_width - (str_arg_l
24838ee52affSYegappan Lakshmanan + number_of_zeros_to_pad));
24848ee52affSYegappan Lakshmanan if (n > 0)
24858ee52affSYegappan Lakshmanan number_of_zeros_to_pad += n;
24868ee52affSYegappan Lakshmanan }
24878ee52affSYegappan Lakshmanan break;
24888ee52affSYegappan Lakshmanan }
24898ee52affSYegappan Lakshmanan
24908ee52affSYegappan Lakshmanan # ifdef FEAT_FLOAT
24918ee52affSYegappan Lakshmanan case 'f':
24928ee52affSYegappan Lakshmanan case 'F':
24938ee52affSYegappan Lakshmanan case 'e':
24948ee52affSYegappan Lakshmanan case 'E':
24958ee52affSYegappan Lakshmanan case 'g':
24968ee52affSYegappan Lakshmanan case 'G':
24978ee52affSYegappan Lakshmanan {
24988ee52affSYegappan Lakshmanan // Floating point.
24998ee52affSYegappan Lakshmanan double f;
25008ee52affSYegappan Lakshmanan double abs_f;
25018ee52affSYegappan Lakshmanan char format[40];
25028ee52affSYegappan Lakshmanan int l;
25038ee52affSYegappan Lakshmanan int remove_trailing_zeroes = FALSE;
25048ee52affSYegappan Lakshmanan
25058ee52affSYegappan Lakshmanan f =
25068ee52affSYegappan Lakshmanan # if defined(FEAT_EVAL)
25078ee52affSYegappan Lakshmanan tvs != NULL ? tv_float(tvs, &arg_idx) :
25088ee52affSYegappan Lakshmanan # endif
25098ee52affSYegappan Lakshmanan va_arg(ap, double);
25108ee52affSYegappan Lakshmanan abs_f = f < 0 ? -f : f;
25118ee52affSYegappan Lakshmanan
25128ee52affSYegappan Lakshmanan if (fmt_spec == 'g' || fmt_spec == 'G')
25138ee52affSYegappan Lakshmanan {
25148ee52affSYegappan Lakshmanan // Would be nice to use %g directly, but it prints
25158ee52affSYegappan Lakshmanan // "1.0" as "1", we don't want that.
25168ee52affSYegappan Lakshmanan if ((abs_f >= 0.001 && abs_f < 10000000.0)
25178ee52affSYegappan Lakshmanan || abs_f == 0.0)
25188ee52affSYegappan Lakshmanan fmt_spec = ASCII_ISUPPER(fmt_spec) ? 'F' : 'f';
25198ee52affSYegappan Lakshmanan else
25208ee52affSYegappan Lakshmanan fmt_spec = fmt_spec == 'g' ? 'e' : 'E';
25218ee52affSYegappan Lakshmanan remove_trailing_zeroes = TRUE;
25228ee52affSYegappan Lakshmanan }
25238ee52affSYegappan Lakshmanan
25248ee52affSYegappan Lakshmanan if ((fmt_spec == 'f' || fmt_spec == 'F') &&
25258ee52affSYegappan Lakshmanan # ifdef VAX
25268ee52affSYegappan Lakshmanan abs_f > 1.0e38
25278ee52affSYegappan Lakshmanan # else
25288ee52affSYegappan Lakshmanan abs_f > 1.0e307
25298ee52affSYegappan Lakshmanan # endif
25308ee52affSYegappan Lakshmanan )
25318ee52affSYegappan Lakshmanan {
25328ee52affSYegappan Lakshmanan // Avoid a buffer overflow
25338ee52affSYegappan Lakshmanan STRCPY(tmp, infinity_str(f > 0.0, fmt_spec,
25348ee52affSYegappan Lakshmanan force_sign, space_for_positive));
25358ee52affSYegappan Lakshmanan str_arg_l = STRLEN(tmp);
25368ee52affSYegappan Lakshmanan zero_padding = 0;
25378ee52affSYegappan Lakshmanan }
25388ee52affSYegappan Lakshmanan else
25398ee52affSYegappan Lakshmanan {
25408ee52affSYegappan Lakshmanan if (isnan(f))
25418ee52affSYegappan Lakshmanan {
25428ee52affSYegappan Lakshmanan // Not a number: nan or NAN
25438ee52affSYegappan Lakshmanan STRCPY(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN"
25448ee52affSYegappan Lakshmanan : "nan");
25458ee52affSYegappan Lakshmanan str_arg_l = 3;
25468ee52affSYegappan Lakshmanan zero_padding = 0;
25478ee52affSYegappan Lakshmanan }
25488ee52affSYegappan Lakshmanan else if (isinf(f))
25498ee52affSYegappan Lakshmanan {
25508ee52affSYegappan Lakshmanan STRCPY(tmp, infinity_str(f > 0.0, fmt_spec,
25518ee52affSYegappan Lakshmanan force_sign, space_for_positive));
25528ee52affSYegappan Lakshmanan str_arg_l = STRLEN(tmp);
25538ee52affSYegappan Lakshmanan zero_padding = 0;
25548ee52affSYegappan Lakshmanan }
25558ee52affSYegappan Lakshmanan else
25568ee52affSYegappan Lakshmanan {
25578ee52affSYegappan Lakshmanan // Regular float number
25588ee52affSYegappan Lakshmanan format[0] = '%';
25598ee52affSYegappan Lakshmanan l = 1;
25608ee52affSYegappan Lakshmanan if (force_sign)
25618ee52affSYegappan Lakshmanan format[l++] = space_for_positive ? ' ' : '+';
25628ee52affSYegappan Lakshmanan if (precision_specified)
25638ee52affSYegappan Lakshmanan {
25648ee52affSYegappan Lakshmanan size_t max_prec = TMP_LEN - 10;
25658ee52affSYegappan Lakshmanan
25668ee52affSYegappan Lakshmanan // Make sure we don't get more digits than we
25678ee52affSYegappan Lakshmanan // have room for.
25688ee52affSYegappan Lakshmanan if ((fmt_spec == 'f' || fmt_spec == 'F')
25698ee52affSYegappan Lakshmanan && abs_f > 1.0)
25708ee52affSYegappan Lakshmanan max_prec -= (size_t)log10(abs_f);
25718ee52affSYegappan Lakshmanan if (precision > max_prec)
25728ee52affSYegappan Lakshmanan precision = max_prec;
25738ee52affSYegappan Lakshmanan l += sprintf(format + l, ".%d", (int)precision);
25748ee52affSYegappan Lakshmanan }
25758ee52affSYegappan Lakshmanan format[l] = fmt_spec == 'F' ? 'f' : fmt_spec;
25768ee52affSYegappan Lakshmanan format[l + 1] = NUL;
25778ee52affSYegappan Lakshmanan
25788ee52affSYegappan Lakshmanan str_arg_l = sprintf(tmp, format, f);
25798ee52affSYegappan Lakshmanan }
25808ee52affSYegappan Lakshmanan
25818ee52affSYegappan Lakshmanan if (remove_trailing_zeroes)
25828ee52affSYegappan Lakshmanan {
25838ee52affSYegappan Lakshmanan int i;
25848ee52affSYegappan Lakshmanan char *tp;
25858ee52affSYegappan Lakshmanan
25868ee52affSYegappan Lakshmanan // Using %g or %G: remove superfluous zeroes.
25878ee52affSYegappan Lakshmanan if (fmt_spec == 'f' || fmt_spec == 'F')
25888ee52affSYegappan Lakshmanan tp = tmp + str_arg_l - 1;
25898ee52affSYegappan Lakshmanan else
25908ee52affSYegappan Lakshmanan {
25918ee52affSYegappan Lakshmanan tp = (char *)vim_strchr((char_u *)tmp,
25928ee52affSYegappan Lakshmanan fmt_spec == 'e' ? 'e' : 'E');
25938ee52affSYegappan Lakshmanan if (tp != NULL)
25948ee52affSYegappan Lakshmanan {
25958ee52affSYegappan Lakshmanan // Remove superfluous '+' and leading
25968ee52affSYegappan Lakshmanan // zeroes from the exponent.
25978ee52affSYegappan Lakshmanan if (tp[1] == '+')
25988ee52affSYegappan Lakshmanan {
25998ee52affSYegappan Lakshmanan // Change "1.0e+07" to "1.0e07"
26008ee52affSYegappan Lakshmanan STRMOVE(tp + 1, tp + 2);
26018ee52affSYegappan Lakshmanan --str_arg_l;
26028ee52affSYegappan Lakshmanan }
26038ee52affSYegappan Lakshmanan i = (tp[1] == '-') ? 2 : 1;
26048ee52affSYegappan Lakshmanan while (tp[i] == '0')
26058ee52affSYegappan Lakshmanan {
26068ee52affSYegappan Lakshmanan // Change "1.0e07" to "1.0e7"
26078ee52affSYegappan Lakshmanan STRMOVE(tp + i, tp + i + 1);
26088ee52affSYegappan Lakshmanan --str_arg_l;
26098ee52affSYegappan Lakshmanan }
26108ee52affSYegappan Lakshmanan --tp;
26118ee52affSYegappan Lakshmanan }
26128ee52affSYegappan Lakshmanan }
26138ee52affSYegappan Lakshmanan
26148ee52affSYegappan Lakshmanan if (tp != NULL && !precision_specified)
26158ee52affSYegappan Lakshmanan // Remove trailing zeroes, but keep the one
26168ee52affSYegappan Lakshmanan // just after a dot.
26178ee52affSYegappan Lakshmanan while (tp > tmp + 2 && *tp == '0'
26188ee52affSYegappan Lakshmanan && tp[-1] != '.')
26198ee52affSYegappan Lakshmanan {
26208ee52affSYegappan Lakshmanan STRMOVE(tp, tp + 1);
26218ee52affSYegappan Lakshmanan --tp;
26228ee52affSYegappan Lakshmanan --str_arg_l;
26238ee52affSYegappan Lakshmanan }
26248ee52affSYegappan Lakshmanan }
26258ee52affSYegappan Lakshmanan else
26268ee52affSYegappan Lakshmanan {
26278ee52affSYegappan Lakshmanan char *tp;
26288ee52affSYegappan Lakshmanan
26298ee52affSYegappan Lakshmanan // Be consistent: some printf("%e") use 1.0e+12
26308ee52affSYegappan Lakshmanan // and some 1.0e+012. Remove one zero in the last
26318ee52affSYegappan Lakshmanan // case.
26328ee52affSYegappan Lakshmanan tp = (char *)vim_strchr((char_u *)tmp,
26338ee52affSYegappan Lakshmanan fmt_spec == 'e' ? 'e' : 'E');
26348ee52affSYegappan Lakshmanan if (tp != NULL && (tp[1] == '+' || tp[1] == '-')
26358ee52affSYegappan Lakshmanan && tp[2] == '0'
26368ee52affSYegappan Lakshmanan && vim_isdigit(tp[3])
26378ee52affSYegappan Lakshmanan && vim_isdigit(tp[4]))
26388ee52affSYegappan Lakshmanan {
26398ee52affSYegappan Lakshmanan STRMOVE(tp + 2, tp + 3);
26408ee52affSYegappan Lakshmanan --str_arg_l;
26418ee52affSYegappan Lakshmanan }
26428ee52affSYegappan Lakshmanan }
26438ee52affSYegappan Lakshmanan }
26448ee52affSYegappan Lakshmanan if (zero_padding && min_field_width > str_arg_l
26458ee52affSYegappan Lakshmanan && (tmp[0] == '-' || force_sign))
26468ee52affSYegappan Lakshmanan {
26478ee52affSYegappan Lakshmanan // padding 0's should be inserted after the sign
26488ee52affSYegappan Lakshmanan number_of_zeros_to_pad = min_field_width - str_arg_l;
26498ee52affSYegappan Lakshmanan zero_padding_insertion_ind = 1;
26508ee52affSYegappan Lakshmanan }
26518ee52affSYegappan Lakshmanan str_arg = tmp;
26528ee52affSYegappan Lakshmanan break;
26538ee52affSYegappan Lakshmanan }
26548ee52affSYegappan Lakshmanan # endif
26558ee52affSYegappan Lakshmanan
26568ee52affSYegappan Lakshmanan default:
26578ee52affSYegappan Lakshmanan // unrecognized conversion specifier, keep format string
26588ee52affSYegappan Lakshmanan // as-is
26598ee52affSYegappan Lakshmanan zero_padding = 0; // turn zero padding off for non-numeric
26608ee52affSYegappan Lakshmanan // conversion
26618ee52affSYegappan Lakshmanan justify_left = 1;
26628ee52affSYegappan Lakshmanan min_field_width = 0; // reset flags
26638ee52affSYegappan Lakshmanan
26648ee52affSYegappan Lakshmanan // discard the unrecognized conversion, just keep *
26658ee52affSYegappan Lakshmanan // the unrecognized conversion character
26668ee52affSYegappan Lakshmanan str_arg = p;
26678ee52affSYegappan Lakshmanan str_arg_l = 0;
26688ee52affSYegappan Lakshmanan if (*p != NUL)
26698ee52affSYegappan Lakshmanan str_arg_l++; // include invalid conversion specifier
26708ee52affSYegappan Lakshmanan // unchanged if not at end-of-string
26718ee52affSYegappan Lakshmanan break;
26728ee52affSYegappan Lakshmanan }
26738ee52affSYegappan Lakshmanan
26748ee52affSYegappan Lakshmanan if (*p != NUL)
26758ee52affSYegappan Lakshmanan p++; // step over the just processed conversion specifier
26768ee52affSYegappan Lakshmanan
26778ee52affSYegappan Lakshmanan // insert padding to the left as requested by min_field_width;
26788ee52affSYegappan Lakshmanan // this does not include the zero padding in case of numerical
26798ee52affSYegappan Lakshmanan // conversions
26808ee52affSYegappan Lakshmanan if (!justify_left)
26818ee52affSYegappan Lakshmanan {
26828ee52affSYegappan Lakshmanan // left padding with blank or zero
26838ee52affSYegappan Lakshmanan int pn = (int)(min_field_width - (str_arg_l + number_of_zeros_to_pad));
26848ee52affSYegappan Lakshmanan
26858ee52affSYegappan Lakshmanan if (pn > 0)
26868ee52affSYegappan Lakshmanan {
26878ee52affSYegappan Lakshmanan if (str_l < str_m)
26888ee52affSYegappan Lakshmanan {
26898ee52affSYegappan Lakshmanan size_t avail = str_m - str_l;
26908ee52affSYegappan Lakshmanan
26918ee52affSYegappan Lakshmanan vim_memset(str + str_l, zero_padding ? '0' : ' ',
26928ee52affSYegappan Lakshmanan (size_t)pn > avail ? avail
26938ee52affSYegappan Lakshmanan : (size_t)pn);
26948ee52affSYegappan Lakshmanan }
26958ee52affSYegappan Lakshmanan str_l += pn;
26968ee52affSYegappan Lakshmanan }
26978ee52affSYegappan Lakshmanan }
26988ee52affSYegappan Lakshmanan
26998ee52affSYegappan Lakshmanan // zero padding as requested by the precision or by the minimal
27008ee52affSYegappan Lakshmanan // field width for numeric conversions required?
27018ee52affSYegappan Lakshmanan if (number_of_zeros_to_pad == 0)
27028ee52affSYegappan Lakshmanan {
27038ee52affSYegappan Lakshmanan // will not copy first part of numeric right now, *
27048ee52affSYegappan Lakshmanan // force it to be copied later in its entirety
27058ee52affSYegappan Lakshmanan zero_padding_insertion_ind = 0;
27068ee52affSYegappan Lakshmanan }
27078ee52affSYegappan Lakshmanan else
27088ee52affSYegappan Lakshmanan {
27098ee52affSYegappan Lakshmanan // insert first part of numerics (sign or '0x') before zero
27108ee52affSYegappan Lakshmanan // padding
27118ee52affSYegappan Lakshmanan int zn = (int)zero_padding_insertion_ind;
27128ee52affSYegappan Lakshmanan
27138ee52affSYegappan Lakshmanan if (zn > 0)
27148ee52affSYegappan Lakshmanan {
27158ee52affSYegappan Lakshmanan if (str_l < str_m)
27168ee52affSYegappan Lakshmanan {
27178ee52affSYegappan Lakshmanan size_t avail = str_m - str_l;
27188ee52affSYegappan Lakshmanan
27198ee52affSYegappan Lakshmanan mch_memmove(str + str_l, str_arg,
27208ee52affSYegappan Lakshmanan (size_t)zn > avail ? avail
27218ee52affSYegappan Lakshmanan : (size_t)zn);
27228ee52affSYegappan Lakshmanan }
27238ee52affSYegappan Lakshmanan str_l += zn;
27248ee52affSYegappan Lakshmanan }
27258ee52affSYegappan Lakshmanan
27268ee52affSYegappan Lakshmanan // insert zero padding as requested by the precision or min
27278ee52affSYegappan Lakshmanan // field width
27288ee52affSYegappan Lakshmanan zn = (int)number_of_zeros_to_pad;
27298ee52affSYegappan Lakshmanan if (zn > 0)
27308ee52affSYegappan Lakshmanan {
27318ee52affSYegappan Lakshmanan if (str_l < str_m)
27328ee52affSYegappan Lakshmanan {
27338ee52affSYegappan Lakshmanan size_t avail = str_m - str_l;
27348ee52affSYegappan Lakshmanan
27358ee52affSYegappan Lakshmanan vim_memset(str + str_l, '0',
27368ee52affSYegappan Lakshmanan (size_t)zn > avail ? avail
27378ee52affSYegappan Lakshmanan : (size_t)zn);
27388ee52affSYegappan Lakshmanan }
27398ee52affSYegappan Lakshmanan str_l += zn;
27408ee52affSYegappan Lakshmanan }
27418ee52affSYegappan Lakshmanan }
27428ee52affSYegappan Lakshmanan
27438ee52affSYegappan Lakshmanan // insert formatted string
27448ee52affSYegappan Lakshmanan // (or as-is conversion specifier for unknown conversions)
27458ee52affSYegappan Lakshmanan {
27468ee52affSYegappan Lakshmanan int sn = (int)(str_arg_l - zero_padding_insertion_ind);
27478ee52affSYegappan Lakshmanan
27488ee52affSYegappan Lakshmanan if (sn > 0)
27498ee52affSYegappan Lakshmanan {
27508ee52affSYegappan Lakshmanan if (str_l < str_m)
27518ee52affSYegappan Lakshmanan {
27528ee52affSYegappan Lakshmanan size_t avail = str_m - str_l;
27538ee52affSYegappan Lakshmanan
27548ee52affSYegappan Lakshmanan mch_memmove(str + str_l,
27558ee52affSYegappan Lakshmanan str_arg + zero_padding_insertion_ind,
27568ee52affSYegappan Lakshmanan (size_t)sn > avail ? avail : (size_t)sn);
27578ee52affSYegappan Lakshmanan }
27588ee52affSYegappan Lakshmanan str_l += sn;
27598ee52affSYegappan Lakshmanan }
27608ee52affSYegappan Lakshmanan }
27618ee52affSYegappan Lakshmanan
27628ee52affSYegappan Lakshmanan // insert right padding
27638ee52affSYegappan Lakshmanan if (justify_left)
27648ee52affSYegappan Lakshmanan {
27658ee52affSYegappan Lakshmanan // right blank padding to the field width
27668ee52affSYegappan Lakshmanan int pn = (int)(min_field_width
27678ee52affSYegappan Lakshmanan - (str_arg_l + number_of_zeros_to_pad));
27688ee52affSYegappan Lakshmanan
27698ee52affSYegappan Lakshmanan if (pn > 0)
27708ee52affSYegappan Lakshmanan {
27718ee52affSYegappan Lakshmanan if (str_l < str_m)
27728ee52affSYegappan Lakshmanan {
27738ee52affSYegappan Lakshmanan size_t avail = str_m - str_l;
27748ee52affSYegappan Lakshmanan
27758ee52affSYegappan Lakshmanan vim_memset(str + str_l, ' ',
27768ee52affSYegappan Lakshmanan (size_t)pn > avail ? avail
27778ee52affSYegappan Lakshmanan : (size_t)pn);
27788ee52affSYegappan Lakshmanan }
27798ee52affSYegappan Lakshmanan str_l += pn;
27808ee52affSYegappan Lakshmanan }
27818ee52affSYegappan Lakshmanan }
27828ee52affSYegappan Lakshmanan vim_free(tofree);
27838ee52affSYegappan Lakshmanan }
27848ee52affSYegappan Lakshmanan }
27858ee52affSYegappan Lakshmanan
27868ee52affSYegappan Lakshmanan if (str_m > 0)
27878ee52affSYegappan Lakshmanan {
27888ee52affSYegappan Lakshmanan // make sure the string is nul-terminated even at the expense of
27898ee52affSYegappan Lakshmanan // overwriting the last character (shouldn't happen, but just in case)
27908ee52affSYegappan Lakshmanan //
27918ee52affSYegappan Lakshmanan str[str_l <= str_m - 1 ? str_l : str_m - 1] = '\0';
27928ee52affSYegappan Lakshmanan }
27938ee52affSYegappan Lakshmanan
27948ee52affSYegappan Lakshmanan if (tvs != NULL && tvs[arg_idx - 1].v_type != VAR_UNKNOWN)
27958ee52affSYegappan Lakshmanan emsg(_("E767: Too many arguments to printf()"));
27968ee52affSYegappan Lakshmanan
27978ee52affSYegappan Lakshmanan // Return the number of characters formatted (excluding trailing nul
27988ee52affSYegappan Lakshmanan // character), that is, the number of characters that would have been
27998ee52affSYegappan Lakshmanan // written to the buffer if it were large enough.
28008ee52affSYegappan Lakshmanan return (int)str_l;
28018ee52affSYegappan Lakshmanan }
28028ee52affSYegappan Lakshmanan
28038ee52affSYegappan Lakshmanan #endif // PROTO
2804