1 
2 static char rcsid[] = "@(#)$Id: wordwrap.c,v 1.3 1996/03/14 17:30:00 wfp5p Exp $";
3 
4 /*******************************************************************************
5  *  The Elm Mail System  -  $Revision: 1.3 $   $State: Exp $
6  *
7  *                      Copyright (c) 1988-1995 USENET Community Trust
8  *			Copyright (c) 1986,1987 Dave Taylor
9  *******************************************************************************
10  * Bug reports, patches, comments, suggestions should be sent to:
11  *
12  *      Bill Pemberton, Elm Coordinator
13  *      flash@virginia.edu
14  *
15  *******************************************************************************
16  * $Log: wordwrap.c,v $
17  * Revision 1.3  1996/03/14  17:30:00  wfp5p
18  * Alpha 9
19  *
20  * Revision 1.2  1995/09/29  17:42:38  wfp5p
21  * Alpha 8 (Chip's big changes)
22  *
23  * Revision 1.1.1.1  1995/04/19  20:38:34  wfp5p
24  * Initial import of elm 2.4 PL0 as base for elm 2.5.
25  *
26  ******************************************************************************/
27 
28 /***  Routines to wrap lines when using the "builtin" editor
29 
30 ***/
31 
32 #include "elm_defs.h"
33 #include "elm_globals.h"
34 
35 unsigned alarm();
36 
37 #define isstopchar(c)		(c == ' ' || c == '\t')
38 #define isslash(c)		(c == '/')
39 #define erase_a_char()		{ WriteChar(BACKSPACE); WriteChar(' '); \
40 			          WriteChar(BACKSPACE); }
41 
42 	/* WARNING: this macro destroys nr */
43 #define erase_tab(nr)		do WriteChar(BACKSPACE); while (--(nr) > 0)
44 
45 int
wrapped_enter(string,tail,x,y,edit_fd,append_current)46 wrapped_enter(string, tail, x, y, edit_fd, append_current)
47 char *string, *tail;
48 int  x,y, *append_current;
49 FILE *edit_fd;
50 
51 {
52 	/** This will display the string on the screen and allow the user to
53 	    either accept it (by pressing RETURN) or alter it according to
54 	    what the user types.   The various flags are:
55 	         string    is the buffer to use (with optional initial value)
56 		 tail	   contains the portion of input to be wrapped to the
57 			   next line
58 	 	 x,y	   is the location we're at on the screen (-1,-1 means
59 			   that we can't use this info and need to find out
60 			   the current location)
61 		 append_current  means that we have an initial string and that
62 			   the cursor should be placed at the END of the line,
63 			   not the beginning (the default).
64 
65 	    If we hit an interrupt or EOF we'll return non-zero.
66 	**/
67 
68 	int ch, wrapcolumn = 70, iindex = 0;
69 	int addon = 0;	/* Space added by tabs. iindex+addon == column */
70 	int tindex = 0;	/* Index to the tabs array. */
71 	int tabs[10];	/* Spaces each tab adds. size <= wrapcolumn/8+1 */
72 	register int ch_count = 0, escaped = FALSE;
73 	long newpos, pos;
74 	char line[SLEN];
75 
76 	if(!(x >=0 && y >= 0))
77 	  GetCursorPos(&x, &y);
78 	PutLine1(x, y, "%s", string);
79 
80 	CleartoEOLN();
81 
82 	if (! *append_current) {
83 	  MoveCursor(x,y);
84 	}
85 	else
86 	  iindex = strlen(string);
87 
88 	/** now we have the screen as we want it and the cursor in the
89 	    right place, we can loop around on the input and return the
90 	    string as soon as the user presses <RETURN> or the line wraps.
91 	**/
92 
93 	do {
94 	  ch = ReadCh();
95 
96 	  if (ch == ctrl('D')) {		/* we've hit EOF */
97 	    *append_current = 0;
98 	    return(1);
99 	  }
100 
101 	  if (ch_count++ == 0) {
102 	    if (ch == '\n' || ch == '\r') {
103 	      *append_current = 0;
104 	      return(0);
105 	    }
106 	    else if (! *append_current) {
107 	      CleartoEOLN();
108 	      iindex = (*append_current? strlen(string) : 0);
109 	    }
110 	  }
111 
112 	  if (!escaped) {
113 	    if (ch == Term.erase_char)
114 	      ch = ctrl('H');
115 	    if (ch == Term.kill_char)
116 	      ch = ctrl('U');
117 	  }
118 
119 	  switch (ch) {
120 
121 	  case '\n':
122 	  case '\r':
123 	    string[iindex] = '\0';
124 	    *append_current = 0;
125 	    return(0);
126 
127 	  case '\0':
128 	    FlushInput(); 	/* remove extraneous chars, if any */
129 	    string[0] = '\0'; /* clean up string, and... */
130 	    *append_current = 0;
131 	    return(-1);
132 
133 	  case ctrl('H'):
134 	    if (iindex > 0) {
135   	      iindex--;
136 	      if (string[iindex] == '\t') {
137 		addon -= tabs[--tindex] - 1;
138 		erase_tab(tabs[tindex]);
139 	      } else erase_a_char();
140 
141 #ifdef FTRUNCATE
142 
143 	    } else { /** backspace to end of previous line **/
144 
145 	      fflush(edit_fd);
146 	      if ((pos = ftell(edit_fd)) <= 0L) { /** no previous line **/
147 		Beep();
148 
149 	      } else {
150 
151 		/** get the last 256 bytes written **/
152 		if ((newpos = pos - 256L) <= 0L) newpos = 0;
153 		(void) fseek(edit_fd, newpos, 0L);
154 		(void) fread(line, sizeof(*line), (int) (pos-newpos),
155 		             edit_fd);
156 		pos--;
157 
158 		/** the last char in line should be '\n'
159 			change it to null **/
160 		if (line[(int) (pos-newpos)] == '\n')
161 		  line[(int) (pos-newpos)] = '\0';
162 
163 		/** find the end of the previous line ('\n') **/
164 		for (pos--; pos > newpos && line[(int) (pos-newpos)] != '\n';
165 			pos--);
166 		/** check to see if this was the first line in the file **/
167 		if (line[(int) (pos-newpos)] == '\n') /** no - it wasn't **/
168 		  pos++;
169 		(void) strcpy(string, &line[(int) (pos-newpos)]);
170 		line[(int) (pos-newpos)] = '\0';
171 
172 		/** truncate the file to the current position
173 			THIS WILL NOT WORK ON SYS-V **/
174 		(void) fseek(edit_fd, newpos, 0L);
175 		(void) fputs(line, edit_fd);
176 		fflush(edit_fd);
177 		(void) ftruncate(fileno(edit_fd), (int) ftell(edit_fd));
178 		(void) fseek(edit_fd, ftell(edit_fd), 0L);
179 
180 		/** rewrite line on screen and continue working **/
181 		GetCursorPos(&x, &y);
182 		if (x > 0) x--;
183 		PutLine1(x, y, "%s", string);
184 		CleartoEOLN();
185 		iindex = strlen(string);
186 
187 		/* Reload tab positions */
188 		addon = tindex = 0;
189 		for (pos = 0; pos < iindex; pos++)
190 		  if (string[pos] == '\t')
191 		    addon += (tabs[tindex++] = 8 - ((pos+addon) & 07)) - 1;
192 	      }
193 
194 #endif
195 
196 	    }
197 	    break;
198 
199 	  case ctrl('W'):
200 	    if (iindex == 0)
201 	      break;		/* no point staying here.. */
202 	    iindex--;
203 	    if (isslash(string[iindex])) {
204 	      erase_a_char();
205 	    }
206 	    else {
207 	      while (iindex >= 0 && isspace(string[iindex])) {
208 		if (string[iindex] == '\t') {
209 		  addon -= tabs[--tindex] - 1;
210 		  erase_tab(tabs[tindex]);
211 		} else erase_a_char();
212 	        iindex--;
213 	      }
214 
215 	      while (iindex >= 0 && ! isstopchar(string[iindex])) {
216 	        iindex--;
217 	        erase_a_char();
218 	      }
219 	      iindex++;	/* and make sure we point at the first AVAILABLE slot */
220 	    }
221 	    break;
222 
223 	  case ctrl('U'):
224 	    MoveCursor(x,y);
225 	    CleartoEOLN();
226 	    iindex = 0;
227 	    break;
228 
229 	  case ctrl('R'):
230 	    string[iindex] = '\0';
231 	    PutLine1(x,y, "%s", string);
232 	    CleartoEOLN();
233 	    break;
234 
235 	  default:
236 	    if (escaped) {
237 	      WriteChar(BACKSPACE);
238 	      iindex--;
239 	    } else if (ch == '\t') {
240 		addon += (tabs[tindex++] = 8 - ((addon+iindex) & 07)) - 1;
241 	    } else if (ch > 0xFF || !isprint(ch)) {
242 	      Beep();
243 	      break;
244 	    }
245 	    string[iindex++] = ch;
246 	    WriteChar(ch);
247 	    break;
248 
249 	  }
250 
251 	  escaped = (!escaped && ch == '\\');
252 
253 	} while (iindex+addon < wrapcolumn);
254 
255 	string[iindex] = '\0';
256 	*append_current = line_wrap(string,tail,&iindex,&tabs[tindex-1]);
257 
258 	return(0);
259 }
260 
261 int
line_wrap(string,tail,count,tabs)262 line_wrap(string,tail,count,tabs)
263 char *string;	/* The string to be wrapped */
264 char *tail;	/* The part of the string which is wrapped */
265 int *count;	/* Offset of string terminator */
266 int *tabs;	/* List of how many spaces each tab adds */
267 {
268 	/** This will check for line wrap.  If the line was wrapped,
269 	    it will back up to white space (if possible), write the
270 	    shortened line, and put the remainder at the beginning
271 	    of the string.  Returns 1 if wrapped, 0 if not.
272 	**/
273 
274 	int n = *count;
275 	int i, j;
276 
277 	/* Look for a space */
278 	while (n && !isstopchar(string[n]))
279 	  --n;
280 
281 	/* If break found */
282 	if (n) {
283 
284 	  /* Copy part to be wrapped */
285 	  for (i=0,j=n+1;j<=*count;tail[i++]=string[j++]);
286 
287 	  /* Skip the break character and any whitespace */
288 	  while (n && isstopchar(string[n]))
289 	    --n;
290 
291 	  if (n) n++; /* Move back into the whitespace */
292 	}
293 
294 	/* If no break found */
295 	if (!n) {
296 	  (*count)--;
297 	  strcpy(tail, &string[*count]);
298 	  erase_a_char();
299 	} else /* Erase the stuff that will wrap */
300 	  while (*count > n) {
301 	    --(*count);
302 	  if (string[*count] == '\t') erase_tab(*tabs--);
303 	  else erase_a_char();
304 	  }
305 
306 	string[*count] = '\0';
307 	return(1);
308 }
309