From patchwork Fri Apr 1 09:33:35 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matteo Croce X-Patchwork-Id: 604638 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3qbx682fVVz9sC3 for ; Fri, 1 Apr 2016 20:34:08 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=sourceware.org header.i=@sourceware.org header.b=uCIYROYJ; dkim-atps=neutral DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:cc:subject:date:message-id; q=dns; s= default; b=AJH+SZSjBgyLocFo3jY8KQ7gcerq0h2jx7dmLvUflXPjfArcDVuzK 7CiNqqHvE5q22jMcei06hXzQM6FJHMZoFjZnUsmZLzl33qKde2A3Kvmv7IqHHlNG nzGniypMK65AWFJmf7bEMtVeol4XrMb6y2Zbzt2u7xY72knUTzwXcA= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:cc:subject:date:message-id; s=default; bh=RmpXttaDT+KyiAvXPHUhgr79i6c=; b=uCIYROYJ4dKkgwr5rvyQkfk3mm8X F9r2hc2wDBJIcG5AjFfypAt+woS//cQnfO9XQjOoG5fx65ZyFFTP1yzNHf+36r// nBRfOdXJQ2rNpHBWkKtkc9tiOnwrYqpO4eVmZ2GFZV2PD0xjfdwQC1mVySvPzOf8 VLg3KKqdM/n4Sbo= Received: (qmail 76126 invoked by alias); 1 Apr 2016 09:33:58 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 76111 invoked by uid 89); 1 Apr 2016 09:33:57 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=3.1 required=5.0 tests=BAYES_50, FREEMAIL_ENVFROM_END_DIGIT, FREEMAIL_FROM, LIKELY_SPAM_BODY, RCVD_IN_DNSWL_LOW, RCVD_IN_SORBS_WEB, SPF_PASS autolearn=no version=3.3.2 spammy=century, Latin, latin, adoption X-HELO: mail-pf0-f178.google.com X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id; bh=rOjjp75BdONH86yVVCm28zbz0QbTNouz3UoF7CcQ7Iw=; b=TDVlDqZPlqlgKxgWrSZjkFx1GqoHc2jPVWCwrn8D8k4WyYqexSlhhUBl9kg3wiT32O 6BWxI/aImlbc0CCJ6mVx7BCAjZBGxrgKxRgJ92TfuV8n92wrg8lSPgbUpyqJP1t6U0I4 GaRlKJEa5ELIEuKbVVHPSDJtWQnmximxQqI7kFS/u85BZK0UmJFtC3KxpjI2BMfJrhI7 zPQj7wvCV9tolcgsuyD1jbXHJgNTWZcOdD03X05LyxuDDJTwsRrxDDPskvJDS064q0Os piKj1yfkHKVp4aDpaV7QFd6VosUmqwa8zy9pwKlnONQMLSdIEk2J60v2WEzmT/OasvYc EGvg== X-Gm-Message-State: AD7BkJJ2ul5oPu+NxPwb0Sp9gNYLLBdzxAAzSyaInpzlUoUhj2RjHZI6fN7xFWy8MaARRA== X-Received: by 10.98.10.29 with SMTP id s29mr29730965pfi.166.1459503233870; Fri, 01 Apr 2016 02:33:53 -0700 (PDT) From: Matteo Croce To: libc-alpha@sourceware.org Cc: Matteo Croce Subject: [PATCH] Add Roman numerals in *printf Date: Fri, 1 Apr 2016 11:33:35 +0200 Message-Id: <1459503215-21039-1-git-send-email-matteo@openwrt.org> 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;