X-Git-Url: https://git.toastfreeware.priv.at/gregoa/bti.git/blobdiff_plain/3383fbc56d1316d69fd2bd05b1ae3df614743e3c..a03d5f2cf2ae11fa03f642547bcd3c169e6c9dda:/bti.c diff --git a/bti.c b/bti.c index b708272..4bf6a6c 100644 --- a/bti.c +++ b/bti.c @@ -33,12 +33,12 @@ #include #include #include -#include #include #include #include #include #include +#include #define zalloc(size) calloc(size, 1) @@ -81,12 +81,17 @@ struct session { char *user; char *group; char *hosturl; + char *hostname; + char *configfile; int bash; + int interactive; int shrink_urls; int dry_run; int page; enum host host; enum action action; + void *readline_handle; + char *(*readline)(const char *); }; struct bti_curl_buffer { @@ -112,6 +117,7 @@ static void display_help(void) fprintf(stdout, " --proxy PROXY:PORT\n"); fprintf(stdout, " --host HOST\n"); fprintf(stdout, " --logfile logfile\n"); + fprintf(stdout, " --config configfile\n"); fprintf(stdout, " --shrink-urls\n"); fprintf(stdout, " --page PAGENUMBER\n"); fprintf(stdout, " --bash\n"); @@ -127,6 +133,102 @@ static void display_version(void) fprintf(stdout, "bti - version %s\n", VERSION); } +static char *get_string(const char *name) +{ + char *temp; + char *string; + + string = zalloc(1000); + if (!string) + exit(1); + if (name != NULL) + fprintf(stdout, "%s", name); + if (!fgets(string, 999, stdin)) + return NULL; + temp = strchr(string, '\n'); + if (temp) + *temp = '\0'; + return string; +} + +/* + * Try to get a handle to a readline function from a variety of different + * libraries. If nothing is present on the system, then fall back to an + * internal one. + * + * Logic originally based off of code in the e2fsutils package in the + * lib/ss/get_readline.c file, which is licensed under the MIT license. + * + * This keeps us from having to relicense the bti codebase if readline + * ever changes its license, as there is no link-time dependancy. + * It is a run-time thing only, and we handle any readline-like library + * in the same manner, making bti not be a derivative work of any + * other program. + */ +static void session_readline_init(struct session *session) +{ + /* Libraries we will try to use for readline/editline functionality */ + const char *libpath = "libreadline.so.6:libreadline.so.5:" + "libreadline.so.4:libreadline.so:libedit.so.2:" + "libedit.so:libeditline.so.0:libeditline.so"; + void *handle = NULL; + char *tmp, *cp, *next; + int (*bind_key)(int, void *); + void (*insert)(void); + + /* default to internal function if we can't or won't find anything */ + session->readline = get_string; + if (!isatty(0)) + return; + session->interactive = 1; + + tmp = malloc(strlen(libpath)+1); + if (!tmp) + return; + strcpy(tmp, libpath); + for (cp = tmp; cp; cp = next) { + next = strchr(cp, ':'); + if (next) + *next++ = 0; + if (*cp == 0) + continue; + handle = dlopen(cp, RTLD_NOW); + if (handle) { + dbg("Using %s for readline library\n", cp); + break; + } + } + free(tmp); + if (!handle) { + dbg("No readline library found.\n"); + return; + } + + session->readline_handle = handle; + session->readline = (char *(*)(const char *))dlsym(handle, "readline"); + if (session->readline == NULL) { + /* something odd happened, default back to internal stuff */ + session->readline_handle = NULL; + session->readline = get_string; + return; + } + + /* + * If we found a library, turn off filename expansion + * as that makes no sense from within bti. + */ + bind_key = (int (*)(int, void *))dlsym(handle, "rl_bind_key"); + insert = (void (*)(void))dlsym(handle, "rl_insert"); + if (bind_key && insert) + bind_key('\t', insert); +} + +static void session_readline_cleanup(struct session *session) +{ + if (session->readline_handle) + dlclose(session->readline_handle); +} + static struct session *session_alloc(void) { struct session *session; @@ -150,6 +252,8 @@ static void session_free(struct session *session) free(session->user); free(session->group); free(session->hosturl); + free(session->hostname); + free(session->configfile); free(session); } @@ -183,6 +287,8 @@ static void bti_curl_buffer_free(struct bti_curl_buffer *buffer) static const char *twitter_host = "https://twitter.com/statuses"; static const char *identica_host = "https://identi.ca/api/statuses"; +static const char *twitter_name = "twitter"; +static const char *identica_name = "identi.ca"; static const char *user_uri = "/user_timeline/"; static const char *update_uri = "/update.xml"; @@ -448,12 +554,7 @@ static void parse_configfile(struct session *session) char *file; int shrink_urls = 0; - /* config file is ~/.bti */ - file = alloca(strlen(session->homedir) + 7); - - sprintf(file, "%s/.bti", session->homedir); - - config_file = fopen(file, "r"); + config_file = fopen(session->configfile, "r"); /* No error if file does not exist or is unreadable. */ if (config_file == NULL) @@ -535,12 +636,15 @@ static void parse_configfile(struct session *session) if (strcasecmp(host, "twitter") == 0) { session->host = HOST_TWITTER; session->hosturl = strdup(twitter_host); + session->hostname = strdup(twitter_name); } else if (strcasecmp(host, "identica") == 0) { session->host = HOST_IDENTICA; session->hosturl = strdup(identica_host); + session->hostname = strdup(identica_name); } else { session->host = HOST_CUSTOM; session->hosturl = strdup(host); + session->hostname = strdup(host); } free(host); } @@ -581,7 +685,6 @@ static void log_session(struct session *session, int retval) { FILE *log_file; char *filename; - char *host; /* Only log something if we have a log file set */ if (!session->logfile) @@ -595,46 +698,36 @@ static void log_session(struct session *session, int retval) log_file = fopen(filename, "a+"); if (log_file == NULL) return; - switch (session->host) { - case HOST_TWITTER: - host = "twitter"; - break; - case HOST_IDENTICA: - host = "identi.ca"; - break; - default: - host = session->hosturl; - break; - } switch (session->action) { case ACTION_UPDATE: if (retval) fprintf(log_file, "%s: host=%s tweet failed\n", - session->time, host); + session->time, session->hostname); else fprintf(log_file, "%s: host=%s tweet=%s\n", - session->time, host, session->tweet); + session->time, session->hostname, + session->tweet); break; case ACTION_FRIENDS: fprintf(log_file, "%s: host=%s retrieving friends timeline\n", - session->time, host); + session->time, session->hostname); break; case ACTION_USER: fprintf(log_file, "%s: host=%s retrieving %s's timeline\n", - session->time, host, session->user); + session->time, session->hostname, session->user); break; case ACTION_REPLIES: fprintf(log_file, "%s: host=%s retrieving replies\n", - session->time, host); + session->time, session->hostname); break; case ACTION_PUBLIC: fprintf(log_file, "%s: host=%s retrieving public timeline\n", - session->time, host); + session->time, session->hostname); break; case ACTION_GROUP: fprintf(log_file, "%s: host=%s retrieving group timeline\n", - session->time, host); + session->time, session->hostname); break; default: break; @@ -655,13 +748,15 @@ static char *get_string_from_stdin(void) if (!fgets(string, 999, stdin)) return NULL; temp = strchr(string, '\n'); - *temp = '\0'; + if (temp) + *temp = '\0'; return string; } -void read_password(char *buf, size_t len) +static void read_password(char *buf, size_t len, char *host) { char pwd[80]; + int retval; struct termios old; struct termios tp; @@ -671,9 +766,11 @@ void read_password(char *buf, size_t len) tp.c_lflag &= (~ECHO); tcsetattr(0, TCSANOW, &tp); - fprintf(stdout, "Enter twitter password: "); + fprintf(stdout, "Enter password for %s: ", host); fflush(stdout); - scanf("%79s", pwd); + tcflow(0, TCOOFF); + retval = scanf("%79s", pwd); + tcflow(0, TCOON); fprintf(stdout, "\n"); tcsetattr(0, TCSANOW, &old); @@ -963,6 +1060,7 @@ int main(int argc, char *argv[], char *envp[]) { "dry-run", 0, NULL, 'n' }, { "page", 1, NULL, 'g' }, { "version", 0, NULL, 'v' }, + { "config", 1, NULL, 'c' }, { } }; struct session *session; @@ -977,7 +1075,6 @@ int main(int argc, char *argv[], char *envp[]) debug = 0; verbose = 0; - rl_bind_key('\t', rl_insert); session = session_alloc(); if (!session) { @@ -990,8 +1087,13 @@ int main(int argc, char *argv[], char *envp[]) session->time = strdup(ctime(&t)); session->time[strlen(session->time)-1] = 0x00; + /* Get the home directory so we can try to find a config file */ session->homedir = strdup(getenv("HOME")); + /* set up a default config file location (traditionally ~/.bti) */ + session->configfile = zalloc(strlen(session->homedir) + 7); + sprintf(session->configfile, "%s/.bti", session->homedir); + curl_global_init(CURL_GLOBAL_ALL); /* Set environment variables first, before reading command line options @@ -1007,7 +1109,7 @@ int main(int argc, char *argv[], char *envp[]) parse_configfile(session); while (1) { - option = getopt_long_only(argc, argv, "dp:P:H:a:A:u:hg:G:snVv", + option = getopt_long_only(argc, argv, "dp:P:H:a:A:u:c:hg:G:snVv", options, NULL); if (option == -1) break; @@ -1083,21 +1185,38 @@ int main(int argc, char *argv[], char *envp[]) case 'H': if (session->hosturl) free(session->hosturl); + if (session->hostname) + free(session->hostname); if (strcasecmp(optarg, "twitter") == 0) { session->host = HOST_TWITTER; session->hosturl = strdup(twitter_host); + session->hostname = strdup(twitter_name); } else if (strcasecmp(optarg, "identica") == 0) { session->host = HOST_IDENTICA; session->hosturl = strdup(identica_host); + session->hostname = strdup(identica_name); } else { session->host = HOST_CUSTOM; session->hosturl = strdup(optarg); + session->hostname = strdup(optarg); } dbg("host = %d\n", session->host); break; case 'b': session->bash = 1; break; + case 'c': + if (session->configfile) + free(session->configfile); + session->configfile = strdup(optarg); + dbg("configfile = %s\n", session->configfile); + + /* + * read the config file now. Yes, this could override previously + * set options from the command line, but the user asked for it... + */ + parse_configfile(session); + break; case 'h': display_help(); goto exit; @@ -1113,6 +1232,7 @@ int main(int argc, char *argv[], char *envp[]) } } + session_readline_init(session); /* * Show the version to make it easier to determine what * is going on here @@ -1134,24 +1254,24 @@ int main(int argc, char *argv[], char *envp[]) if (session->action == ACTION_GROUP && !session->group) { fprintf(stdout, "Enter group name: "); - session->group = readline(NULL); + session->group = session->readline(NULL); } if (!session->account) { - fprintf(stdout, "Enter twitter account: "); - session->account = readline(NULL); + fprintf(stdout, "Enter account for %s: ", session->hostname); + session->account = session->readline(NULL); } if (!session->password) { - read_password(password, sizeof(password)); + read_password(password, sizeof(password), session->hostname); session->password = strdup(password); } if (session->action == ACTION_UPDATE) { - if (session->bash) + if (session->bash || !session->interactive) tweet = get_string_from_stdin(); else - tweet = readline("tweet: "); + tweet = session->readline("tweet: "); if (!tweet || strlen(tweet) == 0) { dbg("no tweet?\n"); return -1; @@ -1176,6 +1296,7 @@ int main(int argc, char *argv[], char *envp[]) if (session->page == 0) session->page = 1; + dbg("config file = %s\n", session->configfile); dbg("account = %s\n", session->account); dbg("password = %s\n", session->password); dbg("host = %d\n", session->host); @@ -1198,6 +1319,7 @@ int main(int argc, char *argv[], char *envp[]) log_session(session, retval); exit: + session_readline_cleanup(session); session_free(session); return retval;; }