diff options
| author | Syndamia <kamen@syndamia.com> | 2024-01-06 11:15:57 +0200 |
|---|---|---|
| committer | Syndamia <kamen@syndamia.com> | 2024-01-06 11:15:57 +0200 |
| commit | cb86d3213519727a7fecd05b18b6a8adff230976 (patch) | |
| tree | 6eb60c98731014da7df5d05212ab94b14e6db29a /src | |
| parent | 56c2acfdbf1e4ee5d3fd44814a31ae64f57b3f49 (diff) | |
| download | pico-web-cb86d3213519727a7fecd05b18b6a8adff230976.tar pico-web-cb86d3213519727a7fecd05b18b6a8adff230976.tar.gz pico-web-cb86d3213519727a7fecd05b18b6a8adff230976.zip | |
(src) Moved source files to a src folder
Diffstat (limited to 'src')
| -rw-r--r-- | src/browser-cli.c | 183 | ||||
| -rw-r--r-- | src/browser-cli.h | 12 | ||||
| -rw-r--r-- | src/browser.c | 99 | ||||
| -rw-r--r-- | src/server-cli.c | 46 | ||||
| -rw-r--r-- | src/server-cli.h | 8 | ||||
| -rw-r--r-- | src/server-connection.c | 133 | ||||
| -rw-r--r-- | src/server-connection.h | 12 | ||||
| -rw-r--r-- | src/server.c | 185 | ||||
| -rw-r--r-- | src/util.c | 180 | ||||
| -rw-r--r-- | src/util.h | 29 |
10 files changed, 887 insertions, 0 deletions
diff --git a/src/browser-cli.c b/src/browser-cli.c new file mode 100644 index 0000000..3bea4ba --- /dev/null +++ b/src/browser-cli.c @@ -0,0 +1,183 @@ +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +#include <string.h> +#include <sds/sds.h> +#include <regex.h> +#include <util.h> + +struct md_syntax { + regex_t anchor; +}; + +struct md_syntax syntax = { + .anchor = NULL, +}; + +int* anchorsIndecies; +int anchorsCount = 0; + +void initRendering() { + /* + * Compile regexes used in rendering + */ + + herr(regcomp(&syntax.anchor, "\\[\\([^]]*\\)\\](\\([^)]*\\))", 0), "regcomp"); +} + +void freeRendering() { + regfree(&syntax.anchor); + + free(anchorsIndecies); +} + +void renderPage(const sds page) { + if (sdslen(page) == 0) { + printf("Server didn't return page!\n"); + return; + } + + sds toPrint = sdsdup(page); + + /* + * Parse Markdown constructs + */ + + /* Substitute and store anchorsIndecies */ + if (anchorsIndecies != NULL) { + free(anchorsIndecies); + anchorsCount = 0; + anchorsIndecies = NULL; + } + toPrint = gsub_getm(toPrint, &syntax.anchor, "\16\033[4m\1\033[0m", &anchorsIndecies, &anchorsCount); + + sds newPrint; + for (int i = 0, anchorInd = 0; i < anchorsCount; i++) { + anchorInd = strchr(toPrint, '\16') - toPrint; + + /* In toPrint, replace '\16' with "\033[30;46m%d\033[0m", where %d is the variable i */ + toPrint[anchorInd] = '\0'; + newPrint = sdsgrowzero(sdsempty(), sdslen(toPrint) + digits(i) + 8 + 4); + sprintf(newPrint, "%s\033[30;46m%d\033[0m%s", toPrint, i, toPrint + anchorInd + 1); + + sdsfree(toPrint); + toPrint = newPrint; + } + + /* + * Print page on stdout + */ + write(1, toPrint, sdslen(toPrint)); + + sdsfree(toPrint); +} + +#define MAX_LEN_COMMAND 16 +#define COMMAND_FORMAT ": %16s" + +int portLen(char* start) { + int count = 0; + while ('0' <= *start && *start <= '9') { + count++; + start++; + } + return count; +} + +int hostLen(char* start) { + int count = 0; + while (*start == '.' || ('0' <= *start && *start <= '9')) { + count++; + start++; + } + return count; +} + +char* findBeginningOfPath(char* uri) { + char* startPath = strchr(uri, '/'); + while (startPath != uri && startPath != NULL) { + if (*(startPath - 1) == '.') startPath--; + else break; + } + return startPath; +} + +int handleCLI(sds *host, sds *port, sds *uri, const sds page) { + // Get a line + char line[1024]; + fgets(line, 1024, stdin); + + // Nothing + if (line[0] == '\0') { + printf("Please enter a valid command!\n"); + return 0; + } + + // Number or URL + if (line[0] != ':') { + sds newURI; + + // Index of anchor + if (isNumber(line)) { + int gotoIndex = 0; + sscanf(line, "%d", &gotoIndex); + + if (gotoIndex < 0 || gotoIndex >= anchorsCount) { + printf("Invalid anchor index!\n"); + return 0; + } + + char* start = strchr(page + anchorsIndecies[gotoIndex], '(') + 1; + newURI = sdsnewlen(start, strchr(start, ')') - start); + } + // New address + else { + newURI = sdsnewlen(line, strlen(line)-1); // skip newline + } + + char* startPath = findBeginningOfPath(newURI); + + // Handle relative URLs + if (startPath == newURI) { + sds beforePath = sdscatsds(sdsnewlen(*uri, findBeginningOfPath(*uri) - *uri), newURI); + sdsfree(newURI); + newURI = beforePath; + startPath = findBeginningOfPath(newURI); + } + + if (*uri != NULL) sdsfree(*uri); + *uri = newURI; + + char* startHost = strchr(newURI, '@'); + char* startPort = strchr(newURI, ':'); + + // Update host + if (startHost != NULL && startHost < startPath) { + if (host != NULL) sdsfree(*host); + *host = sdsnewlen(startHost + 1, hostLen(startHost + 1)); + } + + // Update port + if (startPort != NULL && startPort < startPath) { + if (port != NULL) sdsfree(*port); + *port = sdsnewlen(startPort + 1, portLen(startPort + 1)); + } + + return 0; + } + + // Special command + + // Get command name and it's arguments + // Currently no command takes arguments + char name[MAX_LEN_COMMAND+1] = { '\0' }; + int argsAssigned = sscanf(line, COMMAND_FORMAT, name); + + if (streq(name, "q") || streq(name, "e") || streq(name, "quit") || streq(name, "exit")) { + return 1; + } + + printf("Invalid command %s!\n", name); + return 0; +} diff --git a/src/browser-cli.h b/src/browser-cli.h new file mode 100644 index 0000000..1837c6a --- /dev/null +++ b/src/browser-cli.h @@ -0,0 +1,12 @@ +#ifndef BROWSER_CLI +#define BROWSER_CLI + +#include <sds/sds.h> + +void initRendering(); +void freeRendering(); + +void renderPage(const sds page); +int handleCLI(sds *host, sds *port, sds *uri, const sds page); + +#endif diff --git a/src/browser.c b/src/browser.c new file mode 100644 index 0000000..7268831 --- /dev/null +++ b/src/browser.c @@ -0,0 +1,99 @@ +/* Receives a markdown file and "renders" it + */ +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +#include <string.h> +#include <sds/sds.h> +#include <util.h> +#include <browser-cli.h> + +#define READ_BUFFER_SIZE 512 + +sds get_page(const char* ip, const char* port, const char* URL) { + if (streq(URL, "blank")) return sdsnew("\n"); + + /* + * Create socket for connecting with server + */ + int fd_socket = socket(AF_INET, SOCK_STREAM, 0); + herrc(fd_socket, "socket"); + + int aton_status = 0; + struct sockaddr_in sa_server = { + .sin_family = AF_INET, + .sin_port = atop(port), + .sin_addr = aton(ip, &aton_status), + }; + herrc(aton_status, "inet_aton"); + + /* + * Request page + */ + + int connectStatus = connect(fd_socket, (struct sockaddr*)&sa_server, sizeof(struct sockaddr_in)); + herrc(connectStatus, "connect"); + if (connectStatus < 0) return sdsnew("Couldn't connect to server!\n"); + + write(fd_socket, URL, strlen(URL)); + + /* + * Receive page + */ + + sds page = sdsempty(); + + char buff[READ_BUFFER_SIZE]; + clear_arr(buff); + while (read(fd_socket, buff, READ_BUFFER_SIZE)) { + page = sdscat(page, buff); + clear_arr(buff); + } + + /* + * Final + */ + + close(fd_socket); + return page; +} + +int main(int argc, char* argv[]) { + initRendering(); + + /* + * Server-client communication + */ + + sds page; + sds host = sdsnew("127.0.0.1"); + sds port = sdsnew("8080"); + sds uri = sdsnew("blank"); + + int stopProgram = 0; + while (!stopProgram) { + /* + * Get the page + */ + + printf("\033[30;107m%s\033[0m\n", uri); + page = get_page(host, port, uri); + renderPage(page); + + /* + * Handle user input + */ + stopProgram = handleCLI(&host, &port, &uri, page); + sdsfree(page); + } + + freeRendering(); + sdsfree(host); + sdsfree(port); + sdsfree(uri); +} diff --git a/src/server-cli.c b/src/server-cli.c new file mode 100644 index 0000000..58e5ea4 --- /dev/null +++ b/src/server-cli.c @@ -0,0 +1,46 @@ +#include <util.h> +#include <server-connection.h> +#include <stdio.h> +#include <signal.h> +#include <unistd.h> + +#define MAX_LEN_COMMAND 16 +#define COMMAND_FORMAT ": %16s" + +void handleCLI(sds **vhosts, int vhostsc) { + // Get a line + char line[256]; + fgets(line, 256, stdin); + + // Get command name and it's arguments + // Currently no command takes arguments + char name[MAX_LEN_COMMAND+1]; + int argsAssigned = sscanf(line, COMMAND_FORMAT, name); + + while (!streq(name, "q") && !streq(name, "e") && !streq(name, "quit") && !streq(name, "exit")) { + if (argsAssigned < 1) { + printf("Bad command syntax!\n"); + } + else if (streq(name, "vhosts")) { + for (int i = 0; i < vhostsc; i++) { + printf("Name: \"%s\" Root dir: \"%s\" Error file: \"%s\"\n", + vhosts[i][vh_user], + vhosts[i][vh_path], + vhosts[i][vh_error]); + } + } + else if (streq(name, "help") || streq(name, "h") || streq(name, "?")) { + printf("help,h,?\tPrints this message\nvhosts\t\tPrints all registered virtual hosts\nquit,exit,q,e\tExits the program\n"); + } + else { + printf("Unknown command %s!\n", name); + } + + // Get line and divided it into command name and arguments + fgets(line, 256, stdin); + argsAssigned = sscanf(line, COMMAND_FORMAT, name); + } + + printf("Exiting...\n"); + kill(getppid(), SIGTERM); +} diff --git a/src/server-cli.h b/src/server-cli.h new file mode 100644 index 0000000..b5b5875 --- /dev/null +++ b/src/server-cli.h @@ -0,0 +1,8 @@ +#ifndef H_SERVER_CLI +#define H_SERVER_CLI + +#include <sds/sds.h> + +void handleCLI(sds **vhosts, int vhostsc); + +#endif diff --git a/src/server-connection.c b/src/server-connection.c new file mode 100644 index 0000000..285cc1d --- /dev/null +++ b/src/server-connection.c @@ -0,0 +1,133 @@ +#include <server-connection.h> + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> + +#include <string.h> +#include <util.h> + +sds constructFilePath(const sds root, const char* file); +void sanitizeAddress(char* address); +sds* findVhost(char* address, sds** vhosts, const int vhostsc); +int openError(sds* filePath, int* fd, const sds* vhost, const char* client, const int fd_client); + +void on_connection(const char* client, const int fd_client, sds **vhosts, const int vhostsc) { + printf("[%s@%d] Connected successfully!\n", client, fd_client); + + /* Get address request */ + char address[256]; + memset(address, 0, 256); + + read(fd_client, address, 256); + sanitizeAddress(address); + printf("[%s@%d] Requested %s\n", client, fd_client, address); + + /* Is the username connected with any file path? */ + const sds *vhost = findVhost(address, vhosts, vhostsc); + if (vhost == NULL) { + fprintf(stderr, "[%s@%d] Unknown username in address %s\n", client, fd_client, address); + return; + } + + /* Try to open the requested file or the error file */ + sds filePath = constructFilePath(vhost[vh_path], strchr(address, '@') + 1); + + int fd = 0; + + /* Check if file is directory */ + struct stat buf; + if (stat(filePath, &buf) == 0) { + if (S_ISDIR(buf.st_mode)) { + filePath = sdscat(filePath, "/index.md"); + } + else if (!(S_ISREG(buf.st_mode))) { + fprintf(stderr, "[%s@%d] %s is not a regular file!\n", client, fd_client, filePath); + + if (openError(&filePath, &fd, vhost, client, fd_client)) + return; + } + } + + if (fd <= 0) + fd = open(filePath, O_RDONLY); + + if (fd < 0) { + fprintf(stderr, "[%s@%d] Error opening %s\n", client, fd_client, filePath); + + if (openError(&filePath, &fd, vhost, client, fd_client)) + return; + } + + /* Send the file to the client */ + printf("[%s@%d] Serving %s\n", client, fd_client, filePath); + sdsfree(filePath); + + char buff[256]; + memset(buff, 0, sizeof(buff)); + while (read(fd, buff, 256)) { + write(fd_client, buff, strlen(buff)); + memset(buff, 0, sizeof(buff)); + } + + /* Finalize */ + close(fd); + printf("[%s@%d] Served!\n", client, fd_client); +} + +sds constructFilePath(const sds root, const char* file) { + sds path = sdsdup(root); + if (root[sdslen(root)-1] != '/' && file[0] != '/') + path = sdscat(path, "/"); + path = sdscat(path, file); + if (file[strlen(file)-1] == '/') + path = sdscat(path, "index.md"); + return path; +} + +void sanitizeAddress(char* address) { + /* Remove host and port */ + char* startPath = strchr(address, '/'); + if (startPath == NULL) + startPath = strchr(address, '\0'); + + char* startHost = strchr(address, '@'); + shiftLeft(startHost + 1, address - startHost, startPath - startHost - 1); + + /* Remove ../ */ + for (char* prev = startHost+1, *i = startHost+1; i != NULL && *i != '\0';) { + if (i[1] == '.' && i[2] == '.' && i[3] == '/') { + shiftLeft(prev, strlen(prev), i - prev + 3); + i = prev; + } + else { + prev = i; + i = strchr(i+1, '/'); + } + } +} + +sds* findVhost(char* address, sds** vhosts, const int vhostsc) { + sds* vhost = NULL; + int usernameLen = strchr(address, '@') - address; + for (int i = 0; i < vhostsc; i++) { + if (strncmp(vhosts[i][vh_user], address, usernameLen) == 0) { + vhost = *vhosts + i; + break; + } + } + return vhost; +} + +int openError(sds* filePath, int* fd, const sds* vhost, const char* client, const int fd_client) { + sdsfree(*filePath); + *filePath = constructFilePath(vhost[vh_path], vhost[vh_error]); + *fd = open(*filePath, O_RDONLY); + if (*fd < 0) { + fprintf(stderr, "[%s@%d] Error opening %s\n", client, fd_client, *filePath); + sdsfree(*filePath); + return 1; + } + return 0; +} diff --git a/src/server-connection.h b/src/server-connection.h new file mode 100644 index 0000000..71447ff --- /dev/null +++ b/src/server-connection.h @@ -0,0 +1,12 @@ +#ifndef H_SERVER_CONNECTION +#define H_SERVER_CONNECTION + +#include <sds/sds.h> + +#define vh_user 0 +#define vh_path 1 +#define vh_error 2 + +void on_connection(const char* client, const int fd_client, sds **vhosts, const int vhostsc); + +#endif diff --git a/src/server.c b/src/server.c new file mode 100644 index 0000000..aeff800 --- /dev/null +++ b/src/server.c @@ -0,0 +1,185 @@ +/* The server recieves connections and passes files to the clients + */ +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <signal.h> +#include <errno.h> +#include <sys/select.h> + +#include <string.h> +#include <sds/sds.h> +#include <util.h> + +#include <server-connection.h> +#include <server-cli.h> + +int createCommunicationSocket(const char* ip, const char* port) { + int fd_socket = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); + herr(fd_socket, "socket"); + + int aton_status = 0; + struct sockaddr_in sa_socket = { + .sin_family = AF_INET, + .sin_port = atop(port), + .sin_addr = aton(ip, &aton_status), + }; + herr(aton_status, "inet_aton"); + + // Reuse address when in TIME_WAIT state, after listening socket was closed + // https://stackoverflow.com/a/10651048/12036073 + // https://superuser.com/questions/173535/what-are-close-wait-and-time-wait-states#comment951880_173543 + int true = 1; + herr(setsockopt(fd_socket, SOL_SOCKET, SO_REUSEADDR, &true, sizeof(int)), "setsockopt"); + + herr(bind(fd_socket, (struct sockaddr*)&sa_socket, sizeof(struct sockaddr_in)), "bind"); + + herr(listen(fd_socket, 50), "listen"); + + return fd_socket; +} + +void freeVhosts(sds **vhosts, int vhostsc) { + for (int i = 0; i < vhostsc; i++) { + sdsfreesplitres(vhosts[i], 3); + } + free(vhosts); +} + +int acceptConnections = 1; + +void handler_refuseConnections(int signum) { + acceptConnections = 0; +} + +int main(int argc, char* argv[]) { + /* + * Get server parameters + */ + + sds host = sdsnew("127.0.0.1"); + sds port = sdsnew("8080"); + + int argvOffset = 1; + if (argc > 1 && charCount(argv[1], ',') == 1) { + argvOffset++; + char* sep = strchr(argv[1], ','); + sdsfree(host); + host = sdsnewlen(argv[1], sep - argv[1]); + sdsfree(port); + port = sdsnew(sep + 1); + } + + /* + * Get hosts + */ + + int vhostsc = argc - argvOffset; + sds **vhosts = malloc(vhostsc * sizeof(sds*)); + for (int i = 0, temp = 0; i < vhostsc; i++) { + vhosts[i] = sdssplitlen(argv[i+argvOffset], strlen(argv[i+argvOffset]), ",", 1, &temp); + } + + /* + * Create socket for accepting connections + */ + + int fd_socket = createCommunicationSocket(host, port); + printf("Listening on %s:%s\n", host, port); + + /* + * Server command-line interface + */ + + int pid_cli = fork(); + if (pid_cli == 0) { + close(fd_socket); + handleCLI(vhosts, vhostsc); + freeVhosts(vhosts, vhostsc); + sdsfree(host); + sdsfree(port); + return 0; + } + + /* + * Define variables + */ + + // Client address + struct sockaddr_in sa_client = { + .sin_family = AF_INET, + .sin_port = 0, + .sin_addr.s_addr = 0, + }; + socklen_t sa_client_size = sizeof(struct sockaddr_in); + + int fd_client; + int count = 0; + int pselectStat = 0; + + /* + * Handle connection requests + */ + + // Data for pselect + fd_set rfds; + struct timespec tv = { + /* How long should we block (wait) for fd_socket to have a request */ + .tv_sec = 0, + .tv_nsec = 100000, + }; + + // Stop accepting connections on SIGTERM + signal(SIGTERM, handler_refuseConnections); + + while (acceptConnections) { + /* + * Check if fd_socket has something we can read + */ + + FD_ZERO(&rfds); + FD_SET(fd_socket, &rfds); + + herrc(pselect(fd_socket + 1, &rfds, NULL, NULL, &tv, NULL), "pselect"); + if (!FD_ISSET(fd_socket, &rfds)) continue; + + /* + * Accept the connection + */ + + fd_client = accept(fd_socket, (struct sockaddr*)&sa_client, &sa_client_size); + if (acceptConnections) herrc(fd_client, "accept"); + if (fd_client < 0) continue; + + char* strAddr = inet_ntoa(sa_client.sin_addr); + count++; + + /* + * Logic when connected with client + */ + + int fp = fork(); + if (fp == 0) { + close(fd_socket); + on_connection(strAddr, fd_client, vhosts, argc - 1); + close(fd_client); + freeVhosts(vhosts, vhostsc); + sdsfree(host); + sdsfree(port); + return 0; + } + close(fd_client); + } + + while(wait(NULL) > 0); + freeVhosts(vhosts, vhostsc); + close(fd_socket); + sdsfree(host); + sdsfree(port); +} diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..822a4d2 --- /dev/null +++ b/src/util.c @@ -0,0 +1,180 @@ +#include <util.h> +#include <arpa/inet.h> +#include <stdlib.h> + +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <regex.h> + +/* + * Networking + */ + +uint16_t atop(const char *port) { + return htons(atoi(port)); +} + +struct in_addr aton(const char* cp, int* output) { + struct in_addr inp; + *output = inet_aton(cp, &inp); + return inp; +} + +/* + * Error handling + */ + +void herrc(int output, const char* funcName) { + if (output < 0 && errno != EINTR) { + perror(funcName); + } +} + +void herr(int output, const char* funcName) { + if (output < 0) { + perror(funcName); + exit(errno); + } +} + +/* + * sds string substitution + */ + +sds getMatch(const sds str, const regmatch_t match) { + return sdsnewlen(str + match.rm_so, match.rm_eo - match.rm_so); +} +int validMatch(const regmatch_t match) { + return match.rm_so > -1; +} + +void resizeMatches(int* *matches, int* size) { + if (*size == 0) { + *matches = malloc((*size = 8) * sizeof(int*)); + return; + } + + int* *biggerArr = malloc((*size * 2) * sizeof(int*)); + for (size_t i = 0; i < *size; i++) { + biggerArr[i] = matches[i]; + } + *size *= 2; + free(matches); + *matches = *biggerArr; +} +void pushBackMatch(int* *matches, int *matchesCount, int *matchesSize, int matchStart) { + if (*matchesCount >= *matchesSize) + resizeMatches(matches, matchesSize); + + (*matches)[*matchesCount] = matchStart; + *matchesCount += 1; +} + +#define MATCHSTART str + strInd +sds gsub_getm(sds str, const regex_t *regex, const char* repl, int* *matches, int *matchesCount) { + regmatch_t pmatch[10] = { + { .rm_so = 0, .rm_eo = 0, }, { .rm_so = 0, .rm_eo = 0, }, { .rm_so = 0, .rm_eo = 0, }, + { .rm_so = 0, .rm_eo = 0, }, { .rm_so = 0, .rm_eo = 0, }, { .rm_so = 0, .rm_eo = 0, }, + { .rm_so = 0, .rm_eo = 0, }, { .rm_so = 0, .rm_eo = 0, }, { .rm_so = 0, .rm_eo = 0, }, + { .rm_so = 0, .rm_eo = 0, }, + }; + + int strInd = 0; + int matchesSize = (matchesCount != NULL) ? *matchesCount : 0; + size_t replLen = strlen(repl); + + sds ret = sdsempty(); + /* + * Substitute all occurences of regex with repl in str + */ + // sdslen is in O(1) time + while (strInd < sdslen(str)) { + /* Run regex */ + if (regexec(regex, MATCHSTART, 10, pmatch, 0) != 0) { + /* If there are no matches, return the rest of the string as-is */ + ret = sdscat(ret, MATCHSTART); + break; + } + + /* Store everything before the match as-is */ + ret = sdscatlen(ret, MATCHSTART, pmatch[0].rm_so); + + /* Replace match with repl + * repl can include matched subexpressions */ + for(size_t i = 0; i < replLen; i++) { + if (repl[i] <= '\10') { + if (pmatch[repl[i] % 10].rm_so > -1) { + sds match = getMatch(MATCHSTART, pmatch[repl[i] % 10]); + ret = sdscatsds(ret, match); + sdsfree(match); + } + } + else + ret = sdscatlen(ret, &repl[i], 1); + } + + /* Add index of current match to matches */ + if (matchesCount != NULL) { + pushBackMatch(matches, matchesCount, &matchesSize, strInd + pmatch[0].rm_so); + } + + /* Continute after the current match */ + strInd += pmatch[0].rm_eo; + } + + sdsfree(str); + return ret; +} + +sds gsub(sds str, const regex_t* regex, const char* repl) { + return gsub_getm(str, regex, repl, NULL, NULL); +} + +/* + * other + */ + +int digits(int num) { + if (num < 0) num *= -1; + // This is the fastest way to get the number of digits + if (num < 10) return 1; + if (num < 100) return 2; + if (num < 1000) return 3; + if (num < 10000) return 4; + if (num < 100000) return 5; + if (num < 1000000) return 6; + if (num < 10000000) return 7; + if (num < 100000000) return 8; + if (num < 1000000000) return 9; + // 2147483647 (2^31-1) is max value of int + return 10; +} + +int streq(const char* first, const char* second) { + return strcmp(first, second) == 0; +} + +void shiftLeft(char* str, size_t size, size_t shift) { + while (*(str + shift - 1) != '\0') { + *str = *(str + shift); + str++; + } +} + +int isNumber(char* str) { + while (*str >= ' ') { + if (*str < '0' || *str > '9') return 0; + str++; + } + return 1; +} + +int charCount(char* str, char cmp) { + int count = 0; + while (*str != '\0') { + count += *str == cmp; + str++; + } + return count; +} diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..b2836eb --- /dev/null +++ b/src/util.h @@ -0,0 +1,29 @@ +#ifndef H_UTIL +#define H_UTIL + +#include <inttypes.h> +#include <sds/sds.h> +#include <arpa/inet.h> +#include <regex.h> + +/* Networking */ +uint16_t atop(const char *port); +struct in_addr aton(const char* cp, int* output); + +/* Error handling */ +void herr(int output, const char* funcName); +void herrc(int output, const char* funcName); + +/* sds string substition */ +sds gsub(sds str, const regex_t* regex, const char* repl); +sds gsub_getm(sds str, const regex_t *regex, const char* repl, int* *matches, int *matchesCount); + +/* Other */ +#define clear_arr(arr) memset(arr, 0, sizeof(arr)/sizeof(*arr)) +int digits(int num); +int streq(const char* first, const char* second); +void shiftLeft(char* str, size_t size, size_t shift); +int isNumber(char* str); +int charCount(char* str, char cmp); + +#endif |
