1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * ident	"%Z%%M%	%I%	%E% SMI"
24  *
25  * Copyright (c) 2001 by Sun Microsystems, Inc.
26  * All rights reserved.
27  */
28 
29 /*
30  * Cay S. Horstmann & Gary Cornell, Core Java
31  * Published By Sun Microsystems Press/Prentice-Hall
32  * Copyright (C) 1997 Sun Microsystems Inc.
33  * All Rights Reserved.
34  *
35  * Permission to use, copy, modify, and distribute this
36  * software and its documentation for NON-COMMERCIAL purposes
37  * and without fee is hereby granted provided that this
38  * copyright notice appears in all copies.
39  *
40  * THE AUTHORS AND PUBLISHER MAKE NO REPRESENTATIONS OR
41  * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, EITHER
42  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
43  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
44  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. THE AUTHORS
45  * AND PUBLISHER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED
46  * BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
47  * THIS SOFTWARE OR ITS DERIVATIVES.
48  */
49 
50 /**
51  * A class for formatting numbers that follows printf conventions.
52  * Also implements C-like atoi and atof functions
53  * @version 1.01 15 Feb 1996
54  * @author Cay Horstmann
55  */
56 
57 package com.sun.dhcpmgr.cli.common;
58 
59 import java.io.*;
60 
61 public class Format
62 
63 {
64     /**
65      * Formats the number following printf conventions.
66      * Main limitation: Can only handle one format parameter at a time
67      * Use multiple Format objects to format more than one number
68      * @param s the format string following printf conventions
69      * The string has a prefix, a format code and a suffix. The prefix and
70      * suffix become part of the formatted output. The format code directs the
71      * formatting of the (single) parameter to be formatted. The code has the
72      * following structure
73      * <ul>
74      * <li> a % (required)
75      * <li> a modifier (optional)
76      * <dl>
77      * <dt> + <dd> forces display of + for positive numbers
78      * <dt> 0 <dd> show leading zeroes
79      * <dt> - <dd> align left in the field
80      * <dt> space <dd> prepend a space in front of positive numbers
81      * <dt> # <dd> use "alternate" format. Add 0 or 0x for octal or hexadecimal
82      * numbers. Don't suppress trailing zeroes in general floating point format.
83      * </dl>
84      * <li> an integer denoting field width (optional)
85      * <li> a period followed by an integer denoting precision (optional)
86      * <li> a format descriptor (required)
87      * <dl>
88      * <dt>f <dd> floating point number in fixed format
89      * <dt>e, E <dd> floating point number in exponential notation (scientific
90      * format). The E format results in an uppercase E for the exponent
91      * (1.14130E+003), the e format in a lowercase e.
92      * <dt>g, G <dd> floating point number in general format (fixed format for
93      * small numbers, exponential format for large numbers). Trailing zeroes
94      * are suppressed. The G format results in an uppercase E for the exponent
95      * (if any), the g format in a lowercase e.
96      * <dt>d, i <dd> integer in decimal
97      * <dt>x <dd> integer in hexadecimal
98      * <dt>o <dd> integer in octal
99      * <dt>s <dd> string
100      * <dt>c <dd> character
101      * </dl>
102      * </ul>
103      * @exception IllegalArgumentException if bad format
104      */
Format(String s)105     public Format(String s) {
106 	width = 0;
107 	precision = -1;
108 	pre = "";
109 	post = "";
110 	leading_zeroes = false;
111 	show_plus = false;
112 	alternate = false;
113 	show_space = false;
114 	left_align = false;
115 	fmt = ' ';
116 
117 	int state = 0;
118 	int length = s.length();
119 	int parse_state = 0;
120 	// 0 = prefix, 1 = flags, 2 = width, 3 = precision,
121 	// 4 = format, 5 = end
122 	int i = 0;
123 
124 	while (parse_state == 0) {
125 	    if (i >= length) {
126 		parse_state = 5;
127 	    } else if (s.charAt(i) == '%') {
128 		if (i < length - 1) {
129 		    if (s.charAt(i + 1) == '%') {
130 			pre = pre + '%';
131 			i++;
132 		    } else {
133 			parse_state = 1;
134 		    }
135 	 	} else {
136 		    throw new java.lang.IllegalArgumentException();
137 		}
138 	    } else {
139 		pre = pre + s.charAt(i);
140 	    }
141 	    i++;
142 	}
143 
144 	while (parse_state == 1) {
145 	    if (i >= length) {
146 	        parse_state = 5;
147 	    } else if (s.charAt(i) == ' ') {
148 		show_space = true;
149 	    } else if (s.charAt(i) == '-') {
150 		left_align = true;
151 	    } else if (s.charAt(i) == '+') {
152 		show_plus = true;
153 	    } else if (s.charAt(i) == '0') {
154 		leading_zeroes = true;
155 	    } else if (s.charAt(i) == '#') {
156 		alternate = true;
157 	    } else {
158 		parse_state = 2; i--;
159 	    }
160 	    i++;
161 	}
162 
163 	while (parse_state == 2) {
164 	    if (i >= length) {
165 		parse_state = 5;
166 	    } else if ('0' <= s.charAt(i) && s.charAt(i) <= '9') {
167 		width = width * 10 + s.charAt(i) - '0';
168 		i++;
169 	    } else if (s.charAt(i) == '.') {
170 		parse_state = 3;
171 		precision = 0;
172 		i++;
173 	    } else {
174 		parse_state = 4;
175 	    }
176 	}
177 
178 	while (parse_state == 3) {
179 	    if (i >= length) {
180 		parse_state = 5;
181 	    } else if ('0' <= s.charAt(i) && s.charAt(i) <= '9') {
182 		precision = precision * 10 + s.charAt(i) - '0';
183 		i++;
184 	    } else {
185 		parse_state = 4;
186 	    }
187 	}
188 
189 	if (parse_state == 4) {
190 	    if (i >= length) {
191 		parse_state = 5;
192 	    } else {
193 		fmt = s.charAt(i);
194 	    }
195 	    i++;
196 	}
197 
198 	if (i < length) {
199 	    post = s.substring(i, length);
200 	}
201     }
202 
203     /**
204      * prints a formatted number following printf conventions
205      * @param s a PrintStream
206      * @param fmt the format string
207      * @param x the double to print
208      */
print(java.io.PrintStream s, String fmt, double x)209     public static void print(java.io.PrintStream s, String fmt, double x) {
210 	s.print(new Format(fmt).form(x));
211     }
212 
213     /**
214      * prints a formatted number following printf conventions
215      * @param s a PrintStream
216      * @param fmt the format string
217      * @param x the long to print
218      */
print(java.io.PrintStream s, String fmt, long x)219     public static void print(java.io.PrintStream s, String fmt, long x) {
220 	s.print(new Format(fmt).form(x));
221     }
222 
223     /**
224      * prints a formatted number following printf conventions
225      * @param s a PrintStream
226      * @param fmt the format string
227      * @param x the character to
228      */
print(java.io.PrintStream s, String fmt, char x)229     public static void print(java.io.PrintStream s, String fmt, char x) {
230 	s.print(new Format(fmt).form(x));
231     }
232 
233     /**
234      * prints a formatted number following printf conventions
235      * @param s a PrintStream, fmt the format string
236      * @param x a string that represents the digits to print
237      */
print(java.io.PrintStream s, String fmt, String x)238     public static void print(java.io.PrintStream s, String fmt, String x) {
239 	s.print(new Format(fmt).form(x));
240     }
241 
242     /**
243      * Converts a string of digits(decimal, octal or hex) to an integer
244      * @param s a string
245      * @return the numeric value of the prefix of s representing a base
246      * 10 integer
247      */
atoi(String s)248     public static int atoi(String s) {
249 	return (int)atol(s);
250     }
251 
252     /**
253      * Converts a string of digits(decimal, octal or hex) to a long integer
254      * @param s a string
255      * @return the numeric value of the prefix of s representing a base
256      * 10 integer
257      */
atol(String s)258     public static long atol(String s) {
259 	int i = 0;
260 
261 	while (i < s.length() && Character.isWhitespace(s.charAt(i))) {
262 	    i++;
263 	}
264 
265 	if (i < s.length() && s.charAt(i) == '0') {
266 	    if (i + 1 < s.length() && (s.charAt(i + 1) == 'x' ||
267 		s.charAt(i + 1) == 'X')) {
268 		return parseLong(s.substring(i + 2), 16);
269 	    } else {
270 		return parseLong(s, 8);
271 	    }
272 	} else {
273 	    return parseLong(s, 10);
274 	}
275     }
276 
parseLong(String s, int base)277     private static long parseLong(String s, int base) {
278 	int i = 0;
279 	int sign = 1;
280 	long r = 0;
281 
282 	while (i < s.length() && Character.isWhitespace(s.charAt(i))) {
283 	    i++;
284 	}
285 
286 	if (i < s.length() && s.charAt(i) == '-') {
287 	    sign = -1; i++;
288 	} else if (i < s.length() && s.charAt(i) == '+') {
289 	    i++;
290 	}
291 
292 	while (i < s.length()) {
293 	    char ch = s.charAt(i);
294 	    if ('0' <= ch && ch < '0' + base) {
295 		r = r * base + ch - '0';
296 	    } else if ('A' <= ch && ch < 'A' + base - 10) {
297 		r = r * base + ch - 'A' + 10;
298 	    } else if ('a' <= ch && ch < 'a' + base - 10) {
299 		r = r * base + ch - 'a' + 10;
300 	    } else {
301 		return r * sign;
302 	    }
303 	    i++;
304 	}
305 	return r * sign;
306     }
307 
308     /**
309     * Converts a string of digits to an double
310     * @param s a string
311     */
atof(String s)312     public static double atof(String s) {
313 	int i = 0;
314 	int sign = 1;
315 	double r = 0; // integer part
316 	double f = 0; // fractional part
317 	double p = 1; // exponent of fractional part
318 	int state = 0; // 0 = int part, 1 = frac part
319 
320 	while (i < s.length() && Character.isWhitespace(s.charAt(i))) {
321 	    i++;
322 	}
323 
324 	if (i < s.length() && s.charAt(i) == '-') {
325 	    sign = -1; i++;
326 	} else if (i < s.length() && s.charAt(i) == '+') {
327 	    i++;
328 	}
329 
330 	while (i < s.length()) {
331 	    char ch = s.charAt(i);
332 	    if ('0' <= ch && ch <= '9') {
333 		if (state == 0) {
334 		    r = r * 10 + ch - '0';
335 		} else if (state == 1) {
336 		    p = p / 10;
337 		    r = r + p * (ch - '0');
338 		}
339 	    } else if (ch == '.') {
340 		if (state == 0) {
341 		    state = 1;
342 		} else {
343 		    return sign * r;
344 		}
345 	    } else if (ch == 'e' || ch == 'E') {
346 		long e = (int)parseLong(s.substring(i + 1), 10);
347 		return sign * r * Math.pow(10, e);
348 	    } else {
349 		return sign * r;
350 	    }
351 	    i++;
352 	}
353 	return sign * r;
354     }
355 
356     /**
357      * Formats a double into a string (like sprintf in C)
358      * @param x the number to format
359      * @return the formatted string
360      * @exception IllegalArgumentException if bad argument
361      */
form(double x)362     public String form(double x) {
363 	String r;
364 	if (precision < 0) {
365 	    precision = 6;
366 	}
367 
368 	int s = 1;
369 	if (x < 0) {
370 	    x = -x;
371 	    s = -1;
372 	}
373 
374 	if (fmt == 'f') {
375 	    r = fixed_format(x);
376 	} else if (fmt == 'e' || fmt == 'E' || fmt == 'g' || fmt == 'G') {
377 	    r = exp_format(x);
378 	} else {
379 	    throw new java.lang.IllegalArgumentException();
380 	}
381 
382 	return pad(sign(s, r));
383     }
384 
385     /**
386      * Formats a long integer into a string (like sprintf in C)
387      * @param x the number to format
388      * @return the formatted string
389      */
form(long x)390     public String form(long x) {
391 	String r;
392 	int s = 0;
393 	if (fmt == 'd' || fmt == 'i') {
394 	    s = 1;
395 	    if (x < 0) {
396 		x = -x; s = -1;
397 	    }
398 	    r = "" + x;
399 	} else if (fmt == 'o') {
400 	    r = convert(x, 3, 7, "01234567");
401 	} else if (fmt == 'x') {
402 	    r = convert(x, 4, 15, "0123456789abcdef");
403 	} else if (fmt == 'X') {
404 	    r = convert(x, 4, 15, "0123456789ABCDEF");
405 	} else {
406 	    throw new java.lang.IllegalArgumentException();
407 	}
408 	return pad(sign(s, r));
409     }
410 
411     /**
412      * Formats a character into a string (like sprintf in C)
413      * @param x the value to format
414      * @return the formatted string
415      */
form(char c)416     public String form(char c) {
417 	if (fmt != 'c') {
418 	    throw new java.lang.IllegalArgumentException();
419 	}
420 	String r = "" + c;
421 	return pad(r);
422     }
423 
424     /**
425      * Formats a string into a larger string (like sprintf in C)
426      * @param x the value to format
427      * @return the formatted string
428      */
form(String s)429     public String form(String s) {
430 	if (fmt != 's') {
431 	    throw new java.lang.IllegalArgumentException();
432 	}
433 
434 	if (precision >= 0) {
435 	    s = s.substring(0, precision);
436 	}
437 	return pad(s);
438     }
439 
repeat(char c, int n)440     private static String repeat(char c, int n) {
441 	if (n <= 0) {
442 	    return "";
443 	}
444 	StringBuffer s = new StringBuffer(n);
445 	for (int i = 0; i < n; i++) {
446 	    s.append(c);
447 	}
448 	return s.toString();
449     }
450 
convert(long x, int n, int m, String d)451     private static String convert(long x, int n, int m, String d) {
452 	if (x == 0) {
453 	    return "0";
454 	}
455 	String r = "";
456 	while (x != 0) {
457 	    r = d.charAt((int)(x & m)) + r;
458 	    x = x >>> n;
459 	}
460 	return r;
461     }
462 
pad(String r)463     private String pad(String r) {
464 	String p = repeat(' ', width - r.length());
465 	if (left_align) {
466 	    return pre + r + p + post;
467 	} else {
468 	    return pre + p + r + post;
469 	}
470     }
471 
sign(int s, String r)472     private String sign(int s, String r) {
473 	String p = "";
474 	if (s < 0) {
475 	    p = "-";
476 	} else if (s > 0) {
477 	    if (show_plus) {
478 		p = "+";
479 	    } else if (show_space) {
480 		p = " ";
481 	    }
482 	} else {
483 	    if (fmt == 'o' && alternate && r.length() > 0 &&
484 		r.charAt(0) != '0') {
485 		p = "0";
486 	    } else if (fmt == 'x' && alternate) {
487 		p = "0x";
488 	    } else if (fmt == 'X' && alternate) {
489 		p = "0X";
490 	    }
491 	}
492 
493 	int w = 0;
494 	if (leading_zeroes) {
495 	    w = width;
496 	} else if ((fmt == 'd' || fmt == 'i' || fmt == 'x' ||
497 	    fmt == 'X' || fmt == 'o') && precision > 0) {
498 	    w = precision;
499 	}
500 
501 	return p + repeat('0', w - p.length() - r.length()) + r;
502     }
503 
504 
fixed_format(double d)505     private String fixed_format(double d) {
506 	String f = "";
507 
508 	if (d > 0x7FFFFFFFFFFFFFFFL) {
509 	    return exp_format(d);
510 	}
511 
512 	long l = (long)(precision == 0 ? d + 0.5 : d);
513 	f = f + l;
514 
515 	double fr = d - l; // fractional part
516 	if (fr >= 1 || fr < 0) {
517 	    return exp_format(d);
518 	}
519 
520 	return f + frac_part(fr);
521     }
522 
frac_part(double fr)523     private String frac_part(double fr) {
524 	// precondition: 0 <= fr < 1
525 	String z = "";
526 	if (precision > 0) {
527 	    double factor = 1;
528 	    String leading_zeroes = "";
529 	    for (int i = 1; i <= precision && factor <= 0x7FFFFFFFFFFFFFFFL;
530 		i++) {
531 		factor *= 10;
532 		leading_zeroes = leading_zeroes + "0";
533 	    }
534 	    long l = (long) (factor * fr + 0.5);
535 
536 	    z = leading_zeroes + l;
537 	    z = z.substring(z.length() - precision, z.length());
538 	}
539 
540 
541 	if (precision > 0 || alternate) {
542 	    z = "." + z;
543 	}
544 
545 	if ((fmt == 'G' || fmt == 'g') && !alternate) {
546 	    // remove trailing zeroes and decimal point
547 	    int t = z.length() - 1;
548 	    while (t >= 0 && z.charAt(t) == '0') {
549 		t--;
550 	    }
551 	    if (t >= 0 && z.charAt(t) == '.') {
552 		t--;
553 	    }
554 	    z = z.substring(0, t + 1);
555 	}
556 	return z;
557     }
558 
exp_format(double d)559     private String exp_format(double d) {
560 	String f = "";
561 	int e = 0;
562 	double dd = d;
563 	double factor = 1;
564 
565 	while (dd > 10) {
566 	    e++; factor /= 10; dd = dd / 10;
567 	}
568 
569 	while (dd < 1) {
570 	    e--; factor *= 10; dd = dd * 10;
571 	}
572 
573 	if ((fmt == 'g' || fmt == 'G') && e >= -4 && e < precision) {
574 	    return fixed_format(d);
575 	}
576 
577 	d = d * factor;
578 	f = f + fixed_format(d);
579 
580 	if (fmt == 'e' || fmt == 'g') {
581 	    f = f + "e";
582 	} else {
583 	    f = f + "E";
584 	}
585 
586 	String p = "000";
587 	if (e >= 0)  {
588 	    f = f + "+";
589 	    p = p + e;
590 	} else {
591 	    f = f + "-";
592 	    p = p + (-e);
593 	}
594 
595 	return f + p.substring(p.length() - 3, p.length());
596     }
597 
598     private int width;
599     private int precision;
600     private String pre;
601     private String post;
602     private boolean leading_zeroes;
603     private boolean show_plus;
604     private boolean alternate;
605     private boolean show_space;
606     private boolean left_align;
607     private char fmt; // one of cdeEfgGiosxXos
608 }
609