]>
Commit | Line | Data |
---|---|---|
832ed81a AC |
1 | /* |
2 | * libString, Copyright (C) 1999 Patrick Alken | |
3 | * This library comes with absolutely NO WARRANTY | |
4 | * | |
5 | * Should you choose to use and/or modify this source code, please | |
6 | * do so under the terms of the GNU General Public License under which | |
7 | * this library is distributed. | |
8 | * | |
9 | * $Id: rsdb_snprintf.c 26094 2008-09-19 15:33:46Z androsyn $ | |
10 | */ | |
11 | #include "stdinc.h" | |
12 | #include "rsdb.h" | |
13 | ||
14 | /* | |
15 | * This table is arranged in chronological order from 0-999, | |
16 | * however the numbers are written backwards, so the number 100 | |
17 | * is expressed in this table as "001". | |
18 | * It's purpose is to ensure fast conversions from integers to | |
19 | * ASCII strings. When an integer variable is encountered, a | |
20 | * simple hash algorithm is used to determine where to look | |
21 | * in this array for the corresponding string. | |
22 | * This outperforms continually dividing by 10 and using the | |
23 | * digit obtained as a character, because we can now divide by | |
24 | * 1000 and use the remainder directly, thus cutting down on | |
25 | * the number of costly divisions needed. For an integer's worst | |
26 | * case, 2 divisions are needed because it can only go up to | |
27 | * 32767, so after 2 divisions by 1000, and some algebra, we will | |
28 | * be left with 327 which we can get from this table. This is much | |
29 | * better than the 5 divisions by 10 that we would need if we did | |
30 | * it the conventional way. Of course, if we made this table go | |
31 | * from 0-9999, only 1 division would be needed. | |
32 | * Longs and unsigned ints of course, are another matter :-). | |
33 | * | |
34 | * Patrick Alken <wnder@underworld.net> | |
35 | */ | |
36 | ||
37 | /* | |
38 | * Set this to the number of indices (numbers) in our table | |
39 | */ | |
40 | #define TABLE_MAX 1000 | |
41 | ||
42 | static const char *IntTable[] = { | |
43 | "000", "100", "200", "300", "400", | |
44 | "500", "600", "700", "800", "900", | |
45 | "010", "110", "210", "310", "410", | |
46 | "510", "610", "710", "810", "910", | |
47 | "020", "120", "220", "320", "420", | |
48 | "520", "620", "720", "820", "920", | |
49 | "030", "130", "230", "330", "430", | |
50 | "530", "630", "730", "830", "930", | |
51 | "040", "140", "240", "340", "440", | |
52 | "540", "640", "740", "840", "940", | |
53 | "050", "150", "250", "350", "450", | |
54 | "550", "650", "750", "850", "950", | |
55 | "060", "160", "260", "360", "460", | |
56 | "560", "660", "760", "860", "960", | |
57 | "070", "170", "270", "370", "470", | |
58 | "570", "670", "770", "870", "970", | |
59 | "080", "180", "280", "380", "480", | |
60 | "580", "680", "780", "880", "980", | |
61 | "090", "190", "290", "390", "490", | |
62 | "590", "690", "790", "890", "990", | |
63 | "001", "101", "201", "301", "401", | |
64 | "501", "601", "701", "801", "901", | |
65 | "011", "111", "211", "311", "411", | |
66 | "511", "611", "711", "811", "911", | |
67 | "021", "121", "221", "321", "421", | |
68 | "521", "621", "721", "821", "921", | |
69 | "031", "131", "231", "331", "431", | |
70 | "531", "631", "731", "831", "931", | |
71 | "041", "141", "241", "341", "441", | |
72 | "541", "641", "741", "841", "941", | |
73 | "051", "151", "251", "351", "451", | |
74 | "551", "651", "751", "851", "951", | |
75 | "061", "161", "261", "361", "461", | |
76 | "561", "661", "761", "861", "961", | |
77 | "071", "171", "271", "371", "471", | |
78 | "571", "671", "771", "871", "971", | |
79 | "081", "181", "281", "381", "481", | |
80 | "581", "681", "781", "881", "981", | |
81 | "091", "191", "291", "391", "491", | |
82 | "591", "691", "791", "891", "991", | |
83 | "002", "102", "202", "302", "402", | |
84 | "502", "602", "702", "802", "902", | |
85 | "012", "112", "212", "312", "412", | |
86 | "512", "612", "712", "812", "912", | |
87 | "022", "122", "222", "322", "422", | |
88 | "522", "622", "722", "822", "922", | |
89 | "032", "132", "232", "332", "432", | |
90 | "532", "632", "732", "832", "932", | |
91 | "042", "142", "242", "342", "442", | |
92 | "542", "642", "742", "842", "942", | |
93 | "052", "152", "252", "352", "452", | |
94 | "552", "652", "752", "852", "952", | |
95 | "062", "162", "262", "362", "462", | |
96 | "562", "662", "762", "862", "962", | |
97 | "072", "172", "272", "372", "472", | |
98 | "572", "672", "772", "872", "972", | |
99 | "082", "182", "282", "382", "482", | |
100 | "582", "682", "782", "882", "982", | |
101 | "092", "192", "292", "392", "492", | |
102 | "592", "692", "792", "892", "992", | |
103 | "003", "103", "203", "303", "403", | |
104 | "503", "603", "703", "803", "903", | |
105 | "013", "113", "213", "313", "413", | |
106 | "513", "613", "713", "813", "913", | |
107 | "023", "123", "223", "323", "423", | |
108 | "523", "623", "723", "823", "923", | |
109 | "033", "133", "233", "333", "433", | |
110 | "533", "633", "733", "833", "933", | |
111 | "043", "143", "243", "343", "443", | |
112 | "543", "643", "743", "843", "943", | |
113 | "053", "153", "253", "353", "453", | |
114 | "553", "653", "753", "853", "953", | |
115 | "063", "163", "263", "363", "463", | |
116 | "563", "663", "763", "863", "963", | |
117 | "073", "173", "273", "373", "473", | |
118 | "573", "673", "773", "873", "973", | |
119 | "083", "183", "283", "383", "483", | |
120 | "583", "683", "783", "883", "983", | |
121 | "093", "193", "293", "393", "493", | |
122 | "593", "693", "793", "893", "993", | |
123 | "004", "104", "204", "304", "404", | |
124 | "504", "604", "704", "804", "904", | |
125 | "014", "114", "214", "314", "414", | |
126 | "514", "614", "714", "814", "914", | |
127 | "024", "124", "224", "324", "424", | |
128 | "524", "624", "724", "824", "924", | |
129 | "034", "134", "234", "334", "434", | |
130 | "534", "634", "734", "834", "934", | |
131 | "044", "144", "244", "344", "444", | |
132 | "544", "644", "744", "844", "944", | |
133 | "054", "154", "254", "354", "454", | |
134 | "554", "654", "754", "854", "954", | |
135 | "064", "164", "264", "364", "464", | |
136 | "564", "664", "764", "864", "964", | |
137 | "074", "174", "274", "374", "474", | |
138 | "574", "674", "774", "874", "974", | |
139 | "084", "184", "284", "384", "484", | |
140 | "584", "684", "784", "884", "984", | |
141 | "094", "194", "294", "394", "494", | |
142 | "594", "694", "794", "894", "994", | |
143 | "005", "105", "205", "305", "405", | |
144 | "505", "605", "705", "805", "905", | |
145 | "015", "115", "215", "315", "415", | |
146 | "515", "615", "715", "815", "915", | |
147 | "025", "125", "225", "325", "425", | |
148 | "525", "625", "725", "825", "925", | |
149 | "035", "135", "235", "335", "435", | |
150 | "535", "635", "735", "835", "935", | |
151 | "045", "145", "245", "345", "445", | |
152 | "545", "645", "745", "845", "945", | |
153 | "055", "155", "255", "355", "455", | |
154 | "555", "655", "755", "855", "955", | |
155 | "065", "165", "265", "365", "465", | |
156 | "565", "665", "765", "865", "965", | |
157 | "075", "175", "275", "375", "475", | |
158 | "575", "675", "775", "875", "975", | |
159 | "085", "185", "285", "385", "485", | |
160 | "585", "685", "785", "885", "985", | |
161 | "095", "195", "295", "395", "495", | |
162 | "595", "695", "795", "895", "995", | |
163 | "006", "106", "206", "306", "406", | |
164 | "506", "606", "706", "806", "906", | |
165 | "016", "116", "216", "316", "416", | |
166 | "516", "616", "716", "816", "916", | |
167 | "026", "126", "226", "326", "426", | |
168 | "526", "626", "726", "826", "926", | |
169 | "036", "136", "236", "336", "436", | |
170 | "536", "636", "736", "836", "936", | |
171 | "046", "146", "246", "346", "446", | |
172 | "546", "646", "746", "846", "946", | |
173 | "056", "156", "256", "356", "456", | |
174 | "556", "656", "756", "856", "956", | |
175 | "066", "166", "266", "366", "466", | |
176 | "566", "666", "766", "866", "966", | |
177 | "076", "176", "276", "376", "476", | |
178 | "576", "676", "776", "876", "976", | |
179 | "086", "186", "286", "386", "486", | |
180 | "586", "686", "786", "886", "986", | |
181 | "096", "196", "296", "396", "496", | |
182 | "596", "696", "796", "896", "996", | |
183 | "007", "107", "207", "307", "407", | |
184 | "507", "607", "707", "807", "907", | |
185 | "017", "117", "217", "317", "417", | |
186 | "517", "617", "717", "817", "917", | |
187 | "027", "127", "227", "327", "427", | |
188 | "527", "627", "727", "827", "927", | |
189 | "037", "137", "237", "337", "437", | |
190 | "537", "637", "737", "837", "937", | |
191 | "047", "147", "247", "347", "447", | |
192 | "547", "647", "747", "847", "947", | |
193 | "057", "157", "257", "357", "457", | |
194 | "557", "657", "757", "857", "957", | |
195 | "067", "167", "267", "367", "467", | |
196 | "567", "667", "767", "867", "967", | |
197 | "077", "177", "277", "377", "477", | |
198 | "577", "677", "777", "877", "977", | |
199 | "087", "187", "287", "387", "487", | |
200 | "587", "687", "787", "887", "987", | |
201 | "097", "197", "297", "397", "497", | |
202 | "597", "697", "797", "897", "997", | |
203 | "008", "108", "208", "308", "408", | |
204 | "508", "608", "708", "808", "908", | |
205 | "018", "118", "218", "318", "418", | |
206 | "518", "618", "718", "818", "918", | |
207 | "028", "128", "228", "328", "428", | |
208 | "528", "628", "728", "828", "928", | |
209 | "038", "138", "238", "338", "438", | |
210 | "538", "638", "738", "838", "938", | |
211 | "048", "148", "248", "348", "448", | |
212 | "548", "648", "748", "848", "948", | |
213 | "058", "158", "258", "358", "458", | |
214 | "558", "658", "758", "858", "958", | |
215 | "068", "168", "268", "368", "468", | |
216 | "568", "668", "768", "868", "968", | |
217 | "078", "178", "278", "378", "478", | |
218 | "578", "678", "778", "878", "978", | |
219 | "088", "188", "288", "388", "488", | |
220 | "588", "688", "788", "888", "988", | |
221 | "098", "198", "298", "398", "498", | |
222 | "598", "698", "798", "898", "998", | |
223 | "009", "109", "209", "309", "409", | |
224 | "509", "609", "709", "809", "909", | |
225 | "019", "119", "219", "319", "419", | |
226 | "519", "619", "719", "819", "919", | |
227 | "029", "129", "229", "329", "429", | |
228 | "529", "629", "729", "829", "929", | |
229 | "039", "139", "239", "339", "439", | |
230 | "539", "639", "739", "839", "939", | |
231 | "049", "149", "249", "349", "449", | |
232 | "549", "649", "749", "849", "949", | |
233 | "059", "159", "259", "359", "459", | |
234 | "559", "659", "759", "859", "959", | |
235 | "069", "169", "269", "369", "469", | |
236 | "569", "669", "769", "869", "969", | |
237 | "079", "179", "279", "379", "479", | |
238 | "579", "679", "779", "879", "979", | |
239 | "089", "189", "289", "389", "489", | |
240 | "589", "689", "789", "889", "989", | |
241 | "099", "199", "299", "399", "499", | |
242 | "599", "699", "799", "899", "999" | |
243 | }; | |
244 | ||
245 | /* | |
246 | * Since we calculate the right-most digits for %d %u etc first, | |
247 | * we need a temporary buffer to store them in until we get | |
248 | * to the left-most digits | |
249 | */ | |
250 | ||
251 | #define TEMPBUF_MAX 20 | |
252 | ||
253 | static char TempBuffer[TEMPBUF_MAX]; | |
254 | ||
255 | /* | |
256 | vSnprintf() | |
257 | Backend to Snprintf() - performs the construction of 'dest' | |
258 | using the string 'format' and the given arguments. Also makes sure | |
259 | not more than 'bytes' characters are copied to 'dest' | |
260 | ||
261 | We always allow room for a terminating \0 character, so at most, | |
262 | bytes - 1 characters will be written to dest. | |
263 | ||
264 | Return: Number of characters written, NOT including the terminating | |
265 | \0 character which is *always* placed at the end of the string | |
266 | ||
267 | NOTE: This function handles the following flags only: | |
268 | %s %d %c %u %ld %lu | |
269 | In addition, this function performs *NO* precision, padding, | |
270 | or width formatting. If it receives an unknown % character, | |
271 | it will call vsprintf() to complete the remainder of the | |
272 | string. | |
273 | */ | |
274 | ||
275 | int | |
276 | rs_vsnprintf(char *dest, const size_t bytes, const char *format, va_list args) | |
277 | { | |
278 | char ch; | |
279 | int written = 0; /* bytes written so far */ | |
280 | int maxbytes = bytes - 1; | |
281 | ||
282 | while((ch = *format++) && (written < maxbytes)) | |
283 | { | |
284 | if(ch == '%') | |
285 | { | |
286 | /* | |
287 | * Advance past the % | |
288 | */ | |
289 | ch = *format++; | |
290 | ||
291 | /* | |
292 | * Put the most common cases first - %s %d etc | |
293 | */ | |
294 | ||
295 | if(ch == 's') | |
296 | { | |
297 | const char *str = va_arg(args, const char *); | |
298 | ||
299 | while((*dest = *str)) | |
300 | { | |
301 | ++dest; | |
302 | ++str; | |
303 | ||
304 | if(++written >= maxbytes) | |
305 | break; | |
306 | } | |
307 | ||
308 | continue; | |
309 | } | |
310 | ||
311 | if(ch == 'd') | |
312 | { | |
313 | int num = va_arg(args, int); | |
314 | int quotient; | |
315 | const char *str; | |
316 | char *digitptr = TempBuffer; | |
317 | ||
318 | /* | |
319 | * We have to special-case "0" unfortunately | |
320 | */ | |
321 | if(num == 0) | |
322 | { | |
323 | *dest++ = '0'; | |
324 | ++written; | |
325 | continue; | |
326 | } | |
327 | ||
328 | if(num < 0) | |
329 | { | |
330 | *dest++ = '-'; | |
331 | if(++written >= maxbytes) | |
332 | continue; | |
333 | ||
334 | num = -num; | |
335 | } | |
336 | ||
337 | do | |
338 | { | |
339 | quotient = num / TABLE_MAX; | |
340 | ||
341 | /* | |
342 | * We'll start with the right-most digits of 'num'. | |
343 | * Dividing by TABLE_MAX cuts off all but the X | |
344 | * right-most digits, where X is such that: | |
345 | * | |
346 | * 10^X = TABLE_MAX | |
347 | * | |
348 | * For example, if num = 1200, and TABLE_MAX = 1000, | |
349 | * quotient will be 1. Multiplying this by 1000 and | |
350 | * subtracting from 1200 gives: 1200 - (1 * 1000) = 200. | |
351 | * We then go right to slot 200 in our array and behold! | |
352 | * The string "002" (200 backwards) is conveniently | |
353 | * waiting for us. Then repeat the process with the | |
354 | * digits left. | |
355 | * | |
356 | * The reason we need to have the integers written | |
357 | * backwards, is because we don't know how many digits | |
358 | * there are. If we want to express the number 12130 | |
359 | * for example, our first pass would leave us with 130, | |
360 | * whose slot in the array yields "031", which we | |
361 | * plug into our TempBuffer[]. The next pass gives us | |
362 | * 12, whose slot yields "21" which we append to | |
363 | * TempBuffer[], leaving us with "03121". This is the | |
364 | * exact number we want, only backwards, so it is | |
365 | * a simple matter to reverse the string. If we used | |
366 | * straightfoward numbers, we would have a TempBuffer | |
367 | * looking like this: "13012" which would be a nightmare | |
368 | * to deal with. | |
369 | */ | |
370 | ||
371 | str = IntTable[num - (quotient * TABLE_MAX)]; | |
372 | ||
373 | while((*digitptr = *str)) | |
374 | { | |
375 | ++digitptr; | |
376 | ++str; | |
377 | } | |
378 | } | |
379 | while((num = quotient) != 0); | |
380 | ||
381 | /* | |
382 | * If the last quotient was a 1 or 2 digit number, there | |
383 | * will be one or more leading zeroes in TempBuffer[] - | |
384 | * get rid of them. | |
385 | */ | |
386 | while(*(digitptr - 1) == '0') | |
387 | --digitptr; | |
388 | ||
389 | while(digitptr != TempBuffer) | |
390 | { | |
391 | *dest++ = *--digitptr; | |
392 | if(++written >= maxbytes) | |
393 | break; | |
394 | } | |
395 | ||
396 | continue; | |
397 | } /* if (ch == 'd') */ | |
398 | ||
399 | if(ch == 'c') | |
400 | { | |
401 | *dest++ = va_arg(args, int); | |
402 | ||
403 | ++written; | |
404 | ||
405 | continue; | |
406 | } /* if (ch == 'c') */ | |
407 | ||
408 | if(ch == 'u') | |
409 | { | |
410 | unsigned int num = va_arg(args, unsigned int); | |
411 | unsigned int quotient; | |
412 | const char *str; | |
413 | char *digitptr = TempBuffer; | |
414 | ||
415 | if(num == 0) | |
416 | { | |
417 | *dest++ = '0'; | |
418 | ++written; | |
419 | continue; | |
420 | } | |
421 | ||
422 | do | |
423 | { | |
424 | quotient = num / TABLE_MAX; | |
425 | ||
426 | /* | |
427 | * Very similar to case 'd' | |
428 | */ | |
429 | ||
430 | str = IntTable[num - (quotient * TABLE_MAX)]; | |
431 | ||
432 | while((*digitptr = *str)) | |
433 | { | |
434 | ++digitptr; | |
435 | ++str; | |
436 | } | |
437 | } | |
438 | while((num = quotient) != 0); | |
439 | ||
440 | while(*(digitptr - 1) == '0') | |
441 | --digitptr; | |
442 | ||
443 | while(digitptr != TempBuffer) | |
444 | { | |
445 | *dest++ = *--digitptr; | |
446 | if(++written >= maxbytes) | |
447 | break; | |
448 | } | |
449 | ||
450 | continue; | |
451 | } /* if (ch == 'u') */ | |
452 | ||
453 | if(ch == 'Q') | |
454 | { | |
455 | const char *arg = va_arg(args, const char *); | |
456 | ||
457 | if(arg == NULL) | |
458 | continue; | |
459 | ||
460 | const char *str = rsdb_quote(arg); | |
461 | ||
462 | while((*dest = *str)) | |
463 | { | |
464 | ++dest; | |
465 | ++str; | |
466 | ||
467 | if(++written >= maxbytes) | |
468 | break; | |
469 | } | |
470 | ||
471 | continue; | |
472 | } | |
473 | ||
474 | if(ch == 'l') | |
475 | { | |
476 | if(*format == 'u') | |
477 | { | |
478 | unsigned long num = va_arg(args, unsigned long); | |
479 | unsigned long quotient; | |
480 | const char *str; | |
481 | char *digitptr = TempBuffer; | |
482 | ||
483 | ++format; | |
484 | ||
485 | if(num == 0) | |
486 | { | |
487 | *dest++ = '0'; | |
488 | ++written; | |
489 | continue; | |
490 | } | |
491 | ||
492 | do | |
493 | { | |
494 | quotient = num / TABLE_MAX; | |
495 | ||
496 | /* | |
497 | * Very similar to case 'u' | |
498 | */ | |
499 | ||
500 | str = IntTable[num - (quotient * TABLE_MAX)]; | |
501 | ||
502 | while((*digitptr = *str)) | |
503 | { | |
504 | ++digitptr; | |
505 | ++str; | |
506 | } | |
507 | } | |
508 | while((num = quotient) != 0); | |
509 | ||
510 | while(*(digitptr - 1) == '0') | |
511 | --digitptr; | |
512 | ||
513 | while(digitptr != TempBuffer) | |
514 | { | |
515 | *dest++ = *--digitptr; | |
516 | if(++written >= maxbytes) | |
517 | break; | |
518 | } | |
519 | ||
520 | continue; | |
521 | } | |
522 | else | |
523 | /* if (*format == 'u') */ if(*format == 'd') | |
524 | { | |
525 | long num = va_arg(args, long); | |
526 | long quotient; | |
527 | const char *str; | |
528 | char *digitptr = TempBuffer; | |
529 | ||
530 | ++format; | |
531 | ||
532 | if(num == 0) | |
533 | { | |
534 | *dest++ = '0'; | |
535 | ++written; | |
536 | continue; | |
537 | } | |
538 | ||
539 | if(num < 0) | |
540 | { | |
541 | *dest++ = '-'; | |
542 | if(++written >= maxbytes) | |
543 | continue; | |
544 | ||
545 | num = -num; | |
546 | } | |
547 | ||
548 | do | |
549 | { | |
550 | quotient = num / TABLE_MAX; | |
551 | ||
552 | str = IntTable[num - (quotient * TABLE_MAX)]; | |
553 | ||
554 | while((*digitptr = *str)) | |
555 | { | |
556 | ++digitptr; | |
557 | ++str; | |
558 | } | |
559 | } | |
560 | while((num = quotient) != 0); | |
561 | ||
562 | while(*(digitptr - 1) == '0') | |
563 | --digitptr; | |
564 | ||
565 | while(digitptr != TempBuffer) | |
566 | { | |
567 | *dest++ = *--digitptr; | |
568 | if(++written >= maxbytes) | |
569 | break; | |
570 | } | |
571 | ||
572 | continue; | |
573 | } | |
574 | else /* if (*format == 'd') */ | |
575 | { | |
576 | /* XXX error */ | |
577 | exit(1); | |
578 | } | |
579 | ||
580 | ||
581 | } /* if (ch == 'l') */ | |
582 | ||
583 | if(ch != '%') | |
584 | { | |
585 | /* XXX error */ | |
586 | exit(1); | |
587 | } /* if (ch != '%') */ | |
588 | } /* if (ch == '%') */ | |
589 | ||
590 | *dest++ = ch; | |
591 | ++written; | |
592 | } /* while ((ch = *format++) && (written < maxbytes)) */ | |
593 | ||
594 | /* | |
595 | * Terminate the destination buffer with a \0 | |
596 | */ | |
597 | *dest = '\0'; | |
598 | ||
599 | return (written); | |
600 | } /* vSnprintf() */ | |
601 | ||
602 | /* | |
603 | rs_snprintf() | |
604 | Optimized version of snprintf(). | |
605 | ||
606 | Inputs: dest - destination string | |
607 | bytes - number of bytes to copy | |
608 | format - formatted string | |
609 | args - args to 'format' | |
610 | ||
611 | Return: number of characters copied, NOT including the terminating | |
612 | NULL which is always placed at the end of the string | |
613 | */ | |
614 | ||
615 | int | |
616 | rs_snprintf(char *dest, const size_t bytes, const char *format, ...) | |
617 | { | |
618 | va_list args; | |
619 | int count; | |
620 | ||
621 | va_start(args, format); | |
622 | ||
623 | count = rs_vsnprintf(dest, bytes, format, args); | |
624 | ||
625 | va_end(args); | |
626 | ||
627 | return (count); | |
628 | } /* Snprintf() */ |