/* * use_ipc.c -- interface to the inter-process communication * * PMF -- Padrone's MudFrontend, a frontend for (maybe mostly LP-)mud * Thomas Padron-McCarthy (Email: padrone@lysator.liu.se), 1990, 1991 * Share and enjoy, but be nice: don't steal my program! Hugo is watching! * This file latest updated: May 23, 1993 * */ #include #include #include #include #include "safe_malloc.h" #include "str_galore.h" #include "pmf.h" #include "config.h" #include "globals.h" #define ELEMENT_POINTER_TYPE struct queue_to_mud_entry * #include "generic_list.h" #include "generic_fifo.h" static generic_fifo queue_to_mud = NULL; static generic_list express_list = NULL; struct queue_to_mud_entry { char *line_to_send, *confirm_string; }; static int waiting_for_response; extern char *get_line_from_mud(), *cryptsay_handle_line(); extern char compile_time[], version[]; /*---------------------------------------------------------------------------*/ init_queue_to_mud() { queue_to_mud = create_generic_fifo(); set_generic_fifo_increment(queue_to_mud, QUEUE_TO_MUD_INCREMENT); express_list = create_generic_list(); set_generic_list_increment(express_list, EXPRESS_LIST_INCREMENT); } /* init_queue_to_mud */ struct queue_to_mud_entry *create_queue_entry(line_to_send, confirm_string) char *line_to_send, *confirm_string; { struct queue_to_mud_entry *this_queue_entry; this_queue_entry = (struct queue_to_mud_entry *) safe_malloc(sizeof(struct queue_to_mud_entry)); this_queue_entry->line_to_send = copy_string(line_to_send); if (confirm_string) this_queue_entry->confirm_string = copy_string(confirm_string); else this_queue_entry->confirm_string = NULL; return this_queue_entry; } /* create_queue_entry */ /* Put a line on the queue of lines to be sent to the game. * confirm_string is the string we will wait for as a confirmation from * the game that it has received the line sent. If no confirm_string is NULL, * we will not wait for confirmation. * An empty string means accept any output from the mud game as confirmation. */ queue_mudline(line_to_send, confirm_string) char *line_to_send, *confirm_string; { struct queue_to_mud_entry *this_queue_entry; ASSERT(line_to_send); USER_DEBUG(("Queueing: \"%s\", \"%s\"\n", line_to_send, confirm_string ? confirm_string : "NULL")); this_queue_entry = create_queue_entry(line_to_send, confirm_string); queue_on_generic_fifo(queue_to_mud, this_queue_entry); } /* queue_mudline */ /* Put a line on the express list, to be put first in the queue later. * If we put the lines first in the queue directly, they would be sent * in reverse order, so we must use an extra list. */ express_queue_mudline(line_to_send, confirm_string) char *line_to_send, *confirm_string; { struct queue_to_mud_entry *this_queue_entry; ASSERT(line_to_send); USER_DEBUG(("To express list: \"%s\", \"%s\"\n", line_to_send, confirm_string ? confirm_string : "NULL")); this_queue_entry = create_queue_entry(line_to_send, confirm_string); add_last_to_generic_list(express_list, this_queue_entry); } /* express_queue_mudline */ /* Take the lines from the express list and put them first in the queue to MUD: */ flush_express_list() { struct queue_to_mud_entry *this_queue_entry; while ((this_queue_entry = remove_last_from_generic_list(express_list)) != NULL) { USER_DEBUG(("Express-queueing from express list: \"%s\", \"%s\"\n", this_queue_entry->line_to_send, this_queue_entry->confirm_string ? this_queue_entry->confirm_string : "NULL")); add_first_to_generic_list(get_list_inside_generic_fifo(queue_to_mud), this_queue_entry); } } /* flush_express_list */ /* This function checks if the (probably just received) line_from_mud * matches the (probably the current, global) confirm_string. * Returns 1 or 0. */ int match_confirm_string(line_from_mud, confirm_string, confirm_string_length) char *line_from_mud, *confirm_string; int confirm_string_length; { /* The words returned by dollar_match are never used. */ char *mudline_words[MAX_WORDS_PER_LINE]; char mudline_words_buffer[2 * MAX_LINE_LENGTH]; INTERNAL_DEBUG(("match_confirm_string('%s', '%s', %d)", line_from_mud, confirm_string, confirm_string_length)); return (!strncmp(confirm_string, line_from_mud, confirm_string_length)) || (dollar_match(confirm_string, line_from_mud, mudline_words, mudline_words_buffer) != -1); } /* match_confirm_string */ /* This function should be called periodically (at least a few times each * second). It takes care of all the communication with the mud process. * Never do any read or write calls or any other operations on the socket * except from this function! Returns 0 if nothing happened. * Also, if nothing happened we sleep for a while. */ int communicate_with_mud() { struct queue_to_mud_entry *to_head; char *line_from_mud, *line_to_mud; /* These variables are used to save the state between calls: */ /* static int waiting_for_response = 0; -- moved outside! */ static char confirm_string[MAX_CONFIRM_STRING_LENGTH + 1]; static int confirm_string_length; int retval, was_gag; char *nl_p; #ifdef GNU_READLINE readline_prompt[0] = readline_prompt_first_char; #endif retval = 0; line_from_mud = get_line_from_mud(); /* It is possible that the "line_from_mud" is just a part of a line. */ if (line_from_mud) { USER_DEBUG(("communicate_with_mud: got a line: \"%s\"", line_from_mud)); if ( waiting_for_response && match_confirm_string(line_from_mud, confirm_string, confirm_string_length)) { USER_DEBUG(("communicate_with_mud: got confirm_string \"%s\" in line \"%s\"", confirm_string, line_from_mud)); waiting_for_response = 0; } /* We use the telnet codes too, but do this kludge too: */ if (!strcmp(line_from_mud, "Password: ")) echo_off(); IPC_DEBUG(("line = \"%s\", strcmp(line, \"Password: \") = %d", line_from_mud, strcmp(line_from_mud, "Password: "))); if (receiving) { if (match_confirm_string(line_from_mud, receive_stop_string, strlen(receive_stop_string))) { USER_DEBUG(("communicate_with_mud: got receive_stop_string \"%s\" in line \"%s\"", receive_stop_string, line_from_mud)); stop_receiving(); } else { fprintf(the_open_file, "%s", line_from_mud); } } /* If doing a "getfile": save the text, newlines NOT stripped! */ if (getfiling) getfile_got_text(line_from_mud); /* Is there a newline character at the end of the line? Remove it! */ nl_p = &line_from_mud[strlen(line_from_mud) - 1]; if (*nl_p == '\n') { *nl_p = '\0'; ++nr_received_lines; } else nl_p = NULL; /* Checking for gagging: newlines stripped! */ was_gag = is_gag_line(line_from_mud) && (can_gag_fight || !fighting_line(line_from_mud)); /* The secret stuff! */ if (cryptkey) { char *temp; temp = cryptsay_handle_line(line_from_mud); /* safe_free(line_from_mud); -- this is done in cryptsay_handle_line! */ line_from_mud = temp; } if ((getfiling || receiving) && !show_receive) { if (nl_p) { /* Lars has no line buffering on the sockets, even when inside ed. */ ldisplay("."); fflush(stdout); } } else if (!was_gag) { rdisplay("%s", line_from_mud); /* The line can contain %'s! */ if (nl_p) rdisplay("\n"); } retval += 1; /* You can remove these three automatic actions, * but try to avoid doing that. * At least keep the version query! /Padrone */ /* Very hard-coded: if PMF_QUERY_VERSION is sent, we reply! */ if (!strcmp(line_from_mud, "PMF_QUERY_VERSION")) { char buf[MAX_LINE_LENGTH + 1]; sprintf(buf, "say pmf version \"%s\" compiled %s\n", version, compile_time); message("Your PMF just answered a PMF_QUERY_VERSION message."); queue_mudline(buf, ""); } /* And another one: if PMF_QUERY_HOST is sent, we reply! */ if (!strcmp(line_from_mud, "PMF_QUERY_HOST")) { char namebuf[MAXHOSTNAMELEN + 1], buf[MAX_LINE_LENGTH + 1]; extern char *gethostname(); if (gethostname(namebuf, MAXHOSTNAMELEN) == 0) sprintf(buf, "say host name \"%s\"\n", namebuf); else sprintf(buf, "say host name unknown\n"); message("Your PMF just answered a PMF_QUERY_HOST message."); queue_mudline(buf, ""); } /* And another one: if PMF_QUERY_TERM is sent, we reply! */ if (!strcmp(line_from_mud, "PMF_QUERY_TERM")) { char *termtype, buf[MAX_LINE_LENGTH + 1]; extern char *getenv(); termtype = getenv("TERM"); if (termtype) sprintf(buf, "say terminal type \"%s\"\n", termtype); else sprintf(buf, "say terminal type unknown\n"); message("Your PMF just answered a PMF_QUERY_TERM message."); queue_mudline(buf, ""); } if (robot_mode && !sending && !receiving && !getfiling && !was_gag) { robot_handle_line(line_from_mud); } #ifdef SOUND if (sound_mode && !sending && !receiving && !getfiling && !was_gag) { sound_handle_line(line_from_mud); } #endif } if (!waiting_for_response) { to_head = (struct queue_to_mud_entry *)get_from_generic_fifo(queue_to_mud); if (to_head) { USER_DEBUG(("Got from queue: \"%s\", \"%s\"\n", to_head->line_to_send, to_head->confirm_string ? to_head->confirm_string : "NULL")); line_to_mud = to_head->line_to_send; if (to_head->confirm_string) { confirm_string_length = strlen(to_head->confirm_string); if (confirm_string_length > MAX_CONFIRM_STRING_LENGTH) error("Ridicolously long confirmation string (%d chars): \"%s\".", confirm_string_length, confirm_string); strcpy(confirm_string, to_head->confirm_string); waiting_for_response = 1; USER_DEBUG(("communicate_with_mud: confirm_string set to \"%s\"", confirm_string)); } safe_free(to_head); if (line_to_mud) { USER_DEBUG(("communicate_with_mud: sending line \"%s\"", line_to_mud)); send_to_mud(line_to_mud); safe_free(line_to_mud); } else { USER_DEBUG(("communicate_with_mud: line was null, not sending")); } retval += 2; } } if (!retval) usleep(100000); /* #ifdef GNU_READLINE else pmf_rl_redisplay_line(1, 0); #endif */ return retval; } /* communicate_with_mud */ int ok_to_send() { return query_generic_fifo_size(queue_to_mud) == 0 && !waiting_for_response; } /* ok_to_send */ int query_queue_to_mud_size() { return query_generic_fifo_size(queue_to_mud); } /* query_queue_to_mud_size */ void flush_queue_to_mud() { struct queue_to_mud_entry *to_head; int i; to_head = (struct queue_to_mud_entry *)get_from_generic_fifo(queue_to_mud); if (to_head) { ldisplay("Throwing away the lines on the queue to MUD:\n"); i = 0; while (to_head) { ++i; ldisplay(" %d:\t%s\n", i, to_head->line_to_send); to_head = (struct queue_to_mud_entry *)get_from_generic_fifo(queue_to_mud); } } waiting_for_response = 0; } /* flush_queue_to_mud */ int query_waiting_for_response() { return waiting_for_response; } /* query_waiting_for_response */