Updated Library Documentation (markdown)

2025-09-02 12:36:51 +03:00
parent fff835ae8d
commit 9b7c0a8ead

@@ -1 +1,613 @@
## libhelper library
It provides functions, classes and macros that will make your work easier when writing your string program, such as the name `libhelper`.
---
### Helpful macros and variables
`libhelper` provides some macros and variables to make your work easier.
```c++
// File/directory modes
constexpr mode_t DEFAULT_FILE_PERMS = 0644;
constexpr mode_t DEFAULT_EXTENDED_FILE_PERMS = 0755;
constexpr mode_t DEFAULT_DIR_PERMS = 0755;
// Boolean alternatives (is there anyone who will actually use it???)
constexpr int YES = 1; // YES = true
constexpr int NO = 0; // NO = false
// Macros that can be useful for size conversions
#define KB(x) (x * 1024) // KB(8) = 8192 (8 * 1024)
#define MB(x) (KB(x) * 1024) // MB(4) = 4194304 (KB(4) * 1024)
#define GB(x) (MB(x) * 1024) // GB(1) = 1073741824 (MB(1) * 1024)
// Conversion from bytes to other units
#define TO_KB(x) (x / 1024) // TO_KB(1024) = 1
#define TO_MB(x) (TO_KB(x) / 1024) // TO_MB(2048) (2048 / 1024)
#define TO_GB(x) (TO_MB(x) / 1024) // TO_GB(1048576) (TO_MB(1048576) / 1024)
// Macros to use for text decoration
#define STYLE_RESET "\033[0m"
#define BOLD "\033[1m"
#define FAINT "\033[2m"
#define ITALIC "\033[3m"
#define UNDERLINE "\033[4m"
#define BLINC "\033[5m"
#define FAST_BLINC "\033[6m"
#define STRIKE_THROUGHT "\033[9m"
#define NO_UNDERLINE "\033[24m"
#define NO_BLINC "\033[25m"
#define RED "\033[31m"
#define GREEN "\033[32m"
#define YELLOW "\033[33m"
// If NO_C_TYPE_HANDLERS is defined, these macros are can't defined.
#ifndef NO_C_TYPE_HANDLERS
// ABORT(message), ex: ABORT("memory error!\n")
#define ABORT(msg) \
do { \
fprintf(stderr, "%s%sCRITICAL ERROR%s: %s\nAborting...\n", BOLD, RED, \
STYLE_RESET, msg); \
abort(); \
} while (0)
// ERROR(message, exit), ex: ERROR("an error occured.\n", 1)
#define ERROR(msg, code) \
do { \
fprintf(stderr, "%s%sERROR%s: %s", BOLD, RED, STYLE_RESET, msg); \
exit(code); \
} while (0)
// WARNING(message), ex: WARNING("using default setting.\n")
#define WARNING(msg) \
fprintf(stderr, "%s%sWARNING%s: %s", BOLD, YELLOW, STYLE_RESET, msg);
// INFO(message), ex: INFO("operation ended.\n")
#define INFO(msg) \
fprintf(stdout, "%s%sINFO%s: %s", BOLD, GREEN, STYLE_RESET, msg);
#endif // #ifndef NO_C_TYPE_HANDLERS
// Common version string creator.
// Don't forget to define the BUILD*** macros.
#define MKVERSION(name) \
char vinfo[512]; \
sprintf(vinfo, \
"%s %s [%s %s]\nBuildType: %s\nCMakeVersion: %s\nCompilerVersion: " \
"%s\nBuildFlags: %s", \
name, BUILD_VERSION, BUILD_DATE, BUILD_TIME, BUILD_TYPE, \
BUILD_CMAKE_VERSION, BUILD_COMPILER_VERSION, BUILD_FLAGS); \
return std::string(vinfo)
```
---
### Classes
`libhelper` provides three classes in total. One is for logging (used with macros), one is an error class created with `std::exception`, and the third is a garbage collector.
---
#### Logging Class
A total of four logging levels are provided: information, warning, error, and abort (critical error).
```c++
enum LogLevels {
INFO = static_cast<int>('I'),
WARNING = static_cast<int>('W'),
ERROR = static_cast<int>('E'),
ABORT = static_cast<int>('A')
};
// It actually seemed more logical to use the numerical equivalents of logical letters instead of numbers like 1-2.
```
A class called `Logging` handles everything. However, logging directly using the class isn't practical. There are some macros for this:
```c++
#define LOG(level) \
Helper::Logger(level, __func__, Helper::LoggingProperties::FILE.data(), \
Helper::LoggingProperties::NAME.data(), __FILE__, __LINE__)
#define LOGN(name, level) \
Helper::Logger(level, __func__, Helper::LoggingProperties::FILE.data(), \
name, __FILE__, __LINE__)
#define LOGNF(name, file, level) \
Helper::Logger(level, file, name, __FILE__, __LINE__)
#define LOG_IF(level, condition) \
if (condition) \
Helper::Logger(level, __func__, Helper::LoggingProperties::FILE.data(), \
Helper::LoggingProperties::NAME.data(), __FILE__, __LINE__)
#define LOGN_IF(name, level, condition) \
if (condition) \
Helper::Logger(level, __func__, Helper::LoggingProperties::FILE.data(), \
name, __FILE__, __LINE__)
#define LOGNF_IF(name, file, level, condition) \
if (condition) Helper::Logger(level, __func__, file, name, __FILE__, __LINE__)
```
There are some settings available for logging. These are stored in the `Helper::LoggingProperties` namespace. These are: `FILE` (`std::string_view`), `NAME` (`std::string_view`), `PRINT` (`bool`), `DISABLE` (`bool`). The default values are:
```c++
// Utilities.cpp
namespace Helper {
namespace LoggingProperties {
std::string_view FILE = "last_logs.log", NAME = "main";
bool PRINT = NO, DISABLE = NO;
} // namespace LoggingProperties
} // namespace Helper
```
- `PRINT` specifies whether the log will be printed to the screen. This can be useful for things like verbose logging. The default is `NO` (`false`).
- You can control logging with `DISABLE`. The default is `NO` (`false`). Setting it to true stops logging. No writing to the file.
- `FILE` is the file where the log content will be saved. By default, it's `last_logs.log`. It's created in your current directory.
- `NAME` is the name of the program that prints the log that will appear in the log. By default, it is `main`.
You can change these settings directly or use the provided functions.
```c++
namespace Helper {
namespace LoggingProperties {
void set(std::string_view name, std::string_view file);
void setProgramName(std::string_view name);
void setLogFile(std::string_view file);
void setPrinting(int state);
void setLoggingState(int state); // Disable/enable logging
void reset();
} // namespace LoggingProperties
} // namespae Helper
```
It's pretty clear what the functions named `set***` do. But I'd still like to mention `reset()`. It restores default settings.
Now we can reinforce it with use cases. That would be good.
```c++
#include <libhelper/lib.hpp>
#include <iostream>
int main(int argc, char** argv) {
Helper::LoggingProperties::set("pmt", "/storage/emulated/0/Dccuments/last_pmt_logs.log");
LOG(INFO) << "Hello World!" << std::endl;
LOG_IF(WARNING, argc < 2) << "No argument speficed!" << std::endl;
LOGN(argv[0], INFO) << "Hello World from " << argv[0] << " program!" << std::endl;
LOGNF(argv[0], "last_logs_test.log", INFO) << "LOGNF() test!!!" << std::endl;
LOGN_IF(argv[0], WARNING, argc < 2) << "No argument speficed! (test 2)" << std::endl;
LOGNF_IF(argv[0], "last_logs_test.log", ERROR, argc > 3) << "Too many argument!" << std::endl;
std::cout << "Program complete!" << std::endl;
return 0;
}
```
---
#### Error Class
It is a throwable error class derived from `std::exception`. Thrown errors are also recorded in the logs.
```c++
#include <libhelper/lib.hpp>
#include <iostream>
int main(int argc, char** argv) {
try {
if (argc < 2)
throw Helper::Error("No enoght argument!");
} catch (Helper::Error &e) {
std::cerr << e.what() << std::endl;
return 1;
}
return 0;
}
```
---
#### Garbage Collector Class
The garbage collector is there to free allocated memory (certain types) and close file pointers. In other words, it handles your work for you. It also has additional auxiliary functions.
```c++
namespace Helper {
class garbageCollector {
// Other definations...
public:
~garbageCollector();
void delAfterProgress(char *&_ptr); // Delete allocated char variables after function
void delAfterProgress(uint8_t *&_ptr); // Delete allocated uint8_t variables after funtion
void delFileAfterProgress(const std::string &path); // Delete input file after function
void closeAfterProgress(FILE *&_fp); // Close input file pointers after function
void closeAfterProgress(DIR *&_dp); // Close input directory pointers after function
void closeAfterProgress(int _fd); // Close input file descriptor after function
// You can add whatever you want by using it multiple times
};
// Open input path with flags and add to integrity list. And return file descriptor. open() is emulated
[[nodiscard]] int openAndAddToCloseList(const std::string_view &path,
garbageCollector &collector, int flags,
mode_t mode = 0000);
// fopen() is emulated.
[[nodiscard]] FILE *openAndAddToCloseList(const std::string_view &path,
garbageCollector &collector,
const char *mode);
[[nodiscard]] DIR *openAndAddToCloseList(const std::string_view &path,
garbageCollector &collector);
// You can need use returned values!!! Because these functions marked as nodiscard
} // namespace Helper
```
Usage example...
```c++
#include <libhelper/lib.hpp>
#include <stdlib.h>
#include <stdio.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <iostream>
int main(int argc, char** argv) {
Helper::garbageCollector collector;
int fd1 = open("file.txt", O_TRUNC | O_CREAT, 0644);
collector.closeAfterProgress(fd1); // no need close() anymore
// You can do the same with FILE or DIR
// Do your file progresses...
// Delete file.txt automaticly after program
collector.delFileAfterProgess("file.txt");
int fd2 = Helper::openAndAddToCloseList("file2.txt", collector, O_TRUNC, DEFAULT_FILE_PERMS);
// Do your file progresses...
// WARNING: It does not check whether the file is opened or not.
DIR *dp = Helper::openAndAddToCloseList("dir1", collector);
// Do your directory progresses...
// WARNING: It does not check whether the directory is opened or not.
auto *buffer = new char[4096]; // Allocate memory
collector.delAfterProgrss(buffer);
// You can do the same with uint8_t
return 0;
}
```
---
#### Functions
`libhelper` provides many helper functions. Some of these throw `Helper::Error`. We'll cover each one with code examples.
```c++
#include <libhelper/lib.hpp>
#include <iostream>
#include <stdio.h>
int main(int argc, char** argv) {
/**
Checker functions - don't throw Helper::Error
bool hasSuperUser();
// Returns true if user id equals AID_ROOT.
// See srclib/libhelper/include/private/android_filesystem_config.h
bool hasAdbPermissions();
// Returns true if user id equals AID_SHELL.
// See srclib/libhelper/include/private/android_filesystem_config.h
bool isExists(std::string_view entry);
// Returns true if entry exists (file or directory).
bool fileIsExists(std::string_view file);
// Returns true if given file exists.
bool directoryIsExists(std::string_view directory);
// Returns true if given directory exists.
bool linkIsExists(std::string_view entry);
// Returns true if entry is symbolic or hard link.
bool isLink(std::string_view entry);
// Returns true if entry is a symbolic link.
bool isSymbolicLink(std::string_view entry);
// Alias of isLink().
bool isHardLink(std::string_view entry);
// Returns true if entry is a hard link.
bool areLinked(std::string_view entry1, std::string_view entry2);
// Returns true if entry1 links to entry2 (symbolic link).
*/
if (!Helper::hasSuperUser())
std::cout << "Super user privileges not given." << std::endl;
if (Helper::hasAdbPermissions())
std::cout << "You have ADB shell privileges." << std::endl;
if (Helper::isExists("myentry"))
std::cout << "myentry exists." << std::endl;
if (Helper::fileIsExists("file.txt"))
std::cout << "file.txt exists." << std::endl;
if (Helper::directoryIsExists("mydir"))
std::cout << "mydir exists." << std::endl;
if (Helper::linkIsExists("mylinked_entry"))
std::cout << "mylinked_entry is symbolic or hard link." << std::endl;
if (Helper::isLink("mylinked_entry"))
std::cout << "mylinked_entry is symbolic link." << std::endl;
if (Helper::isHardLink("myhardlinked_entry"))
std::cout << "myhardlinked_entry is hard link." << std::endl;
if (Helper::areLinked("mylinked_entry", "myentry"))
std::cout << "mylinked_entry links to myentry." << std::endl;
/**
File I/O - don't throw Helper::Error
bool writeFile(std::string_view file, std::string_view text);
// Writes given text into file.
// If file does not exist, it is automatically created.
// Returns true on success.
std::optional<std::string> readFile(std::string_view file);
// Reads file content into string.
// On success returns file content.
// On error returns std::nullopt.
*/
if (Helper::writeFile("file.txt", "hello world"))
std::cout << "file.txt written." << std::endl;
auto result = Helper::readFile("file.txt");
if (!result)
std::cerr << "Cannot read file.txt." << std::endl;
else
std::cout << "file.txt: " << *result << std::endl;
/**
Creators - don't throw Helper::Error
bool makeDirectory(std::string_view path);
// Creates a directory at given path.
// Returns true on success.
bool makeRecursiveDirectory(std::string_view paths);
// Creates all directories in given path recursively.
// Returns true on success.
bool createFile(std::string_view path);
// Creates an empty file at given path.
// Returns true on success.
bool createSymlink(std::string_view entry1, std::string_view entry2);
// Creates a symbolic link entry2 pointing to entry1.
// Returns true on success.
*/
if (Helper::makeDirectory("dir"))
std::cout << "dir created." << std::endl;
if (Helper::makeRecursiveDirectory("dir1/dir2/dir3"))
std::cout << "dir1/dir2/dir3 created." << std::endl;
if (Helper::createFile("newfile.txt"))
std::cout << "newfile.txt created." << std::endl;
if (Helper::createSymlink("newfile.txt", "newfile_symlink.txt"))
std::cout << "newfile.txt symlinked to newfile_symlink.txt." << std::endl;
/**
Removers - don't throw Helper::Error
bool eraseEntry(std::string_view entry);
// Removes file or empty directory.
// Returns true on success.
bool eraseDirectoryRecursive(std::string_view directory);
// Removes directory and all its contents recursively.
// Returns true on success.
*/
if (Helper::eraseEntry("file.txt"))
std::cout << "file.txt erased." << std::endl;
if (Helper::eraseDirectoryRecursive("dir1/dir2/dir3"))
std::cout << "dir1/dir2/dir3 erased." << std::endl;
/**
Getters - don't throw Helper::Error
int64_T fileSize(std::string_view file);
// Returns file size in bytes.
// On error returns -1.
std::string readSymlink(std::string_view entry);
// Reads the target of symbolic link.
// Returns empty string on error.
*/
if (size_t size = Helper::fileSize("newfile.txt"); size == -1)
std::cout << "Cannot get size of newfile.txt" << std::endl;
else
std::cout << "Size of newfile.txt: " << size << std::endl;
if (std::string s = Helper::readSymlink("newfile_symlink.txt"); s.empty())
std::cout << "Cannot read symlink: newfile_symlink.txt" << std::endl;
else
std::cout << "newfile_symlink.txt points to " << s << std::endl;
/**
SHA-256 helpers
bool sha256Compare(std::string_view file1, std::string_view file2);
// Compares the SHA-256 hash of two files.
// Returns true if hashes are equal, false otherwise.
// Uses sha256Of() internally.
// Does not throw Helper::Error.
std::optional<std::string> sha256Of(std::string_view path);
// Calculates SHA-256 of a file at given path.
// Returns hex string of hash on success.
// On error, throws Helper::Error.
*/
if (Helper::sha256Compare("file1.bin", "file2.bin"))
std::cout << "file1.bin and file2.bin are identical (SHA-256)" << std::endl;
else
std::cout << "Files differ (SHA-256)" << std::endl;
if (auto hash = Helper::sha256Of("file1.bin"))
std::cout << "SHA-256 of file1.bin: " << *hash << std::endl;
else
std::cout << "Failed to calculate SHA-256 of file1.bin" << std::endl;
/**
Utilities - don't throw Helper::Error
bool copyFile(std::string_view file, std::string_view dest);
// Copies file to dest.
// Returns true on success.
bool runCommand(std::string_view cmd);
// Runs a system command without capturing output.
// Returns true if exit status == 0.
bool confirmPropt(std::string_view message);
// Shows message and asks for y/N from user.
// Returns true if user selects "Yes", false otherwise.
bool changeMode(std::string_view file, mode_t mode);
// Changes file permissions.
// Returns true on success.
bool changeOwner(std::string_view file, uid_t uid, gid_t gid);
// Changes file owner and group.
// Returns true on success.
std::string currentWorkingDirectory();
// Returns current working directory as string.
std::string currentDate();
// Returns current date as string (format: YYYY-MM-DD).
std::string currentTime();
// Returns current time as string (format: HH:MM:SS).
std::string runCommandWithOutput(std::string_view cmd);
// Runs system command and returns its stdout as string.
std::string pathJoin(std::string base, std::string relative);
// Joins base path with relative path and returns result.
std::string pathBasename(std::string_view entry);
// Returns the filename part of given path.
std::string pathDirname(std::string_view entry);
// Returns the directory part of given path.
uint64_t getRandomOffset(uint64_t size, uint64_t bufferSize);
// Returns random offset depending on size and bufferSize.
*/
if (Helper::copyFile("file.txt", "backup.txt"))
std::cout << "file.txt copied to backup.txt" << std::endl;
if (Helper::runCommand("ls -l"))
std::cout << "Command executed successfully" << std::endl;
if (Helper::confirmPropt("Do you want to continue?"))
std::cout << "User confirmed YES" << std::endl;
else
std::cout << "User selected NO" << std::endl;
if (Helper::changeMode("file.txt", 0644))
std::cout << "file.txt mode changed to 644" << std::endl;
if (Helper::changeOwner("file.txt", 1000, 1000))
std::cout << "file.txt owner changed to uid=1000 gid=1000" << std::endl;
std::cout << "CWD: " << Helper::currentWorkingDirectory() << std::endl;
std::cout << "Today: " << Helper::currentDate() << std::endl;
std::cout << "Now: " << Helper::currentTime() << std::endl;
std::string output = Helper::runCommandWithOutput("echo Hello");
std::cout << "Output: " << output << std::endl;
std::cout << "Joined path: " << Helper::pathJoin("/home/user", "docs/file.txt") << std::endl;
std::cout << "Basename: " << Helper::pathBasename("/home/user/docs/file.txt") << std::endl;
std::cout << "Dirname: " << Helper::pathDirname("/home/user/docs/file.txt") << std::endl;
uint64_t offset = Helper::getRandomOffset(10240, 512);
std::cout << "Random offset: " << offset << std::endl;
/**
Android - don't throw Helper::Error
WARNING: IF AN ANDROID-SPECIFIED SYSROOT IS NOT USED, IT WILL NOT BE
ADDED AUTOMATICALLY BECAUSE THERE IS NO __ANDROID__ DECLARATION.
std::string getProperty(std::string_view prop);
// Reads a system property.
// Returns property value as string.
// Returns empty string if property not found.
bool reboot(std::string_view arg);
// Reboots the system with given argument.
// Example args: "reboot", "recovery", "bootloader".
// Returns true on success.
*/
std::string model = Helper::getProperty("ro.product.model");
if (model.empty())
std::cout << "Cannot read property ro.product.model" << std::endl;
else
std::cout << "Device model: " << model << std::endl;
if (Helper::reboot("recovery"))
std::cout << "Rebooting into recovery..." << std::endl;
else
std::cerr << "Failed to reboot" << std::endl;
/**
Library-specific - don't throw Helper::Error
std::string getLibVersion();
// Returns library version string.
*/
std::cout << "Helper library version: " << Helper::getLibVersion() << std::endl;
```
---
#### Compling and linking
If you're using CMake in your project, you can include it directly with `add_subdirectory`. Both the static and shared libraries will be compiled.
```cmake
# Include libhelper library
add_subdirectory(srclib/libhelper)
# Usage example
add_executable(main main.cpp)
target_link_libraries(main PRIVATE helper_shared) # use helper_static if you want link static library
```
If you're going to use it prebuilt, you can find it in the PMT releases. Or you can compile it separately... WARNING: PMT releases only include builds for ARM.
---
## libpartition_map library
soon!