With a complete reorganisation of the directory structure and most of the content converted to pandoc-flavoured markdown. Some TODO's left before this can go live: - Main page - Atom feeds - Bug tracker
326 lines
8.5 KiB
C
326 lines
8.5 KiB
C
/* vinfo - A small commandline visual novel info display thingy
|
|
|
|
Last modified: 2009-11-16
|
|
|
|
This is just a simple example program using the public VNDB API,
|
|
this program is neither useful nor actively maintained. Feel free
|
|
to extend it to suit your needs.
|
|
|
|
Requirements:
|
|
- A POSIX-compatible system
|
|
- GNU getopt()
|
|
- json-c (http://oss.metaparadigm.com/json-c/)
|
|
|
|
Compile:
|
|
gcc vinfo.c `pkg-config --cflags --libs json` -o vinfo
|
|
|
|
Usage:
|
|
vinfo -u <username> -p <password> <vnid>
|
|
|
|
TODO:
|
|
- Giving password on the commandline is a really bad idea,
|
|
need to fix an alternative method.
|
|
- Display more info on VN info output (links/description)
|
|
- Make host/port configurable
|
|
- VN searching
|
|
- More error checking on the received JSON data
|
|
- Producers/releases?
|
|
|
|
|
|
Copyright (c) 2009 Yoran Heling
|
|
|
|
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, FITNESS 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.
|
|
*/
|
|
|
|
|
|
#define PROGRAM "vinfo"
|
|
#define VERSION 0.1
|
|
|
|
#define SERVER "81.204.242.156" /* beta, you may want to change this to whatever api.vndb.org resolves to */
|
|
#define PORT 19534
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
#include <json.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/tcp.h>
|
|
#include <arpa/inet.h>
|
|
|
|
|
|
#define WS "\011\012\015\040"
|
|
#define DIE(str) { perror(str); exit(1); }
|
|
|
|
|
|
char *user, *pass;
|
|
int sock;
|
|
|
|
|
|
/* sends command and returns reply */
|
|
char *docmd(char *cmd) {
|
|
char *res, *fmt;
|
|
unsigned int start, end, read = 0, size = 1024;
|
|
int r;
|
|
json_object *json;
|
|
|
|
if(send(sock, cmd, strlen(cmd), 0) < 0)
|
|
DIE("send");
|
|
|
|
/* receive data */
|
|
res = malloc(size);
|
|
while(memchr(res, 4, read) == NULL) {
|
|
if(read > 102400) {
|
|
printf("ERROR: Too long reply.\n");
|
|
exit(1);
|
|
}
|
|
if(size-read < 500) {
|
|
size *= 2;
|
|
res = realloc(res, size);
|
|
}
|
|
if((r = recv(sock, res+read, size-read, 0)) < 0)
|
|
DIE("recv");
|
|
read += r;
|
|
}
|
|
|
|
/* remove leading and trailing whitespace */
|
|
end = ((char *)memchr(res, 4, read))-res;
|
|
res[end--] = 0;
|
|
while(strspn(res+end, WS))
|
|
end--;
|
|
start = strspn(res, WS);
|
|
|
|
if(start == 0)
|
|
fmt = res;
|
|
else {
|
|
fmt = malloc(end-start+1);
|
|
strcpy(fmt, res+start);
|
|
free(res);
|
|
}
|
|
|
|
/* check for error response */
|
|
if(!strncmp(fmt, "error", strlen("error"))) {
|
|
res = fmt+strlen("error");
|
|
res += strspn(res, WS);
|
|
json = json_tokener_parse(res);
|
|
if(json != NULL && json_object_is_type(json, json_type_object)) {
|
|
printf("ERROR(%s): %s\n",
|
|
json_object_get_string(json_object_object_get(json, "id")),
|
|
json_object_get_string(json_object_object_get(json, "msg")));
|
|
json_object_put(json);
|
|
} else {
|
|
printf("Received something strange...\n> %s\n", fmt);
|
|
}
|
|
free(fmt);
|
|
exit(1);
|
|
}
|
|
|
|
return fmt;
|
|
}
|
|
|
|
|
|
/* opens the TCP socket and logs in */
|
|
void login() {
|
|
struct sockaddr_in addr;
|
|
json_object *arg;
|
|
const char *json;
|
|
char *cmd, *res;
|
|
|
|
/* connect */
|
|
if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
|
DIE("socket");
|
|
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(PORT);
|
|
inet_aton(SERVER, &addr.sin_addr);
|
|
if(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
|
|
DIE("connect");
|
|
|
|
/* construct login command */
|
|
arg = json_object_new_object();
|
|
json_object_object_add(arg, "protocol", json_object_new_int(1));
|
|
json_object_object_add(arg, "client", json_object_new_string(PROGRAM));
|
|
json_object_object_add(arg, "clientver", json_object_new_double(VERSION));
|
|
json_object_object_add(arg, "username", json_object_new_string(user));
|
|
json_object_object_add(arg, "password", json_object_new_string(pass));
|
|
json = json_object_to_json_string(arg);
|
|
cmd = malloc(strlen(json) + 10);
|
|
sprintf(cmd, "login %s\04", json);
|
|
json_object_put(arg);
|
|
|
|
/* login */
|
|
res = docmd(cmd);
|
|
free(cmd);
|
|
if(strncmp("ok", res, 2)) {
|
|
printf("ERROR: Received something strange.\n> %s\n", res);
|
|
free(res);
|
|
exit(1);
|
|
}
|
|
free(res);
|
|
}
|
|
|
|
|
|
void vninfo(int vid) {
|
|
char cmd[50], *res, *tmp, *tmp2;
|
|
const char *str;
|
|
int i, n;
|
|
json_object *json, *vn, *itm;
|
|
|
|
login();
|
|
sprintf(cmd, "get vn basic,details (id=%d)\04", vid);
|
|
res = docmd(cmd);
|
|
if(strncmp("results", res, 7)) {
|
|
printf("ERROR: Received something strange.\n> %s\n", res);
|
|
exit(1);
|
|
}
|
|
tmp = res+7;
|
|
tmp += strspn(tmp, WS);
|
|
if((json = json_tokener_parse(tmp)) == NULL || !json_object_is_type(json, json_type_object)) {
|
|
printf("ERROR: Received something strange.\n> %s\n", res);
|
|
exit(1);
|
|
}
|
|
/*printf("\n%s\n\n", tmp);*/
|
|
|
|
/* note: may want to put in some checks on whether the data indeed confirms to what we expect */
|
|
if(json_object_get_int(json_object_object_get(json, "num")) < 1) {
|
|
printf("No VN with that ID.\n");
|
|
exit(1);
|
|
}
|
|
vn = json_object_array_get_idx(json_object_object_get(json, "items"), 0);
|
|
|
|
/* header + titles */
|
|
printf("------------------------------ v%-5d ----------------------------\n", vid);
|
|
printf(" Title : %s\n", json_object_get_string(json_object_object_get(vn, "title")));
|
|
if((str = json_object_get_string(json_object_object_get(vn, "original"))) != NULL)
|
|
printf(" Official : %s\n", str);
|
|
|
|
/* aliases (string processing in C, meh) */
|
|
str = json_object_get_string(json_object_object_get(vn, "aliases"));
|
|
if(str != NULL) {
|
|
printf(" Aliases : ");
|
|
tmp = strdup(str);
|
|
tmp2 = tmp;
|
|
i = -1;
|
|
do {
|
|
i++;
|
|
if(!tmp[i] || tmp[i] == ',' || tmp[i] == '\n') {
|
|
if(tmp2 != tmp)
|
|
printf(" ");
|
|
tmp2 += strspn(tmp2, ", \n");
|
|
n = tmp[i];
|
|
tmp[i] = 0;
|
|
printf("%s\n", tmp2);
|
|
if(n) {
|
|
i++;
|
|
tmp2 = tmp+i;
|
|
}
|
|
}
|
|
} while(tmp[i]);
|
|
free(tmp);
|
|
}
|
|
|
|
/* release date */
|
|
str = json_object_get_string(json_object_object_get(vn, "released"));
|
|
printf(" First release : %-10s\n", str != NULL ? str : "-");
|
|
|
|
/* length */
|
|
itm = json_object_object_get(vn, "length");
|
|
if(json_object_is_type(itm, json_type_int)) {
|
|
printf(" Length : ");
|
|
switch(json_object_get_int(itm)) {
|
|
case 1: printf("Very short\n"); break;
|
|
case 2: printf("Short\n"); break;
|
|
case 3: printf("Medium\n"); break;
|
|
case 4: printf("Long\n"); break;
|
|
case 5: printf("Very long\n"); break;
|
|
}
|
|
}
|
|
|
|
/* languages */
|
|
itm = json_object_object_get(vn, "languages");
|
|
if(json_object_array_length(itm) > 0) {
|
|
printf(" Available in : ");
|
|
for(i=0; i<json_object_array_length(itm); i++) {
|
|
if(i)
|
|
printf(", ");
|
|
printf("%s", json_object_get_string(json_object_array_get_idx(itm, i)));
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
/* platforms */
|
|
itm = json_object_object_get(vn, "platforms");
|
|
if(json_object_array_length(itm) > 0) {
|
|
printf(" ...for : ");
|
|
for(i=0; i<json_object_array_length(itm); i++) {
|
|
if(i)
|
|
printf(", ");
|
|
printf("%s", json_object_get_string(json_object_array_get_idx(itm, i)));
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
json_object_put(json);
|
|
free(res);
|
|
}
|
|
|
|
|
|
void usage() {
|
|
printf("%s -u username -p password query\n", PROGRAM);
|
|
exit(1);
|
|
}
|
|
|
|
|
|
int main(int argc, char **argv) {
|
|
char *query;
|
|
int r;
|
|
|
|
user = pass = query = NULL;
|
|
|
|
while((r = getopt(argc, argv, "-u:p:")) >= 0) {
|
|
switch(r) {
|
|
case 'u':
|
|
user = optarg;
|
|
break;
|
|
case 'p':
|
|
pass = optarg;
|
|
break;
|
|
case '\1':
|
|
query = optarg;
|
|
break;
|
|
default:
|
|
usage();
|
|
}
|
|
}
|
|
if(!pass || !user || !query)
|
|
usage();
|
|
|
|
if(sscanf(query, "%d", &r) == 1 && r > 0) {
|
|
vninfo(r);
|
|
} else {
|
|
printf("Sorry, searching is not supported yet\n");
|
|
exit(1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|