ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
public_api.c
Go to the documentation of this file.
1
18#include <string.h>
19
21 if (!builder) {
22 return SET_ERRNO(ERROR_INVALID_PARAM, "Builder is NULL");
23 }
24
26
27 for (size_t i = 0; i < g_registry_size; i++) {
28 const registry_entry_t *entry = &g_options_registry[i];
29 if (!entry->long_name) {
30 continue;
31 }
32 // Silent - no debug logging needed
33
34 switch (entry->type) {
36 options_builder_add_string(builder, entry->long_name, entry->short_name, entry->offset,
37 entry->default_value ? (const char *)entry->default_value : "", entry->help_text,
38 entry->group, entry->required, entry->env_var_name, entry->validate_fn);
39 break;
40 case OPTION_TYPE_INT:
41 // Use metadata-aware function if metadata is present
42 if (entry->metadata.numeric_range.max != 0 ||
43 (entry->metadata.enum_values && entry->metadata.enum_values[0] != NULL)) {
45 entry->default_value ? *(const int *)entry->default_value : 0,
46 entry->help_text, entry->group, entry->required, entry->env_var_name,
47 entry->validate_fn, &entry->metadata);
48 } else {
49 options_builder_add_int(builder, entry->long_name, entry->short_name, entry->offset,
50 entry->default_value ? *(const int *)entry->default_value : 0, entry->help_text,
51 entry->group, entry->required, entry->env_var_name, entry->validate_fn);
52 }
53 break;
55 options_builder_add_bool(builder, entry->long_name, entry->short_name, entry->offset,
56 entry->default_value ? *(const bool *)entry->default_value : false, entry->help_text,
57 entry->group, entry->required, entry->env_var_name);
58 break;
60 // Use metadata-aware function if metadata is present (has numeric range)
61 if (entry->metadata.numeric_range.max != 0) {
63 entry->default_value ? *(const double *)entry->default_value : 0.0,
64 entry->help_text, entry->group, entry->required, entry->env_var_name,
65 entry->validate_fn, &entry->metadata, entry->optional_arg);
66 } else {
67 options_builder_add_double(builder, entry->long_name, entry->short_name, entry->offset,
68 entry->default_value ? *(const double *)entry->default_value : 0.0, entry->help_text,
69 entry->group, entry->required, entry->env_var_name, entry->validate_fn,
70 entry->optional_arg);
71 }
72 break;
74 // Always use metadata-aware function to preserve enum/metadata information
76 entry->default_value, entry->default_value_size, entry->parse_fn,
77 entry->help_text, entry->group, entry->required, entry->env_var_name,
78 entry->optional_arg, &entry->metadata);
79 break;
81 // Actions are now registered as options with help text
82 // Look up the corresponding action function based on option name
83 if (strcmp(entry->long_name, "list-webcams") == 0) {
85 entry->group);
86 } else if (strcmp(entry->long_name, "list-microphones") == 0) {
88 entry->help_text, entry->group);
89 } else if (strcmp(entry->long_name, "list-speakers") == 0) {
91 entry->group);
92 } else if (strcmp(entry->long_name, "show-capabilities") == 0) {
94 entry->help_text, entry->group);
95 } else if (strcmp(entry->long_name, "check-update") == 0) {
97 entry->group);
98 } else if (strcmp(entry->long_name, "help") == 0 || strcmp(entry->long_name, "version") == 0) {
99 // Help and version are handled specially in options.c, just add them for help display
100 // They don't have actual action functions - pass a dummy one
101 options_builder_add_action(builder, entry->long_name, entry->short_name, NULL, entry->help_text, entry->group);
102 }
103 break;
104 }
105
106 // Set mode bitmask on the last added descriptor
108
109 // Set custom arg_placeholder if defined
110 if (entry->arg_placeholder) {
112 }
113 }
114
115 return ASCIICHAT_OK;
116}
117
130
138 return g_registry_size;
139}
140
142 if (!long_name) {
143 SET_ERRNO(ERROR_INVALID_PARAM, "Long name is NULL");
144 return NULL;
145 }
146
148
149 const registry_entry_t *entry = registry_find_entry_by_name(long_name);
150 if (!entry) {
151 // Don't log error for binary-level options like "config" that aren't in registry
152 if (strcmp(long_name, "config") != 0) {
153 SET_ERRNO(ERROR_NOT_FOUND, "Option not found: %s", long_name);
154 }
155 return NULL;
156 }
157
158 /* Create descriptor from registry entry */
159 static option_descriptor_t desc;
160 desc.long_name = entry->long_name;
161 desc.short_name = entry->short_name;
162 desc.type = entry->type;
163 desc.offset = entry->offset;
164 desc.help_text = entry->help_text;
165 desc.group = entry->group;
166 desc.hide_from_mode_help = false;
167 desc.hide_from_binary_help = false;
168 desc.default_value = entry->default_value;
169 desc.required = entry->required;
170 desc.env_var_name = entry->env_var_name;
171 desc.validate = entry->validate_fn;
172 desc.parse_fn = entry->parse_fn;
173 desc.action_fn = NULL;
174 desc.owns_memory = entry->owns_memory;
175 desc.optional_arg = entry->optional_arg;
176 desc.mode_bitmask = entry->mode_bitmask;
177
178 return &desc;
179}
180
182 if (short_name == '\0') {
183 SET_ERRNO(ERROR_INVALID_PARAM, "Short name is empty");
184 return NULL;
185 }
186
188
189 const registry_entry_t *entry = registry_find_entry_by_short(short_name);
190 if (!entry) {
191 SET_ERRNO(ERROR_NOT_FOUND, "Option not found: -%c", short_name);
192 return NULL;
193 }
194
195 /* Create descriptor from registry entry */
196 static option_descriptor_t desc;
197 desc.long_name = entry->long_name;
198 desc.short_name = entry->short_name;
199 desc.type = entry->type;
200 desc.offset = entry->offset;
201 desc.help_text = entry->help_text;
202 desc.group = entry->group;
203 desc.hide_from_mode_help = false;
204 desc.hide_from_binary_help = false;
205 desc.default_value = entry->default_value;
206 desc.required = entry->required;
207 desc.env_var_name = entry->env_var_name;
208 desc.validate = entry->validate_fn;
209 desc.parse_fn = entry->parse_fn;
210 desc.action_fn = NULL;
211 desc.owns_memory = entry->owns_memory;
212 desc.optional_arg = entry->optional_arg;
213 desc.mode_bitmask = entry->mode_bitmask;
214
215 return &desc;
216}
217
219 if (!num_options) {
220 SET_ERRNO(ERROR_INVALID_PARAM, "Number of options is NULL");
221 return NULL;
222 }
223
225
226 /* Convert mode to bitmask */
227 option_mode_bitmask_t mode_bitmask = 0;
228 switch (mode) {
229 case MODE_SERVER:
230 mode_bitmask = OPTION_MODE_SERVER;
231 break;
232 case MODE_CLIENT:
233 mode_bitmask = OPTION_MODE_CLIENT;
234 break;
235 case MODE_MIRROR:
236 mode_bitmask = OPTION_MODE_MIRROR;
237 break;
239 mode_bitmask = OPTION_MODE_DISCOVERY_SVC;
240 break;
241 case MODE_DISCOVERY:
242 mode_bitmask = OPTION_MODE_DISCOVERY;
243 break;
244 default:
245 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid mode: %d", mode);
246 *num_options = 0;
247 return NULL;
248 }
249
250 /* Count matching options */
251 size_t count = 0;
252 for (size_t i = 0; i < g_registry_size; i++) {
253 if (g_options_registry[i].mode_bitmask & mode_bitmask) {
254 count++;
255 }
256 }
257
258 if (count == 0) {
259 *num_options = 0;
260 return NULL;
261 }
262
263 /* Allocate array for matching options */
265 if (!filtered) {
266 SET_ERRNO(ERROR_INVALID_STATE, "Failed to allocate filtered options array");
267 *num_options = 0;
268 return NULL;
269 }
270
271 /* Copy matching options */
272 size_t idx = 0;
273 for (size_t i = 0; i < g_registry_size; i++) {
274 if (g_options_registry[i].mode_bitmask & mode_bitmask) {
276 }
277 }
278
279 *num_options = count;
280 return filtered;
281}
282
284 if (!num_options) {
285 SET_ERRNO(ERROR_INVALID_PARAM, "Number of options is NULL");
286 return NULL;
287 }
288
290
291 /* Count binary-level options */
292 size_t count = 0;
293 for (size_t i = 0; i < g_registry_size; i++) {
294 if (g_options_registry[i].mode_bitmask & OPTION_MODE_BINARY) {
295 count++;
296 }
297 }
298
299 if (count == 0) {
300 *num_options = 0;
301 return NULL;
302 }
303
304 /* Allocate array for binary options */
306 if (!binary_opts) {
307 SET_ERRNO(ERROR_INVALID_STATE, "Failed to allocate binary options array");
308 *num_options = 0;
309 return NULL;
310 }
311
312 /* Copy binary options */
313 size_t idx = 0;
314 for (size_t i = 0; i < g_registry_size; i++) {
315 if (g_options_registry[i].mode_bitmask & OPTION_MODE_BINARY) {
316 binary_opts[idx++] = registry_entry_to_descriptor(&g_options_registry[i]);
317 }
318 }
319
320 *num_options = count;
321 return binary_opts;
322}
323
325 size_t *num_options) {
326 if (!num_options) {
327 SET_ERRNO(ERROR_INVALID_PARAM, "num_options is NULL");
328 return NULL;
329 }
330
332
333 // Count matching options
334 size_t count = 0;
335 for (size_t i = 0; i < g_registry_size; i++) {
336 if (registry_entry_applies_to_mode(&g_options_registry[i], mode, for_binary_help)) {
337 count++;
338 }
339 }
340
341 if (count == 0) {
342 *num_options = 0;
343 return NULL;
344 }
345
346 // Allocate array
348 if (!descriptors) {
349 SET_ERRNO(ERROR_MEMORY, "Failed to allocate descriptors array");
350 *num_options = 0;
351 return NULL;
352 }
353
354 // Copy matching options
355 size_t idx = 0;
356 for (size_t i = 0; i < g_registry_size; i++) {
357 if (registry_entry_applies_to_mode(&g_options_registry[i], mode, for_binary_help)) {
358 descriptors[idx++] = registry_entry_to_descriptor(&g_options_registry[i]);
359 }
360 }
361
362 *num_options = count;
363 return descriptors;
364}
365
366// ============================================================================
367// Completion Metadata Access (Phase 3 Implementation)
368// ============================================================================
369
371 if (!long_name) {
372 SET_ERRNO(ERROR_INVALID_PARAM, "Long name is NULL");
373 return NULL;
374 }
375
376 // Look up option in registry and return its metadata
378 for (size_t i = 0; i < g_registry_size; i++) {
379 const registry_entry_t *entry = &g_options_registry[i];
380 if (entry->long_name && strcmp(entry->long_name, long_name) == 0) {
381 // Return the metadata from the registry entry
382 return &entry->metadata;
383 }
384 }
385
386 // If not found, return empty metadata
387 static option_metadata_t empty_metadata = {0};
388 return &empty_metadata;
389}
390
391const char **options_registry_get_enum_values(const char *option_name, const char ***descriptions, size_t *count) {
392 if (!option_name || !count) {
393 SET_ERRNO(ERROR_INVALID_PARAM, "Option name is NULL or count is NULL");
394 if (count)
395 *count = 0;
396 return NULL;
397 }
398
399 const option_metadata_t *meta = options_registry_get_metadata(option_name);
400 if (!meta || meta->input_type != OPTION_INPUT_ENUM || !meta->enum_values || meta->enum_values[0] == NULL) {
401 SET_ERRNO(ERROR_NOT_FOUND, "Option '%s' not found", option_name);
402 *count = 0;
403 if (descriptions)
404 *descriptions = NULL;
405 return NULL;
406 }
407
408 // Count enum values (NULL-terminated array)
409 size_t enum_count = 0;
410 while (meta->enum_values[enum_count] != NULL) {
411 enum_count++;
412 }
413
414 *count = enum_count;
415 if (descriptions) {
416 *descriptions = meta->enum_descriptions;
417 }
418 return meta->enum_values;
419}
420
421bool options_registry_get_numeric_range(const char *option_name, int *min_out, int *max_out, int *step_out) {
422 if (!option_name || !min_out || !max_out || !step_out) {
423 SET_ERRNO(ERROR_INVALID_PARAM, "Option name is NULL or min_out, max_out, or step_out is NULL");
424 return false;
425 }
426
427 const option_metadata_t *meta = options_registry_get_metadata(option_name);
428 if (!meta || meta->input_type != OPTION_INPUT_NUMERIC) {
429 *min_out = 0;
430 *max_out = 0;
431 *step_out = 0;
432 return false;
433 }
434
435 *min_out = meta->numeric_range.min;
436 *max_out = meta->numeric_range.max;
437 *step_out = meta->numeric_range.step;
438 return true;
439}
440
441const char **options_registry_get_examples(const char *option_name, size_t *count) {
442 if (!option_name || !count) {
443 SET_ERRNO(ERROR_INVALID_PARAM, "Option name is NULL or count is NULL");
444 if (count)
445 *count = 0;
446 return NULL;
447 }
448
449 const option_metadata_t *meta = options_registry_get_metadata(option_name);
450 if (!meta || !meta->examples || meta->examples[0] == NULL) {
451 *count = 0;
452 return NULL;
453 }
454
455 // Count examples by finding NULL terminator
456 size_t example_count = 0;
457 for (size_t i = 0; meta->examples[i] != NULL; i++) {
458 example_count++;
459 }
460
461 *count = example_count;
462 return meta->examples;
463}
464
466 if (!option_name) {
467 SET_ERRNO(ERROR_INVALID_PARAM, "Option name is NULL");
468 return OPTION_INPUT_NONE;
469 }
470
471 const option_metadata_t *meta = options_registry_get_metadata(option_name);
472 if (!meta) {
473 SET_ERRNO(ERROR_NOT_FOUND, "Option '%s' not found", option_name);
474 return OPTION_INPUT_NONE;
475 }
476
477 return meta->input_type;
478}
Action option callbacks for ascii-chat.
void options_builder_set_arg_placeholder(options_builder_t *builder, const char *arg_placeholder)
Set custom argument placeholder on the last added option descriptor.
Definition builder.c:992
void options_builder_add_action(options_builder_t *builder, const char *long_name, char short_name, void(*action_fn)(void), const char *help_text, const char *group)
Add action option (executes action and may exit)
Definition builder.c:943
void options_builder_set_mode_bitmask(options_builder_t *builder, option_mode_bitmask_t mode_bitmask)
Set mode bitmask on the last added option descriptor.
Definition builder.c:984
void options_builder_add_bool(options_builder_t *builder, const char *long_name, char short_name, size_t offset, bool default_value, const char *help_text, const char *group, bool required, const char *env_var_name)
Add boolean flag option.
Definition builder.c:657
void options_builder_add_double_with_metadata(options_builder_t *builder, const char *long_name, char short_name, size_t offset, double default_value, const char *help_text, const char *group, bool required, const char *env_var_name, bool(*validate)(const void *, char **), const option_metadata_t *metadata, bool optional_arg)
Definition builder.c:813
void options_builder_add_double(options_builder_t *builder, const char *long_name, char short_name, size_t offset, double default_value, const char *help_text, const char *group, bool required, const char *env_var_name, bool(*validate)(const void *, char **), bool optional_arg)
Definition builder.c:779
void options_builder_add_int_with_metadata(options_builder_t *builder, const char *long_name, char short_name, size_t offset, int default_value, const char *help_text, const char *group, bool required, const char *env_var_name, bool(*validate)(const void *options_struct, char **error_msg), const option_metadata_t *metadata)
Add integer option with metadata (for numeric ranges and examples)
Definition builder.c:710
void options_builder_add_int(options_builder_t *builder, const char *long_name, char short_name, size_t offset, int default_value, const char *help_text, const char *group, bool required, const char *env_var_name, bool(*validate)(const void *, char **))
Definition builder.c:681
void options_builder_add_callback_with_metadata(options_builder_t *builder, const char *long_name, char short_name, size_t offset, const void *default_value, size_t value_size, bool(*parse_fn)(const char *, void *, char **), const char *help_text, const char *group, bool required, const char *env_var_name, bool optional_arg, const option_metadata_t *metadata)
Definition builder.c:916
void options_builder_add_string(options_builder_t *builder, const char *long_name, char short_name, size_t offset, const char *default_value, const char *help_text, const char *group, bool required, const char *env_var_name, bool(*validate)(const void *, char **))
Definition builder.c:742
option_input_type_t
Completion input type for smart shell completions.
Definition builder.h:176
@ OPTION_INPUT_NUMERIC
Numeric value with optional min/max/step.
Definition builder.h:179
@ OPTION_INPUT_NONE
No input (boolean flag)
Definition builder.h:177
@ OPTION_INPUT_ENUM
Choose from fixed set of enum values.
Definition builder.h:178
@ OPTION_TYPE_INT
Integer value (–count 42)
Definition builder.h:163
@ OPTION_TYPE_DOUBLE
Floating point (–ratio 1.5)
Definition builder.h:165
@ OPTION_TYPE_STRING
String value (–name foo)
Definition builder.h:164
@ OPTION_TYPE_ACTION
Action that executes and may exit (–list-webcams, etc.)
Definition builder.h:167
@ OPTION_TYPE_CALLBACK
Custom parser function.
Definition builder.h:166
@ OPTION_TYPE_BOOL
Boolean flag (–flag, no value)
Definition builder.h:162
option_descriptor_t registry_entry_to_descriptor(const registry_entry_t *entry)
Convert registry entry to option descriptor.
Definition core.c:161
const registry_entry_t * registry_find_entry_by_name(const char *long_name)
Get a registry entry by long name.
Definition core.c:127
bool registry_entry_applies_to_mode(const registry_entry_t *entry, asciichat_mode_t mode, bool for_binary_help)
Check if an option applies to the given mode for display purposes.
Definition core.c:200
const registry_entry_t * registry_find_entry_by_short(char short_name)
Get a registry entry by short name.
Definition core.c:145
void registry_init_size(void)
Initialize registry size and metadata.
Definition core.c:108
Internal helper functions and data for registry implementation.
#define SAFE_MALLOC(size, cast)
Definition common.h:264
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ERROR_INVALID_STATE
@ ERROR_NOT_FOUND
@ ERROR_MEMORY
Definition error_codes.h:56
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
asciichat_mode_t
Mode type for options parsing.
option_mode_bitmask_t
Option mode bitmask.
@ MODE_CLIENT
Client mode - network client options.
@ MODE_DISCOVERY_SERVICE
Discovery server mode - session management and WebRTC signaling.
@ MODE_SERVER
Server mode - network server options.
@ MODE_MIRROR
Mirror mode - local webcam viewing (no network)
@ MODE_DISCOVERY
Discovery mode - participant that can dynamically become host.
@ OPTION_MODE_BINARY
Binary-level options (parsed before mode detection)
@ OPTION_MODE_CLIENT
Client mode (bit 1)
@ OPTION_MODE_DISCOVERY
Discovery mode (bit 4)
@ OPTION_MODE_SERVER
Server mode (bit 0)
@ OPTION_MODE_MIRROR
Mirror mode (bit 2)
@ OPTION_MODE_DISCOVERY_SVC
Discovery server mode (bit 3)
void action_check_update(void)
Check for updates and display results.
void action_show_capabilities(void)
Show terminal capabilities and exit.
void action_list_microphones(void)
List available microphone devices and exit.
void action_list_webcams(void)
List available webcam devices and exit.
void action_list_speakers(void)
List available speaker devices and exit.
Shared structures and macros for registry implementation.
const option_descriptor_t * options_registry_get_for_mode(asciichat_mode_t mode, size_t *num_options)
Get all options for a specific mode.
Definition public_api.c:218
const option_descriptor_t * options_registry_get_for_display(asciichat_mode_t mode, bool for_binary_help, size_t *num_options)
Get options for help/completions display with unified filtering.
Definition public_api.c:324
const char ** options_registry_get_enum_values(const char *option_name, const char ***descriptions, size_t *count)
Get enum values for an option.
Definition public_api.c:391
const option_descriptor_t * options_registry_find_by_name(const char *long_name)
Get option descriptor by name.
Definition public_api.c:141
bool options_registry_get_numeric_range(const char *option_name, int *min_out, int *max_out, int *step_out)
Get numeric range for an option.
Definition public_api.c:421
const option_descriptor_t * options_registry_get_binary_options(size_t *num_options)
Get all binary-level options.
Definition public_api.c:283
asciichat_error_t options_registry_add_all_to_builder(options_builder_t *builder)
Add all options from registry to builder.
Definition public_api.c:20
const char ** options_registry_get_examples(const char *option_name, size_t *count)
Get example values for an option.
Definition public_api.c:441
const registry_entry_t * options_registry_get_raw(void)
Get raw access to registry for completions filtering.
Definition public_api.c:126
const option_descriptor_t * options_registry_find_by_short(char short_name)
Get option descriptor by short name.
Definition public_api.c:181
size_t options_registry_get_count(void)
Get total number of registry entries.
Definition public_api.c:136
const option_metadata_t * options_registry_get_metadata(const char *long_name)
Get complete metadata for an option.
Definition public_api.c:370
option_input_type_t options_registry_get_input_type(const char *option_name)
Get input type for an option.
Definition public_api.c:465
registry_entry_t g_options_registry[2048]
Definition registry.c:51
size_t g_registry_size
Definition registry.c:53
Central registry of all command-line options with mode applicability.
Option descriptor.
Definition builder.h:241
bool(* validate)(const void *options_struct, char **error_msg)
Definition builder.h:263
option_mode_bitmask_t mode_bitmask
Which modes this option applies to.
Definition builder.h:278
const char * help_text
Description for –help.
Definition builder.h:251
bool hide_from_mode_help
If true, don't show in mode-specific help (binary-level only)
Definition builder.h:254
size_t offset
offsetof(struct, field) - where to store value
Definition builder.h:248
bool hide_from_binary_help
If true, don't show in binary-level help (e.g., in release builds)
Definition builder.h:255
const char * env_var_name
Environment variable fallback (or NULL)
Definition builder.h:260
const char * group
Group name for help sections (e.g., "NETWORK OPTIONS")
Definition builder.h:252
const void * default_value
Pointer to default value (or NULL if required)
Definition builder.h:258
bool required
If true, option must be provided.
Definition builder.h:259
bool(* parse_fn)(const char *arg, void *dest, char **error_msg)
Definition builder.h:266
bool owns_memory
If true, strings are strdup'd and freed on cleanup.
Definition builder.h:272
const char * long_name
Long option name (e.g., "port")
Definition builder.h:243
char short_name
Short option char (e.g., 'p', or '\0' if none)
Definition builder.h:244
void(* action_fn)(void)
Action to execute (may call exit)
Definition builder.h:269
option_type_t type
Value type.
Definition builder.h:247
bool optional_arg
If true, argument is optional (for callbacks like –verbose)
Definition builder.h:275
Metadata for shell completion generation.
Definition builder.h:192
int max
Maximum value (or 0 if no limit)
Definition builder.h:203
const char ** enum_descriptions
Descriptions parallel to enum_values (null-terminated, e.g., "Auto-detect from terminal")
Definition builder.h:197
int min
Minimum value (or 0 if no limit)
Definition builder.h:202
option_input_type_t input_type
What kind of input this option expects.
Definition builder.h:214
const char ** examples
Example values or command invocations (null-terminated array)
Definition builder.h:208
int step
Step size (0 = no step, continuous)
Definition builder.h:204
const char ** enum_values
Enum value strings (null-terminated, e.g., {"auto", "none", "16", "256", "truecolor",...
Definition builder.h:195
struct option_metadata_t::@19 numeric_range
Options builder.
Definition builder.h:441
Registry entry - stores option definition with mode bitmask and metadata.
option_type_t type
bool optional_arg
const char * group
bool required
bool owns_memory
bool(* validate_fn)(const void *options_struct, char **error_msg)
const char * long_name
size_t default_value_size
option_mode_bitmask_t mode_bitmask
const char * env_var_name
bool(* parse_fn)(const char *arg, void *dest, char **error_msg)
const void * default_value
Default value (single value for all modes, or NULL if mode_default_getter is set)
const char * arg_placeholder
Custom argument placeholder (e.g., "SHELL [FILE]" instead of "STR")
option_metadata_t metadata
Enum values, numeric ranges, examples.
char short_name
const char * help_text
size_t offset