|
| 1 | +libacquire Usage Guide |
| 2 | +====================== |
| 3 | + |
| 4 | +`libacquire` provides a flexible, handle-based API for downloading, verifying, and extracting files. This guide demonstrates how to use the core features of the library. |
| 5 | + |
| 6 | +## Core Concepts |
| 7 | + |
| 8 | +### The `acquire_handle` |
| 9 | + |
| 10 | +The `struct acquire_handle` is the heart of the library. It's an opaque state container that you create once per logical operation. It holds internal state for downloads, checksums, and extractions, and most importantly, it's how you retrieve progress and detailed error information. |
| 11 | + |
| 12 | +- **Create a handle:** `struct acquire_handle *handle = acquire_handle_init();` |
| 13 | +- **Clean up a handle:** `acquire_handle_free(handle);` |
| 14 | + |
| 15 | +You should reuse the same handle for a chained operation (e.g., download -> verify -> extract). |
| 16 | + |
| 17 | +### API Pattern: Synchronous vs. Asynchronous |
| 18 | + |
| 19 | +Every major feature offers two ways to operate: |
| 20 | + |
| 21 | +1. **Synchronous (`_sync`)**: These are simple, blocking functions. They are easy to use but will freeze your application until the operation is complete. |
| 22 | + ```c |
| 23 | + int result = acquire_feature_sync(handle, ...); |
| 24 | + ``` |
| 25 | + |
| 26 | +2. **Asynchronous (`_async_...`)**: These non-blocking functions allow your application to remain responsive. The pattern is always: |
| 27 | + - `acquire_feature_async_start()`: Kicks off the operation. |
| 28 | + - `acquire_feature_async_poll()`: Called in a loop to perform a piece of work and check the status. |
| 29 | + - `acquire_feature_async_cancel()`: Can be called from another thread or signal handler to request cancellation. |
| 30 | + |
| 31 | +### Error Handling |
| 32 | + |
| 33 | +When a function returns `-1` (failure), the `acquire_handle` contains the reason. |
| 34 | + |
| 35 | +```c |
| 36 | +if (result != 0) { |
| 37 | + fprintf(stderr, "Operation failed: [%d] %s\n", |
| 38 | + acquire_handle_get_error_code(handle), |
| 39 | + acquire_handle_get_error_string(handle)); |
| 40 | +} |
| 41 | +``` |
| 42 | + |
| 43 | +--- |
| 44 | + |
| 45 | +## 0. Downloading a File |
| 46 | + |
| 47 | +### a) Synchronous Download |
| 48 | + |
| 49 | +This is the simplest way to download a file. The function blocks until the download is complete or an error occurs. |
| 50 | + |
| 51 | +```c |
| 52 | +#include <stdio.h> |
| 53 | +#include <acquire_handle.h> |
| 54 | +#include <acquire_download.h> |
| 55 | + |
| 56 | +void simple_blocking_download(void) { |
| 57 | + struct acquire_handle *handle = acquire_handle_init(); |
| 58 | + const char *url = "https://httpbin.org/get"; |
| 59 | + const char *destination = "./httpbin.json"; |
| 60 | + int result; |
| 61 | + |
| 62 | + printf("Starting synchronous download of '%s'...\n", url); |
| 63 | + |
| 64 | + result = acquire_download_sync(handle, url, destination); |
| 65 | + |
| 66 | + if (result == 0) { |
| 67 | + printf("Download successful! Saved to '%s'.\n", destination); |
| 68 | + } else { |
| 69 | + fprintf(stderr, "Download failed: %s\n", |
| 70 | + acquire_handle_get_error_string(handle)); |
| 71 | + } |
| 72 | + |
| 73 | + acquire_handle_free(handle); |
| 74 | +} |
| 75 | +``` |
| 76 | +
|
| 77 | +### b) Asynchronous Download with Progress |
| 78 | +
|
| 79 | +This example shows a non-blocking download with a progress indicator, perfect for GUI applications or services that need to remain responsive. |
| 80 | +
|
| 81 | +```c |
| 82 | +#include <stdio.h> |
| 83 | +#include <acquire_handle.h> |
| 84 | +#include <acquire_download.h> |
| 85 | +
|
| 86 | +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) |
| 87 | +#include <synchapi.h> |
| 88 | +#else |
| 89 | +#include <unistd.h> |
| 90 | +#endif |
| 91 | +
|
| 92 | +void non_blocking_download_with_progress(void) { |
| 93 | + struct acquire_handle *handle = acquire_handle_init(); |
| 94 | + const char *url = "http://ipv4.download.thinkbroadband.com/5MB.zip"; |
| 95 | + const char *destination = "./5MB.zip"; |
| 96 | + enum acquire_status status; |
| 97 | +
|
| 98 | + puts("Starting asynchronous download of a 5MB file..."); |
| 99 | + if (acquire_download_async_start(handle, url, destination) != 0) { |
| 100 | + fprintf(stderr, "Failed to start async download: %s\n", |
| 101 | + acquire_handle_get_error_string(handle)); |
| 102 | + acquire_handle_free(handle); |
| 103 | + return; |
| 104 | + } |
| 105 | +
|
| 106 | + puts("Download in progress. Polling for updates..."); |
| 107 | + do { |
| 108 | + status = acquire_download_async_poll(handle); |
| 109 | +
|
| 110 | + if (handle->total_size > 0) { |
| 111 | + double percent = 100.0 * handle->bytes_processed / handle->total_size; |
| 112 | + printf("\rProgress: %lld / %lld bytes (%.0f%%)", |
| 113 | + (long long)handle->bytes_processed, (long long)handle->total_size, percent); |
| 114 | + } else { |
| 115 | + printf("\rProgress: %lld bytes downloaded", (long long)handle->bytes_processed); |
| 116 | + } |
| 117 | + fflush(stdout); |
| 118 | + |
| 119 | +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) |
| 120 | + Sleep(100); /* 100ms */ |
| 121 | +#else |
| 122 | + usleep(100000); /* 100ms */ |
| 123 | +#endif |
| 124 | + } while (status == ACQUIRE_IN_PROGRESS); |
| 125 | +
|
| 126 | + putchar('\n'); |
| 127 | +
|
| 128 | + switch (status) { |
| 129 | + case ACQUIRE_COMPLETE: |
| 130 | + puts("Async download complete!"); |
| 131 | + break; |
| 132 | + case ACQUIRE_ERROR: |
| 133 | + fprintf(stderr, "Async download failed: [%d] %s\n", |
| 134 | + acquire_handle_get_error_code(handle), |
| 135 | + acquire_handle_get_error_string(handle)); |
| 136 | + break; |
| 137 | + default: |
| 138 | + fprintf(stderr, "Download finished with unexpected status: %d\n", status); |
| 139 | + break; |
| 140 | + } |
| 141 | + |
| 142 | + acquire_handle_free(handle); |
| 143 | +} |
| 144 | +``` |
| 145 | + |
| 146 | +--- |
| 147 | + |
| 148 | +## 1. Verifying a File Checksum |
| 149 | + |
| 150 | +Verification follows the same sync/async pattern. This is useful for ensuring the integrity of a downloaded file. |
| 151 | + |
| 152 | +### a) Synchronous Verification |
| 153 | + |
| 154 | +```c |
| 155 | +#include <stdio.h> |
| 156 | +#include <acquire_handle.h> |
| 157 | +#include <acquire_checksums.h> |
| 158 | + |
| 159 | +void verify_file_integrity(const char *filepath, const char *expected_hash) { |
| 160 | + struct acquire_handle *handle = acquire_handle_init(); |
| 161 | + int result; |
| 162 | + |
| 163 | + printf("Verifying SHA256 of '%s'...\n", filepath); |
| 164 | + |
| 165 | + result = acquire_verify_sync(handle, filepath, LIBACQUIRE_SHA256, expected_hash); |
| 166 | + |
| 167 | + if (result == 0) { |
| 168 | + puts("Verification successful: Checksum matches."); |
| 169 | + } else { |
| 170 | + fprintf(stderr, "Verification failed: %s\n", |
| 171 | + acquire_handle_get_error_string(handle)); |
| 172 | + } |
| 173 | + |
| 174 | + acquire_handle_free(handle); |
| 175 | +} |
| 176 | +``` |
| 177 | +
|
| 178 | +--- |
| 179 | +
|
| 180 | +## 2. Extracting an Archive |
| 181 | +
|
| 182 | +### a) Synchronous Extraction |
| 183 | +
|
| 184 | +```c |
| 185 | +#include <stdio.h> |
| 186 | +
|
| 187 | +#include <acquire_handle.h> |
| 188 | +#include <acquire_extract.h> |
| 189 | +
|
| 190 | +void extract_an_archive(const char *archive_path, const char *destination_dir) { |
| 191 | + struct acquire_handle *handle = acquire_handle_init(); |
| 192 | + int result; |
| 193 | +
|
| 194 | + printf("Extracting '%s' to '%s'...\n", archive_path, destination_dir); |
| 195 | +
|
| 196 | + result = acquire_extract_sync(handle, archive_path, destination_dir); |
| 197 | +
|
| 198 | + if (result == 0) { |
| 199 | + printf("Extraction successful.\n"); |
| 200 | + } else { |
| 201 | + fprintf(stderr, "Extraction failed: %s\n", |
| 202 | + acquire_handle_get_error_string(handle)); |
| 203 | + } |
| 204 | +
|
| 205 | + acquire_handle_free(handle); |
| 206 | +} |
| 207 | +``` |
| 208 | + |
| 209 | +### b) Asynchronous Extraction |
| 210 | + |
| 211 | +This is useful for showing progress as each file is extracted from a large archive. |
| 212 | + |
| 213 | +```c |
| 214 | +#include <stdio.h> |
| 215 | + |
| 216 | +#include <acquire_handle.h> |
| 217 | +#include <acquire_extract.h> |
| 218 | + |
| 219 | +/* For usleep/Sleep */ |
| 220 | +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) |
| 221 | +#include <synchapi.h> |
| 222 | +#else |
| 223 | +#include <unistd.h> |
| 224 | +#endif |
| 225 | + |
| 226 | +void non_blocking_extraction(const char *archive_path, const char *destination_dir) { |
| 227 | + struct acquire_handle *handle = acquire_handle_init(); |
| 228 | + enum acquire_status status; |
| 229 | + |
| 230 | + printf("Starting asynchronous extraction...\n"); |
| 231 | + if (acquire_extract_async_start(handle, archive_path, destination_dir) != 0) { |
| 232 | + fprintf(stderr, "Failed to start async extraction: %s\n", |
| 233 | + acquire_handle_get_error_string(handle)); |
| 234 | + acquire_handle_free(handle); |
| 235 | + return; |
| 236 | + } |
| 237 | + |
| 238 | + do { |
| 239 | + status = acquire_extract_async_poll(handle); |
| 240 | + if (status == ACQUIRE_IN_PROGRESS) { |
| 241 | + printf("Extracting: %s\n", handle->current_file); |
| 242 | + } |
| 243 | + } while (status == ACQUIRE_IN_PROGRESS); |
| 244 | + |
| 245 | + if (status == ACQUIRE_COMPLETE) { |
| 246 | + printf("Extraction complete.\n"); |
| 247 | + } else { |
| 248 | + fprintf(stderr, "Extraction failed: [%d] %s\n", |
| 249 | + acquire_handle_get_error_code(handle), |
| 250 | + acquire_handle_get_error_string(handle)); |
| 251 | + } |
| 252 | + |
| 253 | + acquire_handle_free(handle); |
| 254 | +} |
| 255 | + |
| 256 | +``` |
| 257 | +
|
| 258 | +--- |
| 259 | +
|
| 260 | +## 3. Putting It All Together: A Complete Workflow |
| 261 | +
|
| 262 | +This example shows the power of the unified handle API by performing a complete download, verify, and extract sequence for a Node.js headers package. |
| 263 | +
|
| 264 | +```c |
| 265 | +#include <stdio.h> |
| 266 | +#include <stdlib.h> |
| 267 | +
|
| 268 | +#include <acquire_handle.h> |
| 269 | +#include <acquire_download.h> |
| 270 | +#include <acquire_checksums.h> |
| 271 | +#include <acquire_extract.h> |
| 272 | +
|
| 273 | +int download_verify_and_extract(void) { |
| 274 | + /* Define our target and its properties */ |
| 275 | + const char *const url = "https://nodejs.org/dist/v18.17.1/node-v18.17.1-headers.tar.gz"; |
| 276 | + const char *const expected_sha256 = "b6a7a13d2f9758783a3c942d5940e4f26484a95697d8b5847b3b3a7f64243a75"; |
| 277 | + const char *const download_path = "./node-headers.tar.gz"; |
| 278 | + const char *const extract_dir = "./node-headers-extracted"; |
| 279 | +
|
| 280 | + int result; |
| 281 | + |
| 282 | + /* 1. Create a single handle for the entire operation. */ |
| 283 | + struct acquire_handle *handle = acquire_handle_init(); |
| 284 | + if (!handle) { |
| 285 | + fprintf(stderr, "Failed to initialize acquire handle.\n"); |
| 286 | + return 1; |
| 287 | + } |
| 288 | +
|
| 289 | + /* 2. Download the file synchronously. */ |
| 290 | + printf("--- Step 1: Downloading '%s' ---\n", url); |
| 291 | + result = acquire_download_sync(handle, url, download_path); |
| 292 | + if (result != 0) { |
| 293 | + fprintf(stderr, "Download failed: %s\n", acquire_handle_get_error_string(handle)); |
| 294 | + acquire_handle_free(handle); |
| 295 | + return 1; |
| 296 | + } |
| 297 | + printf("Download complete.\n\n"); |
| 298 | +
|
| 299 | + /* 3. Verify the checksum (reusing the same handle). */ |
| 300 | + printf("--- Step 2: Verifying checksum ---\n"); |
| 301 | + result = acquire_verify_sync(handle, download_path, LIBACQUIRE_SHA256, expected_sha256); |
| 302 | + if (result != 0) { |
| 303 | + fprintf(stderr, "Verification failed: %s\n", acquire_handle_get_error_string(handle)); |
| 304 | + acquire_handle_free(handle); |
| 305 | + return 1; |
| 306 | + } |
| 307 | + printf("Verification successful.\n\n"); |
| 308 | + |
| 309 | + /* 4. Extract the archive (reusing the same handle). */ |
| 310 | + printf("--- Step 3: Extracting archive ---\n"); |
| 311 | + result = acquire_extract_sync(handle, download_path, extract_dir); |
| 312 | + if (result != 0) { |
| 313 | + fprintf(stderr, "Extraction failed: %s\n", acquire_handle_get_error_string(handle)); |
| 314 | + acquire_handle_free(handle); |
| 315 | + return 1; |
| 316 | + } |
| 317 | + printf("Extraction complete.\n\n"); |
| 318 | +
|
| 319 | + /* 5. Clean up. */ |
| 320 | + acquire_handle_free(handle); |
| 321 | + printf("--- Workflow successful! ---\n"); |
| 322 | + return 0; |
| 323 | +} |
| 324 | +
|
| 325 | +int main(void) { |
| 326 | + if (download_verify_and_extract() != 0) { |
| 327 | + fputs("The operation failed.", stderr); |
| 328 | + return EXIT_FAILURE; |
| 329 | + } |
| 330 | + return EXIT_SUCCESS; |
| 331 | +} |
| 332 | +``` |
0 commit comments