diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6b83182 --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +CC=cc +CFLAGS=-c -Wall -Wextra -fPIC +LDFLAGS=-lreadline +LD=cc + +SRC=main.c loop.c interface.c +OBJ=$(SRC:.c=.o) +EXE=telegram + +all: $(SRC) $(EXE) + +$(EXE): $(OBJ) + $(LD) $(LDFLAGS) $(OBJ) -o $@ + +.c.o: + $(CC) $(CFLAGS) $< -o $@ + diff --git a/include.h b/include.h new file mode 100644 index 0000000..03f271d --- /dev/null +++ b/include.h @@ -0,0 +1,6 @@ +#ifndef __INCLUDE_H__ +#define __INCLUDE_H__ + +#define UU __attribute__ ((unused)) + +#endif diff --git a/interface.c b/interface.c new file mode 100644 index 0000000..61f6931 --- /dev/null +++ b/interface.c @@ -0,0 +1,156 @@ +#define _GNU_SOURCE +#include +#include + +#include +#include + +#include +#include +#include "include.h" +char *default_prompt = ">"; + +char *get_default_prompt (void) { + return default_prompt; +} + +char *complete_none (const char *text UU, int state UU) { + return 0; +} + +char *commands[] = { + "help", + "msg", + 0 }; + +int commands_flags[] = { + 070, + 072, +}; + +char *a = 0; +char **user_list = &a; +char **chat_list = &a; + +int init_token (char **q) { + char *r = *q; + while (*r == ' ') { r ++; } + if (!*r) { return 0; } + q = &r; + return 1; +} + +char *get_token (char **q, int *l) { + char *r = *q; + while (*r == ' ') { r ++; } + if (!*r) { + q = &r; + *l = 0; + return 0; + } + int neg = 0; + char *s = r; + while (*r && (*r != ' ' || neg)) { + if (*r == '\\') { + neg = 1 - neg; + } else { + neg = 0; + } + } + q = &r; + *l = r - s; + return s; +} + + +int get_complete_mode (void) { + char *q = rl_line_buffer; + if (!init_token (&q)) { return 0; } + int l = 0; + char *r = get_token (&q, &l); + if (!*q) { return 0; } + + char **command = commands; + int n = 0; + int flags = -1; + while (*command) { + if ((int)strlen (*command) == l && !memcmp (r, *command, l)) { + flags = commands_flags[n]; + break; + } + n ++; + command ++; + } + if (flags == -1) { + return -1; + } + int s = 0; + while (1) { + get_token (&q, &l); + if (!*q) { return flags & 7; } + s ++; + if (s <= 4) { flags >>= 3; } + } +} + +int complete_string_list (char **list, int index, const char *text, int len, char **R) { + index ++; + int cc = 0; + while (cc <= 1 && list[index] && strncmp (list[index], text, len)) { + index ++; + if (!list[index]) { index = 0; cc ++; } + } + if (list[index] && cc <= 1) { + *R = strdup (list[index]); + return index; + } else { + *R = 0; + return -1; + } +} +char *command_generator (const char *text, int state) { + static int len, index, mode; + + if (!state) { + len = strlen (text); + index = -1; + + rl_line_buffer[rl_point] = '\0'; /* the effect should be such + * that the cursor position + * is at the end of line for + * the auto completion regex + * above (note the $ at end) + */ + + mode = get_complete_mode (); + } else { + if (index == -1) { return 0; } + } + + if (mode == -1) { return 0; } + + char *R = 0; + switch (mode & 7) { + case 0: + index = complete_string_list (commands, index, text, len, &R); + return R; + case 1: + index = complete_string_list (user_list, index, text, len, &R); + return R; + case 2: + index = complete_string_list (chat_list, index, text, len, &R); + return R; + case 3: + return rl_filename_completion_function(text,state); + default: + return 0; + } +} + +char **complete_text (char *text, int start UU, int end UU) { + return (char **) rl_completion_matches (text, command_generator); +} + +void interpreter (char *line UU) { + assert (0); +} diff --git a/interface.h b/interface.h new file mode 100644 index 0000000..763dde8 --- /dev/null +++ b/interface.h @@ -0,0 +1,7 @@ +#ifndef __INTERFACE_H__ +#define __INTERFACE_H__ +char *get_default_prompt (void); +char *complete_none (const char *text, int state); +char **complete_text (char *text, int start, int end); +void interpreter (char *line); +#endif diff --git a/loop.c b/loop.c new file mode 100644 index 0000000..49be9ab --- /dev/null +++ b/loop.c @@ -0,0 +1,44 @@ +#define READLINE_CALLBACKS + +#include +#include + +#include +#include +#include +#include + +#include "interface.h" +extern char *default_username; +extern char *auth_token; +void set_default_username (const char *s); + + + +int main_loop (void) { + assert (0); +} + +int loop (void) { + size_t size = 0; + char *user = default_username; + + if (!user && !auth_token) { + printf ("Telephone number (with '+' sign): "); + if (getline (&user, &size, stdin) == -1) { + perror ("getline()"); + exit (EXIT_FAILURE); + } + user[strlen (user) - 1] = '\0'; + set_default_username (user); + } + + fflush (stdin); + + rl_callback_handler_install (get_default_prompt (), interpreter); + rl_attempted_completion_function = (CPPFunction *) complete_text; + rl_completion_entry_function = complete_none; + + return main_loop (); +} + diff --git a/loop.h b/loop.h new file mode 100644 index 0000000..d70a3e6 --- /dev/null +++ b/loop.h @@ -0,0 +1,4 @@ +#ifndef __LOOP_H__ +#define __LOOP_H__ +int loop (void); +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..cd3b7af --- /dev/null +++ b/main.c @@ -0,0 +1,198 @@ +/* + main.c: main initialization file + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "loop.h" + +#define PROGNAME "telegram-client" +#define VERSION "0.01" + +#define CONFIG_DIRECTORY ".telegram/" +#define CONFIG_FILE CONFIG_DIRECTORY "config" +#define DOWNLOADS_DIRECTORY "downloads/" + +#define CONFIG_DIRECTORY_MODE 0700 + +#define DEFAULT_CONFIG_CONTENTS \ + "# This is an empty config file\n" \ + "# Feel free to put something here\n" + +char *default_username; +int setup_mode; +char *auth_token; + +void set_default_username (const char *s) { + if (default_username) { + free (default_username); + } + default_username = strdup (s); +} + +void set_setup_mode (void) { + setup_mode = 1; +} + + +/* {{{ TERMINAL */ +tcflag_t old_lflag; +cc_t old_vtime; +struct termios term; + +void get_terminal_attributes (void) { + if (tcgetattr (STDIN_FILENO, &term) < 0) { + perror ("tcgetattr()"); + exit (EXIT_FAILURE); + } + old_lflag = term.c_lflag; + old_vtime = term.c_cc[VTIME]; +} +/* }}} */ + +char *get_home_directory (void) { + struct passwd *current_passwd; + uid_t user_id; + setpwent (); + user_id = getuid (); + while ((current_passwd = getpwent ())) { + if (current_passwd->pw_uid == user_id) { + return current_passwd->pw_dir; + } + } + return 0; +} + +char *get_config_directory (void) { + char *config_directory; + int length = strlen (get_home_directory ()) + strlen (CONFIG_DIRECTORY) + 2; + + config_directory = (char *) calloc (length, sizeof (char)); + sprintf (config_directory, "%s/" CONFIG_DIRECTORY, + get_home_directory ()); + + return config_directory; +} + +char *get_config_filename (void) { + char *config_filename; + int length = strlen (get_home_directory ()) + strlen (CONFIG_FILE) + 2; + + config_filename = (char *) calloc (length, sizeof (char)); + sprintf (config_filename, "%s/" CONFIG_FILE, get_home_directory ()); + return config_filename; +} + +char *get_downloads_directory (void) +{ + char *downloads_directory; + int length = strlen (get_config_directory ()) + strlen (DOWNLOADS_DIRECTORY) + 2; + + downloads_directory = (char *) calloc (length, sizeof (char)); + sprintf (downloads_directory, "%s/" DOWNLOADS_DIRECTORY, get_config_directory ()); + + return downloads_directory; +} + +void running_for_first_time (void) { + struct stat *config_file_stat = NULL; + int config_file_fd; + char *config_directory = get_config_directory (); + char *config_filename = get_config_filename (); + char *downloads_directory = get_downloads_directory (); + + if (mkdir (config_directory, CONFIG_DIRECTORY_MODE) != 0) { + return; + } else { + printf ("\nRunning " PROGNAME " for first time!!\n"); + printf ("[%s] created\n", config_directory); + } + + // see if config file is there + if (stat (config_filename, config_file_stat) != 0) { + // config file missing, so touch it + config_file_fd = open (config_filename, O_CREAT | O_RDWR, S_IRWXU); + if (config_file_fd == -1) { + perror ("open[config_file]"); + exit (EXIT_FAILURE); + } + if (fchmod (config_file_fd, CONFIG_DIRECTORY_MODE) != 0) { + perror ("fchmod[" CONFIG_FILE "]"); + exit (EXIT_FAILURE); + } + if (write (config_file_fd, DEFAULT_CONFIG_CONTENTS, strlen (DEFAULT_CONFIG_CONTENTS)) <= 0) { + perror ("write[config_file]"); + exit (EXIT_FAILURE); + } + close (config_file_fd); + printf ("[%s] created\n", config_filename); + + /* create downloads directory */ + if (mkdir (downloads_directory, 0755) !=0) { + perror ("creating download directory"); + exit (EXIT_FAILURE); + } + } + + set_setup_mode (); +} + +void inner_main (void) { + loop (); +} + +void usage (void) { + printf ("%s [-u username]\n", PROGNAME); + exit (1); +} + +void args_parse (int argc, char **argv) { + int opt = 0; + while ((opt = getopt (argc, argv, "u:h")) != -1) { + switch (opt) { + case 'u': + set_default_username (optarg); + break; + case 'h': + default: + usage (); + break; + } + } +} + +int main (int argc, char **argv) { + running_for_first_time (); + + get_terminal_attributes (); + + args_parse (argc, argv); + + inner_main (); + + return 0; +} diff --git a/telegram.h b/telegram.h new file mode 100644 index 0000000..e69de29