1 /* @(#)message.c	1.29 09/07/28 Copyright 1984-2009 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)message.c	1.29 09/07/28 Copyright 1984-2009 J. Schilling";
6 #endif
7 /*
8  *	Management routines for the system (status) line of the editor
9  *	displayed at the top of the screen.
10  *
11  *	Copyright (c) 1984-2009 J. Schilling
12  */
13 /*
14  * The contents of this file are subject to the terms of the
15  * Common Development and Distribution License, Version 1.0 only
16  * (the "License").  You may not use this file except in compliance
17  * with the License.
18  *
19  * See the file CDDL.Schily.txt in this distribution for details.
20  * A copy of the CDDL is also available via the Internet at
21  * http://www.opensource.org/licenses/cddl1.txt
22  *
23  * When distributing Covered Code, include this CDDL HEADER in each
24  * file and include the License file CDDL.Schily.txt from this distribution.
25  */
26 
27 /*
28  * Layout of the systemline:
29  *
30  * |.123456789.123456789.123456789.123456789.123456789.123456789.123456789...
31  * |info               |num      |takebuf  |filename           |errortext
32  * |1/4 llen           |1/8 llen |1/8 llen |1/4 llen           |1/4 llen
33  *
34  * -	info	informational messages (search pattern, modflag ...)
35  * -	num	actual multiplying factor for the next command
36  * -	take	the name of the current take buffer
37  * -	name	the name of the file currently beeing edited
38  * -	error	error messages
39  */
40 
41 #include "ved.h"
42 #include "terminal.h"
43 #include <schily/varargs.h>
44 
45 /*
46  * INFOSIZE is 1/4th of the linelength of the terminal/window.
47  * If the screen has 1280 pixel and the width of a character is 8 pixel,
48  * a maximum of 160 charcters/line is sufficient.
49  * If the screen has 1280 pixel and the width of a character is 6 pixel,
50  * a maximum of 213 charcters/line is sufficient.
51  * A maximum linelength of 256 should be sufficient in any case.
52  *
53  * INFOSIZE should be at least 20+2 (see cmdline.c)
54  * We use ( 256 / 4 ) + 1. This should be enough for some time.
55  *
56  * The size of the info (left most) string should be big enough to print
57  * out a string of NAMESIZE length. The maximum length is 4 * INFOSIZE.
58  */
59 #define	INFOSIZE	65			/* Allow screen width of 259 */
60 #define	SYSLMIN		NAMESIZE
61 #define	SYSLSIZE	(SYSLMIN > (4*INFOSIZE) ? (4*INFOSIZE) : SYSLMIN)
62 #define	NUMSIZE		(INFOSIZE/2)
63 #define	TAKESIZE	(INFOSIZE/2)
64 
65 LOCAL	Uchar	infostr[SYSLSIZE];		/* left most part of message */
66 LOCAL	Uchar	namestr[2*INFOSIZE];		/* center part, name of file */
67 LOCAL	Uchar	errorstr[INFOSIZE];		/* right part, error messages*/
68 LOCAL	Uchar	numstr[NUMSIZE];		/* display of multiple	    */
69 LOCAL	Uchar	takestr[TAKESIZE];		/* display of take buffer   */
70 LOCAL	Uchar	DefInfostr[INFOSIZE];		/* default info string	    */
71 LOCAL	Uchar	DefErrorstr[INFOSIZE-1];	/* default error string	    */
72 						/* may be prepended by a ' ' */
73 						/* to create error message  */
74 
75 EXPORT	BOOL	updatemsg = 0;			/* infostr is temporary */
76 EXPORT	BOOL	updateerr = 0;			/* errorstr is temporary */
77 EXPORT	BOOL	updatesysline = 0;		/* whole systemline should be redrawn */
78 LOCAL	int	lastmsglen;			/* the len of the last message */
79 
80 LOCAL	int	numplace = 0;		/* where the numstr starts */
81 LOCAL	int	takeplace = 0;		/* where the takestr starts */
82 LOCAL	int	nameplace = 0;		/* where the namestr starts */
83 LOCAL	int	errorplace = 0;		/* where the errorstr starts */
84 LOCAL	ecnt_t	maxmsgnum = (ecnt_t)1;	/* how big a displayed # can be */
85 
86 #define	infosize (numplace - 0)		/* actual length of the infostring */
87 #define	numsize	 (takeplace-numplace)	/* actual length of the numstring */
88 #define	takesize (nameplace-takeplace)	/* actual length of the takestring */
89 #define	namesize (errorplace-nameplace)	/* actual length of the namestring */
90 #define	errorsize (wp->llen-errorplace)	/* actual length of the errorstring */
91 #define	nameerrsize (wp->llen-nameplace) /* actual length of the name+errorstring */
92 
93 EXPORT	void	initmessage	__PR((ewin_t *wp));
94 EXPORT	void	initmsgsize	__PR((ewin_t *wp));
95 EXPORT	void	writemsg	__PR((ewin_t *wp, char *str, ...));
96 EXPORT	void	writenum	__PR((ewin_t *wp, ecnt_t num));
97 EXPORT	void	writetake	__PR((ewin_t *wp, Uchar *str));
98 EXPORT	void	namemsg		__PR((Uchar* name));
99 EXPORT	void	writeerr	__PR((ewin_t *wp, char *str, ...));
100 EXPORT	void	writeserr	__PR((ewin_t *wp, char *str, ...));
101 LOCAL	void	_writeerr	__PR((ewin_t *wp, BOOL dobeep, char *str, va_list args));
102 EXPORT	void	write_errno	__PR((ewin_t *wp, char *msg, ...));
103 EXPORT	void	defaultinfo	__PR((ewin_t *wp, Uchar *str));
104 EXPORT	void	defaulterr	__PR((ewin_t *wp, Uchar *str));
105 LOCAL	void	errdefault	__PR((void));
106 EXPORT	void	refreshmsg	__PR((ewin_t *wp));
107 EXPORT	void	refreshsysline	__PR((ewin_t *wp));
108 EXPORT	void	write_systemline __PR((ewin_t *wp));
109 EXPORT	void	abortmsg	__PR((ewin_t *wp));
110 EXPORT	void	nomarkmsg	__PR((ewin_t *wp));
111 
112 
113 /*---------------------------------------------------------------------------
114 |
115 | Init the system (status) message line.
116 |
117 | Do not print it yet, this will be done later.
118 |
119 +---------------------------------------------------------------------------*/
120 
121 EXPORT void
initmessage(wp)122 initmessage(wp)
123 	ewin_t	*wp;
124 {
125 	infostr[0] = '\0';
126 	namestr[0] = '\0';
127 	errorstr[0] = '\0';
128 	numstr[0] = '\0';
129 	takestr[0] = '\0';
130 	DefInfostr[0] = '\0';
131 	DefErrorstr[0] = '\0';
132 
133 	initmsgsize(wp);
134 }
135 
136 
137 /*---------------------------------------------------------------------------
138 |
139 | Set up the actual sizes for the various strings (will be called on resize)
140 |
141 +---------------------------------------------------------------------------*/
142 
143 EXPORT void
initmsgsize(wp)144 initmsgsize(wp)
145 	ewin_t	*wp;
146 {
147 	register int	maxnumplaces;
148 	register int	i;
149 	register ecnt_t	n;		/* same type as curnum */
150 	register ecnt_t	on;		/* same type as curnum */
151 
152 	numplace	= (2*wp->llen)/8;
153 	takeplace	= (3*wp->llen)/8;
154 	nameplace	= (4*wp->llen)/8;
155 	errorplace	= (6*wp->llen)/8;
156 
157 	/*
158 	 * Overflow bei Berechnung von maxmsgnum verhindern
159 	 */
160 	for (on = (ecnt_t)0, n = (ecnt_t)1, maxnumplaces = 0;
161 			n > 0 && n > on && maxnumplaces < NUMSIZE-1;
162 							maxnumplaces++) {
163 		on = n;
164 		n  = 10*n;
165 	}
166 	maxnumplaces--;
167 
168 	i = takeplace - numplace - 3;
169 	if (i < 1)
170 		i = 1;
171 	if (i < maxnumplaces)
172 		maxnumplaces = i;
173 
174 
175 	maxmsgnum = (ecnt_t)1;
176 	while (--maxnumplaces >= 0)
177 		maxmsgnum = 10*maxmsgnum;
178 }
179 
180 
181 /*---------------------------------------------------------------------------
182 |
183 | Write a message to the (left) info part of the system (status) line.
184 |
185 | If called: writemsg(wp, NULL),	display content of default info string
186 | If called: writemsg(wp, "text"),	display "text" up to the next command
187 |
188 +---------------------------------------------------------------------------*/
189 
190 /* PRINTFLIKE2 */
191 #ifdef	PROTOTYPES
192 EXPORT void
writemsg(ewin_t * wp,char * str,...)193 writemsg(ewin_t *wp, char *str, ...)
194 #else
195 EXPORT void
196 writemsg(wp, str, va_alist)
197 	ewin_t	*wp;
198 	char	*str;
199 	va_dcl
200 #endif
201 {
202 	va_list	args;
203 	int	len = 0;
204 
205 	if (str == 0) {
206 		strncpy(C infostr, C DefInfostr, sizeof (infostr));
207 		infostr[sizeof (infostr)-1] = '\0';
208 		updatemsg = 0;
209 	} else if (*str == '\0') {
210 		infostr[0] = '\0';
211 		updatemsg = 1;
212 	} else {
213 #ifdef	PROTOTYPES
214 		va_start(args, str);
215 #else
216 		va_start(args);
217 #endif
218 		len = snprintf(C infostr, sizeof (infostr), "%r", str, args);
219 		va_end(args);
220 		updatemsg = 1;
221 	}
222 	CURSOR_HOME(wp);
223 	if (len <= infosize) {
224 		len = infosize;
225 		if (updatesysline && len < lastmsglen)
226 			len = lastmsglen;
227 	} else {
228 		updatesysline = 1;
229 	}
230 
231 	/*
232 	 * We don't like the extended message to end in the middle of some text
233 	 * from another field. If we touch the next field, clear it temporary.
234 	 */
235 	if (len > infosize && len <= (infosize+numsize))
236 		len = (infosize+numsize);
237 	if (len > (infosize+numsize) && len <= (infosize+numsize+takesize))
238 		len = (infosize+numsize+takesize);
239 	if (len > (infosize+numsize+takesize) &&
240 				len <= (infosize+numsize+takesize+namesize))
241 		len = (infosize+numsize+takesize+namesize);
242 	lastmsglen = len;
243 
244 	printfield(infostr, len);
245 	setcursor(wp);
246 	if (str != 0)
247 		flush();
248 }
249 
250 
251 /*---------------------------------------------------------------------------
252 |
253 | Write a number to the (left center) info part of the system (status) line.
254 |
255 | If called: writenum(wp, -1),	erase content of the number string
256 | If called: writenum(wp, >=0),	display number
257 |
258 +---------------------------------------------------------------------------*/
259 
260 EXPORT void
writenum(wp,num)261 writenum(wp, num)
262 	ewin_t	*wp;
263 	ecnt_t	num;
264 {
265 	if (num < (ecnt_t)0)
266 		numstr[0] = '\0';
267 	else if (num < maxmsgnum)
268 		snprintf(C numstr, sizeof (numstr), " #:%lld", (Llong)num);
269 	else
270 		strcpy(C numstr, " #:BIG");
271 	MOVE_CURSOR_ABS(wp, 0, numplace);
272 	printfield(numstr, numsize);
273 	setcursor(wp);
274 }
275 
276 
277 /*---------------------------------------------------------------------------
278 |
279 | Write the take buffer name to the center part of the system (status) line.
280 |
281 +---------------------------------------------------------------------------*/
282 
283 EXPORT void
writetake(wp,str)284 writetake(wp, str)
285 	ewin_t	*wp;
286 	Uchar	*str;
287 {
288 	snprintf(C takestr, sizeof (takestr), " \\:%s", str);
289 	MOVE_CURSOR_ABS(wp, 0, takeplace);
290 	printfield(takestr, takesize);
291 	setcursor(wp);
292 }
293 
294 
295 /*---------------------------------------------------------------------------
296 |
297 | Set up the file name in the (right center) part of the system (status) line.
298 |
299 +---------------------------------------------------------------------------*/
300 
301 EXPORT void
namemsg(name)302 namemsg(name)
303 	Uchar	*name;
304 {
305 	snprintf(C namestr, sizeof (namestr), " %s", C name);
306 }
307 
308 /*---------------------------------------------------------------------------
309 |
310 | Write a message to the (right) error part of the system (status) line.
311 |
312 | If called: writeerr(wp, NULL),	display content of default error string
313 | If called: writeerr(wp, "text"),	display error "text" up to the next
314 |					command
315 |
316 | Ring the bell
317 |
318 +---------------------------------------------------------------------------*/
319 
320 /* PRINTFLIKE2 */
321 #ifdef	PROTOTYPES
322 EXPORT void
writeerr(ewin_t * wp,char * str,...)323 writeerr(ewin_t *wp, char *str, ...)
324 #else
325 EXPORT void
326 writeerr(wp, str, va_alist)
327 	ewin_t	*wp;
328 	char	*str;
329 	va_dcl
330 #endif
331 {
332 	va_list	args;
333 
334 #ifdef	PROTOTYPES
335 	va_start(args, str);
336 #else
337 	va_start(args);
338 #endif
339 	_writeerr(wp, TRUE, str, args);
340 	va_end(args);
341 }
342 
343 /*---------------------------------------------------------------------------
344 |
345 | Write a message to the (right) error part of the system (status) line.
346 |
347 | If called: writeserr(wp, NULL),	display content of default error string
348 | If called: writeserr(wp, "text"),	display error "text" up to the next
349 |					command
350 |
351 | Don't ring the bell (be silent)
352 |
353 +---------------------------------------------------------------------------*/
354 
355 /* PRINTFLIKE2 */
356 #ifdef	PROTOTYPES
357 EXPORT void
writeserr(ewin_t * wp,char * str,...)358 writeserr(ewin_t *wp, char *str, ...)
359 #else
360 EXPORT void
361 writeserr(wp, str, va_alist)
362 	ewin_t	*wp;
363 	char	*str;
364 	va_dcl
365 #endif
366 {
367 	va_list	args;
368 
369 #ifdef	PROTOTYPES
370 	va_start(args, str);
371 #else
372 	va_start(args);
373 #endif
374 	_writeerr(wp, FALSE, str, args);
375 	va_end(args);
376 }
377 
378 /*---------------------------------------------------------------------------
379 |
380 | Write a message to the (right) error part of the system (status) line.
381 |
382 | If called: _writeerr(wp, NULL),	display content of default error string
383 | If called: _writeerr(wp, "text"),	display error "text" up to the next
384 |					command
385 |
386 | Whether to ring the bell depends on 'dobeep'.
387 |
388 +---------------------------------------------------------------------------*/
389 
390 LOCAL void
_writeerr(wp,dobeep,str,args)391 _writeerr(wp, dobeep, str, args)
392 	ewin_t	*wp;
393 	BOOL	dobeep;
394 	char	*str;
395 	va_list	args;
396 {
397 	int	len = 0;
398 
399 	if (str == 0) {
400 		errdefault();
401 		updateerr = 0;
402 	} else {
403 		if (*str == '\0') {
404 			errorstr[0] = '\0';
405 			updateerr = 1;
406 		} else {
407 			len = snprintf(C errorstr, sizeof (errorstr), " %r",
408 								str, args);
409 			if (dobeep)
410 				ringbell();
411 			updateerr = 1;
412 		}
413 	}
414 	if (*errorstr) {
415 		if (len < 0 || len > errorsize) {
416 			MOVE_CURSOR_ABS(wp, 0, nameplace);
417 			printfield(errorstr, nameerrsize);
418 		} else {
419 			MOVE_CURSOR_ABS(wp, 0, errorplace);
420 			printfield(errorstr, errorsize);
421 		}
422 	} else {		/* Let name field use rest of screen if no error */
423 		MOVE_CURSOR_ABS(wp, 0, nameplace);
424 		printfield(namestr, nameerrsize);
425 	}
426 	setcursor(wp);
427 	if (str != 0)
428 		flush();
429 }
430 
431 
432 /*---------------------------------------------------------------------------
433 |
434 | Write a message to the (right) error part of the system (status) line.
435 | The message contains the error text or the error number for the last
436 | failed syscall depending on the space.
437 | If the message including the error text will fit the error number
438 | is ommitted. If the text will overflow the space, the error number will be
439 | placed in front of the text because it will possibly be readeable.
440 |
441 +---------------------------------------------------------------------------*/
442 
443 /* PRINTFLIKE2 */
444 #ifdef	PROTOTYPES
445 EXPORT void
write_errno(ewin_t * wp,char * msg,...)446 write_errno(ewin_t *wp, char *msg, ...)
447 #else
448 EXPORT void
449 write_errno(wp, msg, va_alist)
450 	ewin_t	*wp;
451 	char	*msg;
452 	va_dcl
453 #endif
454 {
455 	va_list	args;
456 	char	buf[100];
457 	int	len;
458 	int	err = geterrno();
459 	char	*errstr;
460 
461 	errstr = errmsgstr(err);
462 	if (errstr == NULL)
463 		errstr = "";
464 
465 #ifdef	PROTOTYPES
466 	va_start(args, msg);
467 #else
468 	va_start(args);
469 #endif
470 	len = snprintf(buf, sizeof (buf), "%r: %s", msg, args, errstr);
471 	va_end(args);
472 
473 	if (len <= nameerrsize) {
474 		writeerr(wp, "%s", buf);
475 		return;
476 	}
477 
478 #ifdef	PROTOTYPES
479 	va_start(args, msg);
480 #else
481 	va_start(args);
482 #endif
483 	snprintf(buf, sizeof (buf), "(%d) %r: %s", err, msg, args, errstr);
484 	va_end(args);
485 	writeerr(wp, "%s", buf);
486 }
487 
488 
489 /*---------------------------------------------------------------------------
490 |
491 | Set up the default info string (currently only used for modflag "*")
492 | It is displayed after the command that follows a command that sets
493 | the info string until a new info string is set.
494 |
495 +---------------------------------------------------------------------------*/
496 
497 EXPORT void
defaultinfo(wp,str)498 defaultinfo(wp, str)
499 	ewin_t	*wp;
500 	Uchar	*str;
501 {
502 	if (str == NULL)
503 		DefInfostr[0] = '\0';
504 	else
505 		snprintf(C DefInfostr, sizeof (DefInfostr), "%s", str);
506 	writemsg(wp, "%s", C DefInfostr);
507 
508 }
509 
510 
511 /*---------------------------------------------------------------------------
512 |
513 | Set up the default error string (currently only used for "NEW FILE")
514 | It is displayed after the command that follows a command that sets
515 | the error string until a new error string is set.
516 |
517 +---------------------------------------------------------------------------*/
518 
519 EXPORT void
defaulterr(wp,str)520 defaulterr(wp, str)
521 	ewin_t	*wp;
522 	Uchar	*str;
523 {
524 	if (str == 0) {
525 		DefErrorstr[0] = '\0';
526 		writeerr(wp, "");
527 	} else {
528 		snprintf(C DefErrorstr, sizeof (DefErrorstr), "%s", str);
529 		writeerr(wp, "%s", C DefErrorstr);
530 	}
531 }
532 
533 
534 /*---------------------------------------------------------------------------
535 |
536 | Set the error string to the default error string.
537 | If the default error string contains a message, prepend a blank as a
538 | separator to the file name.
539 |
540 +---------------------------------------------------------------------------*/
541 
542 LOCAL void
errdefault()543 errdefault()
544 {
545 	if (*DefErrorstr == '\0') {
546 		errorstr[0] = '\0';
547 	} else {
548 		/*
549 		 * Permanent error messages should be separated from
550 		 * the filename.
551 		 */
552 		errorstr[0] = ' ';
553 		strcpy(C &errorstr[1], C DefErrorstr); /* no overflow (same size)*/
554 
555 	}
556 }
557 
558 
559 /*---------------------------------------------------------------------------
560 |
561 | Refresh the whole system (status) line e.g when parts get overwritten.
562 |
563 +---------------------------------------------------------------------------*/
564 
565 EXPORT void
refreshmsg(wp)566 refreshmsg(wp)
567 	ewin_t	*wp;
568 {
569 	updatesysline = 0;
570 	CURSOR_HOME(wp);
571 	write_systemline(wp);
572 	setcursor(wp);
573 }
574 
575 /*---------------------------------------------------------------------------
576 |
577 | Refresh the whole system (status) line or parts of it depending on status.
578 |
579 +---------------------------------------------------------------------------*/
580 
581 EXPORT void
refreshsysline(wp)582 refreshsysline(wp)
583 	ewin_t	*wp;
584 {
585 	if (updatesysline) {
586 		refreshmsg(wp);
587 	} else {
588 		if (updateerr)
589 			writeerr(wp, C NULL);
590 		if (updatemsg)
591 			writemsg(wp, C NULL);
592 	}
593 }
594 
595 /*---------------------------------------------------------------------------
596 |
597 | Rewrite the whole system (status) line e.g when parts get overwritten.
598 | Reset info string & error string to the default if necessary.
599 | Allow the file name to use up the space of the error string
600 | if the error string is blank.
601 |
602 +---------------------------------------------------------------------------*/
603 
604 EXPORT void
write_systemline(wp)605 write_systemline(wp)
606 	ewin_t	*wp;
607 {
608 	if (updatemsg) {
609 		strncpy(C infostr, C DefInfostr, sizeof (infostr));
610 		infostr[sizeof (infostr)-1] = '\0';
611 		updatemsg = 0;
612 	}
613 	if (updateerr) {
614 		errdefault();
615 		updateerr = 0;
616 	}
617 	printfield(infostr, infosize);
618 	printfield(numstr, numsize);
619 	printfield(takestr, takesize);
620 
621 	if (*errorstr) {
622 		printfield(namestr, namesize);
623 		printfield(errorstr, errorsize);
624 	} else {
625 		printfield(namestr, nameerrsize);
626 	}
627 }
628 
629 EXPORT void
abortmsg(wp)630 abortmsg(wp)
631 	ewin_t	*wp;
632 {
633 	writeerr(wp, "ABORTED");
634 }
635 
636 EXPORT void
nomarkmsg(wp)637 nomarkmsg(wp)
638 	ewin_t	*wp;
639 {
640 	writeerr(wp, "No Mark !");
641 }
642