larry@tapa.uucp (Larry Pajakowski) (09/22/89)
I introduced a PROFS user to elm and he liked it alot. One thing he did like better about PROFS was the word wrap feature. I have not used profs myself but he said you could just keep typing and profs would break long lines into shorter ones on word boundries. Now since keystrokes in profs are not interactive this implies some simple breaking of long lines into shorter ones after the file was entered. Seems like this would be a nice feature for the builtin editor. Larry larry@abtcser
aem@ibiza.cs.miami.edu (a.e.mossberg) (09/23/89)
larry@tapa.uucp (Larry Pajakowski) writes: >I have not used profs myself but he said you could just keep typing and profs >would break long lines into shorter ones on word boundries. Now since >keystrokes in profs are not interactive this implies some simple breaking of >long lines into shorter ones after the file was entered. Seems like this >would be a nice feature for the builtin editor. That sort of feature belongs in an external editor. The builtin editor should be no more functional than using mailx. Auto wrap is available in vi, emacs, mg, etc etc. Really people, elm is getting out of hand. Remember KISS? aem a.e.mossberg / aem@mthvax.cs.miami.edu / aem@umiami.BITNET / Pahayokee Bioregion The man who dies rich dies disgraced. - Andrew Carnegie
indra@pepsi.amd.com (Indra Singhal) (09/23/89)
In article <1989Sep22.014431.27929@tapa.uucp> larry@tapa.uucp (Larry Pajakowski) writes: >I have not used profs myself but he said you could just keep typing and profs >would break long lines into shorter ones on word boundries... If you use vi as your editor in elm, you can ':set wrapmargin=8' or abbreviated, ':set wm=8' and vi will do the fancy wordwrapping that you desire. I use it all the time!! iNDRA | indra@amdcad.AMD.COM (408) 749-5445 | {ames decwrl gatech pyramid sun uunet}!amdcad!indra | MS 167; Box 3453; Sunnyvale, CA 94088
pokey@well.UUCP (Jef Poskanzer) (09/26/89)
In the referenced message, larry@tapa.uucp (Larry Pajakowski) wrote: } you could just keep typing and profs }would break long lines into shorter ones on word boundries. I recently added this feature to the Elm editor, for use as a novice editor on the WELL. It just adds newlines internally after the line has been typed. This opens up a can of worms, since very few systems will let you type arbitrarily long lines. On our system you can type 256 characters and then the terminal locks up - any further characters except CR just ring the bell. So to solve that, I made versions of gets() and fgets() which go into CBREAK mode and do their own buffering. This actually works quite nicely on most types of serial lines, but we have had a lot of problems getting it to work on our X.25 lines. Still, if you want word-wrap, this is worth trying. --- Jef Jef Poskanzer pokey@well.sf.ca.us {ucbvax, apple, hplabs}!well!pokey "Back off, man - I'm a scientist!" - - - - - - - - - - /** editor.c **/ /** This is a very simple editor designed for general purpose use. This program is a sorta subset of the 'editor=none' option in the Elm program & the Berkeley mailer, etc, etc. (C) Copyright 1986, Dave Taylor Heavily modified by Jef Poskanzer, 1989. **/ /* #define DHAWKISM 1 /* define this for the getenv(EDITOR) weirdness */ #define CBREAKISM 1 /* define this to get the arbitrary-length lines */ /* #define WRAPWARN 1 /* define this to get warnings printed on line-wrap */ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <signal.h> #include <sysexits.h> #define SLEN 100 /* length of a string */ #define BUFLEN 50000 /* length of a line of input */ #define temp "/tmp/pe" /* temp file for pipe stuff */ #define SHELL "sh" /* default shell */ #define EDITOR "bbsed" /* default line editor */ #define VISUAL "vi" /* default screen editor */ #define VISUAL2 "jove" /* second default screen editor */ #define ESCAPE ":" /* default escape character */ #define LINES_TO_SHOW 10 /* lines to show if there is text in the file */ char shell[SLEN], editor[SLEN], visual[SLEN], visual2[SLEN], escape[SLEN]; char *getenv( ), *strcpy( ); #ifdef CBREAKISM char *cb_gets( ), *cb_fgets( ); #else CBREAKISM char *gets( ), *fgets( ); #endif CBREAKISM main(argc, argv) int argc; char *argv[]; { /** assume we're invoked as "<editor> <filename>" **/ FILE *fd; char buffer[BUFLEN]; int file_already_exists = 0; #ifdef CBREAKISM int cont_handler( ); #else CBREAKISM int tstp_handler( ), cont_handler( ); #endif CBREAKISM if (argc == 1) { printf("Usage: %s filename\n", argv[0]); exit(1); } if (getenv("SHELL") != NULL) strcpy(shell, getenv("SHELL")); else strcpy(shell, SHELL); if (getenv("VISUAL") != NULL) strcpy(visual, getenv("VISUAL")); else strcpy(visual, VISUAL); if (getenv("VISUAL2") != NULL) strcpy(visual2, getenv("VISUAL2")); else strcpy(visual2, VISUAL2); #ifdef DHAWKISM strcpy(editor, EDITOR); #else DHAWKISM if (getenv("EDITOR") != NULL) strcpy(editor, getenv("EDITOR")); else strcpy(editor, EDITOR); #endif DHAWKISM if (getenv("MAIL_EDITOR_ESCAPE") != NULL) strcpy(escape, getenv("MAIL_EDITOR_ESCAPE")); else strcpy(escape, ESCAPE); if (access(argv[1], 00) != -1) if (filesize(argv[1]) > 0) { if ((fd = fopen(argv[1], "r")) == NULL) { printf("Couldn't open the file to show what's already there...??\n"); } else { int i; char *r; printf("File already contains information:\n"); for (i=0; (r = fgets(buffer, BUFLEN, fd)) != NULL && i < LINES_TO_SHOW; i++) printf("%s", buffer); if (r == NULL) printf("[end-of-file]\n"); else printf("[more, use %sp to see]\n", escape); fclose(fd); } file_already_exists++; } if ((fd = fopen(argv[1], "a")) == NULL) { printf("Can't open %s for editing!\n", argv[1]); exit(1); } printf("%s message, '^D' to end, or %s? for help.\n", file_already_exists? "Continue entering" : "Enter", escape); #ifdef CBREAKISM signal(SIGTSTP, tstp_handler); #endif CBREAKISM signal(SIGCONT, cont_handler); #ifdef CBREAKISM while (cb_fgets(buffer, BUFLEN, stdin) != NULL) { #else CBREAKISM while (fgets(buffer, BUFLEN, stdin) != NULL) { #endif CBREAKISM if (strcmp(buffer, ".\n") == 0) break; /* outta here! */ else if (buffer[0] == escape[0]) { if (buffer[1] == escape[0]) { visiblebuffer(buffer + 1); formatbuffer(buffer + 1); fputs(buffer + 1, fd); } else { int l; l = strlen(buffer); if (l >= 1 && buffer[l - 1] == '\n') buffer[l - 1] = '\0'; switch (buffer[1]) { case '\0' : printf("(Huh? Try \"%s?\" for help!)\n", escape); break; case '?' : help( ); break; case 'e' : invoke(editor, fd, argv[1]); break; case 'v' : invoke(visual, fd, argv[1]); break; case 'o' : invoke(visual2, fd, argv[1]); break; case 'r' : read_in(buffer, fd); break; case 'w' : write_out(buffer, fd, argv[1]); break; case 'p' : fclose(fd); fd = fopen(argv[1], "r"); while (fgets(buffer, BUFLEN, fd) != NULL) printf("%s", buffer); printf("(continue or ^C to abort)\n"); fd = fopen(argv[1], "a"); break; case '!' : if (strlen(buffer) > 2) { #ifdef CBREAKISM cb_deinit( ); #endif CBREAKISM system((char *) buffer + 2); } else { #ifdef CBREAKISM cb_deinit( ); #endif CBREAKISM system(shell); } printf("(continue or ^C to abort)\n"); break; case '|' : pipe_it((char *) buffer + 2, fd, argv[1]); break; default : printf("(don't know what \"%s%c\" means!)\n", escape, buffer[1]); break; } } } else { visiblebuffer(buffer); formatbuffer(buffer); fputs(buffer, fd); } } fclose(fd); #ifdef CBREAKISM cb_deinit( ); #endif CBREAKISM if (getenv("MAIL_EDITOR_EXIT") != NULL) exit(EX_NOUSER); else exit(0); } visiblebuffer( buffer ) char *buffer; { /* Turn unlawful control characters into visible sequences. ** This expands the string, which is a no-no, but in this case ** we know the string is Huge so there won't be a problem. */ register char *cp1, *cp2, c1, c2; int touched = 0; for ( cp1 = buffer; *cp1 != '\0'; cp1++ ) { c1 = *cp1; /* Check for bad chars. ^G ^I ^J ^L and ^M are allowed. */ if ( ( c1 < ' ' && c1 != '\007' && c1 != '\011' && c1 != '\012' && c1 != '\014' && c1 != '\015' ) || c1 == '\177') { touched = 1; cp2 = cp1; *cp2++ = '^'; c2 = *cp2; if ( c1 == '\177' ) *cp2 = '?'; else *cp2 = c1 + 64; c1 = c2; for ( cp2++; *cp2 != '\0'; cp2++ ) { c2 = *cp2; *cp2 = c1; c1 = c2; } *cp2++ = c1; *cp2 = '\0'; } } if ( touched ) printf( "(making control characters visible, continue)\n" ); } formatbuffer( buffer ) char *buffer; { /* Break long lines at blanks. */ #define MAX_LINE_LENGTH 79 register char *cp1, *cp2; register int l; int touched = 0; cp1 = buffer; l = strlen( cp1 ); while ( l > MAX_LINE_LENGTH ) { /* Look for nearest preceeding blank. */ for ( cp2 = cp1 + MAX_LINE_LENGTH; cp2 > cp1; cp2-- ) if ( *cp2 == ' ' ) { *cp2 = '\n'; touched = 1; l -= ( cp2 - cp1 ) + 1; cp1 = cp2 + 1; break; } if ( *cp2 != '\n' ) { /* Hmm, didn't find any blanks looking backwards! Try forwards. */ for ( cp2 = cp1 + MAX_LINE_LENGTH + 1; *cp2 != '\0'; cp2++ ) { if ( *cp2 == ' ' ) { *cp2 = '\n'; touched = 1; l -= ( cp2 - cp1 ) + 1; cp1 = cp2 + 1; break; } } if ( *cp2 != '\n' ) { /* No blanks at all. Forget it. */ break; } } } #ifdef WRAPWARN if ( touched ) printf( "(reformatting line to %d characters long, continue)\n", MAX_LINE_LENGTH ); #endif WRAPWARN } #ifdef CBREAKISM tstp_handler( ) { cb_deinit( ); signal( SIGTSTP, SIG_DFL ); kill( 0, SIGTSTP ); } #endif CBREAKISM cont_handler( ) { #ifdef CBREAKISM signal( SIGTSTP, tstp_handler ); #endif CBREAKISM printf( "(continue or ^C to abort)\n" ); } help( ) { /** list the possible commands! **/ printf("(The commands available from here are:\n\ %s? list this help menu\n\ %s! either give you a shell, or execute the specified command\n\ %s| pipe the message written so far through the specified command\n\ %se invoke \"%s\" on the response so far\n\ %so invoke \"%s\" on the response so far\n\ %sv invoke \"%s\" on the response so far\n\ %sp print what we've entered so far\n\ %sr read in the specified file\n\ %sw write out the specified file\n\ %s%s enter a line starting with a \"%s\"\n\ To exit from the editor, use <control>-D or a \".\" on a line by itself.)\n", escape, escape, escape, escape, editor, escape, visual2, escape, visual, escape, escape, escape, escape, escape, escape); } invoke(editor, file_descriptor, filename) char *editor, *filename; FILE *file_descriptor; { /** invokes the specified editor, closing the file and opening it again when we're done. If editor = NULL ask the user! **/ char buffer[SLEN]; if (strlen(editor) == 0) { printf("Enter the name of the editor to use: "); fflush(stdout); #ifdef CBREAKISM cb_gets(editor); #else CBREAKISM gets(editor); #endif CBREAKISM if (strlen(editor) == 0) goto end_it; } else { printf("(invoking %s) ", editor); fflush(stdout); } fclose(file_descriptor); sprintf(buffer, "%s %s", editor, filename); #ifdef CBREAKISM cb_deinit( ); #endif CBREAKISM system(buffer); file_descriptor = fopen(filename, "a"); end_it: printf("(continue or ^C to abort)\n"); return; } pipe_it(command, file_descriptor, filename) char *command, *filename; FILE *file_descriptor; { /** this will either accept a previously entered pipe command, as in ":|fmt", or if none, will prompt for one. It's really quite a simple routine! **/ char buffer[SLEN]; if (strlen(command) == 0) { printf("Pipe message through: "); fflush(stdout); #ifdef CBREAKISM cb_gets(command); #else CBREAKISM gets(command); #endif CBREAKISM if (strlen(command) == 0) goto end_it; } else printf("(piping response through \"%s\")\n", command); fclose(file_descriptor); sprintf(buffer, "%s < %s > %s.%d ; mv %s.%d %s", command, filename, temp, getpid( ), temp, getpid( ), filename); #ifdef CBREAKISM cb_deinit( ); #endif CBREAKISM system(buffer); file_descriptor = fopen(filename, "a"); end_it: printf("(continue or ^C to abort)\n"); return; } read_in(fname, fd) char *fname; FILE *fd; { /** read the specified file in, continuing when done. **/ FILE *newfd; register int i, j, lines = 0; char filename[SLEN], buffer[BUFLEN]; if (strlen(fname) < 3) { printf("Enter name of file to read in: "); fflush(stdout); #ifdef CBREAKISM cb_gets(filename); #else CBREAKISM gets(filename); #endif CBREAKISM if (strlen(filename) == 0) goto end_it; } else { for (i=2; fname[i] == ' '; i++) /* count up! */ ; for (j = 0; i < strlen(fname);) filename[j++] = fname[i++]; filename[j] = '\0'; } if ((newfd = fopen(filename, "r")) == NULL) { printf("(can't open file \"%s\" for reading! Continue...)\n", filename); return; } while (fgets(buffer, BUFLEN, newfd) != NULL) { visiblebuffer(buffer); formatbuffer(buffer); fputs(buffer, fd); lines++; } fclose(newfd); printf("(read in %d line%s from file \"%s\" Continue...)\n", lines, lines == 1? "" : "s", filename); return; end_it: printf("(continue or ^C to abort)\n"); return; } write_out(fname, fd, base_filename) char *fname, *base_filename; FILE *fd; { /** write a copy of the current message to the specified file! **/ FILE *newfd; register int i, j, lines = 0; char filename[SLEN], buffer[BUFLEN]; if (strlen(fname) < 3) { printf("Enter name of file to write to: "); fflush(stdout); #ifdef CBREAKISM cb_gets(filename); #else CBREAKISM gets(filename); #endif CBREAKISM if (strlen(filename) == 0) goto end_it; } else { for (i=2; fname[i] == ' '; i++) /* count up! */ ; for (j = 0; i < strlen(fname);) filename[j++] = fname[i++]; filename[j] = '\0'; } if ((newfd = fopen(filename, "w")) == NULL) { printf("(Can't open file \"%s\" for writing! Continue...)\n", filename); return; } fclose(fd); /* close the current file... */ fd = fopen(base_filename, "r"); /* and open for reading */ while (fgets(buffer, BUFLEN, fd) != NULL) { fprintf(newfd, "%s", buffer); lines++; } fclose(newfd); fclose(fd); /* close the current file... */ fd = fopen(base_filename, "a"); /* and open back up for appending */ printf("(saved %d line%s to file \"%s\" Continue...)\n", lines, lines == 1? "" : "s", filename); return; end_it: printf("(continue or ^C to abort)\n"); return; } int filesize(filename) char *filename; { struct stat statbuffer; if (stat(filename, &statbuffer) != 0) return(0); else return((int) statbuffer.st_size); } #ifdef CBREAKISM /* cbreak versions of fgets and gets */ #include <stdio.h> #include <sys/file.h> #include <sys/ioctl.h> int cb_inited = 0; struct sgttyb cb_tty; short cb_save_flags; int cb_save_lmodes, cb_ifd, cb_ofd; FILE *cb_if; int *cb_prevsig; char * cb_fgets( s, n, f ) char *s; int n; FILE *f; { int lmodes; char c, *r; int l; int cb_int_handler( ); if ( cb_inited && f != cb_if ) cb_inited = 0; if ( ! cb_inited ) { /* Check for non-tty. */ cb_if = f; cb_ifd = fileno( cb_if ); if ( ! isatty( cb_ifd ) ) return fgets( s, n, cb_if ); /* Get an output fd for the input device. */ if ( cb_ifd = 0 && isatty( 1 ) ) cb_ofd = 1; else { cb_ofd = open( ttyname( cb_ifd ), O_WRONLY ); if ( cb_ofd == -1 ) { perror( "opening output tty" ); return NULL; } } /* Save tty modes. */ ioctl( cb_ifd, TIOCGETP, &cb_tty ); cb_save_flags = cb_tty.sg_flags; ioctl( cb_ifd, TIOCLGET, &cb_save_lmodes ); cb_inited = 1; cb_prevsig = (int *) signal( SIGINT, cb_int_handler ); if ( cb_prevsig == (int *) -1 ) { perror( "installing signal handler" ); return NULL; } /* Set cbreak mode, and unset echo and control-echo. */ cb_tty.sg_flags |= CBREAK; cb_tty.sg_flags &= ~ECHO; ioctl( cb_ifd, TIOCSETN, &cb_tty ); lmodes = cb_save_lmodes & ~LCTLECH; ioctl( cb_ifd, TIOCLSET, &lmodes ); } /* Read. */ r = s; l = 0; for ( ; ; ) { if ( read( cb_ifd, &c, 1 ) != 1 ) { r = NULL; break; } if ( c == cb_tty.sg_erase || c == '\010' || c == '\177' ) /* ^H DEL */ { if ( l == 0 ) write( cb_ofd, "\007", 1 ); /* ^G */ else { l--; write( cb_ofd, "\010 \010", 3 ); /* ^H ^H */ } } else if ( c == cb_tty.sg_kill ) { if ( l == 0 ) write( cb_ofd, "\007", 1 ); /* ^G */ else { do { l--; write( cb_ofd, "\010 \010", 3 ); /* ^H */ } while ( l > 0 ); } } else switch ( c ) { case '\004': /* ^D */ write( cb_ofd, "^D", 2 ); s[l] = '\0'; if ( l == 0 ) r = NULL; goto out; case '\022': /* ^R */ write( cb_ofd, "^R\n", 3 ); write( cb_ofd, s, l ); break; case '\027': /* ^W */ if ( l == 0 ) write( cb_ofd, "\007", 1 ); /* ^G */ else { while ( l > 0 && s[l-1] == ' ' || s[l-1] == '\t' ) { l--; write( cb_ofd, "\010 \010", 3 ); /* ^H */ } while ( l > 0 && s[l-1] != ' ' && s[l-1] != '\t' ) { l--; write( cb_ofd, "\010 \010", 3 ); /* ^H */ } } break; case '\r': c = '\n'; /* Fall through. */ default: s[l++] = c; write( cb_ofd, &c, 1 ); if ( l >= n - 1 ) { s[l] = '\0'; goto out; } } /* If it was a newline, return. */ if ( c == '\n' ) { s[l] = '\0'; goto out; } } out: /* Done. */ return r; } cb_int_handler( ) { cb_deinit( ); exit( 1 ); } cb_deinit( ) { if ( cb_inited ) { /* Restore tty modes. */ ioctl( cb_ifd, TIOCLSET, &cb_save_lmodes ); cb_tty.sg_flags = cb_save_flags; ioctl( cb_ifd, TIOCSETN, &cb_tty ); (void) signal( SIGINT, cb_prevsig ); /* Close output tty. */ if ( cb_ofd != 1 ) close( cb_ofd ); cb_inited = 0; } } char * cb_gets( s ) char *s; { char *val; val = cb_fgets( s, 32767, stdin ); if ( val != NULL ) { int l; l = strlen( s ); if ( l >= 1 && s[l - 1] == '\n' ) s[l - 1] = '\0'; } return val; } #endif CBREAKISM
pokey@well.UUCP (Jef Poskanzer) (09/26/89)
Oops, apply this patch, I got an #ifdef backwards. *** b Mon Sep 25 17:17:03 1989 --- src/mail_editor.c Mon Sep 25 17:15:47 1989 *************** *** 51,58 char buffer[BUFLEN]; int file_already_exists = 0; #ifdef CBREAKISM - int cont_handler( ); - #else CBREAKISM int tstp_handler( ), cont_handler( ); #endif CBREAKISM --- 51,56 ----- char buffer[BUFLEN]; int file_already_exists = 0; #ifdef CBREAKISM int tstp_handler( ), cont_handler( ); #else CBREAKISM int cont_handler( ); *************** *** 54,59 int cont_handler( ); #else CBREAKISM int tstp_handler( ), cont_handler( ); #endif CBREAKISM if (argc == 1) { --- 52,59 ----- int file_already_exists = 0; #ifdef CBREAKISM int tstp_handler( ), cont_handler( ); + #else CBREAKISM + int cont_handler( ); #endif CBREAKISM if (argc == 1) {