[net.sources] Bubble sorts and such-like

ojs@fortune.UUCP (Oliver Sharp) (06/20/84)

 often faster, but the bubble sort
    is perfectly valid for teaching.  I like to use mergesorts when I
    need fairly fast speed, since this is simple to implement and runs
    in order n*log(n).  It isn't quite as good as quicksort or its
    derivatives, but it isn't bad.  I just like the clean and simple
    implementation it allows and it is easy to explain to people in a
    couple of minutes.

	In general, though, I agree with the person who supported the
	person posting the article (sorry about all those person's).
	If you aren't interested in the article, don't read it.  Any
	submission of interest to at least a couple of people should
	be encouraged - that is what the net is for, after all!

	(still working on a sign-off message)

Oliver Sharp
	   ....!fortune!ojs

ags@pucc-i (Seaman) (06/20/84)

>  A bubble sort is the fastest means possible to sort an already-sorted list 
>  (order n, even omega n!).  

Please explain how the bubble sort is faster than the insertion sort for
an already-sorted list.

The bubble sort requires...
  n-1 comparisons
  0   exchanges.

The insertion sort requires...
  n-1 comparisons
  0   exchanges.

>  I just like the clean and simple implementation it allows and it is easy to 
>  explain to people in a couple of minutes.

Insertion sort is cleaner and simpler and easier to explain, besides being
faster.  If you don't believe it, write down the loop invariants for the
two algorithms and see which one is simpler.  You might also compare the
diagrams which Knuth used to illustrate the two sorts and see which is
simpler.

"The teaching of bubble sorts ought to be considered a criminal offense."
-- 

Dave Seaman			"My hovercraft is full of eels."
..!pur-ee!pucc-i:ags

berry@zinfandel.UUCP (07/03/84)

#R:fortune:-363600:zinfandel:19300002:000:29793
zinfandel!berry    Jun 26 12:50:00 1984

 does
econometric analysis of time series data?
  i need to be able to:
     1. set up a data base of variables with their periodicity,
dates and data.
     2. transform existing variables to create and store new
ones.
     3. use leads and lags.

    thanks,
     karen virtue, ucsd economics dept.

Ted Goldstein has kindly pointed out that the sources
as distributed have an omission error in "hoc.y".

The fourth line (#define code3...) should be
appended by the following text:
	; code (c3)

In addition, I seem to be receiving many messages with no
bodies.  All of them are coming thru seismo.
Perhaps somebody knows why these messages have no bodies ?

	lef@nlm-vax
cat >NET.HEADER << !SMALL!FLAME!

I like to suggest that all people distributing sources in shar format,
be so kind as to put the header in shar format as well.  It's a single
wee bit of effort, that would save a lot of us a wee bit of effort.

-len tower        {ihnp4,harpo}!inmet!tower        Cambridge, MA
!SMALL!FLAME!
the net resources as this simple query, and I want people who read all
these groups.  Specifically:  When writing code that is to work for
BSD and USG unix, the standard method in the past has been to use some
flag in an '#ifdef'.  Now this is fine, BUT:  at different times, I've
seen all of the following: BSD, BSD2, B4.2 for Berkeley, and V7, SYS3,
USG, and USG3.0 for the product originating at Bell Laboratories (Now
known as AT&T Bell Laboratories...for the lawyers reading this.
Happy guys?)

Can we reach some consensus for a convention that makes sense for
ongoing versions, and isn't tied to something that may become obsolete
(such as USG--stood for Unix Support Group)?  Perhaps, if we come up with
something coherent enough, maybe the Berkeley people and WeCo could
actually agree to put an identifier in the 'C' compiler, much in the
way that the machine type is identified in the current compiler.

All suggestions and comments should be MAILED to me. (Flames will
probably go to /dev/water.bucket--or you may get 'em back, with interest...)
Assuming that I get any responses, NLT than two weeks after the last,
I'll summarize the responses to the net.

			Thanks,

				Dave Ihnat
				ihuxx!ignatz
(was not working correctly.

*** /tmp/,RCSt1008279	Fri Jun 29 14:37:01 1984
--- corewar.c	Fri Jun 29 14:35:13 1984
***************
*** 444,451
  	}
  	switch (inst.b_mode)
  	{
! 		case IMMEDIATE: /* error */
! 			if (data.a == inst.b) return 1;
  			else return 2;
  		case DIRECT:
  			if ( data.a == mem[(addr + inst.b) % MEMSIZE].b)

--- 444,451 -----
  	}
  	switch (inst.b_mode)
  	{
! 		case IMMEDIATE:
! 			if (data.b == inst.b) return 1;
  			else return 2;
  		case DIRECT:
  			if ( data.b == mem[(addr + inst.b) % MEMSIZE].b)
***************
*** 448,454
  			if (data.a == inst.b) return 1;
  			else return 2;
  		case DIRECT:
! 			if ( data.a == mem[(addr + inst.b) % MEMSIZE].b)
  				return 1;
  			else return 2;
  		case INDIRECT:

--- 448,454 -----
  			if (data.b == inst.b) return 1;
  			else return 2;
  		case DIRECT:
! 			if ( data.b == mem[(addr + inst.b) % MEMSIZE].b)
  				return 1;
  			else return 2;
  		case INDIRECT:
___________________________________________________________
Matt		University	ARPA: crawford@anl-mcs.arpa
Crawford	of Chicago	UUCP: ihnp4!oddjob!matt
of hoc6's hoc.y  and the K&P version that makes it core dump when 
func's or proc's are read in. Edit hoc.y and replace "getchar()" with
"getc(fin)".  If you are on a USG system, replace "index(" with 
"strchr(".  With these changes the functions "fib" and "fac" (P  274)
and "ack" (P 284) in K&P work fine.

Here is the output from "diff hoc.y hoc.y.orig":

133c133
< 	while ((c=getc(fin)) == ' ' || c == '\t')
---
> 	while ((c=getchar()) == ' ' || c == '\t')
153c153
< 		} while ((c=getc(fin)) != EOF && isalnum(c));
---
> 		} while ((c=getchar()) != EOF && isalnum(c));
201c201
<         char *strchr();
---
>         char *index();  /* `strchr()' in some systems */
206,207c206,207
< 	if (islower(c) && strchr(transtab, c))
< 		return strchr(transtab, c)[1];
---
> 	if (islower(c) && index(transtab, c))
> 		return index(transtab, c)[1];

###
Dan O'Connell
ATT&T @ AT&T Bell Laboratories
Whippany, NJ
floyd!danoc

<nobody eats my lines and lives>

Here are the sources to the Dr. Dobbs. 'red' editor as promised a few
weeks ago. The sources as given here are untouched and virginal as I
thought thats the way you would prefer them. I do have a few hacked
versions of the thing and I have put a few comments in the sources
to point out what I have done to this relatively untouched version.
My fingerprint is KEDB so to get a quick(ish) summary, just do an
	fgrep KEDB ed*.[ch]
I am not, nor do I intend to, document this thing. If you want to
know what it does then either look at the code (it's pretty self
explanatory) or obtain a copy of Dr. Dobbs journal (No 63 1982).
I no longer have this journal (I typed this in some time ago!!)
and unfortunately do not have an unhacked ed1.h. I can't remember
the origonal special function key codes so I've just included
a few I have made up. Feel free to change them yourselves, the originals
weren't particularly logical anyway!!
Anyway, hope you have as much fun hacking these as I didn't.
		Cheers,
			Keith B.
P.S.
	To unbundle, make and enter a new directory, copy the news
files into it, remove the news headers and this blurb from the tops
and run each file through 'sh' (ie. type 'sh fname' for each shar).
If u r not using UNIX then u will have 2 unbundle the files by hand.

---------------- CUT HERE ------------------

echo 'unbundling ed[01].h ed[23].c'
cat >ed0.h <<____EOF____
/* Screen editor:	non-user defined globals.
 * 
 * Source: ed0.c
 * Version: June 19, 1980.
 */

/* Define global constants */

/* Define constants describing a text line */

#define MAXLEN  133	/* max chars per line */
#define MAXLEN1 134	/* MAXLEN + 1 */

/* define operating system constants */

#define SYSFNMAX 15     /* CPM filename length + 1 */

/* Define miscellaneous constants */

#define EOS	0	/* Code sometimes assumes \0 */
#define OK	1
#define ERR	-1	/* Error. Must be <0 */
#define YES	1	/* Must be non zero */
#define NO	0
#define CR	13	/* Carriage return */
#define LF	10	/* Line feed */
#define TAB	9	/* Tab character */
#define HUGE	32000	/* Practically infinity ! */
____EOF____
cat >ed1.h <<____EOF____
/*
 * Screen editor: Special key definitions.
 *
 * Source: ed1.c
 * Version 2: September 6, 1981.
 */

/*
 * Define which keys are used for special edit functions.
 */

#define UP1	25		/* ctrl_Y -- Insert up    */
#define DOWN1	10		/* RETURN -- Insert down  */ 
#define UP2	21		/* ctrl_U -- cursor up    */
#define DOWN2	4		/* ctrl_D -- cursor down  */
#define LEFT1	8		/* ctrl_H -- cursor left  */
#define RIGHT1	18		/* ctrl_R -- cursor right */
#define INS1	11		/* ctrl_K -- insert mode  */
#define EDIT1	5		/* ctrl_E -- edit mode    */ 
#define ESC1	27		/* ESCAPE -- command mode */
#define DEL1	127		/* DELETE -- delete char  */
#define ZAP1	26		/* ctrl_Z -- delete line  */
#define ABT1	24		/* ctrl_X -- undo         */
#define SPLT1	23		/* ctrl_W -- split line   */
#define JOIN1	16		/* ctrl_P -- join lines   */

/*
 * Define length and width of screen and printer.
 */

#define SCRNW	80
#define SCRNW1	79
#define SCRNL	24
#define SCRNL1	23
#define SCRNL2	22
#define COMSBUFSZ 132
____EOF____
cat >ed2.c <<____EOF____
#include "ed0.h"
#include "ed1.h"

/*
 * Screen editor: Main program.
 *
 * source: ed2.c
 * Version: September 5, 1981.
 */

/* define signon message */

#define SIGNON "small c editor as published Dr Dobbs Jan 82"

/*
 * The main program dispatches the routines that
 * handle the various modes.
 */

#define CMNDMODE  1	/* Enter command mode flag */
#define INSMODE   2	/* Enter insert mode flag */
#define EDITMODE  3	/* Enter edit mode flag */
#define EXITMODE  4	/* Exit editor flag */

main()
{
int mode ;
	/* fmt output by default goes to the screen */
	fmtassn(NO) ;
	/* set tabs, clear the screen and sign on */
	fmtset(8) ;
	outclr() ;
	outxy(0,SCRNL1) ;
	message(SIGNON) ;
	outxy(0,1) ;
	/* clear filename[] for save() , resave() */
	name("") ;
	/* clear the main buffer */
	bufnew() ;
	/* start off in command mode */
	mode=CMNDMODE ;
	/* get null line 1 for edit() */
	edgetln() ;
	while(1) {
		if (mode == EXITMODE) {
			break ;
		}
		else if (mode == CMNDMODE) {
			mode=command() ;
		}
		else if (mode == EDITMODE) {
			mode=edit() ;
		}
		else if (mode == INSMODE) {
			mode=insert() ;
		}
		else {
			syserr("main: no mode") ;
			mode=EDITMODE ;
		}
	}
}

/*
 * handle edit mode.
 * Dispatch the proper routine based on one character commands
 */

edit()
{
char buffer[SCRNW1] ;
int v ;
int x,y ;
char c ;
	/*
	 * We cant do edgetln() or edgo() here because
	 * those calls reset the cursor.
	 */
	pmtedit() ;
	while(1) {
		/* get command */
		c=tolower(syscin()) ;
		if ((c == ESC1) || (c == 'c')) {
			/* enter command mode */
			return (CMNDMODE) ;
		}
		else if ((c == INS1) || (c == 'i')) {
			/* enter insert mode */
			return (INSMODE) ;
		}
		else if (special(c) == YES) {
			if ((c == UP1) || (c == DOWN1)) {
				return(INSMODE) ;
			}
			else {
				continue ;
			}
		}
		else if (control(c) == YES) {
			continue ;
		}
		else if (c == ' ') {
			edright() ;
			pmtcol() ;
		}
		else if (c == 'b') {
			edbegin() ;
			pmtcol() ;
		}
		else if (c == 'd') {
			/* scroll down */
			pmtmode("edit: scroll") ;
			while (bufnrbot() == NO) {
				if (chkkey() == YES) {
					break ;
				}
				if (eddn() == ERR) {
					break ;
				}
			}
			pmtedit() ;
		}
		else if (c == 'e') {
			edend() ;
			pmtcol() ;
		}
		else if (c == 'g') {
			/* save x , y in case we dont get number */
			x=outgetx() ;
			y=outgety() ;
			pmtcmnd("edit: goto: ",buffer) ;
			if (number(buffer,&v)) {
				edgo(v,0) ;
			}
			else {
				outxy(x,y) ;
			}
			pmtedit() ;
		}
		else if (c == 'k') {
			pmtmode("edit: kill") ;
			c=syscin() ;
			if ((special(c) == NO) && (control(c) == NO)) {
				edkill(c) ;
			}
			pmtedit() ;
		}
		else if (c == 's') {
			pmtmode("edit: search") ;
			c=syscin() ;
			if ((special(c) == NO) && (control(c) == NO)) {
				edsrch(c) ;
			}
			pmtedit() ;
		}
		else if (c == 'u') {
			/* scroll up */
			pmtmode("edit: scroll") ;
			while (bufattop() == NO) {
				if (chkkey() == YES) {
					break ;
				}
				if (edup() == ERR) {
					break ;
				}
			}
			pmtedit() ;
		}
		else if (c == 'x') {
			pmtmode("edit: eXchange") ;
			c=syscin() ;
			if ((special(c) == NO) && (control(c) == NO)) {
				edchng(c) ;
			}
			pmtedit() ;
		}
		/* do nothing if command not found */
	}
}

/*
 * Insert mode.
 * In this mode the UP1 , UP2 keys reverse there roles ,
 * as do the DOWN1 , DOWN2 keys.
 */

insert()
{
char c ;
	pmtmode("insert") ;
	while (1) {
		/* get command */
		c=syscin() ;
		if (c == ESC1) {
			/* enter command mode */
			return (CMNDMODE) ;
		}
		else if (c == EDIT1) {
			/* enter edit mode */
			return (EDITMODE) ;
		}
		else if (c == INS1) {
			/* do nothing */
			;
		}
		else if (special(c) == YES) {
			if ((c == UP2) || (c == DOWN2)) {
				return (EDITMODE) ;
			}
			else {
				continue ;
			}
		}
		else if (control(c) == YES) {
			/* ignore non-special control characters */
			continue ;
		}
		else {
			/* insert one char in line */
			edins(c) ;
			pmtcol() ;
		}
	}
}

/* Return YES if c is a control char */

control(c)
char c ;
{
	if (c == TAB) {
		return (NO) ;		/* tab is regular */
	}
	else if (c >= 127) {
		return (YES) ;		/* del or high bit on */
	}
	else if (c < 32) {
		return (YES) ;
	}
	else {
		return (NO) ;		/* normal */
	}
}

/*
 * handle the default actions of all special keys.
 * return YES if c is one of the keys.
 */

special(c)
char c ;
{
	if (c == JOIN1) {
		edjoin() ;
		pmtline() ;
		return (YES) ;
	}
	if (c == SPLT1) {
		edsplit() ;
		pmtline() ;
		return (YES) ;
	}
	if (c == ABT1) {
		edabt() ;
		pmtcol() ;
		return (YES) ;
	}
	else if (c == DEL1) {
		eddel() ;
		pmtcol() ;
		return (YES) ;
	}
	else if (c == ZAP1) {
		edzap() ;
		pmtline() ;
		return(YES) ;
	}
	else if (c == UP2) {
		/* move up */
		edup() ;
		pmtline() ;
		return (YES) ;
	}
	else if (c == UP1) {
		/* insert up */
		ednewup() ;
		pmtline() ;
		return (YES) ;
	}
	else if (c == DOWN2) {
		/* move down */
		eddn() ;
		pmtline() ;
		return (YES) ;
	}
	else if (c == DOWN1) {
		/* insert down */
		ednewdn() ;
		pmtline() ;
		return (YES) ;
	}
	else if (c == LEFT1) {
		edleft() ;
		pmtcol() ;
		return (YES) ;
	}
	else if (c == RIGHT1) {
		edright() ;
		pmtcol() ;
		return (YES) ;
	}
	else {
		return (NO) ;
	}
}

/*

 * command() dispatches command routines while
 * in command mode.
 */

command()
{
int v ;
char c ;
char args[SCRNW1] ;
/* char *skipbl() ;	Good idea if your compilers up to it!! KEDB */
char *argp ;
int topline ;
int ypos ;
int oldline ;
int k ;
	/* command mode commands may move the current line.
	 * coomand mode must save the current line on entry
	 * and restore it on exit.
	 */
	edrepl() ;
	/* remember how the screen was drawn on entry */
	oldline=bufln() ;
	ypos=outgety() ;
	topline=oldline-ypos+1 ;
	while(1) {
		outxy(0,SCRNL1) ;
		fmtcrlf() ;
		pmtmode("command:") ;
		getcmnd(args,0) ;
		fmtcrlf() ;
		pmtline() ;
		c=args[0] ;
		if ((c == EDIT1) || (c == INS1)) {
			/* redraw screen */
			if (oldline == bufln()) {
				/* get current line */
				edgetln() ;
				/* redraw old screen */
				bufout(topline,1,SCRNL1) ;
				outxy(0,ypos) ;
			}
			else {
				/* update line and screen */
				edgo(bufln(),0) ;
			}
			if (c == EDIT1) {
				return (EDITMODE) ;
			}
			else {
				return (INSMODE) ;
			}
		}
		else if (tolower(args[0]) == 'g') {
			argp=skipbl(args+1) ;
			if (argp[0] == EOS) {
				edgo(oldline,0) ;
				return (EDITMODE) ;
			}
			else if (number(argp,&v) == YES) {
				edgo(v,0) ;
				return (EDITMODE) ;
			}
			else {
				message("bad line number") ;
			}
		}
		else if (lookup(args,"append")) {
			append(args) ;
		}
		else if (lookup(args,"change")) {
			change(args) ;
		}
		else if (lookup(args,"clear")) {
			clear() ;
		}
		else if (lookup(args,"delete")) {
			delete(args) ;
		}
		else if (lookup(args,"dos")) {
			if (chkbuf() == YES) {
				return (EXITMODE) ;
			}
		}
		else if (lookup(args,"find")) {
			if ((k = find()) >= 0) {
				edgo(bufln(),k) ;
				return (EDITMODE) ;
			}
			else {
				/* get current line */
				bufgo(oldline) ;
				edgetln() ;
				/* stay in command mode */
				message("pattern not found") ;
			}
		}
		else if (lookup(args,"list")) {
			list(args) ;
		}
		else if (lookup(args,"load")) {
			load(args) ;
		}
		else if (lookup(args,"name")) {
			name(args) ;
		}
		else if (lookup(args,"resave")) {
			resave() ;
		}
		else if (lookup(args,"save")) {
			save() ;
		}
		else if (lookup(args,"search")) {
			search(args) ;
		}
		else if (lookup(args,"tabs")) {
			tabs(args) ;
		}
		else {
			message("command not found") ;
		}
	}
}

/* return YES if line starts with a command */

lookup(line,comand) 
char *line , *comand ;
{
	while (*comand) {
		if (tolower(*line++) != *comand++) {
			return (NO) ;
		}
	}
	if ((*line == EOS) || (*line == ' ') || (*line == TAB)) {
		return (YES) ;
	}
	else {
		return (NO) ;
	}
}

/* get next command into argument buffer */

getcmnd(args,offset)
char *args ;
int offset ;
{
int j,k ;
char c ;
	outxy(offset,outgety()) ;
	outdeol() ;
	k=0 ;
	while ((c=syscin()) != CR) {
		if ((c == EDIT1) || (c == INS1)) {
			args[0]=c ;
			return ;
		}
		if ((c == DEL1) || (c == LEFT1)) {
			if (k > 0) {
				outxy(offset,outgety()) ;
				outdeol() ;
				k-- ;
				j=0 ;
				while (j < k) {
					outchar(args[j++]) ;
				}
			}
		}
		else if (c == ABT1) {
			outxy(offset,outgety()) ;
			outdeol() ;
			k=0 ;
		}
		else if ((c != TAB) && ((c < 32) || (c == 127))) {
			/* do nothing */
			continue ;
		}
		else {
			if ((k+offset) < SCRNW1) {
				args[k++]=c ;
				outchar(c) ;
			}
		}
	}
	args[k]=EOS ;
}
____EOF____
cat >ed3.c <<____EOF____
#include "ed0.h"
#include "ed1.h"

/*
 * screen editor: command mode commands.
 *
 * source: ed3.c
 * Version: September 5, 1981.
 *
 */

/* data global to these routines */

char filename[SYSFNMAX] ;
/* char *skiparg(), *skipbl() ;	if your compilers man enough!! (KEDB) */

/*
 * append command.
 * load a file into main buffer at current location.
 * this command does NOT change the current filename.
 */

append(args)
char *args ;
{
char buffer[MAXLEN] ;		/* disk line buffer */
int file ;
int n ;
int topline ;
char locfn[SYSFNMAX] ;		/* local filename */
	/* get filename which follows command */
	if (name1(args,locfn) == ERR) {
		return ;
	}
	if (locfn[0] == EOS) {
		message("no file argument") ;
		return ;
	}
	/* open the new file */
	if ((file=sysopen(locfn,"r")) == ERR) {
		message("file not found") ;
		return ;
	}
	/* read the file into the buffer */
	while ((n=readline(file,buffer,MAXLEN)) >= 0) {
		if (n > MAXLEN) {
			message("line truncated") ;
			n=MAXLEN ;
		}
		if (bufins(buffer,n) == ERR) {
			break ;
		}
		if (bufdn() == ERR) {
			break ;
		}
	}
	/* close the file */
	sysclose(file) ;
	/*
	 * redraw the screen so topline will be at top
	 * of the screen after command() does a CR/LF.
	 */
	topline=max(1,bufln()-SCRNL2) ;
	bufout(topline,2,SCRNL2) ;
	bufgo(topline) ;
}

/* global chang command */

change(args)
char *args ;
{
char oldline[MAXLEN1] ;
char newline[MAXLEN1] ;
char oldpat[MAXLEN1] ;
char newpat[MAXLEN1] ;
int from, to, col, n, k ;
	if (get2args(args,&from,&to) == ERR) {
		return ;
	}
	/* get search and change masks into oldpat and newpat */
	fmtsout("search mask?  ",0) ;
	getcmnd(oldpat,15) ;
	fmtcrlf() ;
	if (oldpat[0] == EOS) {
		return ;
	}
	pmtline() ;
	fmtsout("change mask?  ",0) ;
	getcmnd(newpat,15) ;
	fmtcrlf() ;
	/* make substitutions for lines between from, to */
	while (from <= to) {
		if (chkkey() == YES) {
			break ;
		}
		if (bufgo(from++) == ERR) {
			break ;
		}
		if (bufatbot() == YES) {
			break ;
		}
		n=bufgetln(oldline,MAXLEN) ;
		n=min(n,MAXLEN) ;
		oldline[n]=EOS ;
		/* '^' anchors search */
		if (oldpat[0] == '^') {
			if (amatch(oldline,oldpat+1,0) == YES) {
				k=replace(oldline,newline,
					oldpat+1,newpat,0) ;
				if (k == ERR) {
					return ;
				}
				fmtcrlf() ;
				putdec(bufln(),5) ;
				fmtsout(newline,5) ;
				outdeol() ;
				bufrepl(newline,k) ;
			}
			continue ;
		}
		/* search oldline for oldpat */
		col=0 ;
		while (col < n) {
			if (amatch(oldline,oldpat,col++) == YES) {
				k=replace(oldline,newline,
					oldpat,newpat,col-1) ;
				if (k == ERR) {
					return ;
				}
				fmtcrlf() ;
				putdec(bufln(),5) ;
				fmtsout(newline,5) ;
				outdeol() ;
				bufrepl(newline,k) ;
				break ;
			}
		}
	}
	fmtcrlf() ;
}

/* clear main buffer and filename */

clear()
{
	/* make sure it is ok to clear buffer */
	if (chkbuf() == YES) {
		filename[0]=0 ;
		pmtfile("") ;
		outclr() ;
		outxy(0,SCRNL1) ;
		bufnew() ;
		message("buffer cleared") ;
	}
}

/* multiple line delete command */

delete(args)
char *args ;
{
int from , to ;
	if (get2args(args,&from,&to) == ERR) {
		return ;
	}
	if (from > to) {
		return ;
	}
	/* go to first line to be deleted */
	if (bufgo(from) == ERR) {
		return ;
	}
	/* delete all lines betwwen from and to */
	if (bufdeln(to-from+1) == ERR) {
		return ;
	}
	/* redraw the screen */
	bufout(bufln(),1,SCRNL1) ;
}

/*
 * searches all lines below the current line for a pattern.
 * return -1 if pattern not found.
 * otherwise, return column number of start of pattern.
 */

find()
{
	return (search1(bufln()+1,HUGE,YES)) ;
}

/* list lines to list device */

list(args)
char *args ;
{
char linebuf[MAXLEN1] ;
int n ;
int from , to , line , oldline ;
	/* save the buffers current line */
	oldline=bufln() ;
	/* get starting, ending lines to print */
	if (get2args(args,&from,&to) == ERR) {
		return ;
	}
	/* print lines one at atime to list device */
	line=from ;
	while (line <= to) {
		/* make sure prompt goes to console */
		fmtassn(NO) ;
		/* check for interrupt */
		if (chkkey() == YES) {
			break ;
		}
		/* print line to list device */
		fmtassn(YES) ;
		if (bufgo(line++) != OK) {
			break ;
		}
		if (bufatbot()) {
			break ;
		}
		n=bufgetln(linebuf,MAXLEN1) ;
		n=min(n,MAXLEN) ;
		linebuf[n]=CR ;
		fmtsout(linebuf,0) ;
		fmtcrlf() ;
	}
	/* redirect output to console */
	fmtassn(NO) ;
	/* restore the cursor */
	bufgo(oldline) ;
}

/* load file into buffer */

load(args)
char *args ;
{
char buffer[MAXLEN] ;		/* disk line buffer */
char locfn [SYSFNMAX] ;		/* file name until we check it */
int n ;
int file ;
int topline ;
	/* get filename following command */
	if (name1(args,locfn) == ERR) {
		return ;
	}
	if (locfn[0] == EOS) {
		message("no file argument") ;
		return ;
	}
	/* give user a chance to save the buffer */
	if (chkbuf() == NO) {
		return ;
	}
	/* open the new file */
	if ((file=sysopen(locfn,"r")) == ERR) {
		message("file not found") ;
		return ;
	}
	/* update file name */
	syscopfn(locfn,filename) ;
	pmtfile(filename) ;
	/* clear the buffer */
	bufnew() ;
	/* read the file into the buffer */
	while ((n=readline(file,buffer,MAXLEN)) >= 0) {
		if (n > MAXLEN) {
			message("line truncated") ;
			n=MAXLEN ;
		}
		if (bufins(buffer,n) == ERR) {
			break ;
		}
		if (bufdn() == ERR) {
			break ;
		}
	}
	/* close the file */
	sysclose(file) ;
	/* indicate that the buffer is fresh */
	bufsaved() ;
	/* set current line to line */
	bufgo(1) ;
	/*
	 * redraw the screen so that the top line
	 * will be on line 1 after command() does a CR/LF
	 */
	topline=max(1,bufln()-SCRNL2) ;
	bufout(topline,2,SCRNL2) ;
	bufgo(topline) ;
}

/* change current file name */

name(args)
char *args ;
{
	name1(args,filename) ;
	pmtfile(filename) ;
}

/*
 * checks syntax of args.
 * copy to filename.
 * return OK if the name is valid.
 */

name1(args,filname)
char *args , *filname ;
{
	/* skip command */
	args=skiparg(args) ;
	args=skipbl(args) ;
	/* check filename syntax */
	if (syschkfn(args) == ERR) {
		return (ERR) ;
	}
	/* copy filename */
	syscopfn(args,filname) ;
	return (OK) ;
}

/* save the buffer in an already existing file */

resave()
{
char linebuf[MAXLEN] ;
int file, n, oldline ;
	/* make sure file has a name */
	if (filename[0] == EOS) {
		message("file not named") ;
		return ;
	}
	/* the file must exist for resave */
	if ((file=sysopen(filename,"r")) == ERR) {
		message("file not found") ;
		return ;
	}
	if (sysclose(file) == ERR) {
		return ;
	}
	/* open the file for writing */
	if ((file=sysopen(filename,"w")) == ERR) {
		return ;
	}
	/* save the current position of file */
	oldline=bufln() ;
	/* write out the whole file */
	if (bufgo(1) == ERR) {
		sysclose(file) ;
		return ;
	}
	while (bufatbot() == NO) {
		n=bufgetln(linebuf,MAXLEN) ;
		n=min(n,MAXLEN) ;
		if (pushline(file,linebuf,n) == ERR) {
			break ;
		}
		if (bufdn() == ERR) {
			break ;
		}
	}
	/* indicate if all buffer was saved */
	if (bufatbot()) {
		bufsaved() ;
	}
	/* close file and restore line number */
	sysclose(file) ;
	bufgo(oldline) ;
}

/* save the buffer in a new file */

save()
{
char linebuf[MAXLEN] ;
int file , n , oldline ;
	/* make sure the file is named */
	if (filename[0] == EOS) {
		message("file not named") ;
		return ;
	}
	/* file must NOT exist for save */
	if ((file=sysopen(filename,"r")) != ERR) {
		sysclose(file) ;
		message("file exists") ;
		return ;
	}
	/* open file for writing */
	if ((file=sysopen(filename,"w")) == ERR) {
		return ;
	}
	/* remember current line */
	oldline=bufln() ;
	/* write entire buffer to file */
	if (bufgo(1) == ERR) {
		sysclose(file) ;
		return ;
	}
	while (bufatbot() == NO) {
		n=bufgetln(linebuf,MAXLEN) ;
		n=min(n,MAXLEN) ;
		if (pushline(file,linebuf,n) == ERR) {
			break ;
		}
		if (bufdn() == ERR) {
			break ;
		}
	}
	/* indicate buffer saved if good write */
	if (bufatbot()) {
		bufsaved() ;
	}
	/* restore line and close file */
	bufgo(oldline) ;
	sysclose(file) ;
}

/* global search commands */

search(args)
char *args ;
{
int from , to ;
	if (get2args(args,&from,&to) == ERR) {
		return ;
	}
	search1(from,to,NO) ;
}

/*
 * search lines for a pattern.
 * if flag == YES:	stop at the first match.
 *			return -1 if no match.
 *			otherwise return column number of match.
 * if flag == NO :	print all matches found.
 */

search1(from,to,flag)
int from , to , flag ;
{
char pat  [MAXLEN1] ;
char line [MAXLEN1] ;
int col , n ;
	/* get search mask into pat  */
	fmtsout("search mask?  ",0) ;
	getcmnd(pat,15) ;
	fmtcrlf() ;
	if (pat[0] == EOS) {
		return ;
	}
	/* search all lines between from and to for pat */
	while (from <= to) {
		if (chkkey() == YES) {
			break ;
		}
		if (bufgo(from++) == ERR) {
			break ;
		}
		if (bufatbot() == YES) {
			break ;
		}
		n=bufgetln(line,MAXLEN) ;
		n=min(n,MAXLEN) ;
		line[n]=EOS ;
		/* '^' anchors search */
		if (pat[0] == '^') {
			if (amatch(line,pat+1,0) == YES) {
				if (flag == NO) {
					fmtcrlf() ;
					putdec(bufln(),5) ;
					fmtsout(line,5) ;
					outdeol() ;
				}
				else {
					return(0) ;
				}
			}
			continue ;
		}
		/* search whole line for match */
		col=0 ;
		while (col < n) {
			if (amatch(line,pat,col++) == YES) {
				if (flag == NO) {
					fmtcrlf() ;
					putdec(bufln(),5) ;
					fmtsout(line,5) ;
					outdeol() ;
					break ;
				}
				else {
					return(col-1) ;
				}
			}
		}
	}
	/* all searching is finished */
	if (flag == YES) {
		return (-1) ;
	}
	else {
		fmtcrlf() ;
	}
}

/* set tab stops for fmt routines */

tabs(args)
char *args ;
{
int n, junk ;
	if (get2args(args,&n,&junk) == ERR) {
		return ;
	}
	fmtset(n) ;
}

/* return YES if buffer may be drastically changed */

chkbuf()
{
	if (bufchng() == NO) {
		/* buffer not changed - no problem! */
		return (YES) ;
	}
	fmtsout("buffer not saved. proceed?  ",0) ;
	pmtline() ;
	if (tolower(syscout(syscin())) != 'y') {
		fmtcrlf() ;
		message("cancelled") ;
		return(NO) ;
	}
	else {
		fmtcrlf() ;
		return(YES) ;
	}
}

/* print message from a command */

message(s)
char *s ;
{
	fmtsout(s,0) ;
	fmtcrlf() ;
}

/*
 * get two arguments the argument line args
 * no arguments imply 1 HUGE.
 * one argument implies both args the same.
 */

get2args(args,val1,val2)
char *args ;
int *val1, *val2 ;
{
	/* skip over the command */
	args=skiparg(args) ;
	args=skipbl(args) ;
	if (*args == EOS) {
		*val1=1 ;
		*val2=HUGE ;
		return (OK) ;
	}
	/* check first argument */
	if (number(args,val1) == NO) {
		message("bad argument") ;
		return (ERR) ;
	}
	/* skip over first argument */
	args=skiparg(args) ;
	args=skipbl(args) ;
	/* 1 arg : arg 2 is HUGE */
	if (*args == EOS) {
		*val2=HUGE ;
		return (OK) ;
	}
	/* check second argument */
	if (number(args,val2) == NO) {
		message("bad argument") ;
		return (ERR) ;
	}
	else {
		return (OK) ;
	}
}

/* skip over all except EOS and blanks */

/* char *	(KEDB) */
skiparg(args)
char *args ;
{
	while ((*args != EOS) && (*args != ' ')) {
		args++ ;
	}
	return (args) ;
}

/* skip over all blanks */

/* char *	(KEDB) */
skipbl(args)
char *args ;
{
	while (*args ==  ' ') {
		args++ ;
	}
	return (args) ;
}

/*
 * return YES if the user has pressed any key.
 * blanks cause a transparent pause.
 */

chkkey()
{
int c ;
	c=syscstat() ;
	if (c == 0) {
		/* No character at keyboard */
		return(NO) ;
	}
	else if (c == ' ') {
		/* pause. another blank ends pause. */
		pmtline() ;
		if (syscin() == ' ') {
			return (NO) ;
		}
	}
	/* we got a nonblank character */
	return (YES) ;
}

/*
 * anchored search for pattern in text line at column col.
 * return YES if the pattern starts at col
 */

amatch(line,pat,col)
char *line , *pat ;
int col ;
{
int k ;
	k=0 ;
	while (pat[k] != EOS) {
		if (pat[k] == line[col]) {
			k++ ;
			col++ ;
		}
		else if ((pat[k] == '?') && (line[col] != EOS)) {
			/* question mark matches any char */
			k++ ;
			col++ ;
		}
		else {
			return (NO) ;
		}
	}
	/* the entire pattern matches */
	return (YES) ;
}

/*
 * replace oldpat in oldline by newpat starting at col.
 * put result in newline.
 * return number of characters in newline.
 */

replace(oldline,newline,oldpat,newpat,col)
char *oldline, *newline, *oldpat, *newpat ;
int col ;
{
int k ;
char *tail, *pat ;
	/* copy oldline preceeding col to newline */
	k=0 ;
	while (k < col) {
		newline[k++]=(*oldline++) ;
	}
	/* remember where end of  oldpat in oldline is */
	tail=oldline ;
	pat=oldpat ;
	while (*pat++ != EOS) {
		tail++ ;
	}
	/*
	 * copy newpat to newline.
	 * use oldline and oldpat to resolve question marks
	 *  in newpat.
	 */
	while (*newpat != EOS) {
		if (k > MAXLEN-1) {
			message("new line too long") ;
			return (ERR) ;
		}
		if (*newpat != '?') {
			/* copy newpat to newline */
			newline[k++]=(*newpat++) ;
			continue ;
		}
		/* scan for '?' in oldpat */
		while (*oldpat !=  '?') {
			if (*oldpat == EOS) {
				message("too many ?'s in change mask") ;
				return (ERR) ;
			}
			oldpat++ ;
			oldline++ ;
		}
		/* copy char from oldline too newline */
		newline[k++]=(*oldline++) ;
		oldpat++ ;
		newpat++ ;
	}
	/* copy oldline after oldpat to newline */
	while (*tail != EOS) {
		if (k >= MAXLEN-1) {
			message("new line too long") ;
			return (ERR) ;
		}
		newline[k++]=(*tail++) ;
	}
	newline[k]=EOS ;
	return(k) ;
}
____EOF____

-- 

"Specialist subject, the bleedin' obvious!!"

             Keith Brown  ....!ukc!west44!kbrown
                          ( And other leading Usenet paths )
a
<nobody eats my lines and lives>

Here are the sources to the Dr. Dobbs. 'red' editor as promised a few
weeks ago. The sources as given here are untouched and virginal as I
thought thats the way you would prefer them. I do have a few hacked
versions of the thing and I have put