public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
From: Matteo Croce <matteo@openwrt.org>
To: libc-alpha@sourceware.org
Cc: Matteo Croce <matteo@openwrt.org>
Subject: [PATCH] Add Roman numerals in *printf
Date: Fri, 01 Apr 2016 09:33:00 -0000	[thread overview]
Message-ID: <1459503215-21039-1-git-send-email-matteo@openwrt.org> (raw)

Since Fibonacci adoption in the 13th century, Arabic numbers have become
the standard numeric system used in every culture.
However, older numeral systems are yet used in many contexts,
for example, ancient Roman numerals are often used to represent
software version, protocol revision or even movie chapters.

This patch adds the `%r' modifier in all the *printf functions,
which can be used to represent a number in Roman numerals.
This has two big advantages:

first of all there is no need to hardcode text strings in
the code which needs to be updated on every version change.
e.g. printf("System V booting") or printf("Text Editor for OS X")
can be replaced with:
printf("System %R booting", ver) and printf("Text Editor for OS %R", ver)
and the right version string is generated at runtime.

The second advantage is that Roman numerals are very lengthy and even a small
16 bit number can occupy up to 15 bytes,
thus leading to a 650% increase in code size.

For a technical limitation the maximum number that can be represented in Roman
numerals are limited to 3999, but archaeologist agrees that ancient Romans uses
a superscript to multiply by 1000 and a vertical line to multiply to 1 million.

To avoid using Unicode characters in such basic IO routines, a pair of _ are
used to represent a thousand value, e.g. _XX_ for 20 000
and a | to represent millions, e.g. |L| for 50 000 000. This increase the
maximum integer that can be represented to 499 999 999 which should be
a reasonable value for our concern, bigger numbers will print as `(infinitum)'

Another issue is the zero value which was missing in the Roman system,
indeed the zero sign was "imported" by Fibonacci from North Africa,
so when trying to print 0 as Roman numeral, the string `(nihil)',
which is the Latin word for `nothing', will printed like `(null)` for pointers.

Roman numerals can be generated both in upper and lower case
respectively, with the `%R' and `%r' modifier.
---
 stdio-common/vfprintf.c | 109 +++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 93 insertions(+), 16 deletions(-)

diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c
index 6829d4d..e85c29c 100644
--- a/stdio-common/vfprintf.c
+++ b/stdio-common/vfprintf.c
@@ -215,20 +215,20 @@ static const uint8_t jump_table[] =
     /* '4' */  8, /* '5' */  8, /* '6' */  8, /* '7' */  8,
     /* '8' */  8, /* '9' */  8,            0,            0,
 	       0,            0,            0,            0,
-	       0, /* 'A' */ 26,            0, /* 'C' */ 25,
-	       0, /* 'E' */ 19, /* F */   19, /* 'G' */ 19,
-	       0, /* 'I' */ 29,            0,            0,
+	       0, /* 'A' */ 27,            0, /* 'C' */ 26,
+	       0, /* 'E' */ 20, /* F */   20, /* 'G' */ 20,
+	       0, /* 'I' */ 30,            0,            0,
     /* 'L' */ 12,            0,            0,            0,
-	       0,            0,            0, /* 'S' */ 21,
+	       0,            0, /* 'R' */ 19, /* 'S' */ 22,
 	       0,            0,            0,            0,
     /* 'X' */ 18,            0, /* 'Z' */ 13,            0,
 	       0,            0,            0,            0,
-	       0, /* 'a' */ 26,            0, /* 'c' */ 20,
-    /* 'd' */ 15, /* 'e' */ 19, /* 'f' */ 19, /* 'g' */ 19,
-    /* 'h' */ 10, /* 'i' */ 15, /* 'j' */ 28,            0,
-    /* 'l' */ 11, /* 'm' */ 24, /* 'n' */ 23, /* 'o' */ 17,
-    /* 'p' */ 22, /* 'q' */ 12,            0, /* 's' */ 21,
-    /* 't' */ 27, /* 'u' */ 16,            0,            0,
+	       0, /* 'a' */ 27,            0, /* 'c' */ 21,
+    /* 'd' */ 15, /* 'e' */ 20, /* 'f' */ 20, /* 'g' */ 20,
+    /* 'h' */ 10, /* 'i' */ 15, /* 'j' */ 29,            0,
+    /* 'l' */ 11, /* 'm' */ 25, /* 'n' */ 24, /* 'o' */ 17,
+    /* 'p' */ 23, /* 'q' */ 12, /* 'r' */ 19, /* 's' */ 22,
+    /* 't' */ 28, /* 'u' */ 16,            0,            0,
     /* 'x' */ 18,            0, /* 'z' */ 13
   };
 
@@ -269,7 +269,7 @@ static const uint8_t jump_table[] =
 
 #define STEP0_3_TABLE							      \
     /* Step 0: at the beginning.  */					      \
-    static JUMP_TABLE_TYPE step0_jumps[30] =				      \
+    static JUMP_TABLE_TYPE step0_jumps[31] =				      \
     {									      \
       REF (form_unknown),						      \
       REF (flag_space),		/* for ' ' */				      \
@@ -290,6 +290,7 @@ static const uint8_t jump_table[] =
       REF (form_unsigned),	/* for 'u' */				      \
       REF (form_octal),		/* for 'o' */				      \
       REF (form_hexa),		/* for 'X', 'x' */			      \
+      REF (form_roman),		/* for 'R', 'r' */			      \
       REF (form_float),		/* for 'E', 'e', 'F', 'f', 'G', 'g' */	      \
       REF (form_character),	/* for 'c' */				      \
       REF (form_string),	/* for 's', 'S' */			      \
@@ -303,7 +304,7 @@ static const uint8_t jump_table[] =
       REF (flag_i18n),		/* for 'I' */				      \
     };									      \
     /* Step 1: after processing width.  */				      \
-    static JUMP_TABLE_TYPE step1_jumps[30] =				      \
+    static JUMP_TABLE_TYPE step1_jumps[31] =				      \
     {									      \
       REF (form_unknown),						      \
       REF (form_unknown),	/* for ' ' */				      \
@@ -324,6 +325,7 @@ static const uint8_t jump_table[] =
       REF (form_unsigned),	/* for 'u' */				      \
       REF (form_octal),		/* for 'o' */				      \
       REF (form_hexa),		/* for 'X', 'x' */			      \
+      REF (form_roman),		/* for 'R', 'r' */			      \
       REF (form_float),		/* for 'E', 'e', 'F', 'f', 'G', 'g' */	      \
       REF (form_character),	/* for 'c' */				      \
       REF (form_string),	/* for 's', 'S' */			      \
@@ -337,7 +339,7 @@ static const uint8_t jump_table[] =
       REF (form_unknown)        /* for 'I' */				      \
     };									      \
     /* Step 2: after processing precision.  */				      \
-    static JUMP_TABLE_TYPE step2_jumps[30] =				      \
+    static JUMP_TABLE_TYPE step2_jumps[31] =				      \
     {									      \
       REF (form_unknown),						      \
       REF (form_unknown),	/* for ' ' */				      \
@@ -358,6 +360,7 @@ static const uint8_t jump_table[] =
       REF (form_unsigned),	/* for 'u' */				      \
       REF (form_octal),		/* for 'o' */				      \
       REF (form_hexa),		/* for 'X', 'x' */			      \
+      REF (form_roman),		/* for 'R', 'r' */			      \
       REF (form_float),		/* for 'E', 'e', 'F', 'f', 'G', 'g' */	      \
       REF (form_character),	/* for 'c' */				      \
       REF (form_string),	/* for 's', 'S' */			      \
@@ -371,7 +374,7 @@ static const uint8_t jump_table[] =
       REF (form_unknown)        /* for 'I' */				      \
     };									      \
     /* Step 3a: after processing first 'h' modifier.  */		      \
-    static JUMP_TABLE_TYPE step3a_jumps[30] =				      \
+    static JUMP_TABLE_TYPE step3a_jumps[31] =				      \
     {									      \
       REF (form_unknown),						      \
       REF (form_unknown),	/* for ' ' */				      \
@@ -392,6 +395,7 @@ static const uint8_t jump_table[] =
       REF (form_unsigned),	/* for 'u' */				      \
       REF (form_octal),		/* for 'o' */				      \
       REF (form_hexa),		/* for 'X', 'x' */			      \
+      REF (form_roman),		/* for 'R', 'r' */			      \
       REF (form_unknown),	/* for 'E', 'e', 'F', 'f', 'G', 'g' */	      \
       REF (form_unknown),	/* for 'c' */				      \
       REF (form_unknown),	/* for 's', 'S' */			      \
@@ -405,7 +409,7 @@ static const uint8_t jump_table[] =
       REF (form_unknown)        /* for 'I' */				      \
     };									      \
     /* Step 3b: after processing first 'l' modifier.  */		      \
-    static JUMP_TABLE_TYPE step3b_jumps[30] =				      \
+    static JUMP_TABLE_TYPE step3b_jumps[31] =				      \
     {									      \
       REF (form_unknown),						      \
       REF (form_unknown),	/* for ' ' */				      \
@@ -426,6 +430,7 @@ static const uint8_t jump_table[] =
       REF (form_unsigned),	/* for 'u' */				      \
       REF (form_octal),		/* for 'o' */				      \
       REF (form_hexa),		/* for 'X', 'x' */			      \
+      REF (form_roman),		/* for 'R', 'r' */			      \
       REF (form_float),		/* for 'E', 'e', 'F', 'f', 'G', 'g' */	      \
       REF (form_character),	/* for 'c' */				      \
       REF (form_string),	/* for 's', 'S' */			      \
@@ -441,7 +446,7 @@ static const uint8_t jump_table[] =
 
 #define STEP4_TABLE							      \
     /* Step 4: processing format specifier.  */				      \
-    static JUMP_TABLE_TYPE step4_jumps[30] =				      \
+    static JUMP_TABLE_TYPE step4_jumps[31] =				      \
     {									      \
       REF (form_unknown),						      \
       REF (form_unknown),	/* for ' ' */				      \
@@ -462,6 +467,7 @@ static const uint8_t jump_table[] =
       REF (form_unsigned),	/* for 'u' */				      \
       REF (form_octal),		/* for 'o' */				      \
       REF (form_hexa),		/* for 'X', 'x' */			      \
+      REF (form_roman),		/* for 'R', 'r' */			      \
       REF (form_float),		/* for 'E', 'e', 'F', 'f', 'G', 'g' */	      \
       REF (form_character),	/* for 'c' */				      \
       REF (form_string),	/* for 's', 'S' */			      \
@@ -741,6 +747,11 @@ static const uint8_t jump_table[] =
 	  break;							      \
 	}								      \
 									      \
+    LABEL (form_roman):							      \
+      /* Ancient Roman / Latin number. */				      \
+      roman(s, va_arg (ap, int), spec == L_('R'));			      \
+      break;							              \
+									      \
     LABEL (form_float):							      \
       {									      \
 	/* Floating-point number.  This is handled by printf_fp.c.  */	      \
@@ -1210,6 +1221,72 @@ static const uint8_t jump_table[] =
       break;
 #endif
 
+static const int numbers[] = { 1000, 900, 500, 400, 100, 90,
+			       50, 40, 10, 9, 5, 4, 1 };
+static const CHAR_T *_rul[] = { L_("M"), L_("CM"), L_("D"), L_("CD"), L_("C"),
+			      L_("XC"), L_("L"), L_("XL"), L_("X"),
+			      L_("IX"), L_("V"), L_("IV"), L_("I") };
+static const CHAR_T *_rll[] = { L_("m"), L_("cm"), L_("d"), L_("cd"), L_("c"),
+			      L_("xc"), L_("l"), L_("xl"), L_("x"),
+			      L_("ix"), L_("v"), L_("iv"), L_("i") };
+
+static void roman(FILE *s, long int num, int upper_case)
+{
+  /* used by outchar */
+  int done = 0;
+
+  const CHAR_T **letters = upper_case ? _rul : _rll;
+
+  if(!num)
+    {
+      outstring(L_("(nihil)"), 7);
+      return;
+    }
+
+  if(num < 0)
+    {
+      outchar(L_('-'));
+      num = -num;
+    }
+
+  if(num > 499999999)
+    {
+      outstring(L_("(infinitum)"), 11);
+      return;
+    }
+
+  if(num > 99999)
+    {
+      outchar(L_('|'));
+      roman(s, num / 100000, upper_case);
+      outchar(L_('|'));
+      num %= 100000;
+    }
+
+  if(num > 4999)
+    {
+      outchar(L_('_'));
+      roman(s, num / 1000, upper_case);
+      outchar(L_('_'));
+      num %= 1000;
+    }
+
+  for (int i = 0; i < sizeof(numbers) / sizeof(*numbers); i++)
+    {
+      while (num >= numbers[i])
+	{
+	  outchar(letters[i][0]);
+	  if(letters[i][1])
+	  outchar(letters[i][1]);
+	  num -= numbers[i];
+	}
+    }
+
+  /* suppress warnings */
+  all_done:
+  return;
+}
+
 /* Helper function to provide temporary buffering for unbuffered streams.  */
 static int buffered_vfprintf (FILE *stream, const CHAR_T *fmt, va_list)
      __THROW __attribute__ ((noinline)) internal_function;
-- 
2.5.0

             reply	other threads:[~2016-04-01  9:33 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-04-01  9:33 Matteo Croce [this message]
2016-04-01 12:52 ` Adhemerval Zanella
2016-04-01 14:22 ` Carlos O'Donell
2016-04-02  5:01 ` Paul Eggert
2016-04-04 22:59 ` Martin Sebor

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1459503215-21039-1-git-send-email-matteo@openwrt.org \
    --to=matteo@openwrt.org \
    --cc=libc-alpha@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).