cmd_file.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. /*
  2. *******************************************************************************
  3. \file cmd_file.c
  4. \brief Command-line interface to Bee2: file management
  5. \project bee2/cmd
  6. \created 2022.06.08
  7. \version 2025.06.09
  8. \copyright The Bee2 authors
  9. \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt).
  10. *******************************************************************************
  11. */
  12. #include <stdlib.h>
  13. #include <errno.h>
  14. #include <bee2/core/der.h>
  15. #include <bee2/core/err.h>
  16. #include <bee2/core/file.h>
  17. #include <bee2/core/mem.h>
  18. #include <bee2/core/str.h>
  19. #include <bee2/core/util.h>
  20. #include "bee2/cmd.h"
  21. /*
  22. *******************************************************************************
  23. Размер файла
  24. *******************************************************************************
  25. */
  26. size_t cmdFileSize(const char* name)
  27. {
  28. err_t code;
  29. file_t file;
  30. size_t size;
  31. // pre
  32. ASSERT(strIsValid(name));
  33. // определить размер
  34. code = cmdFileOpen(file, name, "rb");
  35. ERR_CALL_CHECK(code);
  36. size = fileSize(file);
  37. code = cmdFileClose2(file);
  38. return (size == SIZE_MAX || code != ERR_OK) ? SIZE_MAX : size;
  39. }
  40. /*
  41. *******************************************************************************
  42. Чтение / запись
  43. *******************************************************************************
  44. */
  45. err_t cmdFileWrite(const char* name, const void* buf, size_t count)
  46. {
  47. err_t code;
  48. file_t file;
  49. // pre
  50. ASSERT(strIsValid(name));
  51. ASSERT(memIsValid(buf, count));
  52. // записать
  53. code = cmdFileOpen(file, name, "wb");
  54. ERR_CALL_CHECK(code);
  55. code = fileWrite(&count, buf, count, file);
  56. ERR_CALL_HANDLE(code, cmdFileClose(file));
  57. return cmdFileClose2(file);
  58. }
  59. err_t cmdFilePrepend(const char* name, const void* buf, size_t count)
  60. {
  61. const size_t buf1_size = 4096;
  62. err_t code;
  63. file_t file;
  64. size_t size;
  65. size_t pos;
  66. void* buf1;
  67. // pre
  68. ASSERT(strIsValid(name));
  69. ASSERT(memIsValid(buf, count));
  70. // открыть файл
  71. code = cmdFileOpen(file, name, "r+b");
  72. if (code != ERR_OK)
  73. {
  74. // не открывается, но существует?
  75. code = cmdFileOpen(file, name, "rb");
  76. if (code == ERR_OK)
  77. {
  78. cmdFileClose(file);
  79. return ERR_FILE_OPEN;
  80. }
  81. // не существует => создать
  82. return cmdFileWrite(name, buf, count);
  83. }
  84. // определить размер файла
  85. if ((size = fileSize(file)) == SIZE_MAX)
  86. {
  87. cmdFileClose(file);
  88. return ERR_FILE_READ;
  89. }
  90. // сдвинуть содержимое файла вправо
  91. code = cmdBlobCreate(buf1, buf1_size);
  92. ERR_CALL_HANDLE(code, cmdFileClose(file));
  93. for (pos = size; code == ERR_OK && pos;)
  94. {
  95. size_t c;
  96. c = MIN2(buf1_size, pos), pos -= c;
  97. if (!fileSeek(file, pos, SEEK_SET) ||
  98. fileRead2(buf1, c, file) != c ||
  99. !fileSeek(file, pos + count, SEEK_SET))
  100. code = ERR_FILE_READ;
  101. else
  102. code = fileWrite(&c, buf1, c, file);
  103. }
  104. cmdBlobClose(buf1);
  105. ERR_CALL_HANDLE(code, cmdFileClose(file));
  106. // дописать в начало
  107. if (!fileSeek(file, 0, SEEK_SET))
  108. code = ERR_FILE_READ;
  109. else
  110. code = fileWrite(&count, buf, count, file);
  111. ERR_CALL_HANDLE(code, cmdFileClose(file));
  112. // завершить
  113. return cmdFileClose2(file);
  114. }
  115. err_t cmdFileAppend(const char* name, const void* buf, size_t count)
  116. {
  117. err_t code;
  118. file_t file;
  119. // pre
  120. ASSERT(strIsValid(name));
  121. ASSERT(memIsValid(buf, count));
  122. // дописать в конец
  123. code = cmdFileOpen(file, name, "ab");
  124. ERR_CALL_CHECK(code);
  125. code = fileWrite(&count, buf, count, file);
  126. ERR_CALL_HANDLE(code, cmdFileClose(file));
  127. return cmdFileClose2(file);
  128. }
  129. err_t cmdFileReadAll(void* buf, size_t* count, const char* name)
  130. {
  131. err_t code;
  132. file_t file;
  133. // pre
  134. ASSERT(memIsValid(count, O_PER_S));
  135. ASSERT(strIsValid(name));
  136. // открыть файл
  137. code = cmdFileOpen(file, name, "rb");
  138. ERR_CALL_CHECK(code);
  139. // определить длину файла
  140. if (!buf)
  141. {
  142. *count = cmdFileSize(name);
  143. if (*count == SIZE_MAX)
  144. code = ERR_FILE_READ;
  145. }
  146. // читать
  147. else
  148. {
  149. ASSERT(memIsValid(buf, *count));
  150. if (fileRead2(buf, *count, file) != *count)
  151. code = ERR_FILE_READ;
  152. else if (fileSize(file) != *count)
  153. code = ERR_BAD_FILE;
  154. }
  155. ERR_CALL_HANDLE(code, cmdFileClose(file));
  156. return cmdFileClose2(file);
  157. }
  158. /*
  159. *******************************************************************************
  160. Обрезка
  161. *******************************************************************************
  162. */
  163. err_t cmdFileBehead(const char* name, size_t count)
  164. {
  165. const size_t buf_size = 4096;
  166. err_t code;
  167. file_t file;
  168. size_t size;
  169. size_t pos;
  170. void* buf;
  171. // pre
  172. ASSERT(strIsValid(name));
  173. // открыть файл
  174. code = cmdFileOpen(file, name, "r+b");
  175. ERR_CALL_CHECK(code);
  176. // определить размер файла
  177. if ((size = fileSize(file)) == SIZE_MAX)
  178. code = ERR_FILE_READ;
  179. else if (size < count)
  180. code = ERR_FILE_SIZE;
  181. ERR_CALL_HANDLE(code, cmdFileClose(file));
  182. // сдвинуть содержимое файла влево
  183. code = cmdBlobCreate(buf, buf_size);
  184. ERR_CALL_HANDLE(code, cmdFileClose(file));
  185. for (pos = 0; code == ERR_OK && pos < size - count;)
  186. {
  187. size_t c = MIN2(buf_size, size - count - pos);
  188. if (!fileSeek(file, pos + count, SEEK_SET) ||
  189. fileRead2(buf, c, file) != c ||
  190. !fileSeek(file, pos, SEEK_SET))
  191. code = ERR_FILE_READ;
  192. else
  193. code = fileWrite(&c, buf, c, file);
  194. pos += c;
  195. }
  196. cmdBlobClose(buf);
  197. ERR_CALL_HANDLE(code, cmdFileClose(file));
  198. // обрезать файл
  199. if (!fileTrunc(file, size - count))
  200. code = ERR_FILE_WRITE;
  201. ERR_CALL_HANDLE(code, cmdFileClose(file));
  202. // завершить
  203. return cmdFileClose2(file);
  204. }
  205. err_t cmdFileDrop(const char* name, size_t count)
  206. {
  207. err_t code;
  208. file_t file;
  209. size_t size;
  210. // pre
  211. ASSERT(strIsValid(name));
  212. // открыть файл
  213. code = cmdFileOpen(file, name, "r+b");
  214. ERR_CALL_CHECK(code);
  215. // определить размер файла
  216. if ((size = fileSize(file)) == SIZE_MAX)
  217. code = ERR_FILE_READ;
  218. else if (size < count)
  219. code = ERR_FILE_SIZE;
  220. ERR_CALL_HANDLE(code, cmdFileClose(file));
  221. // обрезать файл
  222. if (!fileTrunc(file, size - count))
  223. code = ERR_FILE_WRITE;
  224. ERR_CALL_HANDLE(code, cmdFileClose(file));
  225. // завершить
  226. return cmdFileClose2(file);
  227. }
  228. /*
  229. *******************************************************************************
  230. Дублирование
  231. *******************************************************************************
  232. */
  233. err_t cmdFileDup(const char* oname, const char* iname, size_t skip,
  234. size_t count)
  235. {
  236. const size_t buf_size = 4096;
  237. err_t code;
  238. file_t ifile;
  239. file_t ofile;
  240. void* buf;
  241. // pre
  242. ASSERT(strIsValid(iname) && strIsValid(oname));
  243. // открыть входной файл
  244. code = cmdFileOpen(ifile, iname, "rb");
  245. ERR_CALL_CHECK(code);
  246. // пропустить skip октетов
  247. if (!fileSeek(ifile, skip, SEEK_SET))
  248. {
  249. cmdFileClose(ifile);
  250. return ERR_FILE_READ;
  251. }
  252. // открыть выходной файл
  253. code = cmdFileOpen(ofile, oname, "wb");
  254. ERR_CALL_HANDLE(code, cmdFileClose(ifile));
  255. // подготовить память
  256. code = cmdBlobCreate(buf, buf_size);
  257. ERR_CALL_HANDLE(code, (cmdFileClose(ofile), cmdFileClose(ifile)));
  258. // дублировать count октетов
  259. if (count != SIZE_MAX)
  260. while (count && code == ERR_OK)
  261. {
  262. size_t c = MIN2(buf_size, count);
  263. if (fileRead2(buf, c, ifile) != c)
  264. code = ERR_FILE_READ;
  265. else
  266. code = fileWrite(&c, buf, c, ofile);
  267. count -= c;
  268. }
  269. // дублировать все
  270. else
  271. while (count && code == ERR_OK)
  272. {
  273. count = fileRead2(buf, buf_size, ifile);
  274. if (count == SIZE_MAX)
  275. code = ERR_FILE_READ;
  276. else
  277. code = fileWrite(&count, buf, count, ofile);
  278. }
  279. // завершить
  280. cmdBlobClose(buf);
  281. ERR_CALL_HANDLE(code, (cmdFileClose(ofile), cmdFileClose(ifile)));
  282. code = cmdFileClose2(ofile);
  283. ERR_CALL_HANDLE(code, cmdFileClose(ifile));
  284. return cmdFileClose2(ifile);
  285. }
  286. /*
  287. *******************************************************************************
  288. Проверки
  289. *******************************************************************************
  290. */
  291. err_t cmdFileValNotExist(int count, char* names[])
  292. {
  293. err_t code;
  294. file_t file;
  295. int ch;
  296. for (; count--; names++)
  297. {
  298. ASSERT(strIsValid(*names));
  299. code = cmdFileOpen(file, *names, "rb");
  300. if (code == ERR_OK)
  301. {
  302. code = cmdFileClose2(file);
  303. ERR_CALL_CHECK(code);
  304. if (printf("Some files already exist. Overwrite [y/n]?") < 0)
  305. return ERR_FILE_EXISTS;
  306. do
  307. ch = cmdTermGetch();
  308. while (ch != 'Y' && ch != 'y' && ch != 'N' && ch != 'n' && ch != '\n');
  309. printf("\n");
  310. if (ch == 'N' || ch == 'n' || ch == '\n')
  311. return ERR_FILE_EXISTS;
  312. break;
  313. }
  314. }
  315. return ERR_OK;
  316. }
  317. err_t cmdFileValExist(int count, char* names[])
  318. {
  319. err_t code;
  320. file_t file;
  321. for (; count--; names++)
  322. {
  323. ASSERT(strIsValid(*names));
  324. code = cmdFileOpen(file, *names, "rb");
  325. if (code != ERR_OK)
  326. return ERR_FILE_NOT_FOUND;
  327. code = cmdFileClose2(file);
  328. ERR_CALL_CHECK(code);
  329. }
  330. return ERR_OK;
  331. }
  332. bool_t cmdFileAreSame(const char* file1, const char* file2)
  333. {
  334. bool_t ret;
  335. #ifdef OS_UNIX
  336. char* path1 = realpath(file1, 0);
  337. char* path2 = realpath(file2, 0);
  338. ret = path1 && path2 && strEq(path1, path2);
  339. free(path1), free(path2);
  340. #elif defined OS_WIN
  341. char* path1 = _fullpath(0, file1, 0);
  342. char* path2 = _fullpath(0, file2, 0);
  343. ret = path1 && path2 && strEq(path1, path2);
  344. free(path1), free(path2);
  345. #else
  346. ret = strEq(file1, file2);
  347. #endif
  348. return ret;
  349. }
  350. /*
  351. *******************************************************************************
  352. Аффиксы
  353. *******************************************************************************
  354. */
  355. err_t cmdFilePrefixRead(octet* prefix, size_t* count, const char* name,
  356. size_t offset)
  357. {
  358. const size_t buf_size = 16;
  359. err_t code;
  360. file_t file;
  361. size_t size;
  362. void* buf;
  363. size_t c;
  364. u32 tag;
  365. size_t len;
  366. // pre
  367. ASSERT(strIsValid(name));
  368. ASSERT(memIsValid(count, O_PER_S));
  369. // открыть файл
  370. code = cmdFileOpen(file, name, "rb");
  371. ERR_CALL_CHECK(code);
  372. // определить размер файла
  373. if ((size = fileSize(file)) == SIZE_MAX)
  374. code = ERR_FILE_READ;
  375. else if (offset >= size)
  376. code = ERR_FILE_SIZE;
  377. ERR_CALL_HANDLE(code, cmdFileClose(file));
  378. // подготовить память
  379. code = cmdBlobCreate(buf, buf_size);
  380. ERR_CALL_HANDLE(code, cmdFileClose(file));
  381. // прочитать заголовок префикса
  382. c = MIN2(buf_size, size - offset);
  383. if (!fileSeek(file, offset, SEEK_SET) ||
  384. fileRead2(buf, c, file) != c)
  385. code = ERR_FILE_READ;
  386. ERR_CALL_HANDLE(code, (cmdBlobClose(buf), cmdFileClose(file)));
  387. // определить длину префикса
  388. c = derTLDec(&tag, &len, buf, c);
  389. if (c == SIZE_MAX || offset + c + len > size)
  390. code = ERR_BAD_FORMAT;
  391. ERR_CALL_HANDLE(code, (cmdBlobClose(buf), cmdFileClose(file)));
  392. c += len;
  393. // прочитать префикс
  394. cmdBlobClose(buf);
  395. code = cmdBlobCreate(buf, c);
  396. ERR_CALL_HANDLE(code, cmdFileClose(file));
  397. if (!fileSeek(file, offset, SEEK_SET) ||
  398. fileRead2(buf, c, file) != c)
  399. code = ERR_FILE_READ;
  400. ERR_CALL_HANDLE(code, (cmdBlobClose(buf), cmdFileClose(file)));
  401. // закрыть файл
  402. code = cmdFileClose2(file);
  403. ERR_CALL_HANDLE(code, cmdBlobClose(buf));
  404. // проверить префикс
  405. if (!derIsValid3(buf, c))
  406. code = ERR_BAD_FORMAT;
  407. ERR_CALL_HANDLE(code, cmdBlobClose(buf));
  408. // возвратить префикс и его длину
  409. if (prefix)
  410. {
  411. ASSERT(memIsValid(prefix, *count));
  412. if (*count < c)
  413. code = ERR_OUTOFMEMORY;
  414. else
  415. memCopy(prefix, buf, c);
  416. }
  417. *count = c;
  418. // завершить
  419. cmdBlobClose(buf);
  420. return code;
  421. }
  422. err_t cmdFileSuffixRead(octet* suffix, size_t* count, const char* name,
  423. size_t offset)
  424. {
  425. const size_t buf_size = 16;
  426. err_t code;
  427. file_t file;
  428. size_t size;
  429. void* buf;
  430. size_t c;
  431. u32 tag;
  432. size_t len;
  433. // pre
  434. ASSERT(strIsValid(name));
  435. ASSERT(memIsValid(count, O_PER_S));
  436. // открыть файл
  437. code = cmdFileOpen(file, name, "rb");
  438. ERR_CALL_CHECK(code);
  439. // определить размер файла
  440. if ((size = fileSize(file)) == SIZE_MAX)
  441. code = ERR_FILE_READ;
  442. else if (offset >= size)
  443. code = ERR_FILE_SIZE;
  444. ERR_CALL_HANDLE(code, cmdFileClose(file));
  445. // подготовить память
  446. code = cmdBlobCreate(buf, buf_size);
  447. ERR_CALL_HANDLE(code, cmdFileClose(file));
  448. // прочитать заголовок суффикса
  449. c = MIN2(buf_size, size - offset);
  450. if (!fileSeek(file, size - offset - c, SEEK_SET) ||
  451. fileRead2(buf, c, file) != c)
  452. code = ERR_FILE_READ;
  453. ERR_CALL_HANDLE(code, (cmdBlobClose(buf), cmdFileClose(file)));
  454. // развернуть суффикс
  455. memRev(buf, c);
  456. // определить длину суффикса
  457. c = derTLDec(&tag, &len, buf, c);
  458. if (c == SIZE_MAX || offset + c + len > size)
  459. code = ERR_BAD_FORMAT;
  460. ERR_CALL_HANDLE(code, (cmdBlobClose(buf), cmdFileClose(file)));
  461. c += len;
  462. // прочитать суффикс
  463. cmdBlobClose(buf);
  464. code = cmdBlobCreate(buf, c);
  465. ERR_CALL_HANDLE(code, cmdFileClose(file));
  466. if (!fileSeek(file, size - offset - c, SEEK_SET) ||
  467. fileRead2(buf, c, file) != c)
  468. code = ERR_FILE_READ;
  469. ERR_CALL_HANDLE(code, (cmdBlobClose(buf), cmdFileClose(file)));
  470. // закрыть файл
  471. code = cmdFileClose2(file);
  472. ERR_CALL_HANDLE(code, cmdBlobClose(buf));
  473. // проверить суффикс
  474. memRev(buf, c);
  475. if (!derIsValid3(buf, c))
  476. code = ERR_BAD_FORMAT;
  477. ERR_CALL_HANDLE(code, cmdBlobClose(buf));
  478. memRev(buf, c);
  479. // возвратить суффикс и его длину
  480. if (suffix)
  481. {
  482. ASSERT(memIsValid(suffix, *count));
  483. if (*count < c)
  484. code = ERR_OUTOFMEMORY;
  485. else
  486. memCopy(suffix, buf, c);
  487. }
  488. *count = c;
  489. // завершить
  490. cmdBlobClose(buf);
  491. return code;
  492. }
  493. /*
  494. *******************************************************************************
  495. Удаление
  496. *******************************************************************************
  497. */
  498. #ifdef OS_UNIX
  499. #include <unistd.h>
  500. #define cmdFileUnlink unlink
  501. #elif defined OS_WIN
  502. #include <io.h>
  503. #include <stdio.h>
  504. #define cmdFileUnlink _unlink
  505. #else
  506. static int cmdFileUnlink(const char* name)
  507. {
  508. errno = ENOSYS;
  509. return -1;
  510. }
  511. #endif
  512. err_t cmdFileDel(const char* name)
  513. {
  514. ASSERT(strIsValid(name));
  515. if (cmdFileUnlink(name) == 0)
  516. return ERR_OK;
  517. switch (errno)
  518. {
  519. case EACCES:
  520. return ERR_FILE_READ;
  521. case ENOENT:
  522. return ERR_FILE_NOT_FOUND;
  523. case ENOSYS:
  524. return ERR_NOT_IMPLEMENTED;
  525. default:
  526. return ERR_BAD_FILE;
  527. }
  528. }