Login
7 branches 0 tags
Ben (X13/Arch) Minor cleanup 2059e01 4 years ago 208 Commits
nujel / bin / operation / readline.c
/*
 * Wolkenwelten - Copyright (C) 2020-2021 - Benjamin Vincent Schulenburg
 *
 * This project uses the MIT license, a copy should be included under /LICENSE
 */
#include "readline.h"
#include "../misc.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#ifdef __MINGW32__
	#include <windows.h>
	#include <shlobj.h>
	#include "../../vendor/getline/getline.h"
	/* Since bestline does not support windows,
	 * these serve as a simple replacement.
	 */
	#define BUF_SIZE (1 << 14)
	static void bestlineHistoryLoad(const char *path){(void)path;}
	static void bestlineHistorySave(const char *path){(void)path;}
	static void bestlineHistoryAdd (const char *line){(void)line;}
	static char *bestline(const char *prompt){
		char *buf = NULL;
		size_t bufsize = 0;

		fputs(prompt, stdout);
		const ssize_t ret = getline(&buf,&bufsize,stdin);

		if(ret >= 0){
			buf[MIN(bufsize-1,(size_t)ret)] = 0;
			return buf;
		}else{
			return NULL;
		}
	}
#else
	#include "../../vendor/bestline/bestline.h"
#endif

const char *getHistoryPath(){
	static char buf[512];

	#ifdef __MINGW32__
	char home[512];
	HRESULT result = SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, home);
	if(result != S_OK){
		return NULL;
	}
	#else
	const char* home = getenv("HOME");
	if(!home){
		return NULL;
	}
	#endif

	if(snprintf(buf,sizeof(buf),"%s/.nujel_history",home) <= 0){ // snprintf instead of strcpy/strcat
		fprintf(stderr,"Can't create historyPath, maybe your $HOME is too big?\n");
		return NULL;
	}
	return buf;
}

static lVal *lnfReadline(lClosure *c, lVal *v){
	(void) c;
	const char *prompt = castToString(lCar(v),"> ");
	char *line = bestline(prompt);
	lVal *ret = lValString(line);
	free(line);
	return ret;
}

static lVal *lnfReadlineHistoryPath(lClosure *c, lVal *v){
	(void) c; (void) v;

	return lValString(getHistoryPath());
}

static lVal *lnfReadlineHistoryAdd(lClosure *c, lVal *v){
	(void) c;
	const char *line = castToString(lCar(v),NULL);
	if(line == NULL){return NULL;}
	bestlineHistoryAdd(line);
	return lCar(v);
}

static lVal *lnfReadlineHistoryLoad(lClosure *c, lVal *v){
	(void) c;
	const char *path = castToString(lCar(v),NULL);
	if(path == NULL){return NULL;}
	bestlineHistoryLoad(path);
	return lValBool(true);
}

static lVal *lnfReadlineHistorySave(lClosure *c, lVal *v){
	(void) c;
	const char *path = castToString(lCar(v),NULL);
	if(path == NULL){return NULL;}
	bestlineHistorySave(path);
	return lValBool(true);
}

void lOperationsReadline(lClosure *c){
	lAddNativeFunc(c,"readline",              "[prompt]", "Read a line of input in a user friendly way after writing PROMPT", lnfReadline);
	lAddNativeFunc(c,"readline/history/path", "[]",       "Return the default path to the command history",                   lnfReadlineHistoryPath);
	lAddNativeFunc(c,"readline/history/load", "[path]",   "Load command history from PATH",                                   lnfReadlineHistoryLoad);
	lAddNativeFunc(c,"readline/history/save", "[path]",   "Save command history into PATH",                                   lnfReadlineHistorySave);
	lAddNativeFunc(c,"readline/history/add",  "[line]",   "Add an entry into the command history without saving",             lnfReadlineHistoryAdd);
}