From: wolff@inf.fu-berlin.de (Thomas Wolff)
Subject: Editor mined (2/4)
Organization: Free University of Berlin, Germany
Date: Tue, 28 Jul 1992 15:03:16 GMT

#! /bin/sh
: This is a sharchive -- extract the files by running through sh

echo ---------------------- extracting mined1.c -----------------
sed 's/^,//' << \EOSED > mined1.c
,/*  ==================================================================	*
, *				Editor mined				*
, *				Part 1					*
, *			for documentation see mined.doc			*
, *  ==================================================================	*/
,
,#include "mined.h"
,
,/* #define DEBUG */
,
,/*  ==================================================================	*
, *			Definitions specific for mined1.c		*
, *  ==================================================================	*/
,
,#ifdef unix
,#define helpcommand "man mined"
,#define printcommand "lpr %s"
,#endif
,#ifdef vms
,#define helpcommand "help mined"
,#define printcommand "print %s"
,#endif
,#ifdef msdos
,#define helpcommand "more < \\mined\\mined.hlp"
,/* #define helpcommand "list \\Dienste\\mined\\mined.hlp" */
,#define printcommand "copy %s prn:"
,#endif
,
,/*  ==================================================================	*
, *				Data section				*
, *  ==================================================================	*/
,
,extern int (* key_map [256]) ();
,extern int (* pc_key_map [256]) ();
,
,LINE * header;			/* Head of line list */
,LINE * tail;			/* Last line in line list */
,LINE * cur_line;		/* Current line in use */
,LINE * top_line;		/* First line of screen */
,LINE * bot_line;		/* Last line of screen */
,char * cur_text;		/* Current char on current line in use */
,int last_y;			/* Last y of screen. Usually SCREENMAX */
,int x = 0, y = 0;		/* x, y coordinates on screen */
,
,short YMAX, XMAX;
,char screen [screen_BUFL];	/* I/O buffer for "writes" and "reads" */
,int nlines;			/* Number of lines in file */
,FLAG modified = FALSE;		/* Set when file is modified */
,FLAG viewonly = FALSE;		/* Set when view only mode is selected */
,FLAG overwriteOK = FALSE;	/* Set if current file is OK for overwrite */
,FLAG writable;			/* Set if file cannot be written */
,FLAG loading = TRUE;		/* Loading a file? Init TRUE for error handling */
,FLAG quit = FALSE;		/* Set when quit character is typed */
,FLAG intr = FALSE;		/* Set when intr character is typed */
,FLAG winchg = FALSE;		/* Set when window size has changed */
,FLAG isscreenmode = FALSE;	/* Set when screen mode is on */
,FLAG stat_visible;		/* Set if status_line is visible */
,FLAG pagewrapped = FALSE;	/* Did output on the bottom line wrap and scroll? */
,FLAG waitingforinput = FALSE;	/* Set while waiting for the next command key */
,FLAG rpipe = FALSE;		/* Set if file should be read from stdin */
,FLAG wpipe = FALSE;		/* Set if file should be written to stdout */
,FLAG multiexit = FALSE;		/* Should exit command go to next file? */
,long chars_saved;		/* Nr of chars in buffer */
,int input_fd = STD_IN;		/* File descriptors for terminal dialog */
,int output_fd = STD_ERR;
,int out_count = 0;		/* Index in output buffer */
,char file_name [maxLINE_LEN];	/* Name of file in use */
,char text_buffer [MAX_CHARS];	/* for get_line, modifications, build_string */
,char blank_line [maxLINE_LEN];	/* Line filled with spaces, in case terminal cannot clear line */
,int hop_flag = 0;		/* Counter for the HOP function */
,#ifdef msdos
,char SHIFT_MARK = 175;		/* Char indicating that line continues */
,#else
,char SHIFT_MARK = '';		/* Char indicating that line continues */
,#endif
,char SHIFT_BEG = '\0';		/* Char indicating that line continues left */
,char TABchar = ' ';		/* Char to be shown in place of tab chars */
,char RET_MARK = '\0';		/* Char indicating end of line */
,char RET_BLANK = '\0';		/* Char to fill the end of line with */
,char RET_BLANK2 = '\0';		/* Char to fill last position of line with */
,#ifdef vms
,int fprot = 0;			/* To be used for all file creatings */
,#else
,int fprot = 0644;		/* To be used for all file creatings */
,#endif
,int panic_level = 0;		/* To adjust error handling to situation */
,int fnami;			/* Parameter index of current file name */
,int fnami_min, fnami_max, fnami_cnt;
,char * (* fnamv) [];
,/*
, * Yank variables.
, */
,FLAG yank_status = NOT_VALID;		/* Status of yank_file */
,char * temp_dir;
,char yank_file [maxLINE_LEN];
,char yankie_file [maxLINE_LEN];
,char panic_file [maxLINE_LEN];
,
,/*  ==================================================================	*
, *				Output					*
, *  ==================================================================	*/
,
,#ifdef msdos
,#define iscontrol(c)	(((c) == '\177') || ((u_char) c < (u_char) ' '))
,#else
,#define iscontrol(c)	(((c) == '\177') || ((u_char) ((c) & '\177') < (u_char) ' '))
,#endif
,#define controlchar(c)	(((c) == '\177') ? '?' : (c) + '@')
,
,/*
, * Flush the I/O buffer on filedescriptor fd.
,flush () is flush_buffer (output_fd)
, */
,flush_buffer (fd)
,  int fd;
,{
,  if (out_count <= 0)		/* There is nothing to flush */
,	return FINE;
,#ifdef conio
,  if (fd == output_fd) {
,	cputs (screen);
,  }
,  else
,#endif
,  if (write (fd, screen, out_count) != out_count) {
,	bad_write (fd);
,	return ERRORS;
,  }
,  clear_buffer (); /* Empty buffer: out_count = 0; */
,  return FINE;
,}
,
,/*
, * Bad_write () is called when a write failed. Notify the user.
, */
,bad_write (fd)
,  int fd;
,{
,  if (fd == output_fd) {	/* Cannot write to terminal? */
,	raw_mode (OFF);
,	panicio ("Write error on terminal", NIL_PTR);
,  }
,
,  clear_buffer (); /* out_count = 0; */
,  ring_bell ();
,  error ("Write aborted (File incomplete): ", serror ());
,}
,
,/*
, * Write_char does a buffered output.
,putchar (c) is writechar (output_fd, (c))
, */
,writechar (fd, c)
,  int fd;
,  char c;
,{
,  if (c == '\n') if (fd == output_fd) {
,	writechar (fd, '\015');
,  }
,  screen [out_count ++] = c;
,  if (out_count == screen_BUFL)	/* Flush on screen_BUFL chars */
,	return flush_buffer (fd);
,#ifdef DEBUG
,  if (fd == output_fd) flush ();
,#endif
,  return FINE;
,}
,
,/*
, * Writestring writes the given string on the given filedescriptor.
, * (buffered via writechar via screen !)
,putstring (str) is writestring (output_fd, (str))
, */
,writestring (fd, text)
,  register int fd;
,  register char * text;
,{
,  while (* text)
,	 if (writechar (fd, * text ++) == ERRORS)
,		return ERRORS;
,  return FINE;
,}
,
,/*
, * Print string on terminal, printing controls with ^.
, */
,int lpos = 0;
,print_char (c, limit)
,  register u_char c;
,  register int limit;
,{
,  if (lpos == limit) {putchar (SHIFT_MARK); return ERRORS;}
,  lpos ++;
,  if (iscontrol (c)) {
,	if (putchar ('^') == ERRORS) return ERRORS;
,	if (lpos == limit) {putchar (SHIFT_MARK); return ERRORS;}
,	lpos ++;
,	return putchar (controlchar (c));
,  }
,  else return putchar (c);
,}
,print_string (text, limit)
,  register char * text;
,  register int limit;
,{
,  lpos = 0;
,  while (* text != '\0')
,	 if (print_char (* text ++, limit) == ERRORS)
,		return ERRORS;
,  return FINE;
,}
,
,/*  ==================================================================	*
, *			Buffer oriented output				*
, *  ==================================================================	*/
,/*
, * Convert cnt to nearest tab position
, */
,#define tab(cnt)		(((cnt) + 8) & ~07)
,#define is_tab(c)		((c) == '\t')
,
,/*
, * Put_line prints the given line on the standard output.
, * If offset is not zero, printing will start at that x-coordinate.
, * If the FLAG clear_line is TRUE, then the screen line will be cleared
, * when the end of the line has been reached.
,line_print (line) is put_line (line, 0, TRUE)
, * put_line is directly called only by S () and delete_text ()
, */
,put_line (line, offset, clear_line)
,  LINE * line;		/* Line to print */
,  int offset;		/* Offset to start */
,  FLAG clear_line;	/* Clear to eoln if TRUE */
,{
,  register char * textp = line->text;
,  register int count = get_shift (line->shift_count) * - SHIFT_SIZE;
,  int count_ini = count;
,  int tab_count;			/* Used in tab expansion */
,
,/* Skip all chars as indicated by the offset and the shift_count field */
,  while (count < offset) {
,	if (is_tab (* textp ++))
,		count = tab (count);
,	else
,		count ++;
,  }
,
,  if (count == 0 && count_ini < 0 && SHIFT_BEG != '\0') {
,	putchar (SHIFT_BEG);
,	count ++;
,	if (! is_tab (* textp)) textp ++;
,  }
,
,  while (* textp != '\n' && count < XBREAK) {
,	if (is_tab (* textp)) {		/* Expand tabs to spaces */
,		tab_count = tab (count);
,		while (count < XBREAK && count < tab_count) {
,			count ++;
,			putchar (TABchar);
,		}
,		textp ++;
,	}
,	else {
,		if (iscontrol (* textp)) {
,			reverse_on ();
,			putchar (controlchar (* textp));
,			reverse_off ();
,			textp ++;
,		}
,		else
,			putchar (* textp ++);
,	count ++;
,	}
,  }
,
,/* If line is longer than XBREAK chars, print the shift_mark */
,  if (count == XBREAK && * textp != '\n')
,	putchar (SHIFT_MARK);
,
,/* Mark end of line if so desired */
,  if (* textp == '\n' && RET_MARK != '\0') {
,	putchar (RET_MARK);
,	count ++;
,	if (RET_BLANK) {
,		while (count < XBREAK) {
,			putchar (RET_BLANK);
,			count ++;
,		}
,		if (RET_BLANK2) putchar (RET_BLANK2);
,	}
,  }
,
,/* Clear the rest of the line if clear_line is TRUE */
,  if (clear_line == TRUE) {
,     if (can_clear_eol == TRUE)
,	clear_eol ();
,     else {
,	while (count ++ <= XBREAK)
,		putchar (' ');
,     }
,     putchar ('\n');
,  }
,}
,
,/*
, * Proceed returns the count'th line after `line'. When count is negative
, * it returns the count'th line before `line'. When the next (previous)
, * line is the tail (header) indicating EOF (tof) it stops.
, */
,LINE *
,proceed (line, count)
,  register LINE * line;
,  register int count;
,{
,  if (count < 0)
,	while (count ++ < 0 && line != header)
,		line = line->prev;
,  else
,	while (count -- > 0 && line != tail)
,		line = line->next;
,  return line;
,}
,
,/*
, * Reset assigns bot_line, top_line and cur_line according to `head_line'
, * which must be the first line of the screen, and a y-coordinate,
, * which will be the current y-coordinate (if it isn't larger than last_y)
, */
,reset (head_line, screen_y)
,  LINE * head_line;
,  int screen_y;
,{
,  register LINE * line;
,
,  top_line = line = head_line;
,
,/* Search for bot_line (might be last line in file) */
,  for (last_y = 0; last_y < nlines - 1 && last_y < SCREENMAX
,					&& line->next != tail; last_y ++)
,	line = line->next;
,
,  bot_line = line;
,  y = (screen_y > last_y) ? last_y : screen_y;
,
,/* Set cur_line according to the new y value */
,  cur_line = proceed (top_line, y);
,}
,
,/*
, * Display () shows count + 1 lines on the terminal starting at the given
, * coordinates. When the tail of the list is encountered it will fill the
, * rest of the screen with blank_line's.
, * When count is negative, a backwards print from `line' will be done.
, */
,display (x_coord, y_coord, line, count)
,  int x_coord, y_coord;
,  register LINE * line;
,  register int count;
,{
,  set_cursor (x_coord, y_coord);
,
,/* Find new startline if count is negative */
,  if (count < 0) {
,	line = proceed (line, count);
,	count = - count;
,  }
,
,/* Print the lines */
,  while (line != tail && count -- >= 0) {
,	line_print (line);
,	line = line->next;
,  }
,
,/* Print the blank lines (if any) */
,  if (loading == FALSE) {
,	while (count -- >= 0) {
,		clear_eol ();
,		putchar ('\n');
,	}
,  }
,}
,
,/*
, * Redraw the screen
, */
,RD ()
,{
,  reverse_off ();
,  clear_screen ();
,
,/* Print first page */
,  display (0, 0, top_line, last_y);
,
,/* Clear last line */
,  set_cursor (0, YMAX);
,  clear_eol ();
,  move_y (y);
,  if (stat_visible == TRUE) rd_bottom_line ();
,}
,
,/*  ==================================================================	*
, *			Terminal Dialog					*
, *  ==================================================================	*/
,
,/*
, * promptyn reads in a 'y' or 'n' character.
, */
,u_char
,promptyn ()
,{
,  register u_char c;
,  while ((c = readchar ()) != 'y' && c != 'n' && c != '\033' && quit == FALSE) {
,	ring_bell ();
,	flush ();
,  }
,  if (c == '\033') quit = TRUE;
,  return c;
,}
,
,/*
, * Adjust current window size after WINCH signal
, */
,RDwin ()
,{
,  register LINE * current_line;
,
,  winchg = FALSE;
,  getwinsize ();
,
,  current_line = cur_line;
,  reset (top_line, y);
,/*  move_y (find_y_w_o_RD (current_line)); */
,  move_address (cur_text, find_y_w_o_RD (current_line));
,  RD ();
,  flush ();
,}
,
,/*
, * In case of a QUIT signal, swallow the dummy char generated by catchquit ()
, * called by re_search () and change ()
, */
,swallow_dummy_quit_char ()
,{
,#ifdef UNUSED
,  readchar (); /* Swallow away a quit character delivered by QUIT */
,/* Not needed because this character is ignored by being the CANCEL command */
,#endif UNUSED
,}
,
,/*
, * Readchar () reads one character from the terminal.
, * There are problems due to interruption of the read operation by signals
, * (QUIT, WINCH). The waitingforinput flag is only a partial solution.
, * Unix doesn't provide sufficient facilities to handle these situations
, * neatly and properly. Moreover, different Unix versions yield different
, * surprising effects. However, the use of select () could still be
, * an improvement.
, */
,readchar ()
,{
,  register u_char c;
,
,  waitingforinput = TRUE;
,/*  if (winchg == TRUE) RDwin (); /* This is now done in __readchar () */
,  c = _readchar ();
,  waitingforinput = FALSE;
,  /* the modification   if (quit == TRUE) c = QUITCHAR;   (now in __readchar)
,     must not be placed after resetting the flag  waitingforinput = FALSE;  .
,     Otherwise a QUIT signal coming in just between these two would
,     discard the last valid character just taken up. */
,  return c;
,}
,
,/*-------------------------------------------------------------------------*/
,
,#ifndef msdos
,
,#ifdef msdos
,#ifndef single_pass_compilation
,/* This seems to conflict with the same declaration in minedmp.c when
,   compiled with Turbo-C. C is just too stupid a language.
,   Luckily, we don't need this part at all on MSDOS */
,extern struct {char * fk; int (* fp) ();} keycode [];
,#endif
,#else
,extern struct {char * fk; int (* fp) ();} keycode [];
,#endif
,#define MAXCODELEN 7 /* max. length of function key sequence to be detected,
,			depending on the keycode table */
,
,/*
, * queue collects the keys of an Escape sequence typed in until the 
, * sequence can be detected or rejected.
, * If the queue is not empty, queue [0] contains the character next 
, * to be delivered by _readchar () (it's not a ring buffer).
, * The queue contents is always terminated by a '\0', so queue can also 
, * be taken as a character string.
, */
,static u_char queue [MAXCODELEN + 1], * endp = queue;
,
,int
,q_empty ()
,{
,  return (endp == queue ? 1 : 0);
,}
,
,int
,q_notfull ()
,{
,  return (endp - queue == MAXCODELEN ? 0 : 1);
,}
,
,q_clear ()
,{
,  endp = queue;
,}
,
,int
,q_len ()
,{
,  return endp - queue;
,}
,
,q_put (c)
,  u_char c;
,/* queue must not be full prior to this call! */
,{
,  * endp = c;
,  * ++ endp = '\0';
,}
,
,u_char
,q_get ()
,{
,  u_char c;
,  register u_char * pd, * ps;
,
,  c = * queue; pd = queue; ps = pd + 1;
,  while (ps <= endp) * pd ++ = * ps ++;
,  if (endp > queue) endp --;
,  return c;
,}
,
,/*
, * Look up key sequence in keycode table.
, * findkey (str) >=  0: str == keycode [findkey (str)].fk
, *		 == -1: str is prefix of some entry in keycode
, *		 == -2: str is not contained in keycode
, */
,int
,findkey (str)
,  char * str;
,{
,  static int lastmatch = 0;	/* last index with string matching prefix */
,  register int i;
,
,  if (keycode [0].fk == NIL_PTR) return -2;
,  i = lastmatch;
,  do {
,	if (strncmp (str, keycode [i].fk, strlen (str)) == 0) {
,		lastmatch = i;
,		return (strlen (str) == strlen (keycode [i].fk) ? i : -1);
,	}
,	++ i;
,	if (keycode [i].fk == NIL_PTR) i = 0;
,  } while (i != lastmatch);
,  return -2;
,}
,
,/*
, * Is a character available within a specified number of milliseconds ?
, */
,char_ready_within (msec)
,  int msec;
,{
,  return (q_len () > 0) || inputreadyafter (input_fd, msec);
,}
,
,#else ndef msdos
,
,char_ready_within (msec)
,  int msec;
,{
,  return 0;
,}
,
,#endif def msdos
,
,extern I ();
,int (* keyproc) () = I;
,
,extern grave ();
,extern circumflex ();
,extern acute ();
,extern diaeresis ();
,extern tilde ();
,extern angstrom ();
,
,/*
, * Read a character from terminal, considering function keys and 
, * composing special character of an 8 bit character set.
, * _readchar () takes the following actions:
, *  -	function key sequences according to the table 'keycode' are 
, *	transformed into a special controlling character which is 
, *	assigned the function FUNKEY. Also the intended editor function, 
, *	as taken from the table 'keycode', is saved in a variable for 
, *	use by FUNKEY.
, *  -	the prefix keys for diacritic and special characters are 
, *	combined with the following key to make up the character.
, */
,_readchar ()
,{
,  register u_char ch;
,  int res;
,
,#ifndef msdos
,  if (q_len () > 0) return q_get ();
,#endif
,  ch = __readchar ();
,#ifdef msdos
,  if (ch == '\000') {
,	ch = __readchar ();
,	keyproc = pc_key_map [ch];
,#ifdef DEBUG
,	if (keyproc == I) return ch;
,#endif
,	if ((keyproc == grave) || (keyproc == circumflex)
,		 || (keyproc == acute) || (keyproc == diaeresis)
,		 || (keyproc == tilde) || (keyproc == angstrom))
,		{ch = (* keyproc) (readchar ());
,		 keyproc = I;
,		 return ch;
,		}
,	else return FUNcmd /* index of FUNKEY */;
,  }
,#else
,  if (ch == '\033') {
,	/* q_clear (); */
,	q_put (ch);
,	while  ((res = findkey (queue)) == -1 /* prefix of table entry */
,		&& q_notfull ()
,		&& inputreadyafter (input_fd, 300)
,	       )
,	    q_put (__readchar ());
,	if (quit == TRUE) return '\0';
,	else if (res < 0) /* key pattern not detected in keycode table */
,	     /* {if (q_len () > 1) return 0;
,		 else return ch;} */
,	     return q_get () /* just deliver the typed characters */;
,	else {
,	     q_clear ();
,	     keyproc = keycode [res].fp;
,	     if ((keyproc == grave) || (keyproc == circumflex)
,		 || (keyproc == acute) || (keyproc == diaeresis)
,		 || (keyproc == tilde) || (keyproc == angstrom))
,		{ch = (* keyproc) (readchar ());
,		 keyproc = I;
,		 return ch;
,		}
,	     else return FUNcmd /* index of FUNKEY */;
,	}
,  }
,#endif
,  else
,	return ch;
,}
,
,/*  ==================================================================	*
, *			Status Line Dialog				*
, *  ==================================================================	*/
,
,/*
, * Show concatenation of s1 and s2 on the status line (bottom of screen)
, * If revfl is TRUE, turn on reverse video on both strings. Set stat_visible
, * only if bottom_line is visible.
, * The return value is FINE except for get_string, where it is taken
, * from the call to input ().
,status_line (str1, str2)    is bottom_line (ON, (str1), (str2), NIL_PTR, FALSE)
,error (str1, str2)	    is bottom_line (ON, (str1), (str2), NIL_PTR, FALSE)
,get_string (str1, str2, fl) is bottom_line (ON, (str1), NIL_PTR, (str2), fl)
,clear_status ()		    is bottom_line (OFF, NIL_PTR, NIL_PTR, NIL_PTR, FALSE)
,status_msg (str)	    is status_line (str, NIL_PTR)
,status_beg (str)	    is bottom_line (ON, (str), NIL_PTR, NIL_PTR, TRUE)
, */
,FLAG lastrevfl;
,char * lastinbuf;
,FLAG input_active = FALSE;
,char status_buf [maxLINE_LEN];
,
,rd_bottom_line ()
,{
,  set_cursor (0, YMAX);
,  reverse_on ();
,  if (lastinbuf == NIL_PTR)
,	print_string (status_buf, XBREAK);
,  else {
,	print_string (status_buf, -1);
,	print_string (lastinbuf, -1);
,  }
,  if (! input_active) {
,	reverse_off ();
,	set_cursor (x, y);	/* Set cursor back to old position */
,  }
,  flush ();	/* Perform the actual screen output */
,}
,
,int
,bottom_line (revfl, s1, s2, inbuf, statfl)
,  FLAG revfl;
,  char * s1, * s2;
,  char * inbuf;
,  FLAG statfl;
,{
,  int ret = FINE;
,
,  if (inbuf != NIL_PTR) * inbuf = '\0';
,  lastrevfl = revfl;
,  lastinbuf = inbuf;
,
,  if (pagewrapped == TRUE) {
,	status_buf [0] = '\0';
,	RD ();
,	pagewrapped = FALSE;
,  }
,
,  build_string (status_buf, " %s%s ", unnull (s1), unnull (s2));
,		/* (s1 == NIL_PTR) ? "" : s1, (s2 == NIL_PTR) ? "" : s2); */
,
,  if (revfl == ON && stat_visible == TRUE) {
,	set_cursor (0, YMAX);
,	clear_eol ();
,  }
,  set_cursor (0, YMAX);
,  if (revfl == ON) {		/* Print rev. start sequence */
,	reverse_on ();
,	stat_visible = TRUE;
,  }
,  else				/* Used as clear_status () */
,	stat_visible = FALSE;
,
,  if (inbuf == NIL_PTR)
,	print_string (status_buf, XBREAK);
,  else {
,	print_string (status_buf, -1);
,	input_active = TRUE;
,	ret = input (inbuf, statfl);
,	input_active = FALSE;
,  }
,
,  /* Print normal video */
,  reverse_off ();
,  clear_eol ();
,
,  if (inbuf != NIL_PTR) {
,	set_cursor (0, YMAX);
,  }
,  else if (statfl == TRUE)
,	reverse_on ();
,  else
,	set_cursor (x, y);	/* Set cursor back to old position */
,
,  flush ();	/* Perform the actual screen output */
,  if (ret != FINE) clear_status ();
,  return ret;
,}
,
,/*
, * Input () reads a string from the terminal.
, * Return values:
, *	when QUIT character typed => ERRORS
, *	when empty input and clearfl == TRUE: NO_INPUT
, *	else: FINE
, */
,input (inbuf, clearfl)
,  u_char * inbuf;
,  FLAG clearfl;
,{
,  register u_char * ptr;
,  register u_char c;
,
,  ptr = inbuf;
,  * ptr = '\0';
,  while (quit == FALSE) {
,	flush ();
,	if (lpos >= XBREAK) pagewrapped = TRUE;
,	switch (c = readchar ()) {
,		case '\b' :		/* Erase previous char */
,		case '\177' /* DEL */ :
,			if (ptr > inbuf) {
,				ptr --;
,				reverse_off ();
,				if (iscontrol (* ptr)) {
,					putstring (" \b\b\b  \b\b");
,					lpos = lpos - 2;
,				}
,				else {
,					putstring (" \b\b \b");
,					lpos = lpos - 1;
,				}
,				reverse_on ();
,				putstring (" \b");
,				* ptr = '\0';
,			}
,			else
,				ring_bell ();
,			break;
,		case QUITCHAR :
,		case '\033' :
,			quit = TRUE;
,			break;
,		case '\n' :		/* End of input */
,		case '\015' :
,			/* If inbuf is empty clear status_line */
,			return (ptr == inbuf && clearfl == TRUE) ?
,				NO_INPUT : FINE;
,		default :
,			if (c == '\026' /* ^V */) c = readchar ();
,			if ((ptr - inbuf) < maxLINE_LEN) {
,				if ((c > '\0')) {
,					* ptr ++ = c;
,					* ptr = '\0';
,					print_char (c, -1);
,					putstring (" \b");
,				}
,				else
,					ring_bell ();
,			}
,			else
,				ring_bell ();
,	}
,  }
,  quit = FALSE;
,  return ERRORS;
,}
,
,/*
, * Get_number () reads a number from the terminal.
, * The last character typed in is returned.
, * ERRORS is returned on a bad number or on interrupted input.
, * The resulting number is put into the integer the arguments points to.
, */
,get_number (message, firstdigit, result)
,  char * message;
,  char firstdigit;
,  int * result;
,{
,  register int index;
,  register int count;
,
,  status_beg (message);
,
,  if (firstdigit > '\0')
,	index = firstdigit;
,  else
,	index = readchar ();
,
,  if (index == QUITCHAR) quit = TRUE;
,  if (index == '\033') quit = TRUE;
,  if (quit == FALSE && (index < '0' || index > '9')) {
,	error ("Bad number", NIL_PTR);
,	return ERRORS;
,  }
,
,/* Convert input to a decimal number */
,  count = 0;
,  while (index >= '0' && index <= '9' && quit == FALSE) {
,	print_char (index, -1); flush ();
,	if (lpos >= XBREAK) pagewrapped = TRUE;
,	count *= 10;
,	count += index - '0';
,	index = readchar ();
,	if (index == QUITCHAR) quit = TRUE;
,	if (index == '\033') quit = TRUE;
,  }
,
,  clear_status ();
,  if (quit == TRUE) {
,	clear_status ();
,	return ERRORS;
,  }
,  * result = count;
,  return index;
,}
,
,/*
, * get_digits () reads in a number. In contrast to get_number, it does no
, * echoing, no messaging, and it does not require any digits at all.
, * The last character typed in is returned.
, * The resulting number is put into the integer the arguments points to.
, */
,get_digits (result)
,  int * result;
,{
,  register int index;
,  register int count;
,
,  index = readchar ();
,  if (index == QUITCHAR) quit = TRUE;
,  * result = -1;
,/* Convert input to a decimal number */
,  count = 0;
,  while (index >= '0' && index <= '9' && quit == FALSE) {
,	count *= 10;
,	count += index - '0';
,	* result = count;
,	index = readchar ();
,	if (index == QUITCHAR) quit = TRUE;
,  }
,
,  if (quit == TRUE) {
,	return QUITCHAR;
,  }
,  return index;
,}
,
,/*
, * Get_file () reads a filename from the terminal.
, */
,int
,get_file (message, file)
,  char * message, * file;
,{
,  int ret = get_string (message, file, TRUE);
,#ifndef msdos
,  char * filei;
,  char file1 [maxLINE_LEN];
,
,  if (file [0] == '~' && file [1] == '/') {
,	filei = file; filei ++;
,	build_string (file1, "%s%s", unnull (getenv ("HOME")), filei);
,	build_string (file, file1);
,  }
,#endif
,  return ret;
,}
,
,/*  ==================================================================	*
, *			Text buffer routines				*
, *  ==================================================================	*/
,
,/*
, * Ask the user if he wants to save the file or not.
, */
,ask_save ()
,{
,  register u_char c;
,
,  status_line (file_name [0] ? file_name : "[buffer]" ,
,			" has been modified. Save? (y/n)");
,	/* previously only basename (file_name) was printed */
,  c = promptyn ();
,  clear_status ();
,  if (c == 'y')
,	return WT ();
,  else if (c == 'n')
,	return FINE;
,  else {
,	quit = FALSE;	/* abort character has been given */
,	return ERRORS;
,  }
,}
,
,/*
, * Ask user if named file should be overwritten.
, */
,FLAG
,checkoverwrite (name)
,char * name;
,{
,  u_char c;
,
,  if (access (name, 0 /* F_OK */) < 0)	/* Cannot access file */
,	return TRUE;	/* thus no danger of unwanted damage */
,
,  status_line (name [0] ? name : "[buffer]" ,
,			": OK to overwrite? (y/n)");
,	/* previously only basename (name) was printed */
,  c = promptyn ();
,  clear_status ();
,  if (c == 'y')
,	return TRUE;
,  else if (c == 'n')
,	return FALSE;
,  else {
,/*	quit = FALSE;	/* abort character has been given */
,	return FALSE;
,  }
,}
,
,/*
, * Attach new file name to buffer
, */
,NN ()
,{
,  char file [maxLINE_LEN];	/* Buffer for new file name */
,  if (get_file ("Enter new file name:", file) == ERRORS)
,	return ERRORS;
,  overwriteOK = FALSE;
,  writable = TRUE;
,  modified = TRUE;	/* cf. CHDI command */
,  copy_string (file_name, file);	/* Save new file name */
,/*  set_cursor (x, y);	/* Set cursor back to old position */
,  clear_status ();
,}
,
,/*
, * Write file in core to disc.
, */
,/* Call graph for writing functions:
,	panic --\
,		 > QUED --\
,	ESC q --/	   > ask_save --\
,	ESC e ---> EDIT --/		 \
,	ESC v ---> VIEW -/		  \
,	ESC w -----------------------------> WT
,	ESC z -----------> SUSP ----------/
,	ESC ESC ---------> EXED ---------/
,*/
,long write_count	/* number of chars written */;
,write_file (fd)
,  int fd;
,{
,  register LINE * line;
,
,  write_count = 0L;
,  clear_buffer (); /* out_count = 0; */
,  for (line = header->next; line != tail; line = line->next) {
,	if (line->shift_count & DUMMY) {
,		if (line->next == tail && line->text [0] == '\n')
,			continue;
,	}
,	if (writestring (fd, line->text) == ERRORS) {
,		write_count = -1L;
,		break;
,	}
,	write_count += (long) length_of (line->text);
,  }
,
,  if (write_count > 0L && flush_buffer (fd) == ERRORS)
,	write_count = -1L;
,
,  (void) close (fd);
,}
,
,WT ()
,{
,  char file [maxLINE_LEN];	/* Buffer for new file name */
,  int fd;			/* Filedescriptor of file */
,  int ret;
,
,  if (wpipe) {
,    fd = STD_OUT;
,    status_line ("Writing ", "to standard output");
,    wpipe = FALSE; /* no further write to same stream possible */
,  }
,  else {
,    if (modified == FALSE) {
,	status_msg ("Write not necessary.");
,	return FINE;
,    }
,
,    /* Check if file_name is valid and if file can be written */
,    if (file_name [0] == '\0' || writable == FALSE) {
,	overwriteOK = FALSE;
,	if ((ret = get_file ("Enter file name:", file)) != FINE)
,		return ret;
,	copy_string (file_name, file);		/* Save file name */
,    }
,    if (overwriteOK == FALSE) {
,	if (checkoverwrite (file_name) == TRUE)
,		overwriteOK = TRUE;
,	else {	if (quit == FALSE)
,			writable = FALSE;
,		return ERRORS;
,	}
,    }
,    if ((fd = creat (file_name, fprot)) < 0) {	/* Empty file */
,	error ("Cannot create or write: " /*, file_name */, serror ());
,	writable = FALSE;
,	return ERRORS;
,    }
,    else
,	writable = TRUE;
,
,    status_line ("Writing ", file_name);
,  }
,
,  write_file (fd);
,
,  if (write_count == -1L)
,	return ERRORS;
,
,  modified = FALSE;
,  rpipe = FALSE;	/* File name is now assigned */
,
,/* Display how many chars (and lines) were written */
,  fstatus ("Wrote", write_count);
,  return FINE;
,}
,
,panic_write ()
,{
,  int fd;
,  fd = creat (panic_file, fprot);
,  write_file (fd);
,  if (write_count == -1L)
,	return ERRORS;
,  else	return FINE;
,}
,
,/*
, * Edit/view another file. If the current file has been modified, 
, * ask whether the user wants to save it.
, * (We could allow to switch between edit and view mode without changing 
, * the file, but we would have to consider carefully the relationship 
, * between viewonly and modified.)
, */
,edit_file (prompt, vomode)
,  char * prompt;
,  FLAG vomode;
,{
,  char new_file [maxLINE_LEN];	/* Buffer to hold new file name */
,
,  if (modified == TRUE && ask_save () != FINE)
,	return;
,
,  viewonly = vomode;
,
,/* Get new file name */
,  if (get_file (prompt, new_file) == ERRORS)
,	return;
,
,/* Free old linked list, initialize global variables and load new file */
,  initialize ();
,  clear_screen ();
,  load_file (new_file [0] == '\0' ? NIL_PTR : new_file);
,}
,
,EDIT ()
,{
,  edit_file ("Edit file:", FALSE);
,}
,
,VIEW ()
,{
,  edit_file ("View file:", TRUE);
,}
,
,edit_nth_file (n)
,  int n;
,{
,  int number, index;
,
,  if (modified == TRUE && ask_save () != FINE)
,	return;
,
,  if (n == -1) {
,	index = get_number ("Edit which file (enter number) ...", '\0', & number);
,	if (index == ERRORS) return;
,	n = number - 1 + fnami_min;
,  }
,  if (n < fnami_min) n = fnami_min;
,  if (n > fnami_max) n = fnami_max;
,
,/* Free old linked list, initialize global variables and load new file */
,  initialize ();
,  clear_screen ();
,
,  fnami = n;
,  if (fnami < fnami_min) load_file (NIL_PTR);
,  else load_file ((* fnamv) [fnami]);
,}
,
,NXTFILE ()
,{
,  if (hop_flag > 0) edit_nth_file (fnami_max);
,  else edit_nth_file (fnami + 1);
,}
,
,PRVFILE ()
,{
,  if (hop_flag > 0) edit_nth_file (fnami_min);
,  else edit_nth_file (fnami - 1);
,}
,
,NTHFILE ()
,{
,  edit_nth_file (-1);
,}
,
,/*
, * Leave editor. If the file has changed, ask if the user wants to save it.
, */
,QUED ()
,{
,  if (modified == TRUE && ask_save () != FINE)
,	return ERRORS;
,
,  delete_yank_file ();
,  set_cursor (0, YMAX);
,  putchar ('\n');
,  raw_mode (OFF);
,  exit (0);
,}
,
,/*
, * Exit editor. If the file has changed, save it.
, */
,EXED ()
,{
,  if (multiexit == TRUE) EXFILE ();
,  else {
,	if (modified == TRUE)
,		if (WT () != FINE) return;
,
,	delete_yank_file ();
,	set_cursor (0, YMAX);
,	putchar ('\n');
,	raw_mode (OFF);
,	exit (0);
,  }
,}
,
,/*
, * Exit editing current file. If the file has changed, save it.
, * Edit next file if there is one.
, */
,EXFILE ()
,{
,  if (modified == TRUE)
,	if (WT () != FINE) return;
,
,  if (fnami < fnami_max) NXTFILE ();
,  else {
,	delete_yank_file ();
,	set_cursor (0, YMAX);
,	putchar ('\n');
,	raw_mode (OFF);
,	exit (0);
,  }
,}
,
,/*
, * Count_chars () count the number of chars that the line would occupy on the
, * screen. Counting starts at the real x-coordinate of the line.
, * Was only called by delete_text ().
, */
,#ifdef UNUSED
,count_chars (line)
,  LINE * line;
,{
,  register int cnt = get_shift (line->shift_count) * - SHIFT_SIZE;
,  register char * textp = line->text;
,
,/* Find begin of line on screen */
,  while (cnt < 0) {
,	if (is_tab (* textp ++))
,		cnt = tab (cnt);
,	else
,		cnt ++;
,  }
,
,/* Count number of chars left */
,  cnt = 0;
,  while (* textp != '\n') {
,	if (is_tab (* textp ++))
,		 cnt = tab (cnt);
,	else
,		cnt ++;
,  }
,  return cnt;
,}
,#endif UNUSED
,
,/*-------------------------------------------------------------------------*/
,
,/*
, * move_to: move to given coordinates on screen.
, * move_y: move to given line on screen, staying in last explicit column.
, * move_address: move to given line at given text position.
, * The caller must check that scrolling is not needed.
, * If new x-position is < 0 or > XBREAK, move_it () will check if
, * the line can be shifted. If it can it sets (or resets) the shift_count
, * field of the current line accordingly. By this mechanism, the
, * pseudo-x-positions LINE_START / LINE_END (a very small / big value)
, * perform the appropriate positioning actions.
, * Move also sets cur_text to the right char.
, * "If we're moving to the same x coordinate, try to move the the x-coordinate
, * used on the other previous call." -- This worked erroneously and was 
, * replaced by an explicit old_x variable and move_y call.
, * move_address is directly called by move_next/previous_word(), re_search(), RDwin()
, */
,int old_x = 0;
,move_y (ny)
,  register int ny;
,{
,  move_it (old_x, NIL_PTR, ny);
,}
,move_to (nx, ny)
,  register int nx;
,  register int ny;
,{
,  old_x = x;
,  move_it (nx, NIL_PTR, ny);
,  old_x = x;
,}
,move_address (nadd, ny)
,  register char * nadd;
,  register int ny;
,{
,  old_x = x;
,  move_it (0, nadd, ny);
,  old_x = x;
,}
,move_it (new_x, new_address, new_y)
,  register int new_x;
,  int new_y;
,  char * new_address;
,{
,  register LINE * line = cur_line;	/* For building new cur_line */
,  int shift = 0;			/* How many shifts to make */
,/*  static int rel_x = 0;		/* Remember relative x position */
,/*	This was used as a trick to stay virtually in the previous column 
,	even when moving across shorter lines; but it had >= 2 errors.
,	Renamed to old_x, made globally accessible and explicitly used 
,	by appropriate calls to avoid these problems. TW */
,  int tx = x;
,  char * find_address ();
,
,/* Check for illegal values */
,  if (new_y < 0 || new_y > last_y)
,	return;
,
,/* Adjust y-coordinate and cur_line */
,  if (new_y < y)
,	while (y != new_y) {
,		y --;
,		line = line->prev;
,	}
,  else
,	while (y != new_y) {
,		y ++;
,		line = line->next;
,	}
,
,/* Set or unset relative x-coordinate */
,  if (new_address == NIL_PTR) {
,/*	new_address = find_address (line, (new_x == x) ? rel_x : new_x , & tx);
,/*	if (new_x != x)
,/*		rel_x = tx; */
,	new_address = find_address (line, new_x , & tx);
,	new_x = tx;
,  }
,  else
,	/* rel_x = */ new_x = find_x (line, new_address);
,
,/* Adjust shift_count if new_x lower than 0 or higher than XBREAK */
,  if (new_x < 0 || new_x >= XBREAK) {
,	if (new_x > XBREAK || (new_x == XBREAK && * new_address != '\n'))
,		shift = (new_x - XBREAK) / SHIFT_SIZE + 1;
,	else {
,		shift = new_x / SHIFT_SIZE;
,		if (new_x % SHIFT_SIZE)
,			shift --;
,	}
,
,	if (shift != 0) {
,		line->shift_count += shift;
,		new_x = find_x (line, new_address);
,		set_cursor (0, y);
,		line_print (line);
,		/* rel_x = new_x; */
,	}
,  }
,
,/* Assign and position cursor */
,  x = new_x;
,  cur_text = new_address;
,  cur_line = line;
,  set_cursor (x, y);
,}
,
,/*
, * Find_x () returns the x coordinate belonging to address.
, * (Tabs are expanded).
, */
,find_x (line, address)
,  LINE * line;
,  char * address;
,{
,  register char * textp = line->text;
,  register int nx = get_shift (line->shift_count) * - SHIFT_SIZE;
,
,  while (textp != address && * textp != '\0') {
,	if (is_tab (* textp ++))	/* Expand tabs */
,		nx = tab (nx);
,	else
,		nx ++;
,  }
,  return nx;
,}
,
,/*
, * Find_address () returns the pointer in the line with offset x_coord.
, * (Tabs are expanded).
, * find_address is only called by move_it ()
,get_shift (cnt)		is	((cnt) & DUMMY_MASK) ; DUMMY_MASK is 0x7F
,tab (cnt)		is	(((cnt) + 8) & ~07)
,is_tab (c)		is	((c) == '\t')
, */
,char *
,find_address (line, new_x, cur_x)
,  LINE * line;
,  int new_x;
,  int * cur_x;
,{
,  register char * textp = line->text;
,  register int tx = get_shift (line->shift_count) * - SHIFT_SIZE;
,
,  while (tx < new_x && * textp != '\n') {
,	if (is_tab (* textp)) {
,		if (new_x == old_x /* (* cur_x) */ - 1 && tab (tx) > new_x)
,			break;	/* Moving left over tab */
,		else
,			tx = tab (tx);
,	}
,	else	tx ++;
,	textp ++;
,  }
,  * cur_x = tx;
,  return textp;
,}
,
,/*-------------------------------------------------------------------------*/
,
,/*
, * Initialize is called when a another file is edited. It free's the allocated 
, * space and sets modified back to FALSE and fixes the header/tail pointer.
, */
,initialize ()
,{
,  register LINE * line, * next_line;
,
,/* Delete the whole list */
,  for (line = header->next; line != tail; line = next_line) {
,	next_line = line->next;
,	free_space (line->text);
,	free_space (line);
,  }
,
,/* header and tail should point to itself */
,  line->next = line->prev = line;
,  x = y = 0;
,  rpipe = modified = FALSE;
,}
,
,/*
, * Load_file loads the file with given name or the input pipe into memory.
, * If the file couldn't be opened, just an empty line is installed.
, * Buffer pointers are initialized.
, */
,load_file (file)
,  char * file;
,{
,  load_file_w_o_display (file);
,/* Print screen */
,  display (0, 0, header->next, last_y);
,  move_to (0, 0);
,/* fstatus ("Read", -1L); */
,}
,
,load_file_w_o_display (file)
,  char * file;
,{
,  register LINE * line = header;
,  register int len;
,  long nr_of_chars = 0L;
,  int fd = -1;			/* Filedescriptor for file */
,
,  nlines = 0;			/* Zero lines to start with */
,
,  overwriteOK = FALSE;
,/* Open file */
,  writable = TRUE;		/* Benefit of the doubt */
,  if (file == NIL_PTR) {
,	if (rpipe == FALSE)
,		status_msg ("No file");
,	else {
,		fd = 0;
,		file = "standard input";
,	}
,	file_name [0] = '\0';
,  }
,  else {
,	copy_string (file_name, file);	/* Save file name */
,	if (access (file, 0 /* F_OK */) < 0) {	/* Cannot access file */
,		status_line ("New file ", file);
,		overwriteOK = TRUE;
,	}
,	else if ((fd = open (file, O_RDWR, 0)) >= 0) {
,		overwriteOK = TRUE;
,		writable = TRUE;
,	}
,	else if ((fd = open (file, O_RDONLY, 0)) < 0)
,		error ("Cannot open: " /*, file */, serror ());
,	else {	overwriteOK = TRUE;
,		writable = FALSE;
,	}
,  }
,
,/* Read file */
,  loading = TRUE;		/* Loading file, so set flag */
,  get_line_err1 = NIL_PTR;
,  get_line_err2 = NIL_PTR;
,
,  if (fd >= 0) {
,	status_line ("Reading ", file);
,	while ((len = get_line (fd, text_buffer)) != ERRORS) {
,		line = line_insert (line, text_buffer, len);
,		nr_of_chars += (long) len;
,	}
,	if (nlines == 0)		/* The file was empty! */
,		line = line_insert (line, "\n", 1);
,	clear_buffer (); /* Clear output buffer: out_count = 0; */
,	cur_line = header->next;
,	(void) close (fd);		/* Close file */
,	if ((get_line_err1 != NIL_PTR) || (get_line_err2 != NIL_PTR)) {
,		ring_bell ();
,		error (get_line_err1, get_line_err2);
,		sleep (1);
,	}
,	fstatus ("Read", nr_of_chars);
,  }
,  else				/* Just install a "\n" */
,	(void) line_insert (line, "\n", 1);
,
,  reset (header->next, 0);	/* Initialize pointers */
,  move_to (0, 0);
,  loading = FALSE;		/* Stop loading, reset flag */
,}
,
,/*
, * Get_line reads one line from filedescriptor fd. If EOF is reached on fd,
, * get_line () returns ERRORS, else it returns the length of the string.
, */
,char * get_line_err1;
,char * get_line_err2;
,
,get_line (fd, buffer)
,  int fd;
,  register char buffer [MAX_CHARS];
,{
,  static char * last = NIL_PTR;
,  static char * current = NIL_PTR;
,  static int read_chars;
,  register char * cur_pos = current;
,  char * begin = buffer;
,  char * fini = buffer + MAX_CHARS - 2 /* leave space for '\n\0' */;
,
,  do {
,	if (cur_pos == last) {
,		if ((read_chars = read (fd, screen, screen_BUFL)) <= 0)
,			break;
,		last = & screen [read_chars];
,		cur_pos = screen;
,	}
,	if (* cur_pos == '\0' ) {
,		get_line_err1 = "File contains NULL char's - changed to DEL's -   ";
,		* cur_pos = '\177';
,	}
,#ifdef DEBUG
,	if (* cur_pos == '\r' ) {
,		get_line_err1 = "File contains RET char's - changed to DEL's -   ";
,		* cur_pos = '\177';
,	}
,#endif
,	if (buffer == fini) {
,		get_line_err2 = "Line too long - split";
,		* buffer ++ = '\n';
,		break;
,	}
,  } while ((* buffer ++ = * cur_pos ++) != '\n');
,
,  current = cur_pos;
,  if (read_chars <= 0) {
,	if (buffer == begin)
,		return ERRORS;
,	if (* (buffer - 1) != '\n')
,		if (loading == TRUE) /* Add '\n' to last line of file */
,			* buffer ++ = '\n';
,		else {
,			* buffer = '\0';
,			return NO_LINE;
,		}
,  }
,
,  * buffer = '\0';
,  return buffer - begin;
,}
,
,/*  ==================================================================	*
, *			Auxiliary routines				*
, *  ==================================================================	*/
,
,/*
, * Unnull () changes a NULL string pointer into an empty string pointer 
, * to allow easy feading of string results into build_string / sprintf
, */
,char *
,unnull (s)
,  char * s;
,{
,  if (s == NIL_PTR) return "";
,  else return s;
,}
,
,/*
, * Build_string () prints the arguments as described in fmt, into the buffer.
, * %s indicates a string argument, %d indicates an integer argument.
, */
,#ifndef build_string /* otherwise build_string is sprintf */
,/* VARARGS */
,build_string (buf, fmt, args)
,  register char * buf, * fmt;
,  int args;
,{
,  int * argptr = & args;
,  char * scanp;
,  FLAG islong;
,
,  while (* fmt) {
,     if (* fmt == '%') {
,	fmt ++;
,	if (* fmt == 'l') {islong = TRUE; fmt ++;}
,		     else islong = FALSE;
,	switch (* fmt ++) {
,	case 's' :
,		scanp = (char *) * argptr;
,		break;
,	case 'd' :
,		if (islong == TRUE) {
,			scanp = num_out ((long) * ((long *) argptr));
,			if (sizeof (long) > sizeof (int)) argptr ++;
,		break;
,		}
,		else {
,			scanp = num_out ((long) * argptr);
,			break;
,		}
,	case 'D' :
,		scanp = num_out ((long) * ((long *) argptr));
,		if (sizeof (long) > sizeof (int)) argptr ++;
,		break;
,	default :
,		scanp = "";
,	}
,	while (* buf ++ = * scanp ++)
,		;
,	buf --;
,	argptr ++;
,     }
,     else
,	* buf ++ = * fmt ++;
,  }
,  * buf = '\0';
,}
,#endif not build_string
,
,/*
, * Output an (unsigned) long in a 10 digit field without leading zeros.
, * It returns a pointer to the first digit in the buffer.
, */
,char *
,num_out (number)
,  long number;
,{
,  static char num_buf [11];		/* Buffer to build number */
,  register long digit;			/* Next digit of number */
,  register long pow = 1000000000L;	/* Highest ten power of long */
,  FLAG digit_seen = FALSE;
,  int i;
,
,  for (i = 0; i < 10; i ++) {
,	digit = number / pow;		/* Get next digit */
,	if (digit == 0L && digit_seen == FALSE && i != 9)
,		num_buf [i] = ' ';
,	else {
,		num_buf [i] = '0' + (char) digit;
,		number -= digit * pow;	/* Erase digit */
,		digit_seen = TRUE;
,	}
,	pow /= 10L;			/* Get next digit */
,  }
,  num_buf [11] = '\0';
,  for (i = 0; num_buf [i] == ' '; i ++)	/* Skip leading spaces */
,	;
,  return (& num_buf [i]);
,}
,
,/*
, * make_number () converts a string into a natural number
, * returns the character after the last digit
, */
,make_number (num, str)
,  int * num;
,  char * str;
,{
,  register char * chpoi = str;
,
,  * num = 0;
,  while (* chpoi >= '0' && * chpoi <= '9' && quit == FALSE) {
,	* num *= 10;
,	* num += * chpoi - '0';
,	chpoi ++;
,  }
,  return * chpoi;
,}
,
,/*
, * Length_of () returns the number of characters in the string `string'
, * excluding the '\0'.
, */
,int
,length_of (string)
,  register char * string;
,{
,  register int count = 0;
,
,  if (string != NIL_PTR) {
,	while (* string ++ != '\0')
,		count ++;
,  }
,  return count;
,}
,
,/*
, * Copy_string () copies the string `from' into the string `to'. `To' must be
, * long enough to hold `from'.
, */
,copy_string (to, from)
,  register char * to;
,  register char * from;
,{
,  while ((* to ++ = * from ++))
,	;
,}
,
,/*-------------------------------------------------------------------------*/
,
,/*
, * serrorof delivers the error message of the given errno value.
, * serror delivers the error message of the current errno value.
, * geterrno just returns the current errno value.
, */
,#ifdef vms
,/* #define includeerrno */
,#  ifdef includeerrno
,#  include <errno.h>
,#  else
,    extern volatile int noshare errno;
,    extern volatile int noshare vaxc$errno; /* VMS error code when errno = EVMSERR */
,#  define EVMSERR 65535
,    extern volatile int noshare sys_nerr;
,    extern volatile char noshare * sys_errlist [];
,#  endif
,#else
,  extern int errno;
,  extern int sys_nerr;
,  extern char * sys_errlist [];
,#endif
,
,char *
,serrorof (errno)
,  int errno;
,{
,  if ((errno < 0) || (errno >= sys_nerr))
,	{ static char s [20];
,#ifdef vms
,	  if (errno == EVMSERR)
,	     build_string (s, "VMS error %d", vaxc$errno);
,	  else
,#endif
,	     build_string (s, "Unknown error %d", errno);
,	  return s;
,	}
,  else	return sys_errlist [errno];
,}
,char *
,serror ()
,{ return serrorof (errno); }
,int
,geterrno ()
,{ return errno; }
,
,/*
, * Panic () is called with a mined error msg and an optional system error msg.
, * It is called when something unrecoverable has happened.
, * It writes the message to the terminal, resets the tty and exits.
, * Ask the user if he wants to save his file.
, */
,#define panic_msg(msg)	if (isscreenmode == TRUE) {status_msg (msg); sleep (2);} else printf ("%s\n", msg);
,panic (message, err)
,  register char * message;
,  register char * err;
,{
,  int panic_written;
,
,  panic_level ++;
,
,  if (panic_level < 2) {
,	if (loading == FALSE && modified == TRUE) {
,		if ((panic_written = panic_write ()) == ERRORS) {
,			build_string (text_buffer, "Error writing panic file %s", panic_file);
,			sleep (2);
,		}
,		else
,			build_string (text_buffer, "Panic file %s written", panic_file);
,		ring_bell ();
,		panic_msg (text_buffer);
,	}
,	if (err == NIL_PTR)
,		build_string (text_buffer, "%s", message);
,	else
,		build_string (text_buffer, "%s (Error: %s)", message, err);
,	panic_msg (text_buffer);
,
,	/* "normal" panic handling: */
,	if (loading == FALSE) {
,		if (QUED () == ERRORS)	/* Try to save the file */ {
,			sleep (2);
,			panic_msg ("Error writing file in panic mode - trying to continue");
,			panic_level --;
,			return;
,		}
,	}
,  }
,
,  if (panic_level < 3) {
,	if (isscreenmode == TRUE) {
,		set_cursor (0, YMAX);
,		putchar ('\n');
,		raw_mode (OFF);
,	}
,	delete_yank_file ();
,  }
,  exit (1) /* abort () sends IOT which would again be caught */;
,}
,panicio (message, err)
,  register char * message;
,  register char * err;
,{
,/* Should panic_level already be increased here ? */
,  panic (message, err);
,}
,
,catch_interrupt ()
,{
,  panic ("External signal caught - terminating", NIL_PTR);
,}
,
,/*
, * Memory allocation
, */
,#ifdef msdos
,#include <alloc.h>
,#define allocate farmalloc
,#define freemem farfree
,#else
,extern char * malloc ();
,#define allocate malloc
,#define freemem free
,#endif
,char *
,alloc (bytes)
,  int bytes;
,{
,  char * p;
,
,  if ((p = allocate ((unsigned) bytes)) == NIL_PTR)
,	panic ("Out of memory", NIL_PTR);
,  return p;
,}
,
,free_space (p)
,  char * p;
,{
,  freemem (p);
,}
,
,/*
, * Basename () finds the absolute name of the file out of a given path_name.
, */
,#ifdef UNUSED
,char *
,basename (path)
,  char * path;
,{
,  register char * ptr = path;
,  register char * last = NIL_PTR;
,
,  while (* ptr != '\0') {
,	if (* ptr == '/')
,		last = ptr;
,	ptr ++;
,  }
,  if (last == NIL_PTR)
,	return path;
,  if (* (last + 1) == '\0') {	/* E.g. /usr/tmp/pipo/ */
,	* last = '\0';
,	return basename (path);	/* Try again */
,  }
,  return last + 1;
,}
,#endif
,
,/*
, * Delete file.
, */
,delete_file (file)
,  char * file;
,{
,#ifdef unix
,  unlink (file);
,#endif
,#ifdef msdos
,  unlink (file);
,#endif
,#ifdef vms
,  delete (file);
,#endif
,}
,
,/*
, * Delete yank file if there is one.
, */
,delete_yank_file ()
,{
,  if (yank_status == VALID) delete_file (yank_file);
,}
,
,/*  ==================================================================	*
, *				Miscellaneous				*
, *  ==================================================================	*/
,
,/*
, * Ignore this keystroke.
, */
,I ()
,{
,}
,
,/*
, * Fortifying 'HOP' key.
, */
,HOP ()
,{
,  hop_flag = 2;
,  if (! char_ready_within (500))
,	status_msg ("Continue HOP command (next command fortified) ...");
,}
,
,/*
, * Cancel prefix function.
, */
,CANCEL ()
,{
,  hop_flag = 0;
,  clear_status ();
,}
,
,/*
, * Call proc associated with function key.
, */
,FUNKEY ()
,{
,  (* keyproc) ('\0');
,  keyproc = I;
,}
,
,/*
, * Combined command.
, */
,extern MRT(), MLF();
,
,ESCAPE ()
,{
,  u_char c;
,  int (* func) ();
,
,  if (! char_ready_within (500))
,/*	status_msg ("Continue ESCape command (for help use h) ..."); */
,	status_msg ("ESC(exit) q(uit w(rite e(dit /\\(search) r(eplace d(irectory h(elp ...");
,  if (quit == TRUE) return I ();
,  c = readchar ();
,  if (quit == TRUE) return I ();
,  clear_status ();
,  if ('0' <= c && c <= '9') return REPT (c);
,  switch (c) {
,	case '\033' : return EXED ();	/* alternatively EXFILE () ? */
,	case 'q' : return QUED ();
,	case '/' : return SFW ();
,	case '\\' : return SRV ();
,	case 's' : return GR ();
,	case 'l' : return LR ();
,	case 'r' : return REPL ();
,	case 'w' : return WT ();
,	case 'e' : return EDIT ();
,	case 'v' : return VIEW ();
,	case 'h' : return HELP ();
,	case '?' : return FS ();
,	case '.' : return RD ();
,	case 'i' : return INF ();
,	case 'b' : return WB ();
,	case '=' : return REPT (' ');
,	case 'z' : return SUSP ();
,	case 'd' : return CHDI ();
,	case '!' : return SH ();
,	case ']' : return GOMA ();
,	case 'n' : return NN ();
,	case 'p' : return PBUF ();
,	case 'c' : return CMD ();
,	case ' ' : return I ();
,	case '+' : return NXTFILE ();
,	case '-' : return PRVFILE ();
,	case '#' : return NTHFILE ();
,	case QUITCHAR : return CANCEL ();
,	default : {
,		/* func = key_map [c];
,		   if ((c < ' ') || (func == FUNKEY)) {
,			hop_flag = 1;
,			return (* func) (c);
,		   }
,		   else
,		*/
,			return BAD (c);
,		  }
,  }
,}
,
,/*
, * DIRECT () reads in a direct cursor movement input sequence and moves.
, */
,DIRECT ()
,{
,  u_char c;
,  int xpos, ypos;
,
,  c = get_digits (& ypos); /* c should be ';' */
,  c = get_digits (& xpos);
,  ypos = ypos - 1;
,  xpos = xpos - 1;
,  if (ypos > last_y) ypos = last_y;
,  move_to (xpos, ypos);
,  if (c == 'm') MARK (); /* middle mouse button */
,  if (c == 'r') YA (); /* right mouse button */
,}
,
,/*
, * REPT () prompts for a count and wants a command after that. It repeats the
, * command count times. If a ^\ is given during repeating, stop looping and
, * return to main loop.
, */
,REPT (firstdigit)
,  char firstdigit;
,{
,  register int count;
,  register int (* func) ();
,  int index, number;
,
,  hop_flag = 0;
,  if (firstdigit >= '0' && firstdigit <= '9')
,     index = get_number ("Please continue repeat count...", firstdigit, & number);
,  else
,     index = get_number ("Please enter repeat count...", '\0', & number);
,  if (index == ERRORS) return;
,
,  func = key_map [index];
,  if (func == I) {	/* Function assigned? */
,	clear_status ();
,	return;
,  }
,  if (func == FUNKEY) {
,	func = * keyproc;
,	keyproc = I;
,	index = '\0';
,  }
,
,  count = number;
,  while (count -- > 0 && quit == FALSE) {
,	if (stat_visible == TRUE)
,		clear_status ();
,	(* func) (index);
,	flush ();
,  }
,
,  if (quit == TRUE)		/* Abort has been given */
,	error ("Repeat aborted", NIL_PTR);
,  else
,	clear_status ();
,}
,
,/*
, * Complains to illegal commands and eats up illegal escape sequences.
, */
,BAD (c)
,  u_char c;
,{
,  static char message2 [] = "'**' - type a blank";
,
,  if (c < ' ')	{ message2 [1] = '^'; message2 [2] = c + '@'; }
,  else		{ message2 [1] = ' '; message2 [2] = c; }
,  error ("Unknown command ", message2);
,  while (readchar () != ' ' && quit == FALSE) {
,	ring_bell ();
,	flush ();
,  }
,  clear_status ();
,}
,
,/*
, * Change working directory.
, */
,CHDI ()
,{
,  char new_dir [maxLINE_LEN];	/* Buffer to hold new dir. name */
,
,#ifdef vms
,#  define _getcwd_
,#endif
,#ifdef msdos
,#  define _getcwd_
,#endif
,#ifdef _getcwd_
,  build_string (text_buffer, "Directory: %s, change to:", unnull (getcwd (new_dir, maxLINE_LEN)));
,#else
,  build_string (text_buffer, "Directory: %s, change to:", unnull (getwd (new_dir)));
,#endif
,  if (get_file (text_buffer, new_dir) != FINE)
,	return;
,  if (chdir (new_dir) == 0) {
,/*	set_cursor (x, y);	/* Set cursor back to old position */
,	clear_status ();
,	overwriteOK = FALSE;	/* Same file base name ... */
,	writable = TRUE;
,/*	if (viewmode == FALSE)	*/
,	    modified = TRUE;	/* would mean different file now */
,  }
,  else	error ("Could not change work dir: ", serror ());
,}
,
,/*
, * Print file status.
, */
,FS ()
,{
,  fstatus (file_name [0] ? "" : "[buffer]", -1L);
,}
,
,/*
, * Line_number () finds the line number we're on.
, */
,line_number ()
,{
,  register LINE * line = header->next;
,  register int count = 1;
,
,  while (line != cur_line) {
,	count ++;
,	line = line->next;
,  }
,
,  return count;
,}
,
,/*
, * Display a line telling how many chars and lines the file contains. Also tell
, * whether the file is readonly and/or modified.
,fstatus (mess, cnt) is	file_status ((mess), (cnt), file_name, \
,				     nlines, writable, modified, viewonly)
, */
,file_status (message, count, file, lines, writefl, changed, viewing)
,  char * message;
,  register long count;		/* Contains number of characters in file */
,  char * file;
,  int lines;
,  FLAG writefl, changed, viewing;
,{
,  register LINE * line;
,  static char msg [maxLINE_LEN + 40];	/* Buffer to hold line */
,  char yank_msg [maxLINE_LEN];	/* Buffer for msg of yank_file */
,
,  if (count < 0)		/* Not valid. Count chars in file */
,	for (line = header->next; line != tail; line = line->next)
,		count += length_of (line->text);
,
,  if (yank_status == VALID)	/* Append buffer info */
,	build_string (yank_msg, " Buffer: %ld char%s.", chars_saved,
,					(chars_saved == 1L) ? "" : "s");
,  else
,	yank_msg [0] = '\0';
,
,  build_string (msg,
,		"%s %s%s%s%s, %d line%s, %ld char%s. Line %d.%s",
,		message,
,		(rpipe == TRUE && * message != '[') ?
,			"standard input" : file,
,			/* previously only basename (file) was printed */
,		(viewing == TRUE) ? " (View only)" : "",
,		(changed == TRUE) ? " (modified)" : "",
,		(writefl == FALSE) ? " (Readonly)" : "",
,		lines, (lines == 1) ? "" : "s",
,		count, (count == 1L) ? "" : "s",
,		line_number (),
,		yank_msg);
,
,  status_msg (msg);		/* Print the information */
,}
,
,/*
, * Show Help information on screen
, */
,HELP ()
,{
,  clear_screen ();
,  status_msg ("Wait for help...");
,  flush ();
,  raw_mode (OFF);
,  if (getenv ("MINEDHELP"))
,	system ((char *) getenv ("MINEDHELP"));
,  else	system (helpcommand);
,  sleep (1);
,  raw_mode (ON);
,  clear_status ();
,  RDwin ();
,}
,
,/*
, * Print buffer
, */
,PBUF ()
,{
,  int fd;
,  char cmd [maxLINE_LEN];	/* Buffer for print command */
,
,  if ((fd = scratch_file (READ, FALSE)) == ERRORS) {
,	error ("Buffer is empty.", NIL_PTR);
,	return;
,  }
,  close (fd);
,  build_string (cmd, getenv ("MINEDPRINT") ?
,		(char *) getenv ("MINEDPRINT") : printcommand, yank_file);
,  clear_status ();
,  set_cursor (0, YMAX);
,  flush ();
,  system (cmd);
,  sleep (1);
,  RDwin ();
,}
,
,/*
, * Pipe buffer
, */
,CMD ()
,{
,  int fd;
,  char cmd [maxLINE_LEN];	/* Buffer for command */
,  char command [maxLINE_LEN];	/* Buffer for full command */
,
,  if ((fd = scratch_file (READ, FALSE)) == ERRORS) {
,	error ("Buffer is empty.", NIL_PTR);
,	return;
,  }
,  close (fd);
,  if (get_string ("Command with buffer as input:", cmd, TRUE) != FINE)
,	return;
,  build_string (command, "%s < %s", cmd, yank_file);
,  clear_status ();
,  set_cursor (0, YMAX);
,  flush ();
,  raw_mode (OFF);
,  system (command);
,  sleep (1);
,  raw_mode (ON);
,  RDwin ();
,}
,
,/*
, * Called if an operation is not implemented
, */
,notimpl ()
,{
,  error ("Command not implemented", NIL_PTR);
,}
,
,/*
, * Suspend editor after writing back the file.
, */
,SUSP ()
,{
,  if (cansuspendmyself == TRUE) {
,	if (hop_flag == 0 && modified == TRUE)
,		if (WT () == ERRORS) return ERRORS;
,	set_cursor (0, YMAX);
,	raw_mode (OFF);
,	suspendmyself ();
,	raw_mode (ON);
,	clear_status ();
,	RDwin ();
,  }
,  else notimpl ();
,}
,
,/*
, * Call an interactive shell.
, */
,SH ()
,{
,#ifdef unix
,  register int w;
,  int pid, status, waiterr;
,
,  switch (pid = vfork ()) {
,	case -1:			/* Error */
,		error ("Cannot fork: ", serror ());
,		return;
,	case 0:				/* This is the child */
,		set_cursor (0, YMAX);
,		putchar ('\n');
,		raw_mode (OFF);
,		if (rpipe) {			/* Fix stdin */
,			close (STD_IN);
,			if (open ("/dev/tty", O_RDONLY, 0) < 0)
,				  exit (126);
,		}
,		execl (getenv ("SHELL"), getenv ("SHELL"), 0);
,		_exit (127);	/* Exit with 127 */
,	default:			/* This is the parent */
,		do {
,			w = wait (& status);
,		} while (w != -1 && w != pid);
,		waiterr = geterrno ();
,  }
,
,  raw_mode (ON);
,  RDwin ();
,
,  if (w == -1) {
,	error ("Wait error: ", serrorof (waiterr));
,	if (((status >> 8) == 127) || ((status >> 8) == 126)) sleep (2);
,  }
,  if ((status >> 8) == 127)		/* Child died with 127 */
,	error (getenv ("SHELL"), ": cannot exec this ${SHELL} (not found / not enough memory ?)");
,  else if ((status >> 8) == 126)
,	error ("Cannot open /dev/tty as fd #0", NIL_PTR);
,#else
,  notimpl ();
,#endif
,}
,
,/*  ==================================================================	*
, *				Main					*
, *  ==================================================================	*/
,
,main (argc, argv)
,  int argc;
,  char * argv [];
,{
,  register int index;	/* index in key table */
,  int initlinenum;
,  int initlinenumi = 0;
,  LINE * initlinenumline;
,  char * Mark;
,  FLAG goon;
,
,/* fprot = umask (0); */
,
,  if (getenv ("NoCtrlSQ") || getenv ("NoControlSQ")) {
,	/* ^S and ^Q may come arbitrarily from terminal, so don't use them */
,	key_map ['\021'] = I;
,	key_map ['\023'] = I;
,  }
,  if (getenv ("MINEDMULT")) multiexit = TRUE;
,  Mark = (char *) getenv ("MINEDSHIFT");
,  if (Mark != NIL_PTR) {
,	SHIFT_MARK = Mark [0];
,	if (Mark [0] != '\0') SHIFT_BEG = Mark [1];
,  }
,  Mark = (char *) getenv ("MINEDTAB");
,  if (Mark != NIL_PTR) TABchar = (Mark [0] == '\0' ? '' : Mark [0]);
,  Mark = (char *) getenv ("MINEDRET");
,  if (Mark != NIL_PTR) {
,	RET_MARK = Mark [0];
,	if (RET_MARK) RET_BLANK = Mark [1];
,	if (RET_BLANK) RET_BLANK2 = Mark [2];
,  }
,
,  get_term ();
,
,  if (! can_clear_eol) {
,	for (index = 0; index < XMAX; index ++) /* Fill blank_line with spaces*/
,	    blank_line [index] = ' ';
,	blank_line [XMAX] = '\0';
,  }
,
,  fnami = 1;
,  goon = TRUE;
,  do {
,    if (fnami < argc) {
,	if (* argv [fnami] == '+') {
,	   initlinenumi = fnami;
,	   fnami += 1;
,	}
,	else if (streq (argv [fnami], "-v")) {
,	   viewonly = TRUE;
,	   fnami += 1;
,	}
,	else goon = FALSE;
,    } else goon = FALSE;
,  } while (goon == TRUE);
,
,  fnami_min = fnami;
,  fnami_max = argc - 1;
,  fnami_cnt = argc - fnami_min;
,  fnamv = argv;	/* Why does this produce a warning? C is such a stupid language! */
,  if (! (fnami < argc))
,     fnami = 0;
,
,  if (! isatty (STD_IN)) {	/* Reading from pipe */
,	if (fnami != 0) {
,		panic ("Cannot read both pipe and file", NIL_PTR);
,	}
,	rpipe = TRUE;
,	modified = TRUE;	/* Set modified flag not to loose buffer */
,	if ((input_fd = open ("/dev/tty", O_RDONLY, 0)) < 0)
,	   panic ("Cannot open /dev/tty for read", serror ());
,  }
,  if (! isatty (STD_OUT)) {
,	wpipe = TRUE;
,	modified = TRUE; /* Set modified flag not to ignore buffer on exit */
,    /*	if ((output_fd = open ("/dev/tty", O_WRONLY, 0)) < 0)
,	   panic ("Cannot open /dev/tty for write", serror ()); */
,  }
,
,  raw_mode (ON);	/* Set tty to appropriate mode */
,  clear_screen ();
,
,/*
, * Generate names of paste files and of panic-file
, */
,#ifdef unix
,  temp_dir = (char *) getenv ("TMPDIR");
,  if (temp_dir == NIL_PTR || temp_dir [0] == '\0') temp_dir = "/tmp";
,  if (getenv ("USER")) {
,	build_string (yankie_file, "%s/minedbuf.%s", temp_dir, getenv ("USER"));
,	build_string (panic_file, "%s/minedpanic.%s.%d", temp_dir, getenv ("USER"), getpid ());
,  }
,  else {
,	build_string (yankie_file, "%s/minedbuf.%d", temp_dir, geteuid ());
,	build_string (panic_file, "%s/minedpanic.%d.%d", temp_dir, geteuid (), getpid ());
,  }
,#endif
,#ifdef vms
,  if (getenv ("SYS$SCRATCH"))
,	temp_dir = "SYS$SCRATCH";
,  else	temp_dir = "SYS$LOGIN";
,  if (getenv ("USER")) {
,	build_string (yankie_file, "%s:$MINEDBUF.%s", temp_dir, getenv ("USER"));
,	build_string (panic_file, "%s:$MINEDPANIC.%s.%d", temp_dir, getenv ("USER"), getpid ());
,  }
,  else {
,	build_string (yankie_file, "%s:$MINEDBUF.%d", temp_dir, geteuid ());
,	build_string (panic_file, "%s:$MINEDPANIC.%d.%d", temp_dir, geteuid (), getpid ());
,  }
,#endif
,#ifdef msdos
,  temp_dir = (char *) getenv ("TEMP");
,  if (temp_dir == NIL_PTR || temp_dir [0] == '\0') temp_dir = (char *) getenv ("TMP");
,  if (temp_dir == NIL_PTR || temp_dir [0] == '\0') temp_dir = "\\";
,  build_string (yankie_file, "%s\\minedbuf", temp_dir);
,  build_string (panic_file, "%s\\mined-pa.nic", temp_dir);
,#endif
,
,  header = tail = (LINE *) alloc (sizeof (LINE)); /* Make header of list*/
,  header->text = NIL_PTR;
,  header->next = tail->prev = header;
,
,/* Load the file (if any) */
,  if (fnami == 0)
,	load_file_w_o_display (NIL_PTR);
,  else {
,	/* This should be applied to all file names, or better, not at all:
,	if (length_of (argv [fnami]) > maxLINE_LEN) {
,		argv [fnami] [maxLINE_LEN] = '\0';
,	}
,	*/
,	load_file_w_o_display (argv [fnami]);
,  }
,  loading = TRUE;	/* keep loading flag TRUE until entering main loop */
,
,  if (initlinenumi != 0) {
,     make_number (& initlinenum, argv [initlinenumi] + 1);
,     if (initlinenum > 0) {
,	if (initlinenum <= 0 || (initlinenumline = proceed (header->next, initlinenum - 1)) == tail)
,	   error ("Illegal line number: ", num_out ((long) initlinenum));
,	else {
,	   move_to (x, find_y_w_o_RD (initlinenumline));
,	   fstatus ("Read", -1L);
,	}
,     }
,  }
,  if (wpipe) {
,	file_name [0] = '\0'; /* don't let user believe he's editing a file */
,	fstatus ("Editing for standard output", -1L);
,  }
,  RD ();
,  flush ();
,  catch_signals (catch_interrupt);
,  loading = FALSE;
,
,/* Main loop of the editor */
,  for (;;) {
,	index = readchar ();
,	if (stat_visible == TRUE)
,		clear_status ();
,	if (quit == FALSE) {	/* Call the function for the typed key */
,		(* key_map [index]) (index);
,		if (hop_flag > 0) hop_flag --;
,		flush ();	/* Flush output (if any) */
,	}
,	if (quit == TRUE) {
,		CANCEL ();
,		quit = FALSE;
,	}
,  }
,  /* NOTREACHED */
,}
,
,/*  ==================================================================	*
, *				End					*
, *  ==================================================================	*/
EOSED

exit 0

Xref: sparky comp.editors:1792 comp.emacs:2662