|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T i
Length: 16663 (0x4117) Types: TextFile Names: »io.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987 └─⟦this⟧ »EUUGD18/General/Life/io.c«
#ifdef SCCS static char *sccsid = "@(#)io.c 1.10 2/1/85"; static char *cpyrid = "@(#)Copyright (C) 1985 by D Bell"; #endif #include "life.h" #include <errno.h> #include <sys/ioctl.h> extern int errno; /* error return value */ int tty_char(), tty_term(); /* terminal routines */ int file_char(), file_term(); /* file routines */ int loop_char(), loop_term(); /* loop routines */ int macro_char(), macro_term(); /* macro routines */ int getdpychar(); /* for dpyread to call */ /* * Read the next character from the appropriate input source. * Negative answer from source indicates end of file. * This routine is usually called indirectly by scanchar. */ readchar() { register struct input *ip; /* input structure being used */ register int ch; /* current character */ while (1) { /* continue until not end of file */ ip = curinput; ch = ip->i_getchar(ip); /* read next character */ if (ch >= 0) break; ip->i_term(ip); /* end of file, clean up */ } return(ch); } /* * Determine if the next input character to be read is from the terminal. * Returns nonzero if so. */ ttyisinput() { register struct input *ip; /* input structure being checked */ ip = curinput; while ((ip->i_type == INP_LOOP) && (ip->i_first)) ip--; return(ip->i_type == INP_TTY); } /* * Setup to read commands from the terminal. The lowest level of input * never quits, and is unusual in that it doesn't usually block waiting for * input. Returns nonzero if failed. */ settty() { register struct input *ip; /* current input */ ip = curinput + 1; /* allocate next structure */ if (ip >= &inputs[MAXINPUT]) { return(1); } ip->i_getchar = tty_char; /* set up for I/O */ ip->i_term = tty_term; ip->i_type = INP_TTY; update = 1; curinput = ip; return(0); } /* * Read next character from the terminal if it is ready. If nothing is * going on we will wait for it anyway, to prevent excessive runtimes. * We set the interactive flag to indicate we are talking to user. */ tty_char() { long n; /* char count */ int ch; /* char to return */ char ch_k; interact = 1; if ((dowait == 0) && (redraw || update || (genleft > 0))) { if ((ioctl(STDIN, FIONREAD, &n) == 0) && (n <= 0)) { scaneof(); /* no char available now */ } } do { if (stop) return('\0'); /* stop will be seen later */ errno = 0; n = read(STDIN, &ch_k, 1); /* read one char */ ch = ch_k; } while ((n < 0) && (errno == EINTR)); if (n <= 0) { return(-1); /* error or end of file */ } if (errorstring) { /* disable error message */ errorstring = NULL; update = 1; } return(ch &= 0x7f); } /* * Terminate reading from the terminal. If we are reading from the lowest * level of terminal input, this is a no-op. */ tty_term(ip) register struct input *ip; /* input structure */ { if (ip > inputs) ip--; curinput = ip; update = 1; } /* * Setup to read commands from a given file name. When the file is complete, * some parameters (like the current cursor position) will be restored to their * original value. Returns nonzero if cannot set up to read the file. */ setfile(name) register char *name; /* file name to read from */ { register struct input *ip; /* current input structure */ ip = curinput + 1; /* use next structure */ if (ip >= &inputs[MAXINPUT]) { return(1); } ip->i_file = openlibfile(name, 0); /* open the file */ if (ip->i_file < 0) { return(1); } ip->i_begptr = (char *) malloc(FILESIZE); /* allocate data buffer */ if (ip->i_begptr == NULL) { close(ip->i_file); return(1); } ip->i_endptr = ip->i_begptr; /* set up rest of io */ ip->i_curptr = ip->i_begptr; ip->i_getchar = file_char; ip->i_term = file_term; ip->i_type = INP_FILE; ip->i_obj = curobj; /* save current state */ ip->i_row = crow; ip->i_col = ccol; ip->i_prow = prow; ip->i_pcol = pcol; prow = crow; pcol = ccol; update = 1; curinput = ip; /* this is now current input */ return(0); } /* * Open a life library file, trying various transformed names if necessary. * The order of the transformations which are tried is: * 1. Name exactly as given. * 2. Name with ".l" appended to it. * 3. Name in the user's library directory. * 4. Name with ".l" appended to it in the user's library. * 5 Name in the system's library directory. * 6. Name with ".l" appended to it in the system library. * Returns the file descriptor if the open is successful, or -1 if all the * open attempts failed. */ openlibfile(name, mode) register char *name; /* original file name */ register int mode; /* desired open mode */ { register int fd; /* file descriptor */ char buf[2000]; /* transformed names */ fd = open(name, mode); /* try name straight */ if (fd >= 0) return(fd); sprintf(buf, "%s.l", name); /* try name with .l appended */ fd = open(buf, mode); if (fd >= 0) return(fd); if (*name == '/') return(-1); /* quit now if absolute name */ if (userlib) { /* look in user's lib area */ sprintf(buf, "%s/%s", userlib, name); fd = open(buf, mode); if (fd >= 0) return(fd); strcat(buf, ".l"); /* try with .l */ fd = open(buf, mode); if (fd >= 0) return(fd); } sprintf(buf, "%s/%s", LIFELIB, name); /* look in general lib area */ fd = open(buf, mode); if (fd >= 0) return(fd); strcat(buf, ".l"); /* last try with .l */ return(open(buf, mode)); } /* * Here to read next character from a file. Called by readchar when * input source is a file. */ file_char(ip) register struct input *ip; /* input structure */ { if (ip->i_curptr >= ip->i_endptr) { /* get next chunk of file */ ip->i_curptr = ip->i_begptr; ip->i_endptr = ip->i_begptr; ip->i_endptr += read(ip->i_file, ip->i_begptr, FILESIZE); if (ip->i_endptr <= ip->i_begptr) { return(-1); /* end of file */ } } return(*ip->i_curptr++ & 0x7f); } /* * Here on end of file or error to close the input file and restore some * of the previous state such as the cursor location. */ file_term(ip) register struct input *ip; /* input structure */ { close(ip->i_file); free(ip->i_begptr); curobj = ip->i_obj; crow = ip->i_row; ccol = ip->i_col; prow = ip->i_prow; pcol = ip->i_pcol; update = 1; curinput = (ip - 1); } /* * Setup for execution of a loop. This remembers the initial and final * loop values, and sets up to remember commands as they are given. * This is also used for defining a macro command. The given character * will be assigned the string defined by the loop. * Returns nonzero if failed. */ setloop(begval, endval, ch) { register struct input *ip; /* input structure */ ip = curinput + 1; /* allocate next structure */ if (ip >= &inputs[MAXINPUT]) { return(1); } ip->i_begptr = (char *) malloc(LOOPSIZE); /* allocate buffer */ if (ip->i_begptr == NULL) { return(1); } ip->i_endptr = ip->i_begptr + LOOPSIZE; /* set up for I/O */ ip->i_curptr = ip->i_begptr; ip->i_first = 1; ip->i_getchar = loop_char; ip->i_term = loop_term; ip->i_type = INP_LOOP; ip->i_curval = begval; ip->i_endval = endval; ip->i_macro = ch; update = 1; curinput = ip; return(0); } /* * End the range of the currently defined loop. At this point, all of * the characters of the loop have been read in and saved, and we can * just proceed to iterate over them. The next read will find out that * the first iteration of the loop is over. */ endloop() { register struct input *ip; /* current input */ ip = curinput; if (ip->i_type != INP_LOOP) error("Loop not being defined"); ip->i_endptr = ip->i_curptr - 1; /* end before loop term cmd */ ip->i_first = 0; } /* * Read one character from a loop buffer. If at the end of the buffer, the * pointer is reset so that the buffer is reread. When enough iterations * have been processed, we are done. A special case exists the first time * through the loop at the first nesting level, in that we don't yet have * the characters necessary for the loop, and so we have to read them by * ourself. */ loop_char(ip) register struct input *ip; /* input structure */ { register int ch; if (ip->i_first) { /* collecting input chars */ if (ip->i_curptr >= ip->i_endptr) error("Loop too long"); ch = ip[-1].i_getchar(ip - 1); /* char from previous level */ if (ch < 0) error("End of file in loop"); *ip->i_curptr++ = ch; return(ch); } if (ip->i_curptr >= ip->i_endptr) { /* done with one iteration */ if (ip->i_curval == ip->i_endval) { return(-1); /* end of file */ } ip->i_curval += ((ip->i_curval < ip->i_endval) ? 1 : -1); ip->i_curptr = ip->i_begptr; } return(*ip->i_curptr++); } /* * Terminate reading from a loop buffer. If this was the definition of * a macro character, remember it for later. */ loop_term(ip) struct input *ip; /* input structure */ { register struct macro *mp; /* macro being defined */ mp = ¯os[ip->i_macro - 'a']; if ((mp >= macros) && (mp < ¯os[26])) { if (mp->m_begptr) free(mp->m_begptr); mp->m_begptr = ip->i_begptr; mp->m_endptr = ip->i_endptr; } else free(ip->i_begptr); /* or free buffer */ update = 1; curinput = (ip - 1); } /* Set up to read a defined macro command. Returns nonzero if failed. */ setmacro(arg1, arg2, ch) { register struct input *ip; /* current input */ register struct macro *mp; /* macro command */ mp = ¯os[ch - 'a']; /* verify macro character */ if ((mp < macros) || (mp >= ¯os[26]) || (mp->m_begptr == NULL)) { return(1); } ip = curinput + 1; /* use next input structure */ if (ip >= &inputs[MAXINPUT]) { return(1); } ip->i_getchar = macro_char; /* set up for I/O */ ip->i_term = macro_term; ip->i_begptr = mp->m_begptr; ip->i_curptr = mp->m_begptr; ip->i_endptr = mp->m_endptr; ip->i_type = INP_MACRO; ip->i_macro = ch; ip->i_curval = arg1; ip->i_endval = arg2; update = 1; curinput = ip; return(0); } /* Here to read next character from macro definition. */ macro_char(ip) register struct input *ip; /* input structure */ { if (ip->i_curptr >= ip->i_endptr) { return(-1); /* end of file */ } return(*ip->i_curptr++); } /* Here to terminate reading from a macro definition. */ macro_term(ip) struct input *ip; /* input structure */ { curinput = (ip - 1); update = 1; } /* * Read a line from the user terminated by a newline (which is removed). * Editing of the input line is fully handled. The prompt string and the * input line are only visible if the current input is from the terminal. * Returns a pointer to the null-terminated string. */ char * readstring(prompt) register char *prompt; /* prompt string */ { int i; /* number of characters read */ scanreset(); /* no more scan interference */ if (prompt == NULL) prompt = ""; /* set prompt if given NULL */ if (ttyisinput() == 0) prompt = NULL; /* no window stuff if not tty */ if (prompt) dpywindow(0, 0, 0, -1); /* show input in top line */ dowait = 1; /* must wait for tty chars */ i = dpyread(prompt, getdpychar, stringbuf, FILESIZE); /* read it */ dowait = 0; /* back to normal */ stringbuf[i] = '\0'; /* terminate line */ update = 1; return(stringbuf); } /* * Routine called by dpyread to read the next character of input. * We return end of input on the newline character. */ getdpychar() { register int ch; /* character just read */ ch = readchar(); if (stop || (ch == '\n')) return(-1); return(ch); } /* * Routine called to wait until a space, newline, or escape character * is typed. It is assumed that the screen contains a display which * we can append our message to. */ spacewait() { register int ch; /* read character */ scanreset(); /* throw out stored chars */ dpystr("\nType <space> to return\n"); /* append our message */ dpyclrwindow(); dpyhome(); dpyupdate(); /* show the result */ redraw = 1; dowait = 1; /* must wait for chars */ do { if (stop) break; ch = readchar(); } while ((ch != ' ') && (ch != ESC) && (ch != '\n')); dowait = 0; } /* * Write the current object out to the named file, with the given maximum * sizes for "pretty" output. If the size is exceeded, the output is * compressed. The file as written can be read in as commands which will * regenerate the object. */ writeobject(obj, name, maxrows, maxcols) struct object *obj; /* object to write */ char *name; /* filename to use */ { register FILE *fd; /* file structure */ register struct row *rp; /* current row structure */ register struct cell *cp; /* current cell */ register int row; /* current row value */ register int col; /* current column value */ struct row *trp; /* temporary row structure */ int minrow; /* minimum row of object */ int maxrow; /* maximum row of object */ int mincol; /* minimum column of object */ int maxcol; /* maximum column of object */ int curmin; /* current minimum column */ int curmax; /* current maximum column */ int testmin; /* test minimum column of rows */ int testmax; /* test maximum column of rows */ minmax(obj, &minrow, &maxrow, &mincol, &maxcol); if (minrow > maxrow) error("Null object"); /* nothing to write */ fd = fopen(name, "w"); if (fd == NULL) error("Cannot open output file"); fprintf(fd, "! \"%s\" (cells %d length %d width %d generation %d)\n", obj->o_name, obj->o_count, maxrow - minrow + 1, maxcol - mincol + 1, obj->o_gen); if (obj->o_currow > minrow) fprintf(fd, "%dk", obj->o_currow - minrow); if (obj->o_currow < minrow) fprintf(fd, "%dj", minrow - obj->o_currow); if (obj->o_curcol > mincol) fprintf(fd, "%dh", obj->o_curcol - mincol); if (obj->o_curcol < mincol) fprintf(fd, "%dl", mincol - obj->o_curcol); fprintf(fd, "@!\n"); curmin = INFINITY; curmax = -INFINITY; rp = obj->o_firstrow; row = minrow; for (; rp != termrow; rp = rp->r_next) { /* * See if user wants to stop. */ if (stop) { fclose(fd); return; } /* * Skip down to the next row with something in it. */ if (rp->r_firstcell == termcell) continue; if (rp->r_row > (row + maxrows)) { /* skip to right row */ fprintf(fd, "%d\n", rp->r_row - row); row = rp->r_row; } while (rp->r_row > row) { fputs(".\n", fd); row++; } /* * Output the current row, compressing if it is too wide. */ col = mincol; cp = rp->r_firstcell; if ((cp->c_col + maxcols) < rp->r_lastcell->c_col) { while (cp != termcell) { /* * Write all adjacent blanks. */ if (cp->c_col > col + 2) { fprintf(fd, "%d.", cp->c_col - col); col = cp->c_col; } while (cp->c_col > col) { fputc('.', fd); col++; } /* * Write all adjacent cells. */ while ((cp->c_col + 1) == cp->c_next->c_col) { cp = cp->c_next; } if (cp->c_col >= col + 2) { fprintf(fd, "%dO", cp->c_col - col + 1); col = cp->c_col + 1; } while (col <= cp->c_col) { fputc('O', fd); col++; } cp = cp->c_next; } fputc('\n', fd); row++; curmin = INFINITY; curmax = -INFINITY; continue; } /* * Here if the row doesn't need compressing. See if several * rows from this one on can all fit in the same range. If * so, set things up so they will align themselves nicely. */ if ((cp->c_col < curmin) || (rp->r_lastcell->c_col > curmax)) { curmin = cp->c_col; curmax = rp->r_lastcell->c_col; trp = rp; for (; rp != termrow; rp = rp->r_next) { if (rp->r_firstcell == termcell) continue; testmin = rp->r_firstcell->c_col; if (testmin > curmin) testmin = curmin; testmax = rp->r_lastcell->c_col; if (testmax < curmax) testmax = curmax; if ((testmax - testmin) >= maxcols) break; curmin = testmin; curmax = testmax; } rp = trp; } /* * Type the row, with the initial shift if necessary. */ if (curmin != mincol) { fprintf(fd, "%d.", curmin - mincol); col = curmin; } for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) { while (cp->c_col > col) { fputc('.', fd); col++; } fputc('O', fd); col++; } fputc('\n', fd); row++; } fclose(fd); } /* Write all defined macros to a file so they can be read back in later. */ writemacros(name) char *name; /* file name to write to */ { register int f; /* file descriptor */ register struct macro *mp; /* current macro */ int ch; /* character */ f = creat(name, 0666); if (f < 0) error("Cannot create file"); for (mp = macros; mp < ¯os[26]; mp++) { if ((mp->m_begptr == NULL) || (mp->m_begptr >= mp->m_endptr)) { continue; } ch = 'a' + (mp - macros); write(f, "<", 1); write(f, &ch, 1); write(f, mp->m_begptr, mp->m_endptr - mp->m_begptr); write(f, ">!\n", 3); } close(f); }