stdio.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  1. /*
  2. * stdio.c
  3. *
  4. * Copyright (C) 2021 bzt (bztsrc@gitlab)
  5. *
  6. * Permission is hereby granted, free of charge, to any person
  7. * obtaining a copy of this software and associated documentation
  8. * files (the "Software"), to deal in the Software without
  9. * restriction, including without limitation the rights to use, copy,
  10. * modify, merge, publish, distribute, sublicense, and/or sell copies
  11. * of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be
  15. * included in all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  19. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  20. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  21. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  22. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  24. * DEALINGS IN THE SOFTWARE.
  25. *
  26. * This file is part of the POSIX-UEFI package.
  27. * @brief Implementing functions which are defined in stdio.h
  28. *
  29. */
  30. #include <uefi.h>
  31. static efi_file_handle_t *__root_dir = NULL;
  32. static efi_serial_io_protocol_t *__ser = NULL;
  33. static block_file_t *__blk_devs = NULL;
  34. static uintn_t __blk_ndevs = 0;
  35. extern time_t __mktime_efi(efi_time_t *t);
  36. void __stdio_cleanup(void);
  37. void __stdio_seterrno(efi_status_t status);
  38. int __remove (const char_t *__filename, int isdir);
  39. void __stdio_cleanup(void)
  40. {
  41. #ifndef UEFI_NO_UTF8
  42. if(__argvutf8)
  43. BS->FreePool(__argvutf8);
  44. #endif
  45. if(__blk_devs) {
  46. free(__blk_devs);
  47. __blk_devs = NULL;
  48. __blk_ndevs = 0;
  49. }
  50. }
  51. void __stdio_seterrno(efi_status_t status)
  52. {
  53. switch((int)(status & 0xffff)) {
  54. case EFI_WRITE_PROTECTED & 0xffff: errno = EROFS; break;
  55. case EFI_ACCESS_DENIED & 0xffff: errno = EACCES; break;
  56. case EFI_VOLUME_FULL & 0xffff: errno = ENOSPC; break;
  57. case EFI_NOT_FOUND & 0xffff: errno = ENOENT; break;
  58. case EFI_INVALID_PARAMETER & 0xffff: errno = EINVAL; break;
  59. default: errno = EIO; break;
  60. }
  61. }
  62. int fstat (FILE *__f, struct stat *__buf)
  63. {
  64. efi_guid_t infGuid = EFI_FILE_INFO_GUID;
  65. efi_file_info_t info;
  66. uintn_t fsiz = (uintn_t)sizeof(efi_file_info_t);
  67. efi_status_t status;
  68. uintn_t i;
  69. if(!__f || !__buf) {
  70. errno = EINVAL;
  71. return -1;
  72. }
  73. memset(__buf, 0, sizeof(struct stat));
  74. if(__f == stdin) {
  75. __buf->st_mode = S_IREAD | S_IFIFO;
  76. return 0;
  77. }
  78. if(__f == stdout || __f == stderr) {
  79. __buf->st_mode = S_IWRITE | S_IFIFO;
  80. return 0;
  81. }
  82. if(__ser && __f == (FILE*)__ser) {
  83. __buf->st_mode = S_IREAD | S_IWRITE | S_IFCHR;
  84. return 0;
  85. }
  86. for(i = 0; i < __blk_ndevs; i++)
  87. if(__f == (FILE*)__blk_devs[i].bio) {
  88. __buf->st_mode = S_IREAD | S_IWRITE | S_IFBLK;
  89. __buf->st_size = (off_t)__blk_devs[i].bio->Media->BlockSize * ((off_t)__blk_devs[i].bio->Media->LastBlock + 1);
  90. __buf->st_blocks = __blk_devs[i].bio->Media->LastBlock + 1;
  91. return 0;
  92. }
  93. status = __f->GetInfo(__f, &infGuid, &fsiz, &info);
  94. if(EFI_ERROR(status)) {
  95. __stdio_seterrno(status);
  96. return -1;
  97. }
  98. __buf->st_mode = S_IREAD |
  99. (info.Attribute & EFI_FILE_READ_ONLY ? 0 : S_IWRITE) |
  100. (info.Attribute & EFI_FILE_DIRECTORY ? S_IFDIR : S_IFREG);
  101. __buf->st_size = (off_t)info.FileSize;
  102. __buf->st_blocks = (blkcnt_t)info.PhysicalSize;
  103. __buf->st_atime = __mktime_efi(&info.LastAccessTime);
  104. __buf->st_mtime = __mktime_efi(&info.ModificationTime);
  105. __buf->st_ctime = __mktime_efi(&info.CreateTime);
  106. return 0;
  107. }
  108. int fclose (FILE *__stream)
  109. {
  110. efi_status_t status = EFI_SUCCESS;
  111. uintn_t i;
  112. if(!__stream) {
  113. errno = EINVAL;
  114. return 0;
  115. }
  116. if(__stream == stdin || __stream == stdout || __stream == stderr || (__ser && __stream == (FILE*)__ser)) {
  117. return 1;
  118. }
  119. for(i = 0; i < __blk_ndevs; i++)
  120. if(__stream == (FILE*)__blk_devs[i].bio)
  121. return 1;
  122. status = __stream->Close(__stream);
  123. free(__stream);
  124. return !EFI_ERROR(status);
  125. }
  126. int fflush (FILE *__stream)
  127. {
  128. efi_status_t status = EFI_SUCCESS;
  129. uintn_t i;
  130. if(!__stream) {
  131. errno = EINVAL;
  132. return 0;
  133. }
  134. if(__stream == stdin || __stream == stdout || __stream == stderr || (__ser && __stream == (FILE*)__ser)) {
  135. return 1;
  136. }
  137. for(i = 0; i < __blk_ndevs; i++)
  138. if(__stream == (FILE*)__blk_devs[i].bio) {
  139. return 1;
  140. }
  141. status = __stream->Flush(__stream);
  142. return !EFI_ERROR(status);
  143. }
  144. int __remove (const char_t *__filename, int isdir)
  145. {
  146. efi_status_t status;
  147. efi_guid_t infGuid = EFI_FILE_INFO_GUID;
  148. efi_file_info_t info;
  149. uintn_t fsiz = (uintn_t)sizeof(efi_file_info_t), i;
  150. /* little hack to support read and write mode for Delete() and stat() without create mode or checks */
  151. FILE *f = fopen(__filename, CL("*"));
  152. if(errno)
  153. return 1;
  154. if(!f || f == stdin || f == stdout || f == stderr || (__ser && f == (FILE*)__ser)) {
  155. errno = EBADF;
  156. return 1;
  157. }
  158. for(i = 0; i < __blk_ndevs; i++)
  159. if(f == (FILE*)__blk_devs[i].bio) {
  160. errno = EBADF;
  161. return 1;
  162. }
  163. if(isdir != -1) {
  164. status = f->GetInfo(f, &infGuid, &fsiz, &info);
  165. if(EFI_ERROR(status)) goto err;
  166. if(isdir == 0 && (info.Attribute & EFI_FILE_DIRECTORY)) {
  167. fclose(f); errno = EISDIR;
  168. return -1;
  169. }
  170. if(isdir == 1 && !(info.Attribute & EFI_FILE_DIRECTORY)) {
  171. fclose(f); errno = ENOTDIR;
  172. return -1;
  173. }
  174. }
  175. status = f->Delete(f);
  176. if(EFI_ERROR(status)) {
  177. err: __stdio_seterrno(status);
  178. fclose(f);
  179. return -1;
  180. }
  181. /* no need for fclose(f); */
  182. free(f);
  183. return 0;
  184. }
  185. int remove (const char_t *__filename)
  186. {
  187. return __remove(__filename, -1);
  188. }
  189. FILE *fopen (const char_t *__filename, const char_t *__modes)
  190. {
  191. FILE *ret;
  192. efi_status_t status;
  193. efi_guid_t sfsGuid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
  194. efi_simple_file_system_protocol_t *sfs = NULL;
  195. efi_guid_t infGuid = EFI_FILE_INFO_GUID;
  196. efi_file_info_t info;
  197. uintn_t fsiz = (uintn_t)sizeof(efi_file_info_t), par, i;
  198. #ifndef UEFI_NO_UTF8
  199. wchar_t wcname[BUFSIZ];
  200. #endif
  201. errno = 0;
  202. if(!__filename || !*__filename || !__modes || (__modes[0] != CL('r') && __modes[0] != CL('w') && __modes[0] != CL('a') &&
  203. __modes[0] != CL('*')) || (__modes[1] != 0 && __modes[1] != CL('d') && __modes[1] != CL('+'))) {
  204. errno = EINVAL;
  205. return NULL;
  206. }
  207. /* fake some device names. UEFI has no concept of device files */
  208. if(!strcmp(__filename, CL("/dev/stdin"))) {
  209. if(__modes[0] == CL('w') || __modes[0] == CL('a')) { errno = EPERM; return NULL; }
  210. return stdin;
  211. }
  212. if(!strcmp(__filename, CL("/dev/stdout"))) {
  213. if(__modes[0] == CL('r')) { errno = EPERM; return NULL; }
  214. return stdout;
  215. }
  216. if(!strcmp(__filename, CL("/dev/stderr"))) {
  217. if(__modes[0] == CL('r')) { errno = EPERM; return NULL; }
  218. return stderr;
  219. }
  220. if(!memcmp(__filename, CL("/dev/serial"), 11 * sizeof(char_t))) {
  221. par = (uintn_t)atol(__filename + 11);
  222. if(!__ser) {
  223. efi_guid_t serGuid = EFI_SERIAL_IO_PROTOCOL_GUID;
  224. status = BS->LocateProtocol(&serGuid, NULL, (void**)&__ser);
  225. if(EFI_ERROR(status) || !__ser) { errno = ENOENT; return NULL; }
  226. }
  227. __ser->SetAttributes(__ser, par > 9600 ? par : 115200, 0, 1000, NoParity, 8, OneStopBit);
  228. return (FILE*)__ser;
  229. }
  230. if(!memcmp(__filename, CL("/dev/disk"), 9 * sizeof(char_t))) {
  231. par = (uintn_t)atol(__filename + 9);
  232. if(!__blk_ndevs) {
  233. efi_guid_t bioGuid = EFI_BLOCK_IO_PROTOCOL_GUID;
  234. efi_handle_t handles[128];
  235. uintn_t handle_size = sizeof(handles);
  236. status = BS->LocateHandle(ByProtocol, &bioGuid, NULL, &handle_size, (efi_handle_t*)&handles);
  237. if(!EFI_ERROR(status)) {
  238. handle_size /= (uintn_t)sizeof(efi_handle_t);
  239. /* workaround a bug in TianoCore, it reports zero size even though the data is in the buffer */
  240. if(handle_size < 1)
  241. handle_size = (uintn_t)sizeof(handles) / sizeof(efi_handle_t);
  242. __blk_devs = (block_file_t*)malloc(handle_size * sizeof(block_file_t));
  243. if(__blk_devs) {
  244. memset(__blk_devs, 0, handle_size * sizeof(block_file_t));
  245. for(i = __blk_ndevs = 0; i < handle_size; i++)
  246. if(handles[i] && !EFI_ERROR(BS->HandleProtocol(handles[i], &bioGuid, (void **) &__blk_devs[__blk_ndevs].bio)) &&
  247. __blk_devs[__blk_ndevs].bio && __blk_devs[__blk_ndevs].bio->Media &&
  248. __blk_devs[__blk_ndevs].bio->Media->BlockSize > 0)
  249. __blk_ndevs++;
  250. } else
  251. __blk_ndevs = 0;
  252. }
  253. }
  254. if(__blk_ndevs && par < __blk_ndevs)
  255. return (FILE*)__blk_devs[par].bio;
  256. errno = ENOENT;
  257. return NULL;
  258. }
  259. if(!__root_dir && LIP) {
  260. status = BS->HandleProtocol(LIP->DeviceHandle, &sfsGuid, (void **)&sfs);
  261. if(!EFI_ERROR(status))
  262. status = sfs->OpenVolume(sfs, &__root_dir);
  263. }
  264. if(!__root_dir) {
  265. errno = ENODEV;
  266. return NULL;
  267. }
  268. ret = (FILE*)malloc(sizeof(FILE));
  269. if(!ret) return NULL;
  270. /* normally write means read,write,create. But for remove (internal '*' mode), we need read,write without create
  271. * also mode 'w' in POSIX means write-only (without read), but that's not working on certain firmware, we must
  272. * pass read too. This poses a problem of truncating a write-only file, see issue #26, we have to do that manually */
  273. #ifndef UEFI_NO_UTF8
  274. mbstowcs((wchar_t*)&wcname, __filename, BUFSIZ - 1);
  275. status = __root_dir->Open(__root_dir, &ret, (wchar_t*)&wcname,
  276. #else
  277. status = __root_dir->Open(__root_dir, &ret, (wchar_t*)__filename,
  278. #endif
  279. __modes[0] == CL('w') || __modes[0] == CL('a') ? (EFI_FILE_MODE_WRITE | EFI_FILE_MODE_READ | EFI_FILE_MODE_CREATE) :
  280. EFI_FILE_MODE_READ | (__modes[0] == CL('*') || __modes[1] == CL('+') ? EFI_FILE_MODE_WRITE : 0),
  281. __modes[1] == CL('d') ? EFI_FILE_DIRECTORY : 0);
  282. if(EFI_ERROR(status)) {
  283. err: __stdio_seterrno(status);
  284. free(ret); return NULL;
  285. }
  286. if(__modes[0] == CL('*')) return ret;
  287. status = ret->GetInfo(ret, &infGuid, &fsiz, &info);
  288. if(EFI_ERROR(status)) goto err;
  289. if(__modes[1] == CL('d') && !(info.Attribute & EFI_FILE_DIRECTORY)) {
  290. ret->Close(ret); free(ret); errno = ENOTDIR; return NULL;
  291. }
  292. if(__modes[1] != CL('d') && (info.Attribute & EFI_FILE_DIRECTORY)) {
  293. ret->Close(ret); free(ret); errno = EISDIR; return NULL;
  294. }
  295. if(__modes[0] == CL('a')) fseek(ret, 0, SEEK_END);
  296. if(__modes[0] == CL('w')) {
  297. /* manually truncate file size
  298. * See https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiFileHandleLib/UefiFileHandleLib.c
  299. * function FileHandleSetSize */
  300. info.FileSize = 0;
  301. ret->SetInfo(ret, &infGuid, fsiz, &info);
  302. }
  303. return ret;
  304. }
  305. size_t fread (void *__ptr, size_t __size, size_t __n, FILE *__stream)
  306. {
  307. uintn_t bs = __size * __n, i, n;
  308. efi_status_t status;
  309. if(!__ptr || __size < 1 || __n < 1 || !__stream) {
  310. errno = EINVAL;
  311. return 0;
  312. }
  313. if(__stream == stdin || __stream == stdout || __stream == stderr) {
  314. errno = ESPIPE;
  315. return 0;
  316. }
  317. if(__ser && __stream == (FILE*)__ser) {
  318. status = __ser->Read(__ser, &bs, __ptr);
  319. } else {
  320. for(i = 0; i < __blk_ndevs; i++)
  321. if(__stream == (FILE*)__blk_devs[i].bio) {
  322. n = __blk_devs[i].offset / __blk_devs[i].bio->Media->BlockSize;
  323. bs = (bs / __blk_devs[i].bio->Media->BlockSize) * __blk_devs[i].bio->Media->BlockSize;
  324. status = __blk_devs[i].bio->ReadBlocks(__blk_devs[i].bio, __blk_devs[i].bio->Media->MediaId, n, bs, __ptr);
  325. if(EFI_ERROR(status)) {
  326. __stdio_seterrno(status);
  327. return 0;
  328. }
  329. __blk_devs[i].offset += bs;
  330. return bs / __size;
  331. }
  332. status = __stream->Read(__stream, &bs, __ptr);
  333. }
  334. if(EFI_ERROR(status)) {
  335. __stdio_seterrno(status);
  336. return 0;
  337. }
  338. return bs / __size;
  339. }
  340. size_t fwrite (const void *__ptr, size_t __size, size_t __n, FILE *__stream)
  341. {
  342. uintn_t bs = __size * __n, n, i;
  343. efi_status_t status;
  344. if(!__ptr || __size < 1 || __n < 1 || !__stream) {
  345. errno = EINVAL;
  346. return 0;
  347. }
  348. if(__stream == stdin || __stream == stdout || __stream == stderr) {
  349. errno = ESPIPE;
  350. return 0;
  351. }
  352. if(__ser && __stream == (FILE*)__ser) {
  353. status = __ser->Write(__ser, &bs, (void*)__ptr);
  354. } else {
  355. for(i = 0; i < __blk_ndevs; i++)
  356. if(__stream == (FILE*)__blk_devs[i].bio) {
  357. n = __blk_devs[i].offset / __blk_devs[i].bio->Media->BlockSize;
  358. bs = (bs / __blk_devs[i].bio->Media->BlockSize) * __blk_devs[i].bio->Media->BlockSize;
  359. status = __blk_devs[i].bio->WriteBlocks(__blk_devs[i].bio, __blk_devs[i].bio->Media->MediaId, n, bs, (void*)__ptr);
  360. if(EFI_ERROR(status)) {
  361. __stdio_seterrno(status);
  362. return 0;
  363. }
  364. __blk_devs[i].offset += bs;
  365. return bs / __size;
  366. }
  367. status = __stream->Write(__stream, &bs, (void *)__ptr);
  368. }
  369. if(EFI_ERROR(status)) {
  370. __stdio_seterrno(status);
  371. return 0;
  372. }
  373. return bs / __size;
  374. }
  375. int fseek (FILE *__stream, long int __off, int __whence)
  376. {
  377. off_t off = 0;
  378. efi_status_t status;
  379. efi_guid_t infoGuid = EFI_FILE_INFO_GUID;
  380. efi_file_info_t info;
  381. uintn_t fsiz = sizeof(efi_file_info_t), i;
  382. if(!__stream || (__whence != SEEK_SET && __whence != SEEK_CUR && __whence != SEEK_END)) {
  383. errno = EINVAL;
  384. return -1;
  385. }
  386. if(__stream == stdin || __stream == stdout || __stream == stderr) {
  387. errno = ESPIPE;
  388. return -1;
  389. }
  390. if(__ser && __stream == (FILE*)__ser) {
  391. errno = EBADF;
  392. return -1;
  393. }
  394. for(i = 0; i < __blk_ndevs; i++)
  395. if(__stream == (FILE*)__blk_devs[i].bio) {
  396. off = (uint64_t)__blk_devs[i].bio->Media->BlockSize * (uint64_t)__blk_devs[i].bio->Media->LastBlock;
  397. switch(__whence) {
  398. case SEEK_END:
  399. __blk_devs[i].offset = off + __off;
  400. break;
  401. case SEEK_CUR:
  402. __blk_devs[i].offset += __off;
  403. break;
  404. case SEEK_SET:
  405. __blk_devs[i].offset = __off;
  406. break;
  407. }
  408. if(__blk_devs[i].offset < 0) __blk_devs[i].offset = 0;
  409. if(__blk_devs[i].offset > off) __blk_devs[i].offset = off;
  410. __blk_devs[i].offset = (__blk_devs[i].offset / __blk_devs[i].bio->Media->BlockSize) *
  411. __blk_devs[i].bio->Media->BlockSize;
  412. return 0;
  413. }
  414. switch(__whence) {
  415. case SEEK_END:
  416. status = __stream->GetInfo(__stream, &infoGuid, &fsiz, &info);
  417. if(!EFI_ERROR(status)) {
  418. off = info.FileSize + __off;
  419. status = __stream->SetPosition(__stream, off);
  420. }
  421. break;
  422. case SEEK_CUR:
  423. status = __stream->GetPosition(__stream, &off);
  424. if(!EFI_ERROR(status)) {
  425. off += __off;
  426. status = __stream->SetPosition(__stream, off);
  427. }
  428. break;
  429. default:
  430. status = __stream->SetPosition(__stream, __off);
  431. break;
  432. }
  433. return EFI_ERROR(status) ? -1 : 0;
  434. }
  435. long int ftell (FILE *__stream)
  436. {
  437. uint64_t off = 0;
  438. uintn_t i;
  439. efi_status_t status;
  440. if(!__stream) {
  441. errno = EINVAL;
  442. return -1;
  443. }
  444. if(__stream == stdin || __stream == stdout || __stream == stderr) {
  445. errno = ESPIPE;
  446. return -1;
  447. }
  448. if(__ser && __stream == (FILE*)__ser) {
  449. errno = EBADF;
  450. return -1;
  451. }
  452. for(i = 0; i < __blk_ndevs; i++)
  453. if(__stream == (FILE*)__blk_devs[i].bio) {
  454. return (long int)__blk_devs[i].offset;
  455. }
  456. status = __stream->GetPosition(__stream, &off);
  457. return EFI_ERROR(status) ? -1 : (long int)off;
  458. }
  459. int feof (FILE *__stream)
  460. {
  461. uint64_t off = 0;
  462. efi_guid_t infGuid = EFI_FILE_INFO_GUID;
  463. efi_file_info_t info;
  464. uintn_t fsiz = (uintn_t)sizeof(efi_file_info_t), i;
  465. efi_status_t status;
  466. if(!__stream) {
  467. errno = EINVAL;
  468. return 0;
  469. }
  470. if(__stream == stdin || __stream == stdout || __stream == stderr) {
  471. errno = ESPIPE;
  472. return 0;
  473. }
  474. if(__ser && __stream == (FILE*)__ser) {
  475. errno = EBADF;
  476. return 0;
  477. }
  478. for(i = 0; i < __blk_ndevs; i++)
  479. if(__stream == (FILE*)__blk_devs[i].bio) {
  480. errno = EBADF;
  481. return __blk_devs[i].offset == (off_t)__blk_devs[i].bio->Media->BlockSize * (off_t)__blk_devs[i].bio->Media->LastBlock;
  482. }
  483. status = __stream->GetPosition(__stream, &off);
  484. if(EFI_ERROR(status)) {
  485. err: __stdio_seterrno(status);
  486. return 1;
  487. }
  488. status = __stream->GetInfo(__stream, &infGuid, &fsiz, &info);
  489. if(EFI_ERROR(status)) goto err;
  490. __stream->SetPosition(__stream, off);
  491. return info.FileSize == off;
  492. }
  493. int vsnprintf(char_t *dst, size_t maxlen, const char_t *fmt, __builtin_va_list args)
  494. {
  495. #define needsescape(a) (a==CL('\"') || a==CL('\\') || a==CL('\a') || a==CL('\b') || a==CL('\033') || a==CL('\f') || \
  496. a==CL('\r') || a==CL('\n') || a==CL('\t') || a==CL('\v'))
  497. efi_physical_address_t m;
  498. uint8_t *mem;
  499. uint64_t arg;
  500. int64_t iarg;
  501. int len, sign, i, j;
  502. char_t *p, *orig=dst, *end = dst + maxlen - 1, tmpstr[24], pad, n;
  503. #ifdef UEFI_NO_UTF8
  504. char *c;
  505. #endif
  506. if(dst==NULL || fmt==NULL)
  507. return 0;
  508. arg = 0;
  509. while(*fmt && dst < end) {
  510. if(*fmt==CL('%')) {
  511. fmt++;
  512. if(*fmt==CL('%')) goto put;
  513. len=0; pad=CL(' ');
  514. if(*fmt==CL('0')) pad=CL('0');
  515. while(*fmt>=CL('0') && *fmt<=CL('9')) {
  516. len *= 10;
  517. len += *fmt-CL('0');
  518. fmt++;
  519. }
  520. if(*fmt==CL('l')) fmt++;
  521. if(*fmt==CL('c')) {
  522. arg = __builtin_va_arg(args, uint32_t);
  523. #ifndef UEFI_NO_UTF8
  524. if(arg<0x80) { *dst++ = arg; } else
  525. if(arg<0x800) { *dst++ = ((arg>>6)&0x1F)|0xC0; *dst++ = (arg&0x3F)|0x80; } else
  526. { *dst++ = ((arg>>12)&0x0F)|0xE0; *dst++ = ((arg>>6)&0x3F)|0x80; *dst++ = (arg&0x3F)|0x80; }
  527. #else
  528. *dst++ = (wchar_t)(arg & 0xffff);
  529. #endif
  530. fmt++;
  531. continue;
  532. } else
  533. if(*fmt==CL('d') || *fmt==CL('i') || *fmt==CL('u')) {
  534. iarg = __builtin_va_arg(args, int64_t);
  535. sign=0;
  536. if(*fmt!=CL('u') && iarg<0) {
  537. arg=-iarg;
  538. sign++;
  539. } else arg=(uint64_t)iarg;
  540. i=23;
  541. tmpstr[i]=0;
  542. do {
  543. tmpstr[--i]=CL('0')+(arg%10);
  544. arg/=10;
  545. } while(arg!=0 && i>0);
  546. if(sign) {
  547. tmpstr[--i]=CL('-');
  548. }
  549. if(len>0 && len<23) {
  550. while(i && i>23-len) {
  551. tmpstr[--i]=pad;
  552. }
  553. }
  554. p=&tmpstr[i];
  555. goto copystring;
  556. } else
  557. if(*fmt==CL('p')) {
  558. arg = __builtin_va_arg(args, uint64_t);
  559. len = 16; pad = CL('0'); goto hex;
  560. } else
  561. if(*fmt==CL('x') || *fmt==CL('X')) {
  562. arg = __builtin_va_arg(args, uint64_t);
  563. hex: i=16;
  564. tmpstr[i]=0;
  565. do {
  566. n=arg & 0xf;
  567. /* 0-9 => '0'-'9', 10-15 => 'A'-'F' */
  568. tmpstr[--i]=n+(n>9?(*fmt==CL('X')?0x37:0x57):0x30);
  569. arg>>=4;
  570. } while(arg!=0 && i>0);
  571. /* padding, only leading zeros */
  572. if(len>0 && len<=16) {
  573. while(i>16-len) {
  574. tmpstr[--i]=CL('0');
  575. }
  576. }
  577. p=&tmpstr[i];
  578. goto copystring;
  579. } else
  580. if(*fmt==CL('s') || *fmt==CL('q')) {
  581. p = __builtin_va_arg(args, char_t*);
  582. copystring: if(p==NULL) {
  583. p=CL("(null)");
  584. }
  585. while(*p && dst + 2 < end) {
  586. if(*fmt==CL('q') && needsescape(*p)) {
  587. *dst++ = CL('\\');
  588. switch(*p) {
  589. case CL('\a'): *dst++ = CL('a'); break;
  590. case CL('\b'): *dst++ = CL('b'); break;
  591. case 27: *dst++ = CL('e'); break; /* gcc 10.2 doesn't like CL('\e') in ansi mode */
  592. case CL('\f'): *dst++ = CL('f'); break;
  593. case CL('\n'): *dst++ = CL('n'); break;
  594. case CL('\r'): *dst++ = CL('r'); break;
  595. case CL('\t'): *dst++ = CL('t'); break;
  596. case CL('\v'): *dst++ = CL('v'); break;
  597. default: *dst++ = *p++; break;
  598. }
  599. } else {
  600. if(*p == CL('\n') && (orig == dst || *(dst - 1) != CL('\r'))) *dst++ = CL('\r');
  601. *dst++ = *p++;
  602. }
  603. }
  604. } else
  605. #ifdef UEFI_NO_UTF8
  606. if(*fmt==L'S' || *fmt==L'Q') {
  607. c = __builtin_va_arg(args, char*);
  608. if(c==NULL) goto copystring;
  609. while(*p && dst + 2 < end) {
  610. arg = *c;
  611. if((*c & 128) != 0) {
  612. if((*c & 32) == 0 ) {
  613. arg = ((*c & 0x1F)<<6)|(*(c+1) & 0x3F);
  614. c += 1;
  615. } else
  616. if((*c & 16) == 0 ) {
  617. arg = ((*c & 0xF)<<12)|((*(c+1) & 0x3F)<<6)|(*(c+2) & 0x3F);
  618. c += 2;
  619. } else
  620. if((*c & 8) == 0 ) {
  621. arg = ((*c & 0x7)<<18)|((*(c+1) & 0x3F)<<12)|((*(c+2) & 0x3F)<<6)|(*(c+3) & 0x3F);
  622. c += 3;
  623. } else
  624. arg = L'?';
  625. }
  626. if(!arg) break;
  627. if(*fmt==L'Q' && needsescape(arg)) {
  628. *dst++ = L'\\';
  629. switch(arg) {
  630. case L'\a': *dst++ = L'a'; break;
  631. case L'\b': *dst++ = L'b'; break;
  632. case 27: *dst++ = L'e'; break; /* gcc 10.2 doesn't like L'\e' in ansi mode */
  633. case L'\f': *dst++ = L'f'; break;
  634. case L'\n': *dst++ = L'n'; break;
  635. case L'\r': *dst++ = L'r'; break;
  636. case L'\t': *dst++ = L't'; break;
  637. case L'\v': *dst++ = L'v'; break;
  638. default: *dst++ = arg; break;
  639. }
  640. } else {
  641. if(arg == L'\n') *dst++ = L'\r';
  642. *dst++ = (wchar_t)(arg & 0xffff);
  643. }
  644. }
  645. } else
  646. #endif
  647. if(*fmt==CL('D')) {
  648. m = __builtin_va_arg(args, efi_physical_address_t);
  649. for(j = 0; j < (len < 1 ? 1 : (len > 16 ? 16 : len)); j++) {
  650. for(i = 44; i >= 0; i -= 4) {
  651. n = (m >> i) & 15; *dst++ = n + (n>9?0x37:0x30);
  652. if(dst >= end) goto zro;
  653. }
  654. *dst++ = CL(':'); if(dst >= end) goto zro;
  655. *dst++ = CL(' '); if(dst >= end) goto zro;
  656. mem = (uint8_t*)m;
  657. for(i = 0; i < 16; i++) {
  658. n = (mem[i] >> 4) & 15; *dst++ = n + (n>9?0x37:0x30); if(dst >= end) goto zro;
  659. n = mem[i] & 15; *dst++ = n + (n>9?0x37:0x30); if(dst >= end) goto zro;
  660. *dst++ = CL(' ');if(dst >= end) goto zro;
  661. }
  662. *dst++ = CL(' '); if(dst >= end) goto zro;
  663. for(i = 0; i < 16; i++) {
  664. *dst++ = (mem[i] < 32 || mem[i] >= 127 ? CL('.') : (char_t)mem[i]);
  665. if(dst >= end) goto zro;
  666. }
  667. *dst++ = CL('\r'); if(dst >= end) goto zro;
  668. *dst++ = CL('\n'); if(dst >= end) goto zro;
  669. m += 16;
  670. }
  671. }
  672. } else {
  673. put: if(*fmt == CL('\n') && (orig == dst || *(dst - 1) != CL('\r'))) *dst++ = CL('\r');
  674. *dst++ = *fmt;
  675. }
  676. fmt++;
  677. }
  678. zro:*dst=0;
  679. return (int)(dst-orig);
  680. #undef needsescape
  681. }
  682. int vsprintf(char_t *dst, const char_t *fmt, __builtin_va_list args)
  683. {
  684. return vsnprintf(dst, BUFSIZ, fmt, args);
  685. }
  686. int sprintf(char_t *dst, const char_t* fmt, ...)
  687. {
  688. int ret;
  689. __builtin_va_list args;
  690. __builtin_va_start(args, fmt);
  691. ret = vsnprintf(dst, BUFSIZ, fmt, args);
  692. __builtin_va_end(args);
  693. return ret;
  694. }
  695. int snprintf(char_t *dst, size_t maxlen, const char_t* fmt, ...)
  696. {
  697. int ret;
  698. __builtin_va_list args;
  699. __builtin_va_start(args, fmt);
  700. ret = vsnprintf(dst, maxlen, fmt, args);
  701. __builtin_va_end(args);
  702. return ret;
  703. }
  704. int vprintf(const char_t* fmt, __builtin_va_list args)
  705. {
  706. int ret;
  707. wchar_t dst[BUFSIZ];
  708. #ifndef UEFI_NO_UTF8
  709. char_t tmp[BUFSIZ];
  710. ret = vsnprintf(tmp, BUFSIZ, fmt, args);
  711. mbstowcs(dst, tmp, BUFSIZ - 1);
  712. #else
  713. ret = vsnprintf(dst, BUFSIZ, fmt, args);
  714. #endif
  715. ST->ConOut->OutputString(ST->ConOut, (wchar_t *)&dst);
  716. return ret;
  717. }
  718. int printf(const char_t* fmt, ...)
  719. {
  720. int ret;
  721. __builtin_va_list args;
  722. __builtin_va_start(args, fmt);
  723. ret = vprintf(fmt, args);
  724. __builtin_va_end(args);
  725. return ret;
  726. }
  727. int vfprintf (FILE *__stream, const char_t *__format, __builtin_va_list args)
  728. {
  729. wchar_t dst[BUFSIZ];
  730. char_t tmp[BUFSIZ];
  731. uintn_t ret, i;
  732. #ifndef UEFI_NO_UTF8
  733. ret = (uintn_t)vsnprintf(tmp, BUFSIZ, __format, args);
  734. ret = mbstowcs(dst, tmp, BUFSIZ - 1);
  735. #else
  736. ret = vsnprintf(dst, BUFSIZ, __format, args);
  737. #endif
  738. if(ret < 1 || !__stream || __stream == stdin) return 0;
  739. for(i = 0; i < __blk_ndevs; i++)
  740. if(__stream == (FILE*)__blk_devs[i].bio) {
  741. errno = EBADF;
  742. return -1;
  743. }
  744. if(__stream == stdout)
  745. ST->ConOut->OutputString(ST->ConOut, (wchar_t*)&dst);
  746. else if(__stream == stderr)
  747. ST->StdErr->OutputString(ST->StdErr, (wchar_t*)&dst);
  748. else if(__ser && __stream == (FILE*)__ser) {
  749. #ifdef UEFI_NO_UTF8
  750. wcstombs((char*)&tmp, dst, BUFSIZ - 1);
  751. #endif
  752. __ser->Write(__ser, &ret, (void*)&tmp);
  753. } else
  754. #ifndef UEFI_NO_UTF8
  755. __stream->Write(__stream, &ret, (void*)&tmp);
  756. #else
  757. __stream->Write(__stream, &ret, (void*)&dst);
  758. #endif
  759. return (int)ret;
  760. }
  761. int fprintf (FILE *__stream, const char_t *__format, ...)
  762. {
  763. int ret;
  764. __builtin_va_list args;
  765. __builtin_va_start(args, __format);
  766. ret = vfprintf(__stream, __format, args);
  767. __builtin_va_end(args);
  768. return ret;
  769. }
  770. int getchar_ifany (void)
  771. {
  772. efi_input_key_t key = { 0 };
  773. efi_status_t status = ST->ConIn->ReadKeyStroke(ST->ConIn, &key);
  774. return EFI_ERROR(status) ? 0 : key.UnicodeChar;
  775. }
  776. int getchar (void)
  777. {
  778. uintn_t idx;
  779. BS->WaitForEvent(1, &ST->ConIn->WaitForKey, &idx);
  780. return getchar_ifany();
  781. }
  782. int putchar (int __c)
  783. {
  784. wchar_t tmp[2];
  785. tmp[0] = (wchar_t)__c;
  786. tmp[1] = 0;
  787. ST->ConOut->OutputString(ST->ConOut, (__c == L'\n' ? (wchar_t*)L"\r\n" : (wchar_t*)&tmp));
  788. return (int)tmp[0];
  789. }