/****************************************************************************** * MODULE : tm_axiom.c * DESCRIPTION: Glue between TeXmacs and Axiom * COPYRIGHT : (C) 1999 Andrey Grozin ******************************************************************************* * This software falls under the GNU general public license and comes WITHOUT * ANY WARRANTY WHATSOEVER. See the file $TEXMACS_PATH/LICENSE for more details. * If you don't have this file, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ******************************************************************************/ // Heavly modified by Enrique Perez-Terron 2009 // Task: filter data in two directions, stdin/stdout talks to TeXmacs, // axin/axout talks to Axiom. We are not playing with "select()" like telnet, // so we must take care to not read from Axiom when Axiom is waiting for // input. That is, we must detect when the input from Axiom contains a prompt. // // Otherwise we also do some transformations. // Comment out to disable logging #define LOGFILE "/tmp/tm_axiom.log" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> /* According to POSIX.1-2001 we need this for select() */ #include <sys/select.h> /* According to earlier standards */ #include <sys/time.h> #include <sys/types.h> #include <unistd.h> // Codes for type of data seen from Axiom. LONG means buffer full. #define NORMAL 0 // Complete data are being returned, not of more specific type #define LONG 1 // Expect more data. Neither prompt nor newline seen. (Buffer full.) #define END 2 // EOF, no data being returned. #define PROMPT 3 // Propmt seen #define MATH 4 // Normal data with Math string recognized #define TYPE 5 // Normal data with Type string recognized #define DASHES 6 // Normal data with dashes property recognized #ifdef LOGFILE char *Codename[] = { "NORMAL: ", "LONG : ", "END : ", "PROMPT: ", "MATH : ", "TYPE : ", "DASHES: " }; #endif // Put data in "buf" while processing. "len" holds strlen(buf). // Mathmode data from Axiom is copied to "mathbuf", for some conversions. #define LEN 256 #define MATHBUFSIZE 40960 char buf[LEN]; char mathbuf[MATHBUFSIZE]; int len; // Some strings we need to recognize in the input char Prompt[] = "-> "; char Math[] = "$$\n"; char Type[] = "Type: "; // Axin, like stdin, is where this program reads. *out is for writing. FILE *Axin, *Axout; #ifdef LOGFILE FILE *Log; #endif // tex_to_latex() - convert "\\root{blah} \\of {" to "\\sqrt[blah]{" // Auxiliary: return pointer to first char after balanced {} pairs. char *balanced(char *ptr) { int level = 0, c; while (c = *ptr++) { if (c == '{') { level++; continue; } if (c == '}') { level--; if (!level) return ptr; } } return NULL; } // convert; return number of chars buf is shortened. int tex_to_latex(char *buf) { char *A = buf, *B = buf, *C, *D; int shortened = 0; // Remove all newlines from buffer. Memmove, not strcpy, because of overlap. while (A = strchr(A, '\n')) { memmove(A, A+1, strlen(A+1)+1); shortened ++; } // Convert all "\\root {blah} \\of {" to "\\sqrt[blah]{" while ((A = strstr(B, "\\root {")) && (C = balanced(A+6)) // A+6 includes the '{' && (strncmp(--C, "} \\of {", 7) == 0)) { // --C includes the '}' B = A + 7; // just after "\\root {", this is the earliest point where // another "\root {" could be, and where the next turn in the loop will seach D = C + 6; // Includes the '{' after " \\to ". strncpy(A, "\\sqrt[", 6); A += 6; memmove(A, B, C-B); A += C-B; *A++ = ']'; memmove(A, D, strlen(D) + 1); shortened += 6; } return shortened; } // Get a chunk of data from TeXmacs. // We assume TeXmacs only sends '\n'-terminated data, one line at a time. // Return the terminating condition, LONG == buffer full, END == end of file. int fromTeXmacs(void) { static eof_seen = 0; char *p = buf; char *e = buf + sizeof buf; int code = LONG; if (eof_seen) code = END; else { while (p < e) { int c = getchar(); if (c == EOF) { // Report actual data seen before reporting END. code = (p==buf ? END : NORMAL); eof_seen = 1; break; } *p++ = c; if (c == '\n') {code = NORMAL; break;} } } *p = '\0'; len = p - buf; #ifdef LOGFILE fputs("From TeXmacs: ", Log); fputs(buf, Log); // Always terminate the log output with a newline. if (code == END) fputs("--end of TeXmacs data--\n", Log); else if (len == 0 || buf[len-1] != '\n') fputs("\\--unfinished--\\\n", Log); fflush(Log); #endif return code; } // Read a chunk of data from Axiom. Return code for ending condition. // If a prompt is seen, it is copied to the buffer, just like other input. // Only \r is suppressed. // Return kind of terminating condition, or result of tests ordered through flags. // It might seem like an error in this program that if we are looking for a string // like the prompt or the Math, if the string appears stradling a buffer limit, we do not // detect it. However, we only expect these strings in the initial part of any line. int fromAxiom(int flags, int prompt_limit) { static int eof_seen = 0; char *p = buf; char *e = buf + sizeof buf; int prompt_chars_seen = 0; int code = LONG; if (eof_seen) code = END; else { while (p < e) { int c = getc(Axin); if (c == EOF) { // Report actual data seen before reporting END. code = (p==buf ? END : NORMAL); eof_seen = 1; break; } if (c == '\r') continue; *p++ = c; if (c == '\n') {code = NORMAL; break;} if (flags & (1<<PROMPT)) { if (c == Prompt[prompt_chars_seen]) { prompt_chars_seen++; if (prompt_chars_seen == sizeof(Prompt) - 1) { code = PROMPT; break; } continue; } prompt_chars_seen = 0; if (p >= buf + prompt_limit) flags &= ~(1<<PROMPT); } } } *p = '\0'; len = p - buf; // Detect particular types of lines if (code == NORMAL) { if (flags & (1 << MATH)) if (strcmp(buf, Math) == 0) code = MATH; if (flags & (1 << DASHES)) if (len == 78 && strspn(buf, "-") == 77) code = DASHES; if (flags & (1 << TYPE)) if (len == 78) { p = buf; while(*p == ' ') p++; if (strncmp(p, Type, sizeof(Type)-1) == 0) code = TYPE; } } #ifdef LOGFILE fputs(Codename[code], Log); fputs(buf, Log); // Always supply a newline to the log if (code == END) fputs("--end of Axiom data\n", Log); else if (code == PROMPT) fputs("\n", Log); else if (len <= 0 || buf[len-1] != '\n') fputs("\\--unfinished--\\\n", Log); fflush(Log); #endif return code; } #ifdef LOGFILE void log_line(char *prefix, char *msg) { fputs(prefix, Log); fputs(msg, Log); if (msg[strlen(msg) - 1] != '\n') fputs("\\--no-newline\n", Log); fflush(Log); } #endif typedef enum {Noflush = 0, Flush} Flush_t; void toAxiom(Flush_t flush, char *msg) { fputs(msg, Axout); if (flush) fflush(Axout); #ifdef LOGFILE log_line("To AXIOM: ", msg); #endif } void toTeXmacs(Flush_t flush, char *msg) { fputs(msg, stdout); if (flush) fflush(stdout); #ifdef LOGFILE log_line("To TeXmacs: ", msg); #endif } int send_and_get_prompt(char *msg) { int code, silent = 1; // Send toAxiom(Flush, msg); // Get response. do { code = fromAxiom(1 << PROMPT, 0); // Expect just a prompt, but if anything goes wrong... if (code != PROMPT) { if (silent) { silent = 0; int msglen = strcspn(msg, "\n"); printf("\2latex:\\red Command \"%.*s\" produced response: \n", msglen, msg); #ifdef LOGFILE fprintf(Log, "RETURNED: \2latex:\\red Command \"%.*s\" produced response: \n", msglen, msg); #endif } toTeXmacs(Noflush, buf); } } while (code != END && code != PROMPT); if (! silent) toTeXmacs(Flush, "\\black\5\5"); return code; } // Check if Haystack contains Needle, or if a final substring of Haystack is an initial // substring of Needle. The point is that haystack is only the initial bufferfull of // a longer haystack, and we want to detect needle if it straddles the buffer boudary. char *detect_possible(char *needle, char * haystack, char* end_of_haystack) { char *p = strstr(haystack, needle); if (p) return p; char *e = end_of_haystack; for (p = e - strlen(needle) + 1; p < e; p++) if (strncmp(needle, p, e - p) == 0) return p; return NULL; } void session(void) { int code; #ifdef LOGFILE Log = fopen(LOGFILE, "w"); if (! Log) { fprintf(stderr, "Could not open log file \"" LOGFILE "\"\n"); fprintf(stderr, "Logging to stderr instead.\n"); Log = stderr; } #endif // Get the banner. Suppress output after seeing second line of dashes int dashlines = 0; code = fromAxiom((1<<PROMPT)|(1 << DASHES), 10); while (code != END && code != PROMPT) { if (code != PROMPT && dashlines < 2) toTeXmacs(Noflush, buf); if (code == DASHES) dashlines++; code = fromAxiom((1<<PROMPT)|(1 << DASHES), 10); } fflush(stdout); if (code == END) goto finish; // Currently Axiom outputs the initial prompt twice. We have to read it to // get commands and responses in synch. I will bite the bullet, and use select(). fd_set readfds, writefds, exceptfds; struct timeval timeout; FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds); FD_SET(fileno(Axin), &readfds); timeout.tv_sec = 1; // timeout one second. timeout.tv_usec = 0; int readable = select(fileno(Axin) + 1, &readfds, &writefds, &exceptfds, &timeout); if (readable) { do { code = fromAxiom(1<<PROMPT, 10); } while (code != END && code != PROMPT); if (code == END) goto finish; } // Send initial commands if (send_and_get_prompt(")set messages prompt plain\n") == END) goto finish; if (send_and_get_prompt(")set messages autoload off\n") == END) goto finish; if (send_and_get_prompt(")set quit unprotected\n") == END) goto finish; if (send_and_get_prompt(")set output tex on\n") == END) goto finish; if (send_and_get_prompt(")set output algebra off\n") == END) goto finish; // Main prompt-read-write loop do { // prompt TeXmacs toTeXmacs(Flush, "\2channel:prompt\5\2latex:\\red$\\rightarrow$\\ \5\5"); toTeXmacs(Noflush, "\2verbatim:"); // Get the user input, send to axiom. Read until newline. do { code = fromTeXmacs(); toAxiom(Noflush, buf); } while (code == LONG); if (len > 0 && buf[len-1] != '\n') toAxiom(Noflush, "\n"); fflush(Axout); // Get Axiom's response int flags = (1 << PROMPT) | (1 << MATH) | (1 << TYPE); int mathmode = 0; char *mathptr = mathbuf; while ((code = fromAxiom(flags, 0)) != END && code != PROMPT) { if (code == MATH) { // MATH lines come in pairs, wrapping math mode lines. mathmode = ! mathmode; if (mathmode) { // Don't look for TYPE lines until mathmode is off again. flags &= ~(1 << TYPE); // Alert TeXmacs that now comes mathmode data toTeXmacs(Noflush, "\2latex:$\\displaystyle\n"); } else { // When turning off math mode, send contents of mathbuf to TeXmacs. flags |= 1 << TYPE; *mathptr = '\0'; tex_to_latex(mathbuf); toTeXmacs(Noflush, mathbuf); mathptr = mathbuf; // Alert TeXmacs that math mode is ending toTeXmacs(Noflush, "$\5\n"); } // Don't send MATH lines to TeXmacs continue; } if (! mathmode) { if (code == TYPE) { char *p = strstr(buf, Type) + strlen(Type); toTeXmacs(Noflush, "\2latex:\\axiomtype{"); toTeXmacs(Noflush, p); toTeXmacs(Noflush, "}\5"); } else toTeXmacs(Noflush, buf); continue; } // During mathmode, collect all output in mathbuf, with current position mathptr. if (mathptr + len + 1 >= mathbuf + sizeof mathbuf) { // Data does not fit in mathbuf. Ship some of the data to TeXmacs, to make room. // Avoid splitting data in the middle of structres that need conversion // by tex_to_latex() - best effort. *mathptr = '\0'; mathptr -= tex_to_latex(mathbuf); // idempotent - fortunately char *p = detect_possible("\\root {", mathbuf, mathptr); if (p && (mathptr - p) + len < sizeof mathbuf) { // Ship portion before "\\root {" *p = '\0'; toTeXmacs(Noflush, mathbuf); *p = '\\'; int plen = strlen(p); memmove(mathbuf, p, plen); mathptr = mathbuf + plen; } else { // Ship all of it and hope the best toTeXmacs(Noflush, mathbuf); mathptr = mathbuf; } } memcpy(mathptr, buf, len); mathptr += len; } fflush(stdout); } while (code != END); finish: toTeXmacs(Flush, "\2latex:\\red The end\\black\5\5"); } void fatal(char *mess) { fprintf(stdout,"\2latex:\\red Cannot %s\\black\5\5",mess); #ifdef LOGFILE fprintf(Log,"To TeXmacs: \2latex:\\red Cannot %s\\black\5\5\\--no-newline\n",mess); #endif exit(1); } int main() { int p1[2],p2[2]; if (pipe(p1)<0) fatal("create pipe"); if (pipe(p2)<0) fatal("create pipe"); switch (fork()) { case -1: fatal("fork"); case 0: /* Axiom */ dup2(p1[1],1); close(p1[1]); close(p1[0]); dup2(p2[0],0); close(p2[0]); close(p2[1]); execlp("axiom","axiom","-noclef",0); fatal("exec axiom"); default: /* parent */ close(p1[1]); close(p2[0]); Axout=fdopen(p2[1],"w"); Axin=fdopen(p1[0],"r"); session(); } return 0; }
Archive powered by MHonArc 2.6.19.