printf.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. /*++
  2. Copyright (c) 2013 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. printf.c
  5. Abstract:
  6. This module implements the printf utility.
  7. Author:
  8. Evan Green 16-Jul-2013
  9. Environment:
  10. POSIX
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include <minoca/lib/types.h>
  16. #include <ctype.h>
  17. #include <errno.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include "swlib.h"
  21. //
  22. // ---------------------------------------------------------------- Definitions
  23. //
  24. //
  25. // ------------------------------------------------------ Data Type Definitions
  26. //
  27. //
  28. // ----------------------------------------------- Internal Function Prototypes
  29. //
  30. BOOL
  31. PrintfUnescapeString (
  32. PSTR String
  33. );
  34. VOID
  35. PrintfRemoveCarriageReturns (
  36. PSTR String
  37. );
  38. //
  39. // -------------------------------------------------------------------- Globals
  40. //
  41. //
  42. // ------------------------------------------------------------------ Functions
  43. //
  44. INT
  45. PrintfMain (
  46. INT ArgumentCount,
  47. CHAR **Arguments
  48. )
  49. /*++
  50. Routine Description:
  51. This routine implements the main entry point for the printf utility.
  52. Arguments:
  53. ArgumentCount - Supplies the number of arguments on the command line.
  54. Arguments - Supplies an array of pointers to strings representing the
  55. arguments.
  56. Return Value:
  57. 0 on success.
  58. Non-zero on failure.
  59. --*/
  60. {
  61. PSTR AfterScan;
  62. PSTR Argument;
  63. ULONG ArgumentIndex;
  64. LONGLONG BigInteger;
  65. CHAR Character;
  66. CHAR ClobberedCharacter;
  67. ULONG ConversionIndex;
  68. CHAR ConversionSpecifier;
  69. CHAR Digit;
  70. ULONG DigitIndex;
  71. PSTR Format;
  72. BOOL FoundFormatSpecifiers;
  73. ULONG Index;
  74. INT Integer;
  75. BOOL PrintCharacter;
  76. BOOL QuadWord;
  77. INT ReturnValue;
  78. BOOL Stop;
  79. BOOL WasBackslash;
  80. if (ArgumentCount < 2) {
  81. ReturnValue = 1;
  82. goto MainEnd;
  83. }
  84. ReturnValue = 0;
  85. Format = Arguments[1];
  86. FoundFormatSpecifiers = FALSE;
  87. PrintfRemoveCarriageReturns(Format);
  88. ArgumentIndex = 2;
  89. Index = 0;
  90. Stop = FALSE;
  91. WasBackslash = FALSE;
  92. while (Stop == FALSE) {
  93. PrintCharacter = TRUE;
  94. Character = Format[Index];
  95. if (Character == '\0') {
  96. if ((ArgumentIndex < ArgumentCount) &&
  97. (FoundFormatSpecifiers != FALSE)) {
  98. Index = 0;
  99. WasBackslash = FALSE;
  100. continue;
  101. }
  102. break;
  103. }
  104. if (WasBackslash != FALSE) {
  105. if (Character == '\\') {
  106. Character = '\\';
  107. } else if (Character == 'a') {
  108. Character = '\a';
  109. } else if (Character == 'b') {
  110. Character = '\b';
  111. } else if (Character == 'f') {
  112. Character = '\f';
  113. } else if (Character == 'n') {
  114. Character = '\n';
  115. } else if (Character == 'r') {
  116. Character = '\r';
  117. } else if (Character == 't') {
  118. Character = '\t';
  119. } else if (Character == 'v') {
  120. Character = '\v';
  121. } else if (Character == 'x') {
  122. Character = 0;
  123. for (DigitIndex = 1; DigitIndex < 3; DigitIndex += 1) {
  124. Digit = Format[Index + DigitIndex];
  125. if (isdigit(Digit)) {
  126. Digit -= '0';
  127. } else if ((Digit >= 'A') && (Digit <= 'F')) {
  128. Digit = (Digit - 'A') + 0xA;
  129. } else if ((Digit >= 'a') && (Digit <= 'f')) {
  130. Digit = (Digit - 'a') + 0xA;
  131. } else {
  132. break;
  133. }
  134. Character = (Character * 16) + Digit;
  135. }
  136. Index += DigitIndex - 1;
  137. } else if ((Character >= '0') && (Character <= '7')) {
  138. //
  139. // The first digit is already in the character, which is why
  140. // the loop only iterates twice for three digits.
  141. //
  142. Character -= '0';
  143. for (DigitIndex = 1; DigitIndex < 3; DigitIndex += 1) {
  144. Digit = Format[Index + DigitIndex];
  145. if ((Digit < '0') || (Digit > '7')) {
  146. break;
  147. }
  148. Character = (Character * 8) + (Digit - '0');
  149. }
  150. Index += DigitIndex - 1;
  151. //
  152. // An unknown backslash escape was used. Treat the backslash
  153. // literally.
  154. //
  155. } else {
  156. printf("\\%c", Character);
  157. PrintCharacter = FALSE;
  158. }
  159. //
  160. // This character is not preceded by a backslash.
  161. //
  162. } else {
  163. if (Character == '\\') {
  164. PrintCharacter = FALSE;
  165. } else if (Character == '%') {
  166. ConversionIndex = Index + 1;
  167. //
  168. // Get through any flags.
  169. //
  170. while (TRUE) {
  171. Character = Format[ConversionIndex];
  172. if ((Character != '+') && (Character != '-') &&
  173. (Character != ' ') && (Character != '#') &&
  174. (Character != '0')) {
  175. break;
  176. }
  177. ConversionIndex += 1;
  178. }
  179. //
  180. // Get through any field width digits.
  181. //
  182. while (isdigit(Format[ConversionIndex])) {
  183. ConversionIndex += 1;
  184. }
  185. //
  186. // Get through an optional dot and precision.
  187. //
  188. if (Format[ConversionIndex] == '.') {
  189. ConversionIndex += 1;
  190. }
  191. while (isdigit(Format[ConversionIndex])) {
  192. ConversionIndex += 1;
  193. }
  194. //
  195. // Look to see if length modifiers exist that make this a quad
  196. // word. Then get past all length modifiers.
  197. //
  198. QuadWord = FALSE;
  199. if ((Format[ConversionIndex] == 'l') &&
  200. (Format[ConversionIndex + 1] == 'l')) {
  201. QuadWord = TRUE;
  202. }
  203. while (TRUE) {
  204. Character = Format[ConversionIndex];
  205. if ((Character != 'h') && (Character != 'l') &&
  206. (Character != 'j') && (Character != 'z') &&
  207. (Character != 't') && (Character != 'L')) {
  208. break;
  209. }
  210. ConversionIndex += 1;
  211. }
  212. //
  213. // Get the conversion specifier and temporarily null terminate
  214. // the format specifier.
  215. //
  216. ConversionSpecifier = Format[ConversionIndex];
  217. ConversionIndex += 1;
  218. ClobberedCharacter = Format[ConversionIndex];
  219. Format[ConversionIndex] = '\0';
  220. if (ArgumentIndex < ArgumentCount) {
  221. Argument = Arguments[ArgumentIndex];
  222. } else {
  223. Argument = NULL;
  224. }
  225. //
  226. // Convert an integer of some kind.
  227. //
  228. if ((ConversionSpecifier == 'd') ||
  229. (ConversionSpecifier == 'i') ||
  230. (ConversionSpecifier == 'o') ||
  231. (ConversionSpecifier == 'u') ||
  232. (ConversionSpecifier == 'x') ||
  233. (ConversionSpecifier == 'X') ||
  234. (ConversionSpecifier == 'c') ||
  235. (ConversionSpecifier == 'p') ||
  236. (ConversionSpecifier == 'C')) {
  237. if (QuadWord != FALSE) {
  238. BigInteger = strtoll(Argument, &AfterScan, 0);
  239. if (((BigInteger == -1) && (errno != 0)) ||
  240. (AfterScan == Argument)) {
  241. SwPrintError(0, Argument, "Invalid number");
  242. ReturnValue = errno;
  243. }
  244. printf(Format + Index, BigInteger);
  245. } else {
  246. if ((ConversionSpecifier == 'c') ||
  247. (ConversionSpecifier == 'C')) {
  248. Integer = 0;
  249. if (Argument != NULL) {
  250. Integer = Argument[0];
  251. }
  252. } else {
  253. Integer = strtol(Argument, &AfterScan, 0);
  254. if ((AfterScan == Argument) ||
  255. (*AfterScan != '\0')) {
  256. SwPrintError(0, Argument, "Invalid number");
  257. ReturnValue = errno;
  258. }
  259. }
  260. printf(Format + Index, Integer);
  261. }
  262. //
  263. // Convert a string.
  264. //
  265. } else if ((ConversionSpecifier == 's') ||
  266. (ConversionSpecifier == 'b')) {
  267. if ((ConversionSpecifier == 'b') && (Argument != NULL)) {
  268. Stop = PrintfUnescapeString(Argument);
  269. Format[ConversionIndex - 1] = 's';
  270. }
  271. if (Argument == NULL) {
  272. Argument = "";
  273. }
  274. PrintfRemoveCarriageReturns(Argument);
  275. printf(Format + Index, Argument);
  276. //
  277. // Oh, it's just an unescaped percent.
  278. //
  279. } else if (ConversionSpecifier == '%') {
  280. fputc('%', stdout);
  281. Argument = NULL;
  282. //
  283. // This is an unknown conversion specifier.
  284. //
  285. } else {
  286. SwPrintError(0,
  287. NULL,
  288. "Unknown conversion specifier '%c'",
  289. ConversionSpecifier);
  290. ReturnValue = 1;
  291. Argument = NULL;
  292. }
  293. if (Argument != NULL) {
  294. ArgumentIndex += 1;
  295. FoundFormatSpecifiers = TRUE;
  296. }
  297. //
  298. // Restore the string and move beyond this specifier.
  299. //
  300. Format[ConversionIndex] = ClobberedCharacter;
  301. Index = ConversionIndex - 1;
  302. PrintCharacter = FALSE;
  303. }
  304. }
  305. if (PrintCharacter != FALSE) {
  306. fputc(Character, stdout);
  307. }
  308. if (Character == '\\') {
  309. WasBackslash = !WasBackslash;
  310. } else {
  311. WasBackslash = FALSE;
  312. }
  313. Index += 1;
  314. }
  315. MainEnd:
  316. return ReturnValue;
  317. }
  318. //
  319. // --------------------------------------------------------- Internal Functions
  320. //
  321. BOOL
  322. PrintfUnescapeString (
  323. PSTR String
  324. )
  325. /*++
  326. Routine Description:
  327. This routine unescapes backslash sequences \\ \a \b \f \n \r \t \v, \c, and
  328. \0ddd (where d is zero to three octal digits).
  329. Arguments:
  330. String - Supplies a pointer to the string to unescape.
  331. Return Value:
  332. TRUE if a \c was found.
  333. FALSE if no \c was found.
  334. --*/
  335. {
  336. CHAR Character;
  337. ULONG DeleteCount;
  338. CHAR Digit;
  339. ULONG DigitIndex;
  340. ULONG Index;
  341. BOOL Stop;
  342. UINTN StringSize;
  343. BOOL WasBackslash;
  344. Index = 0;
  345. StringSize = strlen(String) + 1;
  346. Stop = FALSE;
  347. WasBackslash = FALSE;
  348. while (Stop == FALSE) {
  349. Character = String[Index];
  350. if (Character == '\0') {
  351. break;
  352. }
  353. if (WasBackslash != FALSE) {
  354. DeleteCount = 1;
  355. if (Character == '\\') {
  356. Character = '\\';
  357. } else if (Character == 'a') {
  358. Character = '\a';
  359. } else if (Character == 'b') {
  360. Character = '\b';
  361. } else if (Character == 'c') {
  362. Character = '\0';
  363. Stop = TRUE;
  364. } else if (Character == 'f') {
  365. Character = '\f';
  366. } else if (Character == 'n') {
  367. Character = '\n';
  368. } else if (Character == 'r') {
  369. Character = '\r';
  370. } else if (Character == 't') {
  371. Character = '\t';
  372. } else if (Character == 'v') {
  373. Character = '\v';
  374. } else if (Character == '0') {
  375. Character = 0;
  376. for (DigitIndex = 1; DigitIndex < 4; DigitIndex += 1) {
  377. Digit = String[Index + DigitIndex];
  378. if ((Digit < '0') || (Digit > '7')) {
  379. break;
  380. }
  381. Character = (Character * 8) + (Digit - '0');
  382. }
  383. DeleteCount = 1 + DigitIndex;
  384. //
  385. // An unknown backslash escape was used. Treat the backslash
  386. // literally.
  387. //
  388. } else {
  389. DeleteCount = 0;
  390. }
  391. if (DeleteCount != 0) {
  392. Index -= 1;
  393. SwStringRemoveRegion(String,
  394. &StringSize,
  395. Index,
  396. DeleteCount);
  397. String[Index] = Character;
  398. }
  399. }
  400. if (Character == '\\') {
  401. WasBackslash = !WasBackslash;
  402. } else {
  403. WasBackslash = FALSE;
  404. }
  405. Index += 1;
  406. }
  407. return Stop;
  408. }
  409. VOID
  410. PrintfRemoveCarriageReturns (
  411. PSTR String
  412. )
  413. /*++
  414. Routine Description:
  415. This routine removes any \r characters from the input string, as they
  416. pile up in Windows platforms and are generally useless.
  417. Arguments:
  418. String - Supplies a pointer to the string to strip of \r characters.
  419. Return Value:
  420. None.
  421. --*/
  422. {
  423. UINTN Index;
  424. UINTN StringSize;
  425. StringSize = strlen(String) + 1;
  426. Index = 0;
  427. while (String[Index] != '\0') {
  428. if (String[Index] == '\r') {
  429. SwStringRemoveRegion(String, &StringSize, Index, 1);
  430. } else {
  431. Index += 1;
  432. }
  433. }
  434. return;
  435. }