ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
media/input.c
Go to the documentation of this file.
2#include <ascii-chat/common.h>
4#include <ctype.h>
5#include <string.h>
6
7static int hex_digit(unsigned char c) {
8 if (c >= '0' && c <= '9')
9 return c - '0';
10 c = (unsigned char)tolower(c);
11 return c >= 'a' && c <= 'f' ? c - 'a' + 10 : -1;
12}
13
14asciichat_error_t media_input_normalize(const char *text, char *output, size_t capacity) {
15 if (!text || !output || capacity < 2)
16 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid media input buffer");
17 output[0] = '\0';
18 while (isspace((unsigned char)*text))
19 ++text;
20 size_t length = strlen(text);
21 while (length && isspace((unsigned char)text[length - 1]))
22 --length;
23 if (!length)
24 return SET_ERRNO(ERROR_INVALID_PARAM, "Enter one media path or URL");
25 if (length > 2 && text[0] == '&' && text[1] == ' ') {
26 text += 2;
27 length -= 2;
28 }
29 char quote = (*text == '\'' || *text == '"') ? *text : 0;
30 if (quote) {
31 if (length < 2 || text[length - 1] != quote)
32 return SET_ERRNO(ERROR_INVALID_PARAM, "Choose one file with matching quotes");
33 ++text;
34 length -= 2;
35 }
36 bool uri = length >= 7 && strncmp(text, "file://", sizeof("file://") - 1) == 0;
37 bool url = !uri && (strstr(text, "://") != NULL);
38 if (uri) {
39 text += 7;
40 length -= 7;
41 if (length >= 10 && strncmp(text, "localhost/", sizeof("localhost/") - 1) == 0) {
42 text += 9;
43 length -= 9;
44 } else if (*text != '/') {
45 return SET_ERRNO(ERROR_INVALID_PARAM, "Only local file URIs are supported");
46 }
47#ifdef _WIN32
48 if (length >= 3 && text[0] == '/' && isalpha((unsigned char)text[1]) && text[2] == ':') {
49 ++text;
50 --length;
51 }
52#endif
53 }
54 bool windows_path = length >= 2 && (text[1] == ':' || (text[0] == '\\' && text[1] == '\\'));
55 size_t used = 0;
56 size_t i = 0;
57 while (i < length) {
58 unsigned char c = (unsigned char)text[i];
59 if (uri && c == '%') {
60 if (i + 2 >= length || hex_digit((unsigned char)text[i + 1]) < 0 || hex_digit((unsigned char)text[i + 2]) < 0)
61 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid escape in file URI");
62 c = (unsigned char)(hex_digit((unsigned char)text[i + 1]) * 16 + hex_digit((unsigned char)text[i + 2]));
63 i += 2;
64 } else if (!url && !uri && !quote && !windows_path && c == '\\' && i + 1 < length &&
65 (text[i + 1] == ' ' || text[i + 1] == '\'' || text[i + 1] == '"' || text[i + 1] == '\\')) {
66 c = (unsigned char)text[++i];
67 } else if (!url && !uri && (c == '\'' || c == '"')) {
68 if (quote && c == (unsigned char)quote)
69 return SET_ERRNO(ERROR_INVALID_PARAM, "Choose one media file at a time");
70 }
71 if (c < 32 || c == 127)
72 return SET_ERRNO(ERROR_INVALID_PARAM, "Choose one media path or URL without control characters");
73 if (used + 1 >= capacity)
74 return SET_ERRNO(ERROR_INVALID_PARAM, "Media path or URL is too long");
75 output[used++] = (char)c;
76 ++i;
77 }
78 output[used] = '\0';
79 if (!utf8_is_valid(output))
80 return SET_ERRNO(ERROR_INVALID_PARAM, "Media path or URL is not valid UTF-8");
81 return used ? ASCIICHAT_OK : SET_ERRNO(ERROR_INVALID_PARAM, "Empty media path");
82}
#define SET_ERRNO(code,...)
⚙️ Common definitions, error codes, macros, and types shared throughout the application
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_INVALID_PARAM
bool utf8_is_valid(const char *str)
Check if a string is valid UTF-8.
Definition utf8.c:128
asciichat_error_t media_input_normalize(const char *text, char *output, size_t capacity)
Definition media/input.c:14
🔤 String Manipulation and Shell Escaping Utilities
🔤 UTF-8 Encoding and Decoding Utilities