first version
This commit is contained in:
18
LICENSE.txt
Normal file
18
LICENSE.txt
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
Copyright (c) 2025 SyntaxError!
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
35
Makefile
Normal file
35
Makefile
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# ------------------------------
|
||||||
|
# Neocities CLI Makefile (Portable)
|
||||||
|
# ------------------------------
|
||||||
|
|
||||||
|
# Default compiler
|
||||||
|
CC ?= gcc
|
||||||
|
|
||||||
|
# Compiler flags
|
||||||
|
CFLAGS ?= -Wall -O2 -Iinclude
|
||||||
|
|
||||||
|
# Libraries
|
||||||
|
LIBS ?= -lcurl -ljansson
|
||||||
|
|
||||||
|
# Source and target
|
||||||
|
SRC = src/main.c
|
||||||
|
BIN = neocities
|
||||||
|
|
||||||
|
# Detect Windows (MSYS2 / MinGW)
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
RM = del /Q
|
||||||
|
BIN_EXT = .exe
|
||||||
|
else
|
||||||
|
RM = rm -f
|
||||||
|
BIN_EXT =
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Build executable
|
||||||
|
all: $(BIN)$(BIN_EXT)
|
||||||
|
|
||||||
|
$(BIN)$(BIN_EXT): $(SRC)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^ $(LIBS)
|
||||||
|
|
||||||
|
# Clean build files
|
||||||
|
clean:
|
||||||
|
$(RM) $(BIN)$(BIN_EXT)
|
||||||
122
README.md
122
README.md
@@ -1 +1,121 @@
|
|||||||
# neocities-c
|
# Neocities C CLI
|
||||||
|
|
||||||
|
A simple command-line tool written in C that interacts with the [Neocities API](https://neocities.org/api).
|
||||||
|
Currently, only the `--info` command is implemented — it fetches and displays basic information about your Neocities site.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- Fetch site information using Neocities API
|
||||||
|
- Authenticates via environment variables
|
||||||
|
- JSON parsing with [jansson](https://digip.org/jansson/)
|
||||||
|
- Works on Linux, Windows (MSYS2/MinGW), and Android (via Termux)
|
||||||
|
- Compilable with a Makefile for easy builds
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
You’ll need:
|
||||||
|
- A C compiler (`gcc` or `clang`)
|
||||||
|
- [libcurl](https://curl.se/libcurl/)
|
||||||
|
- [jansson](https://digip.org/jansson/)
|
||||||
|
- `make`
|
||||||
|
|
||||||
|
### Linux (Debian/Ubuntu-based)
|
||||||
|
```bash
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install build-essential libcurl4-openssl-dev libjansson-dev make
|
||||||
|
```
|
||||||
|
|
||||||
|
### Windows (MSYS2 / MinGW)
|
||||||
|
```bash
|
||||||
|
pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-curl mingw-w64-x86_64-jansson make
|
||||||
|
```
|
||||||
|
|
||||||
|
### Android (Termux)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pkg install clang curl-dev jansson make
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Run in cloned repository:
|
||||||
|
```bash
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
This will generate the executable neocities (or neocities.exe on Windows).
|
||||||
|
|
||||||
|
To clean build files:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make clean
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
Before running, export your Neocities credentials:
|
||||||
|
|
||||||
|
#### Linux / macOS / Termux
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export NEOCITIES_USER="your_username"
|
||||||
|
export NEOCITIES_PASS="your_password"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Windows (PowerShell)
|
||||||
|
|
||||||
|
```batch
|
||||||
|
setx NEOCITIES_USER "your_username"
|
||||||
|
setx NEOCITIES_PASS "your_password"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./neocities --info
|
||||||
|
```
|
||||||
|
|
||||||
|
Example output:
|
||||||
|
|
||||||
|
```
|
||||||
|
Neocities C CLI
|
||||||
|
|
||||||
|
Sitename: example
|
||||||
|
Hits: 42
|
||||||
|
Created at: 2024-01-10
|
||||||
|
Last updated: 2025-11-01
|
||||||
|
Domain: domain.com
|
||||||
|
Tags: art, blog, personal.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Optional: Install Globally
|
||||||
|
|
||||||
|
You can move the compiled executable to a directory in your PATH to run it from anywhere:
|
||||||
|
|
||||||
|
#### Linux / Termux
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mv neocities /usr/local/bin/
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Windows (PowerShell)
|
||||||
|
Move neocities.exe to a folder in your PATH, e.g., C:\Windows\System32\.
|
||||||
|
|
||||||
|
After that, you can run:
|
||||||
|
|
||||||
|
```batch
|
||||||
|
neocities --info
|
||||||
|
```
|
||||||
|
|
||||||
|
from any folder without specifying the path.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
Only the --info command is implemented for now.
|
||||||
|
|
||||||
|
If you get curl_easy_perform errors, check your network and SSL setup.
|
||||||
|
|
||||||
|
On Termux, use clang for the best compatibility.
|
||||||
122
src/main.c
Normal file
122
src/main.c
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <jansson.h>
|
||||||
|
|
||||||
|
struct response {
|
||||||
|
char *data;
|
||||||
|
size_t len;
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t write_callback(void *data, size_t size, size_t nmemb, void *userdata) {
|
||||||
|
struct response *resp = (struct response *)userdata;
|
||||||
|
size_t chunk_size = size * nmemb;
|
||||||
|
|
||||||
|
char *tmp = realloc(resp->data, resp->len + chunk_size + 1);
|
||||||
|
if (!tmp) {
|
||||||
|
fprintf(stderr, "Erro de memória\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
resp->data = tmp;
|
||||||
|
memcpy(resp->data + resp->len, data, chunk_size);
|
||||||
|
resp->len += chunk_size;
|
||||||
|
resp->data[resp->len] = '\0';
|
||||||
|
return chunk_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fetch_neocities_info() {
|
||||||
|
const char *user = getenv("NEOCITIES_USER");
|
||||||
|
const char *pass = getenv("NEOCITIES_PASS");
|
||||||
|
if (!user || !pass) {
|
||||||
|
fprintf(stderr, "Variáveis NEOCITIES_USER ou NEOCITIES_PASS não definidas!\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char auth[256];
|
||||||
|
snprintf(auth, sizeof(auth), "%s:%s", user, pass);
|
||||||
|
|
||||||
|
CURL *curl = curl_easy_init();
|
||||||
|
if (!curl) {
|
||||||
|
fprintf(stderr, "Erro ao inicializar cURL\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char infourl[256];
|
||||||
|
snprintf(infourl, sizeof(infourl), "https://neocities.org/api/info?sitename=%s", user);
|
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, infourl);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_USERPWD, auth);
|
||||||
|
|
||||||
|
struct response resp = { .data = malloc(1), .len = 0 };
|
||||||
|
resp.data[0] = '\0';
|
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
|
||||||
|
|
||||||
|
CURLcode res = curl_easy_perform(curl);
|
||||||
|
if (res != CURLE_OK) {
|
||||||
|
fprintf(stderr, "Erro na requisição: %s\n", curl_easy_strerror(res));
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
free(resp.data);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
|
||||||
|
json_error_t error;
|
||||||
|
json_t *obj = json_loads(resp.data, 0, &error);
|
||||||
|
free(resp.data);
|
||||||
|
|
||||||
|
if (!obj) {
|
||||||
|
fprintf(stderr, "Erro ao parsear JSON: %s\n", error.text);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_t *result_obj = json_object_get(obj, "result");
|
||||||
|
const char *result_str = json_string_value(result_obj);
|
||||||
|
|
||||||
|
if (strcmp(result_str, "error") == 0) {
|
||||||
|
const char *err_type = json_string_value(json_object_get(obj, "error_type"));
|
||||||
|
const char *err_msg = json_string_value(json_object_get(obj, "message"));
|
||||||
|
|
||||||
|
if (strcmp(err_type, "invalid_auth") == 0) {
|
||||||
|
printf("Usuário ou senha incorretos!\n");
|
||||||
|
} else {
|
||||||
|
printf("Erro! %s\n", err_msg);
|
||||||
|
}
|
||||||
|
json_decref(obj);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(result_str, "success") == 0) {
|
||||||
|
json_t *info_obj = json_object_get(obj, "info");
|
||||||
|
printf("\nSitename: %s\n", json_string_value(json_object_get(info_obj, "sitename")));
|
||||||
|
printf("Hits: %d\n", (int)json_integer_value(json_object_get(info_obj, "hits")));
|
||||||
|
printf("Created at: %s\n", json_string_value(json_object_get(info_obj, "created_at")));
|
||||||
|
printf("Last updated: %s\n", json_string_value(json_object_get(info_obj, "last_updated")));
|
||||||
|
printf("Domain: %s\n", json_string_value(json_object_get(info_obj, "domain")));
|
||||||
|
|
||||||
|
json_t *tags = json_object_get(info_obj, "tags");
|
||||||
|
printf("Tags: ");
|
||||||
|
size_t index;
|
||||||
|
json_t *tag;
|
||||||
|
json_array_foreach(tags, index, tag) {
|
||||||
|
printf("#%s%s", json_string_value(tag), (index < json_array_size(tags) - 1) ? ", " : ".");
|
||||||
|
}
|
||||||
|
printf("\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
json_decref(obj);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
printf("\nNeocities C CLI\n");
|
||||||
|
|
||||||
|
if (argc < 2 || strcmp(argv[1], "--info") != 0) {
|
||||||
|
printf("\nnothing to do.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetch_neocities_info();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user