# MZ_OS <!-- omit in toc -->

These functions provide support for handling common file system operations.

- [Path](#path)
  - [mz\_path\_combine](#mz_path_combine)
  - [mz\_path\_append\_slash](#mz_path_append_slash)
  - [mz\_path\_remove\_slash](#mz_path_remove_slash)
  - [mz\_path\_has\_slash](#mz_path_has_slash)
  - [mz\_path\_convert\_slashes](#mz_path_convert_slashes)
  - [mz\_path\_compare\_wc](#mz_path_compare_wc)
  - [mz\_path\_resolve](#mz_path_resolve)
  - [mz\_path\_remove\_filename](#mz_path_remove_filename)
  - [mz\_path\_remove\_extension](#mz_path_remove_extension)
  - [mz\_path\_get\_filename](#mz_path_get_filename)
- [Directory](#directory)
  - [mz\_dir\_has\_unsafe\_symlink](#mz_dir_has_unsafe_symlink)
  - [mz\_dir\_make](#mz_dir_make)
- [File](#file)
  - [mz\_file\_get\_crc](#mz_file_get_crc)
- [Operating System](#operating-system)
  - [mz\_os\_unicode\_string\_create](#mz_os_unicode_string_create)
  - [mz\_os\_unicode\_string\_delete](#mz_os_unicode_string_delete)
  - [mz\_os\_utf8\_string\_create](#mz_os_utf8_string_create)
  - [mz\_os\_utf8\_string\_delete](#mz_os_utf8_string_delete)
  - [mz\_os\_rand](#mz_os_rand)
  - [mz\_os\_rename](#mz_os_rename)
  - [mz\_os\_unlink](#mz_os_unlink)
  - [mz\_os\_file\_exists](#mz_os_file_exists)
  - [mz\_os\_get\_file\_size](#mz_os_get_file_size)
  - [mz\_os\_get\_file\_date](#mz_os_get_file_date)
  - [mz\_os\_set\_file\_date](#mz_os_set_file_date)
  - [mz\_os\_get\_file\_attribs](#mz_os_get_file_attribs)
  - [mz\_os\_set\_file\_attribs](#mz_os_set_file_attribs)
  - [mz\_os\_get\_temp\_path](#mz_os_get_temp_path)
  - [mz\_os\_make\_dir](#mz_os_make_dir)
  - [mz\_os\_open\_dir](#mz_os_open_dir)
  - [mz\_os\_read\_dir](#mz_os_read_dir)
  - [mz\_os\_close\_dir](#mz_os_close_dir)
  - [mz\_os\_is\_dir](#mz_os_is_dir)
  - [mz\_os\_is\_symlink](#mz_os_is_symlink)
  - [mz\_os\_make\_symlink](#mz_os_make_symlink)
  - [mz\_os\_read\_symlink](#mz_os_read_symlink)
  - [mz\_os\_ms\_time](#mz_os_ms_time)

## Path

The _mz_path_ family of functions are helper functions used when constructing file system paths.

### mz_path_combine

Combines two paths.

**Arguments**
|Type|Name|Description|
|-|-|-|
|char *|path|Base path|
|const char *|join|Path to append|
|int32_t|max_path|Maximum path buffer size|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if successful|

**Example**
```
char path[120];
strncat(path, "c:\\windows\\", sizeof(path));
mz_path_combine(path, "temp", sizeof(path));
printf("Combined path: %s\n", path);
```

### mz_path_append_slash

Appends a path slash on to the end of the path. To get the path slash character for the compiler platform use _MZ_PATH_SLASH_PLATFORM_ preprocessor define.

**Arguments**
|Type|Name|Description|
|-|-|-|
|char *|path|Path|
|int32_t|max_path|Maximum bytes to store path|
|char|slash|Path slash character|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if successful|

**Example**
```
char path[120];
strncat(path, "c:\\windows", sizeof(path));
mz_path_append_slash(path, sizeof(path), MZ_PATH_SLASH_PLATFORM);
printf("Path with end slash: %s\n", path);
```

### mz_path_remove_slash

Removes a path slash from the end of the path.

**Arguments**
|Type|Name|Description|
|-|-|-|
|char *|path|Path|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if successful|

**Example**
```
char path[120];
strncat(path, "c:\\windows\\", sizeof(path));
mz_path_remove_slash(path);
printf("Path with no slash: %s\n", path);
```

### mz_path_has_slash

Returns whether or not the path ends with slash.

**Arguments**
|Type|Name|Description|
|-|-|-|
|const char *|path|Path|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if has slash.|

**Example**
```
const char *path = "c:\\windows\\";
if (mz_path_has_slash(path) == MZ_OK)
    printf("Path ends with a slash\n");
else
    printf("Path does not end with a slash\n");
```

### mz_path_convert_slashes

Converts the slashes in a path. This can be used to convert all unix path slashes to windows path slashes or conver all windows path slashes to unix path slashes. If there are mixed slashes in the path, it can unify them to all one format.

**Arguments**
|Type|Name|Description|
|-|-|-|
|char *|path|Path|
|char|slash|Path slash character|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if has slash.|

**Example**
```
char path[120];
strncat(path, "c:\\windows\\", sizeof(path));
if (mz_path_convert_slashes(path, MZ_PATH_SLASH_UNIX) == MZ_OK)
    printf("Path converted to unix slashes: %s\n", path);
```

### mz_path_compare_wc

Compares two paths with a wildcard.

**Arguments**
|Type|Name|Description|
|-|-|-|
|const char *|path|Path|
|const char *|wildcard|Wildcard pattern|
|uint8_t|ignore_case|Ignore case during comparison if 1.|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if matched, MZ_EXIST_ERROR if not matched.|

**Example**
```
const char *path = "test.txt";
if (mz_path_compare_wc(path, "*.txt", 0) == MZ_OK)
    printf("%s is a text file\n", path);
else
    printf("%s is not a text file\n", path);
```

### mz_path_resolve

Resolves a path. Path parts that only contain dots will be resolved. If a path part contains a single dot, it will be remoed. If a path part contains two dots, it will remove the last path part. This function can be used to prevent the  _zipslip_ vulnerability and ensure that files are not written outside of their intended target.

**Arguments**
|Type|Name|Description|
|-|-|-|
|const char *|path|Path|
|char *|target|Resolved path character array|
|int32_t|max_target|Maximum size of resolved path array|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if successful|

**Example**
```
const char *path = "c:\\windows\\..\\";
char target[120];
mz_path_resolve(path, target, sizeof(target));
printf("Resolved path: %s\n", target);
```

### mz_path_remove_filename

Removes the filename from a path.

**Arguments**
|Type|Name|Description|
|-|-|-|
|char *|path|Path|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if successful|

**Example**
```
char path[120];
strncpy(path, "c:\\windows\\test.txt", sizeof(path));
printf("Path: %s\n", path);
mz_path_remove_filename(path);
printf("Path with no filename: %s\n", path);
```

### mz_path_remove_extension

Remove the extension from a path.

**Arguments**
|Type|Name|Description|
|-|-|-|
|char *|path|Path|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if successful|

**Example**
```
char path[120];
strncpy(path, "c:\\windows\\test.txt", sizeof(path));
printf("Path: %s\n", path);
mz_path_remove_extension(path);
printf("Path with no file extension: %s\n", path);
```

### mz_path_get_filename

Get the filename from a path.

**Arguments**
|Type|Name|Description|
|-|-|-|
|const char *|path|Path|
|const char **|filename|Pointer to filename string|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if successful|

**Example**
```
const char *path = "c:\\windows\\test.txt";
const char *filename = NULL;
printf("Path: %s\n", path);
if (mz_path_get_filename(path, &filename) == MZ_OK)
    printf("Filename: %s\n", filename);
else
    printf("Path has no filename\n");
```

## Directory

### mz_dir_has_unsafe_symlink

Checks if any existing component of a path is a symbolic link that escapes a base path. This function is used to prevent symlink-based path traversal attacks during archive extraction.

**Arguments**
|Type|Name|Description|
|-|-|-|
|const char *|path|Path to check|
|const char *|base_path|Base path that symlinks must not escape|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if path is safe, MZ_EXIST_ERROR if an unsafe symlink is found.|

**Example**
```
const char *base_path = "/tmp/extract/";
const char *file_path = "/tmp/extract/subdir/file.txt";
if (mz_dir_has_unsafe_symlink(file_path, base_path) == MZ_OK)
    printf("Path is safe to write\n");
else
    printf("Path contains unsafe symlink\n");
```

### mz_dir_make

Creates a directory recursively.

**Arguments**
|Type|Name|Description|
|-|-|-|
|const char *|path|Path|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if successful|

**Example**
```
const char *path = "c:\\temp\\x\\y\\z\\";
if (mz_dir_make(path) == MZ_OK)
    printf("Dir was created: %s\n", path);
else
    printf("Dir was not created: %s\n", path);
```

## File

### mz_file_get_crc

Gets the crc32 hash of a file. This function helps provide functional backwards compatibility.

**Arguments**
|Type|Name|Description|
|-|-|-|
|const char *|path|Path to calculate CRC value for|
|uint32_t *|result_crc|Pointer to store the calculated CRC value|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if successful|

**Example**
```
const char *path = "c:\\temp\\test.txt";
uint32_t crc = 0;
if (mz_file_get_crc(path, &crc) == MZ_OK)
    printf("File %s CRC: %08x\n", path, crc);
else
    printf("Failed to calculate CRC: %s\n", path);
```

## Operating System

The _mz_os_ family of functions wrap all platform specific code necessary to zip and unzip files.

### mz_os_unicode_string_create

Create unicode string from a string with another encoding.

**Arguments**
|Type|Name|Description|
|-|-|-|
|const char *|string|String to convert|
|int32_t|encoding|String encoding (See [MZ_ENCODING](mz_encoding.md))|

**Return**
|Type|Description|
|-|-|
|wchar_t *|Returns pointer to unicode string if successful, otherwise NULL.|

**Example**
```
char *test = "test";
wchar_t *test_unicode = mz_os_unicode_string_create(test, MZ_ENCODING_UTF8);
if (test_unicode) {
    printf("Unicode test string created\n");
    mz_os_unicode_string_delete(&test_unicode);
}
```

### mz_os_unicode_string_delete

Delete a unicode string that was created with _mz_os_unicode_string_create_.

**Arguments**
|Type|Name|Description|
|-|-|-|
|wchar_t **|string|Pointer to unicode string|

**Example**
```
char *test = "test";
wchar_t *test_unicode = mz_os_unicode_string_create(test, MZ_ENCODING_UTF8);
if (test_unicode) {
    printf("Unicode test string created\n");
    mz_os_unicode_string_delete(&test_unicode);
}
```

### mz_os_utf8_string_create

Create a utf8 string from a string with another encoding.

**Arguments**
|Type|Name|Description|
|-|-|-|
|const char *|string|String to convert|
|int32_t|encoding|String encoding (See [MZ_ENCODING](mz_encoding.md))|

**Return**
|Type|Description|
|-|-|
|char *|Returns pointer to UTF-8 encoded string if successful, otherwise NULL.|

**Example**
```
char *test = "test";
char *test_utf8 = mz_os_utf8_string_create(test, MZ_ENCODING_CODEPAGE_437);
if (test_utf8) {
    printf("UTF-8 test string created\n");
    mz_os_utf8_string_delete(&test_utf8);
}
```

### mz_os_utf8_string_delete

Delete a utf8 string that was created with _mz_os_utf8_string_create_.

**Arguments**
|Type|Name|Description|
|-|-|-|
|char **|string|Pointer to utf8 encoded string|

**Example**
```
char *test = "test";
char *test_utf8 = mz_os_utf8_string_create(test, MZ_ENCODING_CODEPAGE_437);
if (test_utf8) {
    printf("UTF-8 test string created\n");
    mz_os_utf8_string_delete(&test_utf8);
}
```

### mz_os_rand

Random number generator (not cryptographically secure). For a cryptographically secure random number generator use _mz_crypt_rand_.

**Arguments**
|Type|Name|Description|
|-|-|-|
|uint8_t *|buf|Buffer to fill with random data|
|int32_t|size|Maximum size of buffer array|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if successful|

**Example**
```
uint8_t buf[120];
if (mz_os_rand(buf, sizeof(buf)) == MZ_OK)
    printf("%d bytes of random data generated\n", sizeof(buf));
```

### mz_os_rename

Rename a file.

**Arguments**
|Type|Name|Description|
|-|-|-|
|const char *|source_path|Original path|
|const char *|target_path|New path|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if successful|

**Example**
```
if (mz_os_rename("c:\\test1.txt", "c:\\test2.txt") == MZ_OK)
    printf("File was renamed successfully\n");
```

### mz_os_unlink

Delete an existing file.

**Arguments**
|Type|Name|Description|
|-|-|-|
|const char *|path|File path|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if successful|

**Example**
```
if (mz_os_unlink("c:\\test2.txt") == MZ_OK)
    printf("File was deleted successfully\n");
```

### mz_os_file_exists

Check to see if a file exists.

**Arguments**
|Type|Name|Description|
|-|-|-|
|const char *|path|File path|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if successful|

**Example**
```
const char *path = "c:\\test2.txt";
if (mz_os_file_exists(path) == MZ_OK)
    printf("File %s exists\n", path);
else
    printf("File %s does not exist\n", path);
```

### mz_os_get_file_size

Gets the length of a file.

**Arguments**
|Type|Name|Description|
|-|-|-|
|const char *|path|File path|

**Return**
|Type|Description|
|-|-|
|int64_t|Size of file, does not check for existence of file.|

**Example**
```
const char *path = "c:\\test3.txt";
if (mz_os_file_exists(path) == MZ_OK) {
    int64_t file_size = mz_os_get_file_size(path);
    printf("File %s size %lld\n", path, file_size);
} else {
    printf("File %s does not exist\n", path);
}
```

### mz_os_get_file_date

Gets a file's modified, access, and creation dates if supported. Creation date is not supported on Linux based systems and zero is returned for _creation_date_ on those systems.

**Arguments**
|Type|Name|Description|
|-|-|-|
|const char *|path|File path|
|time_t *|modified_date|Pointer to store file's modified unix timestamp|
|time_t *|accessed_date|Pointer to store file's accessed unix timestamp|
|time_t *|creation_date|Pointer to store file's creation unix timestamp|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if successful|

**Example**
```
const char *path = "c:\\test4.txt";
time_t modified_date, accessed_date, creation_date;
if (mz_os_get_file_date(path, &modified_date, &accessed_date, &creation_date) == MZ_OK)
    printf("File %s modified %lld accessed %lld creation %lld\n", path, modified_date, accessed_date, creation_date);
```

### mz_os_set_file_date

Sets a file's modified, access, and creation dates if supported. Creation date is not supported on Linux based systems.

**Arguments**
|Type|Name|Description|
|-|-|-|
|const char *|path|File path|
|time_t|modified_date|File's modified unix timestamp|
|time_t|accessed_date|File's accessed unix timestamp|
|time_t|creation_date|File's creation unix timestamp|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if successful|

**Example**
```
const char *src_path = "c:\\test4.txt";
const char *target_path = "c:\\test5.txt";
time_t modified_date, accessed_date, creation_date;
if (mz_os_get_file_date(src_path, &modified_date, &accessed_date, &creation_date) == MZ_OK) {
    printf("Source file %s modified %lld accessed %lld creation %lld\n", path, modified_date, accessed_date, creation_date);
    if (mz_os_set_file_date(target_path, modified_date, accessed_date, creation_date) == MZ_OK) {
        printf("Target file dates changed successfully\n");
    }
}
```

### mz_os_get_file_attribs

Gets a file's attributes.

**Arguments**
|Type|Name|Description|
|-|-|-|
|const char *|path|File path|
|uint32_t *|attributes|Pointer to store file attributes value|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if successful|

**Example**
```
const char *path = "c:\\test6.txt";
uint32_t attributes = 0;
if (mz_os_get_file_attribs(path, &attributes) == MZ_OK)
    printf("File %s attributes %08x\n", attributes);
```

### mz_os_set_file_attribs

Sets a file's attributes.

**Arguments**
|Type|Name|Description|
|-|-|-|
|const char *|path|File path|
|uint32_t|attributes|File attributes value|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if successful|

**Example**
```
const char *path = "c:\\test6.txt";
uint32_t attributes = 0;
if (mz_os_get_file_attribs(path, &attributes) == MZ_OK) {
    printf("File %s attributes %08x\n", attributes);
    attributes |= FILE_ATTRIBUTE_READONLY;
    if (mz_os_set_file_attribs(path, attributes) == MZ_OK) {
        printf("File changed to readonly\n");
    }
}
```

### mz_os_get_temp_path

Gets a unique temporary file path.

**Arguments**
|Type|Name|Description|
|-|-|-|
|char *|path|Buffer to store the temporary path|
|int32_t|max_path|Maximum size of the path buffer|
|const char *|prefix|Optional prefix for the temporary filename (can be NULL)|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if successful|

**Example**
```
char tmp_path[256];
if (mz_os_get_temp_path(tmp_path, sizeof(tmp_path), "mz_") == MZ_OK)
    printf("Temporary path: %s\n", tmp_path);
```

### mz_os_make_dir

Creates a directory. To recursively create a directory use _mz_dir_make_.

**Arguments**
|Type|Name|Description|
|-|-|-|
|const char *|path|Directory path|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if successful|

**Example**
```
if (mz_os_make_dir("c:\\testdir\\") == MZ_OK)
    printf("Test directory created\n");
```

### mz_os_open_dir

Opens a directory for listing.

**Arguments**
|Type|Name|Description|
|-|-|-|
|const char *|path|Directory path|

**Return**
|Type|Description|
|-|-|
|DIR *|Directory enumeration handle, returns NULL if error.|

**Example**
```
const char *search_dir = "c:\\test1\\";
DIR *dir = mz_open_dir(search_dir);
if (dir) {
    printf("Dir %s was opened\n", search_dir);
    mz_os_close_dir(dir);
}
```

### mz_os_read_dir

Reads a directory listing entry.

**Arguments**
|Type|Name|Description|
|-|-|-|
|DIR *|dir|Directory enumeration handle|

**Return**
|Type|Description|
|-|-|
|struct dirent *|Pointer to directory entry information structure. To get the name of the directory use the _d_name_ structure field.|

**Example**
```
const char *search_dir = "c:\\test2\\";
DIR *dir = mz_open_dir(search_dir);
if (dir) {
    struct dirent *entry = NULL;
    printf("Dir %s was opened\n", search_dir);
    while ((entry = mz_os_read_dir(dir))) {
        printf("Dir entry: %s was opened\n", entry->d_name);
    }
    mz_os_close_dir(dir);
}
```

### mz_os_close_dir

Closes a directory that has been opened for listing.

**Arguments**
|Type|Name|Description|
|-|-|-|
|DIR *|dir|Directory enumeration handle|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if successful|

**Example**
```
const char *search_dir = "c:\\test3\\";
DIR *dir = mz_open_dir(search_dir);
if (dir) {
    struct dirent *entry = NULL;
    printf("Dir %s was opened\n", search_dir);
    while ((entry = mz_os_read_dir(dir))) {
        printf("Dir entry: %s was opened\n", entry->d_name);
    }
    mz_os_close_dir(dir);
}
```

### mz_os_is_dir

Checks to see if path is a directory.

**Arguments**
|Type|Name|Description|
|-|-|-|
|const char *|path|File system path|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if it is a directory.|

**Example**
```
const char *path = "c:\\test7.txt";
if (mz_os_is_dir(path) == MZ_OK)
    printf("Path %s is a directory\n", path);
else
    printf("Path %s is not a directory\n", path);
```

### mz_os_is_symlink

Checks to see if path is a symbolic link.

**Arguments**
|Type|Name|Description|
|-|-|-|
|const char *|path|File system path|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if it is a symbolic link.|

**Example**
```
const char *path = "c:\\test7.txt";
if (mz_os_is_symlink(path) == MZ_OK)
    printf("Path %s is a symbolic link\n", path);
else
    printf("Path %s is not a symbolic link\n", path);
```

### mz_os_make_symlink

Creates a symbolic link pointing to a target.

**Arguments**
|Type|Name|Description|
|-|-|-|
|const char *|path|Link path|
|const char *|target_path|Actual path|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if successful|

**Example**
```
const char *path = "c:\\test7.txt";
const char *target_path = "c:\\test8.txt";
if (mz_os_make_symlink(path, target_path) == MZ_OK)
    printf("Symbolic link created at %s pointing to %s\n", path, target_path);
```

### mz_os_read_symlink

Gets the target path for a symbolic link.

**Arguments**
|Type|Name|Description|
|-|-|-|
|const char *|path|Link path|
|char *|target_path|Actual path|
|int32_t|max_path|Maximum bytes to store actual path|

**Return**
|Type|Description|
|-|-|
|int32_t|[MZ_ERROR](mz_error.md) code, MZ_OK if successful|

**Example**
```
const char *path = "c:\\test7.txt";
const char *target_path = "c:\\test8.txt";
if (mz_os_make_symlink(path, target_path) == MZ_OK) {
    char actual_path[120];
    printf("Symbolic link created at %s pointing to %s\n", path, target_path);
    if (mz_os_read_symlink(path, actual_path, sizeof(actual_path)) == MZ_OK) {
        if (strcmp(target_path, actual_path) == 0) {
            printf("Confirmed symbolic link created\n");
        }
    }
}
```

### mz_os_ms_time

Gets the time in milliseconds.

**Return**
|Type|Description|
|-|-|
|uint64_t|Current time in milliseconds|

**Example**
```
uint64_t current_time = mz_os_ms_time();
printf("Current time in %lldms\n", current_time);
```
