apdu_log.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. #include "apdu_log.h"
  2. #include <storage/storage.h>
  3. #include <flipper_format/flipper_format.h>
  4. #include <toolbox/stream/file_stream.h>
  5. #include <toolbox/stream/buffered_file_stream.h>
  6. #include <toolbox/args.h>
  7. #define TAG "APDULog"
  8. struct APDULog {
  9. Stream* stream;
  10. size_t total_lines;
  11. };
  12. static inline void apdu_log_add_ending_new_line(APDULog* instance) {
  13. if(stream_seek(instance->stream, -1, StreamOffsetFromEnd)) {
  14. uint8_t last_char = 0;
  15. // Check if the last char is new line or add a new line
  16. if(stream_read(instance->stream, &last_char, 1) == 1 && last_char != '\n') {
  17. FURI_LOG_D(TAG, "Adding new line ending");
  18. stream_write_char(instance->stream, '\n');
  19. }
  20. stream_rewind(instance->stream);
  21. }
  22. }
  23. static bool apdu_log_read_log_line(APDULog* instance, FuriString* line, bool* is_endfile) {
  24. if(stream_read_line(instance->stream, line) == false) {
  25. *is_endfile = true;
  26. }
  27. else {
  28. size_t newline_index = furi_string_search_char(line, '\n', 0);
  29. if(newline_index != FURI_STRING_FAILURE) {
  30. furi_string_left(line, newline_index);
  31. }
  32. FURI_LOG_T(
  33. TAG, "Read line: %s, len: %zu", furi_string_get_cstr(line), furi_string_size(line));
  34. return true;
  35. }
  36. return false;
  37. }
  38. bool apdu_log_check_presence(const char* path) {
  39. furi_check(path);
  40. Storage* storage = furi_record_open(RECORD_STORAGE);
  41. bool log_present = storage_common_stat(storage, path, NULL) == FSE_OK;
  42. furi_record_close(RECORD_STORAGE);
  43. return log_present;
  44. }
  45. APDULog* apdu_log_alloc(const char* path, APDULogMode mode) {
  46. furi_check(path);
  47. APDULog* instance = malloc(sizeof(APDULog));
  48. Storage* storage = furi_record_open(RECORD_STORAGE);
  49. instance->stream = buffered_file_stream_alloc(storage);
  50. FS_OpenMode open_mode = (mode == APDULogModeOpenAlways) ? FSOM_OPEN_ALWAYS :
  51. FSOM_OPEN_EXISTING;
  52. instance->total_lines = 0;
  53. bool file_exists =
  54. buffered_file_stream_open(instance->stream, path, FSAM_READ_WRITE, open_mode);
  55. if(!file_exists) {
  56. buffered_file_stream_close(instance->stream);
  57. } else {
  58. // Eventually add new line character in the last line to avoid skipping lines
  59. apdu_log_add_ending_new_line(instance);
  60. }
  61. FuriString* line = furi_string_alloc();
  62. bool is_endfile = false;
  63. // In this loop we only count the entries in the file
  64. // We prefer not to load the whole file in memory for space reasons
  65. while(file_exists && !is_endfile) {
  66. bool read_log = apdu_log_read_log_line(instance, line, &is_endfile);
  67. if(read_log) {
  68. instance->total_lines++;
  69. }
  70. }
  71. stream_rewind(instance->stream);
  72. FURI_LOG_I(TAG, "Loaded log with %zu lines", instance->total_lines);
  73. furi_string_free(line);
  74. return instance;
  75. }
  76. void apdu_log_free(APDULog* instance) {
  77. furi_check(instance);
  78. furi_check(instance->stream);
  79. buffered_file_stream_close(instance->stream);
  80. stream_free(instance->stream);
  81. free(instance);
  82. furi_record_close(RECORD_STORAGE);
  83. }
  84. size_t apdu_log_get_total_lines(APDULog* instance) {
  85. furi_check(instance);
  86. return instance->total_lines;
  87. }
  88. bool apdu_log_rewind(APDULog* instance) {
  89. furi_check(instance);
  90. furi_check(instance->stream);
  91. return stream_rewind(instance->stream);
  92. }
  93. bool apdu_log_get_next_log_str(APDULog* instance, FuriString* log) {
  94. furi_assert(instance);
  95. furi_assert(instance->stream);
  96. furi_assert(log);
  97. bool log_read = false;
  98. bool is_endfile = false;
  99. furi_string_reset(log);
  100. while(!log_read && !is_endfile)
  101. log_read = apdu_log_read_log_line(instance, log, &is_endfile);
  102. return log_read;
  103. }