/* $Header$*/ /* * Copyright © 2002 Keith Packard and Bart Massey. * All Rights Reserved. See the file COPYING in this directory * for licensing information. */ extend namespace File { int default_output_precision = -1; int infinite_output_precision = -2; public exception too_many_arguments (string format, poly[*] args) /* * Raised when too many arguments are passed to a printf * function */; public exception too_few_arguments (string format, poly[*] args) /* * Raised when too few arguments are passed to a printf * function */; void to_utf8 (int c, void(int c) f) { int bits; if (c < 0x0) return; else if (c < 0x80) { f(c); bits= -6; } else if (c < 0x800) { f(((c >> 6) & 0x1F) | 0xC0); bits= 0; } else if (c < 0x10000) { f(((c >> 12) & 0x0F) | 0xE0); bits= 6; } else if (c < 0x200000) { f(((c >> 18) & 0x07) | 0xF0); bits= 12; } else if (c < 0x4000000) { f(((c >> 24) & 0x03) | 0xF8); bits= 18; } else if (c < 0x80000000) { f(((c >> 30) & 0x01) | 0xFC); bits= 24; } else return; for ( ; bits >= 0; bits-= 6) f(((c >> bits) & 0x3F) | 0x80); } public void vfprintf (file f, string format, poly[*] args) /* * print 'args' to 'f' according to 'format' */ { /* * A private namespace to walk the format string */ namespace fmt { int i = 0; int len = String::length(format); /* * Return whether the end of the string has been reached */ public bool done () { return i >= len; } /* * Return current character, raising an exception if * the end of the string has been reached */ public int c() { if (done()) raise invalid_argument ("invalid format", 1, format); return format[i]; } /* * Step to next character */ public void step () { ++i; } /* * Return true and step if current character matches 'm' */ public bool match(int m) { if (c() == m) { step(); return true; } return false; } /* * Return a number from the format string */ public int number () { int n = 0; while ('0' <= c() && c() <= '9') { n = n * 10 + c() - '0'; step(); } return n; } } namespace arg { int n = 0; public poly next () { if (n == dim(args)) raise too_few_arguments (format, args); return args[n++]; } public void done () { if (n != dim(args)) raise too_many_arguments (format, args); } } while (!fmt::done() && !File::error (f)) { if (fmt::match ('%') && fmt::c() != '%') { /* * width */ string fill = " "; if (fmt::match('0')) fill = "0"; int width = 0; if (fmt::match('*')) width = arg::next (); else { int sign = 1; if (fmt::match('-')) sign = -1; width = fmt::number () * sign; } /* * precision */ int prec = default_output_precision; if (fmt::match ('.')) { if (fmt::match('-')) prec = infinite_output_precision; else if (fmt::match('*')) prec = arg::next (); else prec = fmt::number (); } /* * typecheck and select base */ const struct { string formats; bool(poly) test; string message; int base; }[*] fmts = { { formats = "s", test = is_string, message = "string", base = 10 }, { formats = "dD", test = is_int, message = "int", base = 10 }, { formats = "bB", test = is_int, message = "int", base = 2 }, { formats = "oO", test = is_int, message = "int", base = 8 }, { formats = "xX", test = is_int, message = "int", base = 16 }, { formats = "efEF", test = is_number, message = "real", base = 10 } }; poly this_arg = arg::next(); string this_fmt = String::new (fmt::c()); int this_base = 10; for (int i = 0; i < dim (fmts); i++) if (String::index (fmts[i].formats, this_fmt) >= 0) { if (!fmts[i].test (this_arg)) raise invalid_argument (fmts[i].message + "format with non-" + fmts[i].message, 1, this_arg); this_base = fmts[i].base; break; } print (f, this_arg, this_fmt, this_base, width, prec, fill); } else putc (fmt::c(), f); fmt::step (); } arg::done (); } public void fprintf (file f, string format, poly args...) /* * print 'args' to 'f' according to 'format' */ { vfprintf (f, format, args); } public void vprintf (string format, poly[*] args) /* * print 'args' to stdout according to 'format' */ { vfprintf (stdout, format, args); } public string vsprintf (string format, poly[*] args) /* * print 'args' to a string according to 'format' */ { file f = File::string_write (); vfprintf (f, format, args); return File::string_string (f); } public namespace PrintfGlobals { public void printf (string format, poly args...) /* * print 'args' to stdout according to 'format' */ { vprintf (format, args); } public string sprintf (string format, poly args...) /* * print 'args' to a string according to 'format' */ { return vsprintf (format, args); } } public import PrintfGlobals; } public import File::PrintfGlobals;