clocale.pp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2008 by the Free Pascal development team.
  4. Init rtl formating variables based on libc locales
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. { Initial implementation by petr kristan }
  12. unit clocale;
  13. {$mode objfpc}
  14. interface
  15. {$ifdef localedebug}
  16. // for easier debugging, allows to print untransformed values in test
  17. Type TOrgFormatSettings = record
  18. ShortDateFormat,
  19. LongDateFormat ,
  20. ShortTimeFormat,
  21. LongTimeFormat ,
  22. CurrencyString1,
  23. CurrencyString2: string;
  24. end;
  25. var OrgFormatSettings : TOrgFormatSettings;
  26. {$endif}
  27. implementation
  28. {$linklib c}
  29. Uses
  30. SysUtils, unixtype, initc;
  31. Const
  32. {$if defined(BSD) or defined(SUNOS) or defined(aix)}
  33. // Darwin, FreeBSD, Solaris, AIX. Note the lead underscores are added.
  34. {$i clocale.inc}
  35. {$else}
  36. // checked for Linux only, but might be general glibc.
  37. __LC_CTYPE = 0;
  38. __LC_NUMERIC = 1;
  39. __LC_TIME = 2;
  40. __LC_COLLATE = 3;
  41. __LC_MONETARY = 4;
  42. __LC_MESSAGES = 5;
  43. __LC_ALL = 6;
  44. ABDAY_1 = (__LC_TIME shl 16);
  45. DAY_1 = (ABDAY_1)+7;
  46. ABMON_1 = (ABDAY_1)+14;
  47. MON_1 = (ABDAY_1)+26;
  48. AM_STR = (ABDAY_1)+38;
  49. PM_STR = (ABDAY_1)+39;
  50. D_T_FMT = (ABDAY_1)+40;
  51. D_FMT = (ABDAY_1)+41;
  52. T_FMT = (ABDAY_1)+42;
  53. T_FMT_AMPM = (ABDAY_1)+43;
  54. __DECIMAL_POINT = (__LC_NUMERIC shl 16);
  55. RADIXCHAR = __DECIMAL_POINT;
  56. __THOUSANDS_SEP = (__DECIMAL_POINT)+1;
  57. __INT_CURR_SYMBOL = (__LC_MONETARY shl 16);
  58. __CURRENCY_SYMBOL = (__INT_CURR_SYMBOL)+1;
  59. __MON_DECIMAL_POINT = (__INT_CURR_SYMBOL)+2;
  60. __MON_THOUSANDS_SEP = (__INT_CURR_SYMBOL)+3;
  61. __MON_GROUPING = (__INT_CURR_SYMBOL)+4;
  62. __POSITIVE_SIGN = (__INT_CURR_SYMBOL)+5;
  63. __NEGATIVE_SIGN = (__INT_CURR_SYMBOL)+6;
  64. __INT_FRAC_DIGITS = (__INT_CURR_SYMBOL)+7;
  65. __FRAC_DIGITS = (__INT_CURR_SYMBOL)+8;
  66. __P_CS_PRECEDES = (__INT_CURR_SYMBOL)+9;
  67. __P_SEP_BY_SPACE = (__INT_CURR_SYMBOL)+10;
  68. __N_CS_PRECEDES = (__INT_CURR_SYMBOL)+11;
  69. __N_SEP_BY_SPACE = (__INT_CURR_SYMBOL)+12;
  70. __P_SIGN_POSN = (__INT_CURR_SYMBOL)+13;
  71. __N_SIGN_POSN = (__INT_CURR_SYMBOL)+14;
  72. _NL_MONETARY_CRNCYSTR = (__INT_CURR_SYMBOL)+15;
  73. {$endif}
  74. {$ifdef netbsd}
  75. { NetBSD has a new setlocale function defined in /usr/include/locale.h
  76. that should be used }
  77. function setlocale(category: cint; locale: pchar): pchar; cdecl; external clib name '__setlocale_mb_len_max_32';
  78. {$else}
  79. function setlocale(category: cint; locale: pchar): pchar; cdecl; external clib name 'setlocale';
  80. {$endif}
  81. function nl_langinfo(__item: cint):Pchar;cdecl;external clib name 'nl_langinfo';
  82. procedure GetFormatSettings(out fmts: TFormatSettings);
  83. function GetLocaleStr(item: cint): string;
  84. begin
  85. GetLocaleStr := AnsiString(nl_langinfo(item));
  86. end;
  87. function GetLocaleChar(item: cint): char;
  88. begin
  89. GetLocaleChar := nl_langinfo(item)^;
  90. end;
  91. procedure OmitModifiers(const s: string; var i: integer);
  92. var
  93. l: Integer;
  94. begin
  95. l := Length(s);
  96. //possible flag, with specifier or modifier - glibc exension
  97. while (i<=l) and (s[i] in ['0'..'9', '_', '-', '^', '#', 'E', 'O']) do
  98. inc(i);
  99. end;
  100. function FindSeparator(const s: string; Def: char): char;
  101. var
  102. i: integer;
  103. begin
  104. FindSeparator := Def;
  105. i := Pos('%', s);
  106. if i=0 then
  107. Exit;
  108. inc(i);
  109. OmitModifiers(s, i);
  110. inc(i);
  111. if i<=Length(s) then
  112. FindSeparator := s[i];
  113. end;
  114. function TransformFormatStr(const s: string): string;
  115. var
  116. i, l: integer;
  117. clock12:boolean;
  118. begin
  119. clock12:=false; // should ampm get appended?
  120. TransformFormatStr := '';
  121. i := 1;
  122. l := Length(s);
  123. while i<=l do begin
  124. if s[i]='%' then begin
  125. inc(i);
  126. OmitModifiers(s, i);
  127. if i>l then
  128. Exit;
  129. case s[i] of
  130. 'a': TransformFormatStr := TransformFormatStr + 'ddd';
  131. 'A': TransformFormatStr := TransformFormatStr + 'dddd';
  132. 'b': TransformFormatStr := TransformFormatStr + 'mmm';
  133. 'B': TransformFormatStr := TransformFormatStr + 'mmmm';
  134. 'c': TransformFormatStr := TransformFormatStr + 'c';
  135. //'C':
  136. 'd': TransformFormatStr := TransformFormatStr + 'dd';
  137. 'D': TransformFormatStr := TransformFormatStr + 'mm"/"dd"/"yy';
  138. 'e': TransformFormatStr := TransformFormatStr + 'd';
  139. 'F': TransformFormatStr := TransformFormatStr + 'yyyy-mm-dd';
  140. 'g': TransformFormatStr := TransformFormatStr + 'yy';
  141. 'G': TransformFormatStr := TransformFormatStr + 'yyyy';
  142. 'h': TransformFormatStr := TransformFormatStr + 'mmm';
  143. 'H': TransformFormatStr := TransformFormatStr + 'hh';
  144. 'I': begin
  145. TransformFormatStr := TransformFormatStr + 'hh';
  146. clock12:=true;
  147. end;
  148. //'j':
  149. 'k': TransformFormatStr := TransformFormatStr + 'h';
  150. 'l': begin
  151. TransformFormatStr := TransformFormatStr + 'h';
  152. clock12:=true;
  153. end;
  154. 'm': TransformFormatStr := TransformFormatStr + 'mm';
  155. 'M': TransformFormatStr := TransformFormatStr + 'nn';
  156. 'n': TransformFormatStr := TransformFormatStr + sLineBreak;
  157. 'p','P':
  158. begin
  159. TransformFormatStr := TransformFormatStr + 'ampm';
  160. clock12:=false;
  161. end;
  162. 'r': begin
  163. TransformFormatStr := TransformFormatStr + 'hh:nn:ss';
  164. clock12:=true;
  165. end;
  166. 'R': TransformFormatStr := TransformFormatStr + 'hh:nn';
  167. //'s':
  168. 'S': TransformFormatStr := TransformFormatStr + 'ss';
  169. 't': TransformFormatStr := TransformFormatStr + #9;
  170. 'T': TransformFormatStr := TransformFormatStr + 'hh:nn:ss';
  171. //'u':
  172. //'U':
  173. //'V':
  174. //'w':
  175. //'W':
  176. 'x': TransformFormatStr := TransformFormatStr + 'ddddd';
  177. 'X': TransformFormatStr := TransformFormatStr + 't';
  178. 'y': TransformFormatStr := TransformFormatStr + 'yy';
  179. 'Y': TransformFormatStr := TransformFormatStr + 'yyyy';
  180. //'z':
  181. //'Z':
  182. '%': TransformFormatStr := TransformFormatStr + '%';
  183. end;
  184. end else
  185. TransformFormatStr := TransformFormatStr + s[i];
  186. inc(i);
  187. end;
  188. i:=length(TransformFormatStr);
  189. if clock12 and (i>0) then
  190. begin
  191. if transformformatstr[i]<>' ' then
  192. TransformFormatStr := TransformFormatStr + ' ';
  193. TransformFormatStr := TransformFormatStr + 'ampm';
  194. end;
  195. end;
  196. const
  197. // sign prec sep
  198. NegFormatsTable: array [0..4, 0..1, 0..1] of byte = (
  199. ( (4, 15), (0, 14) ), //Parentheses surround the quantity and currency_symbol
  200. ( (5, 8), (1, 9) ), //The sign string precedes the quantity and currency_symbol
  201. ( (7, 10), (3, 11) ), //The sign string follows the quantity and currency_symbol
  202. ( (6, 13), (1, 9) ), //The sign string immediately precedes the currency_symbol
  203. ( (7, 10), (2, 12) ) //The sign string immediately follows the currency_symbol
  204. );
  205. var
  206. i: integer;
  207. prec, sep, signp: byte;
  208. {$if defined(BSD) or defined(SUNOS) or defined(aix)}
  209. plocale : plconv;
  210. {$ENDIF}
  211. begin
  212. setlocale(__LC_ALL,'');
  213. for i := 1 to 12 do
  214. begin
  215. fmts.ShortMonthNames[i]:=GetLocaleStr(ABMON_1+i-1);
  216. fmts.LongMonthNames[i]:=GetLocaleStr(MON_1+i-1);
  217. end;
  218. for i := 1 to 7 do
  219. begin
  220. fmts.ShortDayNames[i]:=GetLocaleStr(ABDAY_1+i-1);
  221. fmts.LongDayNames[i]:=GetLocaleStr(DAY_1+i-1);
  222. end;
  223. //Date stuff
  224. fmts.ShortDateFormat := GetLocaleStr(D_FMT);
  225. {$ifdef localedebug}
  226. OrgFormatSettings.ShortDateFormat:=fmts.shortdateformat;
  227. {$endif}
  228. fmts.DateSeparator := FindSeparator(fmts.ShortDateFormat, fmts.DateSeparator);
  229. fmts.ShortDateFormat := TransformFormatStr(fmts.ShortDateFormat);
  230. fmts.LongDateFormat := GetLocaleStr(D_FMT);
  231. {$ifdef localedebug}
  232. OrgFormatSettings.LongDateFormat:=fmts.longdateformat;
  233. {$endif}
  234. fmts.LongDateFormat := TransformFormatStr(fmts.LongDateFormat);
  235. //Time stuff
  236. fmts.TimeAMString := GetLocaleStr(AM_STR);
  237. fmts.TimePMString := GetLocaleStr(PM_STR);
  238. fmts.ShortTimeFormat := GetLocaleStr(T_FMT);
  239. {$ifdef localedebug}
  240. OrgFormatSettings.ShortTimeFormat:=fmts.shorttimeformat;
  241. {$endif}
  242. fmts.TimeSeparator := FindSeparator(fmts.ShortTimeFormat, fmts.TimeSeparator);
  243. fmts.ShortTimeFormat := TransformFormatStr(fmts.ShortTimeFormat);
  244. fmts.LongTimeFormat := GetLocaleStr(T_FMT_AMPM);
  245. {$ifdef localedebug}
  246. OrgFormatSettings.LongTimeFormat:=fmts.longtimeformat;
  247. {$endif}
  248. if (fmts.LongTimeFormat='') then
  249. fmts.LongTimeFormat:=fmts.ShortTimeFormat
  250. else
  251. fmts.LongTimeFormat := TransformFormatStr(fmts.LongTimeFormat);
  252. {$if defined(BSD) or defined(SUNOS) or defined(aix)}
  253. plocale:=localeconv;
  254. // for these fields there is a separate BSD derived POSIX function.
  255. if not assigned(plocale) then exit; // for now.
  256. fmts.CurrencyString:=plocale^.currency_symbol; // int_CURR_SYMBOL (in latin chars)
  257. if fmts.CurrencyString='' then
  258. fmts.CurrencyString:=plocale^.int_curr_symbol;
  259. fmts.CurrencyDecimals:=ord(plocale^.FRAC_DIGITS);
  260. {$ifdef localedebug}
  261. OrgFormatSettings.CurrencyString1:=plocale^.currency_symbol;
  262. OrgFormatSettings.CurrencyString2:=plocale^.int_curr_symbol;
  263. {$endif}
  264. prec:=ord(plocale^.P_CS_PRECEDES);
  265. sep:=ord(plocale^.P_SEP_BY_SPACE);
  266. if (prec<=1) and (sep<=1) then
  267. fmts.CurrencyFormat := byte(not boolean(prec)) + sep shl 1;
  268. prec := ord(plocale^.N_CS_PRECEDES);
  269. sep := ord(plocale^.N_SEP_BY_SPACE);
  270. signp := ord(plocale^.N_SIGN_POSN);
  271. if (signp in [0..4]) and (prec in [0, 1]) and (sep in [0, 1]) then
  272. fmts.NegCurrFormat := NegFormatsTable[signp, prec, sep];
  273. //Number stuff
  274. fmts.ThousandSeparator:=plocale^.THOUSANDS_SEP[0];
  275. {$else}
  276. //Currency stuff
  277. fmts.CurrencyString := GetLocaleStr(_NL_MONETARY_CRNCYSTR);
  278. {$ifdef localedebug}
  279. OrgFormatSettings.CurrencyString1:=fmts.currencystring;
  280. OrgFormatSettings.CurrencyString2:='';
  281. {$endif}
  282. fmts.CurrencyString := Copy(fmts.CurrencyString, 2, Length(fmts.CurrencyString));
  283. fmts.CurrencyDecimals := StrToIntDef(GetLocaleStr(__FRAC_DIGITS), fmts.CurrencyDecimals);
  284. prec := byte(GetLocaleChar(__P_CS_PRECEDES));
  285. sep := byte(GetLocaleChar(__P_SEP_BY_SPACE));
  286. if (prec<=1) and (sep<=1) then
  287. fmts.CurrencyFormat := byte(not boolean(prec)) + sep shl 1;
  288. prec := byte(GetLocaleChar(__N_CS_PRECEDES));
  289. sep := byte(GetLocaleChar(__N_SEP_BY_SPACE));
  290. signp := byte(GetLocaleChar(__N_SIGN_POSN));
  291. if (signp in [0..4]) and (prec in [0, 1]) and (sep in [0, 1]) then
  292. fmts.NegCurrFormat := NegFormatsTable[signp, prec, sep];
  293. //Number stuff
  294. fmts.ThousandSeparator:=GetLocaleChar(__THOUSANDS_SEP);
  295. Sep := ord(GetLocaleChar(__MON_THOUSANDS_SEP));
  296. if fmts.ThousandSeparator=#0 then
  297. fmts.ThousandSeparator := char(Sep);
  298. {$endif}
  299. fmts.DecimalSeparator:=GetLocaleChar(RADIXCHAR);
  300. end;
  301. initialization
  302. GetFormatSettings(DefaultFormatSettings);
  303. end.