welcome - welcome - a terminal program which always greets you

git clone git://git.bcharge.de/welcome.git

About | Log | Files | Refs | License

welcome.c (6989B)


/* See LICENSE file for copyright and license details. */

#include <curses.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <curl/curl.h>
#include <locale.h>
#include <pwd.h>
#include <unistd.h>

#define MAXPIPE 1024
#define EVBOX_W 50

typedef struct {
	char *ptr;
	size_t len;
} string;

// Structure to hold all the data we need to fetch
typedef struct {
	string location;
	string temperature;
	string precip;
	string sunlife;
	string wind;
	char *events;
	char *today;
	size_t w_pipe_events;
	size_t w_pipe_today;
	int fetch_complete;  // Flag to indicate when fetching is done
} FetchData;

void init_string(string *s)
{
	s->len = 0;
	s->ptr = malloc(s->len+1);
	if (s->ptr == NULL) {
		fprintf(stderr, "malloc() failed\n");
		exit(EXIT_FAILURE);
	}
	s->ptr[0] = '\0';
}

size_t writefunc(void *ptr, size_t size, size_t nmemb, string *s)
{
	size_t new_len = s->len + size*nmemb;
	s->ptr = realloc(s->ptr, new_len+1);
	if (s->ptr == NULL) {
		fprintf(stderr, "realloc() failed\n");
		exit(EXIT_FAILURE);
	}
	memcpy(s->ptr+s->len, ptr, size*nmemb);
	s->ptr[new_len] = '\0';
	s->len = new_len;

	return size*nmemb;
}

int curl(char* input, string* output)
{
	CURL *curl;
	CURLcode res;

	curl = curl_easy_init();
	if(curl) {
		//string s;
		//init_string(&s);

		curl_easy_setopt(curl, CURLOPT_URL, input);
		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
		curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
		curl_easy_setopt(curl, CURLOPT_WRITEDATA, output);
		res = curl_easy_perform(curl);

		//printf("Inside curl: %s", output->ptr);
		//free(s.ptr);

		/* always cleanup */
		curl_easy_cleanup(curl);
	}
	return 0;
}

int syspipe(char* input, char* output, size_t* max_width)
{
	//char buf[11500] = {0};
	char* buf = output;

	FILE *fp;

	if ((fp = popen(input, "r")) == NULL) {
		printf("Error opening pipe!\n");
	}
	size_t len=MAXPIPE;
	//printf("INPUT: %s\n", input);
	getdelim(&buf, &len, '\0', fp);
	//printf("OUTPUT: %s", output);
	*max_width = 0;
	size_t current = 0;
	int ch;
	for (int i = 0; buf[i] != 0; i++) {
		ch = buf[i];
		if(ch == '\n') {
			if(current > *max_width) *max_width = current;
			current = 0;
		}
		else {
			current++;
		}
	}
	if(current > *max_width) *max_width = current;

	if (pclose(fp)) {
		printf("Command not found or exited with error status\n");
	}

	return 0;
}

char* get_first_name()
{
	// Try to get a first name from passwd entry
	struct passwd* pw = getpwuid(getuid());
	if (pw != NULL && pw->pw_gecos != NULL && strlen(pw->pw_gecos) > 0) {
		char* name_copy = strdup(pw->pw_gecos);
		char* first_part = strtok(name_copy, ",");

		if (first_part != NULL) {
			char* first_name = strtok(first_part, " ");
			if (first_name != NULL) {
				return strdup(first_name);
			}
			free(name_copy);
		}
		free(name_copy);
	}

	// If it fails, return username
	if (pw != NULL) {
		return strdup(pw->pw_name);
	}

	// Last resort
	return strdup("user");
}

int print_greet(WINDOW* frame)
{
	char buf[500] = {0};
	char* name = get_first_name();
	char command[100];
	sprintf(command, "figlet Welcome %s!", name);
	const char *cmd = command;

	FILE *fp;

	if ((fp = popen(cmd, "r")) == NULL) {
		printf("Error opening pipe!\n");
	}
	//ssize_t bytes_read = getdelim(&buf, &len, '\0', fp);
	int lineno = 0;
	int shift = 0;
	while (fgets(buf, 400, fp) != NULL) {
		if(lineno==0)
			shift=strlen(buf)/2;
		mvwaddstr(frame,++lineno, COLS/2-shift, buf);
		//printw(buf);
		//printf("OUTPUT: %s", buf);
	}

	if (pclose(fp)) {
		printf("Command not found or exited with error status\n");
	}

	return 0;
}

// Thread function to fetch all the data
void* fetch_data(void* arg)
{
	FetchData* data = (FetchData*)arg;

	curl("wttr.in/?format=%l", &data->location);
	curl("wttr.in/?format=%t%20(%f)", &data->temperature);
	curl("wttr.in/?format=%w", &data->wind);
	curl("wttr.in/?format=%p", &data->precip);
	curl("wttr.in/?format=%S%20-%20%s", &data->sunlife);
	syspipe("calcurse -r7 --format-apt='- %S -> %E %m\n' --output-datefmt=\"%B %d, %Y\"",
			data->events, &data->w_pipe_events);
	syspipe("date '+%B %d, %Y' | tr -d '\n'", data->today, &data->w_pipe_today);

	data->fetch_complete = 1;
	return NULL;
}

int main()
{
	setlocale(LC_ALL, "");
	initscr();
	cbreak();
	noecho();
	curs_set(0);
	start_color();

	use_default_colors();
	init_pair(1, COLOR_RED, -1);
	init_pair(2, COLOR_GREEN, -1);

	WINDOW* mainframe = newwin(LINES,COLS-2,0,1);
	wattron(mainframe,COLOR_PAIR(1));
	//printw("Hello World !!!");
	wattron(mainframe,A_BLINK);
	wattron(mainframe,A_BOLD);
	print_greet(mainframe);
	wattroff(mainframe,A_BLINK);

	/****************************
	*  Extract the information  *
	*****************************/
	FetchData data = {0};
	init_string(&data.location);
	init_string(&data.temperature);
	init_string(&data.precip);
	init_string(&data.sunlife);
	init_string(&data.wind);
	data.events = calloc(MAXPIPE + 1, sizeof(char));
	data.today = calloc(MAXPIPE + 1, sizeof(char));
	data.fetch_complete = 0;

	WINDOW *BOARD[10];
	// Create and start the fetch thread
	pthread_t fetch_thread;
	pthread_create(&fetch_thread, NULL, fetch_data, &data);

	// While data is being fetched, animate the skulls
	refresh();
	char *skull  = calloc(MAXPIPE + 1,sizeof(char));
	size_t w_pipe_skull  = 0;
	syspipe("cat skull", skull, &w_pipe_skull);
	//wprintw(mainframe,"%s\n", skull);
	//wrefresh(mainframe);
	int num_skulls = COLS/w_pipe_skull;
	int left_margin = (COLS-w_pipe_skull*num_skulls)/2;
	for (int i = 0; i < COLS/w_pipe_skull; i++) {
		BOARD[i] = derwin(mainframe, 8, 21, 20, i*20+left_margin);
	}
	int loop_cntr = 0;
	do{
		int color = loop_cntr%2 ? 1 : 2;
		for (int i = 0; i < COLS/w_pipe_skull; i++) {
			werase(BOARD[i]);
			wattron(BOARD[i], COLOR_PAIR(color));
			wprintw(BOARD[i], skull);
			wrefresh(BOARD[i]);
			napms(100);
		}
		loop_cntr++;
	}
	while (!data.fetch_complete);

	if(data.events[0]=='\0')
		strcpy(data.events,"🎉 Looks like you have not planned anything 🎉\n");

	// Wait for fetch thread to complete
	pthread_join(fetch_thread, NULL);


	/**************************
	*  Print the information  *
	***************************/
	wprintw(mainframe, "Today is:\t\t%s\n"         , data.today);
	wprintw(mainframe, "We are in:\t\t%s\n"        , data.location.ptr);
	wprintw(mainframe, "Temperature:\t\t%s\n"      , data.temperature.ptr);
	wprintw(mainframe, "Wind speed:\t\t%s\n"       , data.wind.ptr);
	wprintw(mainframe, "Precipitation:\t\t%s\n"    , data.precip.ptr);
	wprintw(mainframe, "Sunrise and sunset:\t%s\n" , data.sunlife.ptr);
	wrefresh(mainframe);

	WINDOW* fr = newwin(LINES/2,EVBOX_W,LINES/2,(COLS-EVBOX_W)/2);
	WINDOW* c  = derwin(fr,LINES/2-2,EVBOX_W-4,1,2);
	box(fr,0,0);
	//wmove(fr, 0, 0);
	//printw("Events within next 7 days:\t%s\n",events);
	char evts_title[]="Events within next 7 days\n\n";
	mvwaddstr(c,0,(EVBOX_W-strlen(evts_title))/2-1,evts_title);
	wprintw(c,data.events);
	wrefresh(fr);

	getch();
	endwin();

	/* TODO: Clean memory <11-12-23, bakar> */

	return 0;
}