xref: /original-bsd/usr.bin/tn3270/ctlr/outbound.c (revision b218b2ee)
1 /*
2  * Copyright (c) 1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 static char sccsid[] = "@(#)outbound.c	4.2 (Berkeley) 02/06/89";
20 #endif /* not lint */
21 
22 #include <stdio.h>
23 
24 #include "../general/general.h"
25 
26 #include "hostctlr.h"
27 #include "oia.h"
28 #include "screen.h"
29 #include "../api/ebc_disp.h"
30 
31 #include "../general/globals.h"
32 #include "externs.h"
33 #include "declare.h"
34 
35 #define SetHighestLowest(position) { \
36 					if (position < Lowest) { \
37 					    Lowest = position; \
38 					} \
39 					if (position > Highest) { \
40 					    Highest = position; \
41 					} \
42 				    }
43 
44 
45 static int	LastWasTerminated = 1;	/* was "control" = 1 last time? */
46 
47 /* some globals */
48 
49 #if	!defined(PURE3274)
50 int	OutputClock;		/* what time it is */
51 int	TransparentClock;		/* time we were last in transparent */
52 #endif	/* !defined(PURE3274) */
53 
54 char CIABuffer[64] = {
55     0x40, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
56     0xc8, 0xc9, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
57     0x50, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
58     0xd8, 0xd9, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
59     0x60, 0x61, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
60     0xe8, 0xe9, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
61     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
62     0xf8, 0xf9, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f
63 };
64 
65 static struct orders_def orders_def[] = ORDERS_DEF;
66 
67 /*
68  * init_ctlr()
69  *
70  *	Initialize all data from the 'data' portion to their startup values.
71  */
72 
73 void
74 init_ctlr()
75 {
76     LastWasTerminated = 1;
77     init_inbound();
78     init_oia();
79 }
80 
81 
82 FieldInc(position)
83 register int	position;		/* Position in previous field */
84 {
85     register ScreenImage *ptr;
86 
87     ptr = (ScreenImage *)memNSchr((char *)Host+position+1, ATTR_MASK,
88 			HighestScreen()-position, ATTR_MASK, sizeof Host[0]);
89     if (ptr == 0) {
90 	ptr = (ScreenImage *)memNSchr((char *)Host+LowestScreen(), ATTR_MASK,
91 			position-LowestScreen(), ATTR_MASK, sizeof Host[0]);
92 	if (ptr == 0) {
93 	    return LowestScreen();
94 	}
95     }
96     return ptr-Host;
97 }
98 
99 FieldDec(position)
100 int	position;
101 {
102     register ScreenImage *ptr;
103 
104     ptr = (ScreenImage *)memNSchr((char *)(Host+position)-1, ATTR_MASK,
105 			position-LowestScreen(), ATTR_MASK, -sizeof Host[0]);
106     if (ptr == 0) {
107 	ptr = (ScreenImage *)memNSchr((char *)Host+HighestScreen(), ATTR_MASK,
108 			HighestScreen()-position, ATTR_MASK, -sizeof Host[0]);
109 	if (ptr == 0) {
110 	    return LowestScreen();
111 	}
112     }
113     return ptr-Host;
114 }
115 
116 /* Clear3270 - called to clear the screen */
117 
118 void
119 Clear3270()
120 {
121     ClearArray(Host);
122     DeleteAllFields();		/* get rid of all fields */
123     BufferAddress = SetBufferAddress(0,0);
124     CursorAddress = SetBufferAddress(0,0);
125     Lowest = LowestScreen();
126     Highest = HighestScreen();
127 }
128 
129 /* AddHost - called to add a character to the buffer.
130  *	We use a macro in this module, since we call it so
131  *	often from loops.
132  *
133  *	NOTE: It is a macro, so don't go around using AddHost(p, *c++), or
134  *	anything similar.  (I don't define any temporary variables, again
135  *	just for the speed.)
136  */
137 void
138 AddHost(position, character)
139 int	position;
140 char	character;
141 {
142 #   define	AddHostA(p,c)					\
143     {								\
144 	if (IsStartField(p)) {					\
145 	    DeleteField(p);					\
146 	    Highest = HighestScreen();				\
147 	    Lowest = LowestScreen();				\
148 	    SetHighestLowest(p);				\
149 	}							\
150 	SetHost(p, c);						\
151     }
152 #   define	AddHost(p,c)					\
153     {								\
154 	if (c != GetHost(p)) {					\
155 	    SetHighestLowest(p);				\
156 	}							\
157 	AddHostA(p,c);						\
158     }	/* end of macro of AddHost */
159 
160     AddHost(position, character);
161 }
162 
163 /* returns the number of characters consumed */
164 int
165 DataFromNetwork(Buffer, count, control)
166 char	*Buffer;				/* what the data is */
167 register int	count;				/* and how much there is */
168 int	control;				/* this buffer ended block? */
169 {
170     int origCount;
171     register unsigned char *buffer = (unsigned char *)Buffer;
172     register int c;
173     register int i;
174     static int Command;
175     static int Wcc;
176 
177     origCount = count;
178 
179     /*
180      * If this is the start of a new data stream, then look
181      * for an op-code and (possibly) a WCC.
182      */
183     if (LastWasTerminated) {
184 
185 	if (count < 2) {
186 	    if (count == 0) {
187 		ExitString("Short count received from host!\n", 1);
188 		return(count);
189 	    }
190 	    Command = buffer[0];
191 	    switch (Command) {		/* This had better be a read command */
192 	    case CMD_READ_MODIFIED:
193 	    case CMD_SNA_READ_MODIFIED:
194 	    case CMD_SNA_READ_MODIFIED_ALL:
195 		SetOiaOnlineA(&OperatorInformationArea);
196 		SetOiaModified();
197 		DoReadModified(Command);
198 		break;
199 	    case CMD_READ_BUFFER:
200 	    case CMD_SNA_READ_BUFFER:
201 		SetOiaOnlineA(&OperatorInformationArea);
202 		SetOiaModified();
203 		DoReadBuffer();
204 		break;
205 	    default:
206 		{
207 		    char s_buffer[100];
208 
209 		    sprintf(s_buffer,
210 			"Unexpected read command code 0x%x received.\n",
211 								    Command);
212 		    ExitString(s_buffer, 1);
213 		    break;
214 		}
215 	    }
216 	    return(1);			/* We consumed everything */
217 	}
218 	Command = buffer[0];
219 	Wcc = buffer[1];
220 	if (Wcc & WCC_RESET_MDT) {
221 	    i = c = WhereAttrByte(LowestScreen());
222 	    do {
223 		if (HasMdt(i)) {
224 		    TurnOffMdt(i);
225 		}
226 		i = FieldInc(i);
227 	    } while (i != c);
228 	}
229 
230 	switch (Command) {
231 	case CMD_ERASE_WRITE:
232 	case CMD_ERASE_WRITE_ALTERNATE:
233 	case CMD_SNA_ERASE_WRITE:
234 	case CMD_SNA_ERASE_WRITE_ALTERNATE:
235 	    {
236 		int newlines, newcolumns;
237 
238 		SetOiaOnlineA(&OperatorInformationArea);
239 		ResetOiaTWait(&OperatorInformationArea);
240 		SetOiaModified();
241 		if ((Command == CMD_ERASE_WRITE)
242 				|| (Command == CMD_SNA_ERASE_WRITE)) {
243 		    newlines = 24;
244 		    newcolumns = 80;
245 		} else {
246 		    newlines = MaxNumberLines;
247 		    newcolumns = MaxNumberColumns;
248 		}
249 		if ((newlines != NumberLines)
250 				|| (newcolumns != NumberColumns)) {
251 			/*
252 			 * The LocalClearScreen() is really for when we
253 			 * are going from a larger screen to a smaller
254 			 * screen, and we need to clear off the stuff
255 			 * at the end of the lines, or the lines at
256 			 * the end of the screen.
257 			 */
258 		    LocalClearScreen();
259 		    NumberLines = newlines;
260 		    NumberColumns = newcolumns;
261 		    ScreenSize = NumberLines * NumberColumns;
262 		}
263 		Clear3270();
264 #if	!defined(PURE3274)
265 		if (TransparentClock == OutputClock) {
266 		    TransStop();
267 		}
268 #endif	/* !defined(PURE3274) */
269 		break;
270 	    }
271 
272 	case CMD_ERASE_ALL_UNPROTECTED:
273 	case CMD_SNA_ERASE_ALL_UNPROTECTED:
274 	    SetOiaOnlineA(&OperatorInformationArea);
275 	    ResetOiaTWait(&OperatorInformationArea);
276 	    SetOiaModified();
277 	    CursorAddress = HighestScreen()+1;
278 	    for (i = LowestScreen(); i <= HighestScreen(); i = ScreenInc(i)) {
279 		if (IsUnProtected(i)) {
280 		    if (CursorAddress > i) {
281 			CursorAddress = i;
282 		    }
283 		    AddHost(i, '\0');
284 		}
285 		if (HasMdt(i)) {
286 		    TurnOffMdt(i);
287 		}
288 	    }
289 	    if (CursorAddress == HighestScreen()+1) {
290 		CursorAddress = SetBufferAddress(0,0);
291 	    }
292 	    UnLocked = 1;
293 	    AidByte = 0;
294 	    ResetOiaSystemLocked(&OperatorInformationArea);
295 	    SetOiaModified();
296 	    TerminalIn();
297 	    break;
298 	case CMD_WRITE:
299 	case CMD_SNA_WRITE:
300 	    SetOiaOnlineA(&OperatorInformationArea);
301 	    ResetOiaTWait(&OperatorInformationArea);
302 	    SetOiaModified();
303 	    break;
304 	default:
305 	    {
306 		char s_buffer[100];
307 
308 		sprintf(s_buffer,
309 			"Unexpected write command code 0x%x received.\n",
310 								Command);
311 		ExitString(s_buffer, 1);
312 		break;
313 	    }
314 	}
315 
316 	count -= 2;			/* strip off command and wcc */
317 	buffer += 2;
318 
319     } else {
320 #if	!defined(PURE3274)
321 	if (TransparentClock == OutputClock) {
322 	    TransOut(buffer, count, -1, control);
323 	    count = 0;
324 	}
325 #endif	/* !defined(PURE3274) */
326     }
327     LastWasTerminated = 0;		/* then, reset at end... */
328 
329     while (count) {
330 	count--;
331 	c = *buffer++;
332 	if (IsOrder(c)) {
333 	    /* handle an order */
334 	    switch (c) {
335 #		define Ensure(x)	if (count < x) { \
336 					    if (!control) { \
337 						return(origCount-(count+1)); \
338 					    } else { \
339 						/* XXX - should not occur */ \
340 						count = 0; \
341 						break; \
342 					    } \
343 					}
344 	    case ORDER_SF:
345 		Ensure(1);
346 		c = *buffer++;
347 		count--;
348 		if ( ! (IsStartField(BufferAddress) &&
349 					FieldAttributes(BufferAddress) == c)) {
350 		    SetHighestLowest(BufferAddress);
351 		    NewField(BufferAddress,c);
352 		}
353 		BufferAddress = ScreenInc(BufferAddress);
354 		break;
355 	    case ORDER_SBA:
356 		Ensure(2);
357 		i = buffer[0];
358 		c = buffer[1];
359 #if	!defined(PURE3274)
360 		/* Check for transparent write */
361 		if ((i == 0) && ((c == 0) || (c == 1) || (c == 5))) {
362 		    TransparentClock = OutputClock+1;
363 		    TransOut(buffer+2, count-2, c, control);
364 		    buffer += count;
365 		    count -= count;
366 		    break;
367 		}
368 #endif	/* !defined(PURE3274) */
369 		BufferAddress = Addr3270(i, c);
370 		buffer += 2;
371 		count -= 2;
372 		break;
373 	    case ORDER_IC:
374 		CursorAddress = BufferAddress;
375 		break;
376 	    /*
377 	     * XXX - PT is supposed to null fill the screen buffer
378 	     * under certain draconian conditions.
379 	     */
380 	    case ORDER_PT:
381 		i = BufferAddress;
382 		do {
383 		    if (IsStartField(i)) {
384 			if (!IsProtected(ScreenInc(i))) {
385 			    break;
386 			}
387 		    }
388 		    i = ScreenInc(i);
389 		} while (i != HighestScreen());
390 		BufferAddress = ScreenInc(i);
391 		break;
392 	    case ORDER_RA:
393 		Ensure(3);
394 		i = Addr3270(buffer[0], buffer[1]);
395 		if ((i < 0) || (i > HighestScreen())) {
396 		    char s_buffer[200];
397 
398 		    sprintf(s_buffer, "tn3270:  %s%d.\n\t%s%d%s%d%s\n",
399 			"Invalid 3270 order 'Repeat to Address' to address ",
400 			i,
401 			"(Screen currently set to ",
402 			NumberLines,
403 			" by ",
404 			NumberColumns,
405 			".)");
406 		    ExitString(s_buffer, 1);
407 		    /*NOTREACHED*/
408 		}
409 		c = buffer[2];
410 		if (c == ORDER_GE) {
411 		    Ensure(4);
412 		    c = buffer[3];
413 		    buffer += 4;
414 		    count -= 4;
415 		} else {
416 		    buffer += 3;
417 		    count -= 3;
418 		}
419 		do {
420 		    AddHost(BufferAddress, ebc_disp[c]);
421 		    BufferAddress = ScreenInc(BufferAddress);
422 		} while (BufferAddress != i);
423 		break;
424 	    case ORDER_EUA:    /* (from [here,there), ie: half open interval] */
425 		Ensure(2);
426 		/*
427 		 * Compiler error - msc version 4.0:
428 		 *			"expression too complicated".
429 		 */
430 		i = WhereAttrByte(BufferAddress);
431 		c = FieldAttributes(i);
432 		i = Addr3270(buffer[0], buffer[1]);
433 		if ((i < 0) || (i > HighestScreen())) {
434 		    char s_buffer[200];
435 
436 		    sprintf(s_buffer, "tn3270:  %s%d.\n\t%s%d%s%d%s\n",
437 			"Invalid 3270 order 'Erase Unprotected to Address' to address ",
438 			i,
439 			"(Screen currently set to ",
440 			NumberLines,
441 			" by ",
442 			NumberColumns,
443 			".)");
444 		    ExitString(s_buffer, 1);
445 		    /*NOTREACHED*/
446 		}
447 		do {
448 		    if (IsStartField(BufferAddress)) {
449 			c = FieldAttributes(BufferAddress);
450 		    } else if (!IsProtectedAttr(BufferAddress, c)) {
451 			AddHost(BufferAddress, 0);
452 		    }
453 		    BufferAddress = ScreenInc(BufferAddress);
454 		} while (i != BufferAddress);
455 		buffer += 2;
456 		count -= 2;
457 		break;
458 	    case ORDER_GE:
459 		Ensure(2);
460 		/* XXX Should do SOMETHING! */
461 		/* XXX buffer += 0; */
462 		/* XXX count -= 0; *//* For now, just use this character */
463 		break;
464 	    case ORDER_YALE:		/* special YALE defined order */
465 		Ensure(2);	/* need at least two characters */
466 		if (*buffer == 0x5b) {
467 		    i = OptOrder(buffer+1, count-1, control);
468 		    if (i == 0) {
469 			return(origCount-(count+1));	/* come here again */
470 		    } else {
471 			buffer += 1 + i;
472 			count  -= (1 + i);
473 		    }
474 		}
475 		break;
476 	    default:
477 		{
478 		    char s_buffer[100];
479 		    static struct orders_def unk_order
480 						= { 0, "??", "(unknown)" };
481 		    struct orders_def *porder = &unk_order;
482 		    int s_i;
483 
484 		    for (s_i = 0; s_i <= highestof(orders_def); s_i++) {
485 			if (orders_def[s_i].code == c) {
486 			    porder = &orders_def[s_i];
487 			    break;
488 			}
489 		    }
490 		    sprintf(s_buffer,
491 			"Unsupported order '%s' (%s, 0x%x) received.\n",
492 			porder->long_name, porder->short_name, c);
493 		    ExitString(s_buffer, 1);
494 		    /*NOTREACHED*/
495 		}
496 	    }
497 	    if (count < 0) {
498 		count = 0;
499 	    }
500 	} else {
501 	    /* Data comes in large clumps - take it all */
502 	    i = BufferAddress;
503 	    AddHostA(i, ebc_disp[c]);
504 	    SetHighestLowest(i);
505 	    i = ScreenInc(i);
506 	    c = *buffer;
507 	    while (count && !IsOrder(c)) {
508 		AddHostA(i, ebc_disp[c]);
509 		i = ScreenInc(i);
510 		if (i == LowestScreen()) {
511 		    SetHighestLowest(HighestScreen());
512 		}
513 		count--;
514 		buffer++;
515 		c = *buffer;
516 	    }
517 	    SetHighestLowest(i);
518 	    BufferAddress = i;
519 	}
520     }
521     if (count == 0) {
522 	if (control) {
523 #if	!defined(PURE3274)
524 	    OutputClock++;		/* time rolls on */
525 #endif	/* !defined(PURE3274) */
526 	    if (Wcc & WCC_RESTORE) {
527 #if	!defined(PURE3274)
528 		if (TransparentClock != OutputClock) {
529 		    AidByte = 0;
530 		}
531 #else	/* !defined(PURE3274) */
532 		AidByte = 0;
533 #endif	/* !defined(PURE3274) */
534 		UnLocked = 1;
535 		ResetOiaSystemLocked(&OperatorInformationArea);
536 		SetOiaModified();
537 		SetPsModified();
538 		TerminalIn();
539 	    }
540 	    if (Wcc & WCC_ALARM) {
541 		RingBell((char *)0);
542 	    }
543 	}
544 	LastWasTerminated = control;	/* state for next time */
545 	return(origCount);
546     } else {
547 	return(origCount-count);
548     }
549 }
550 
551 /*
552  * Init3270()
553  *
554  * Initialize any 3270 (controller) variables to an initial state
555  * in preparation for accepting a connection.
556  */
557 
558 void
559 Init3270()
560 {
561     int i;
562 
563     OptInit();		/* initialize mappings */
564 
565     ClearArray(Host);
566 
567     ClearArray(Orders);
568     for (i = 0; i <= highestof(orders_def); i++) {
569 	Orders[orders_def[i].code] = 1;
570     }
571 
572     DeleteAllFields();		/* Clear screen */
573     Lowest = HighestScreen()+1;
574     Highest = LowestScreen()-1;
575     CursorAddress = BufferAddress = SetBufferAddress(0,0);
576     UnLocked = 1;
577 #if	!defined(PURE3274)
578     OutputClock = 1;
579     TransparentClock = -1;
580 #endif	/* !defined(PURE3274) */
581     SetOiaReady3274(&OperatorInformationArea);
582 }
583 
584 
585 void
586 Stop3270()
587 {
588     ResetOiaReady3274(&OperatorInformationArea);
589 }
590