1 /*********************************************************** 2 Copyright IBM Corporation 1987 3 4 All Rights Reserved 5 6 Permission to use, copy, modify, and distribute this software and its 7 documentation for any purpose and without fee is hereby granted, 8 provided that the above copyright notice appear in all copies and that 9 both that copyright notice and this permission notice appear in 10 supporting documentation, and that the name of IBM not be 11 used in advertising or publicity pertaining to distribution of the 12 software without specific, written prior permission. 13 14 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 15 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 16 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 17 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 18 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 19 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 20 SOFTWARE. 21 22 ******************************************************************/ 23 24 /* 25 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison 26 */ 27 /* 28 * ARGO TP 29 * 30 * $Header: tp_subr2.c,v 5.5 88/11/18 17:28:55 nhall Exp $ 31 * $Source: /usr/argo/sys/netiso/RCS/tp_subr2.c,v $ 32 * @(#)tp_subr2.c 7.5 (Berkeley) 01/09/91 33 * 34 * Some auxiliary routines: 35 * tp_protocol_error: required by xebec- called when a combo of state, 36 * event, predicate isn't covered for by the transition file. 37 * tp_indicate: gives indications(signals) to the user process 38 * tp_getoptions: initializes variables that are affected by the options 39 * chosen. 40 */ 41 42 #ifndef lint 43 static char *rcsid = "$Header: tp_subr2.c,v 5.5 88/11/18 17:28:55 nhall Exp $"; 44 #endif lint 45 46 /* this def'n is to cause the expansion of this macro in the 47 * routine tp_local_credit : 48 */ 49 #define LOCAL_CREDIT_EXPAND 50 51 #include "param.h" 52 #include "mbuf.h" 53 #include "socket.h" 54 #include "socketvar.h" 55 #include "domain.h" 56 #include "protosw.h" 57 #include "errno.h" 58 #include "types.h" 59 #include "time.h" 60 #include "kernel.h" 61 #undef MNULL 62 #include "argo_debug.h" 63 #include "tp_param.h" 64 #include "tp_ip.h" 65 #include "iso.h" 66 #include "iso_errno.h" 67 #include "iso_pcb.h" 68 #include "tp_timer.h" 69 #include "tp_stat.h" 70 #include "tp_tpdu.h" 71 #include "tp_pcb.h" 72 #include "tp_seq.h" 73 #include "tp_trace.h" 74 #include "tp_user.h" 75 #include "cons.h" 76 77 #include "../net/if.h" 78 #ifdef TRUE 79 #undef FALSE 80 #undef TRUE 81 #endif 82 #include "../netccitt/x25.h" 83 #include "../netccitt/pk.h" 84 #include "../netccitt/pk_var.h" 85 86 /* 87 * NAME: tp_local_credit() 88 * 89 * CALLED FROM: 90 * tp_emit(), tp_usrreq() 91 * 92 * FUNCTION and ARGUMENTS: 93 * Computes the local credit and stashes it in tpcb->tp_lcredit. 94 * It's a macro in the production system rather than a procdure. 95 * 96 * RETURNS: 97 * 98 * SIDE EFFECTS: 99 * 100 * NOTES: 101 * This doesn't actually get called in a production system - 102 * the macro gets expanded instead in place of calls to this proc. 103 * But for debugging, we call this and that allows us to add 104 * debugging messages easily here. 105 */ 106 void 107 tp_local_credit(tpcb) 108 struct tp_pcb *tpcb; 109 { 110 LOCAL_CREDIT(tpcb); 111 IFDEBUG(D_CREDIT) 112 printf("ref 0x%x lcdt 0x%x l_tpdusize 0x%x decbit 0x%x\n", 113 tpcb->tp_refp - tp_ref, 114 tpcb->tp_lcredit, 115 tpcb->tp_l_tpdusize, 116 tpcb->tp_decbit, 117 tpcb->tp_cong_win 118 ); 119 ENDDEBUG 120 IFTRACE(D_CREDIT) 121 tptraceTPCB(TPPTmisc, 122 "lcdt tpdusz \n", 123 tpcb->tp_lcredit, tpcb->tp_l_tpdusize, 0, 0); 124 ENDTRACE 125 } 126 127 /* 128 * NAME: tp_protocol_error() 129 * 130 * CALLED FROM: 131 * tp_driver(), when it doesn't know what to do with 132 * a combo of event, state, predicate 133 * 134 * FUNCTION and ARGUMENTS: 135 * print error mesg 136 * 137 * RETURN VALUE: 138 * EIO - always 139 * 140 * SIDE EFFECTS: 141 * 142 * NOTES: 143 */ 144 int 145 tp_protocol_error(e,tpcb) 146 struct tp_event *e; 147 struct tp_pcb *tpcb; 148 { 149 printf("TP PROTOCOL ERROR! tpcb 0x%x event 0x%x, state 0x%x\n", 150 tpcb, e->ev_number, tpcb->tp_state); 151 IFTRACE(D_DRIVER) 152 tptraceTPCB(TPPTmisc, "PROTOCOL ERROR tpcb event state", 153 tpcb, e->ev_number, tpcb->tp_state, 0 ); 154 ENDTRACE 155 return EIO; /* for lack of anything better */ 156 } 157 158 159 /* Not used at the moment */ 160 ProtoHook 161 tp_drain() 162 { 163 return 0; 164 } 165 166 167 /* 168 * NAME: tp_indicate() 169 * 170 * CALLED FROM: 171 * tp.trans when XPD arrive, when a connection is being disconnected by 172 * the arrival of a DR or ER, and when a connection times out. 173 * 174 * FUNCTION and ARGUMENTS: 175 * (ind) is the type of indication : T_DISCONNECT, T_XPD 176 * (error) is an E* value that will be put in the socket structure 177 * to be passed along to the user later. 178 * Gives a SIGURG to the user process or group indicated by the socket 179 * attached to the tpcb. 180 * 181 * RETURNS: Rien 182 * 183 * SIDE EFFECTS: 184 * 185 * NOTES: 186 */ 187 void 188 tp_indicate(ind, tpcb, error) 189 int ind; 190 u_short error; 191 register struct tp_pcb *tpcb; 192 { 193 register struct socket *so = tpcb->tp_sock; 194 IFTRACE(D_INDICATION) 195 tptraceTPCB(TPPTindicate, ind, *(u_short *)(tpcb->tp_lsuffix), 196 *(u_short *)(tpcb->tp_fsuffix), error,so->so_pgid); 197 ENDTRACE 198 IFDEBUG(D_INDICATION) 199 char *ls, *fs; 200 ls = tpcb->tp_lsuffix, 201 fs = tpcb->tp_fsuffix, 202 203 printf( 204 "indicate 0x%x lsuf 0x%02x%02x fsuf 0x%02x%02x err 0x%x noind 0x%x ref 0x%x\n", 205 ind, 206 *ls, *(ls+1), *fs, *(fs+1), 207 error, /*so->so_pgrp,*/ 208 tpcb->tp_no_disc_indications, 209 tpcb->tp_lref); 210 ENDDEBUG 211 212 so->so_error = error; 213 214 if (ind == T_DISCONNECT) { 215 if ( tpcb->tp_no_disc_indications ) 216 return; 217 } 218 IFTRACE(D_INDICATION) 219 tptraceTPCB(TPPTmisc, "doing sohasoutofband(so)", so,0,0,0); 220 ENDTRACE 221 sohasoutofband(so); 222 } 223 224 /* 225 * NAME : tp_getoptions() 226 * 227 * CALLED FROM: 228 * tp.trans whenever we go into OPEN state 229 * 230 * FUNCTION and ARGUMENTS: 231 * sets the proper flags and values in the tpcb, to control 232 * the appropriate actions for the given class, options, 233 * sequence space, etc, etc. 234 * 235 * RETURNS: Nada 236 * 237 * SIDE EFFECTS: 238 * 239 * NOTES: 240 */ 241 void 242 tp_getoptions(tpcb) 243 struct tp_pcb *tpcb; 244 { 245 tpcb->tp_seqmask = 246 tpcb->tp_xtd_format ? TP_XTD_FMT_MASK : TP_NML_FMT_MASK ; 247 tpcb->tp_seqbit = 248 tpcb->tp_xtd_format ? TP_XTD_FMT_BIT : TP_NML_FMT_BIT ; 249 tpcb->tp_seqhalf = tpcb->tp_seqbit >> 1; 250 tpcb->tp_dt_ticks = 251 MAX(tpcb->tp_dt_ticks, (tpcb->tp_peer_acktime + 2)); 252 253 } 254 255 /* 256 * NAME: tp_recycle_tsuffix() 257 * 258 * CALLED FROM: 259 * Called when a ref is frozen. 260 * 261 * FUNCTION and ARGUMENTS: 262 * allows the suffix to be reused. 263 * 264 * RETURNS: zilch 265 * 266 * SIDE EFFECTS: 267 * 268 * NOTES: 269 */ 270 void 271 tp_recycle_tsuffix(tpcb) 272 struct tp_pcb *tpcb; 273 { 274 bzero((caddr_t)tpcb->tp_lsuffix, sizeof( tpcb->tp_lsuffix)); 275 bzero((caddr_t)tpcb->tp_fsuffix, sizeof( tpcb->tp_fsuffix)); 276 tpcb->tp_fsuffixlen = tpcb->tp_lsuffixlen = 0; 277 278 (tpcb->tp_nlproto->nlp_recycle_suffix)(tpcb->tp_npcb); 279 } 280 281 /* 282 * NAME: tp_quench() 283 * 284 * CALLED FROM: 285 * tp{af}_quench() when ICMP source quench or similar thing arrives. 286 * 287 * FUNCTION and ARGUMENTS: 288 * Drop the congestion window back to 1. 289 * Congestion window scheme: 290 * Initial value is 1. ("slow start" as Nagle, et. al. call it) 291 * For each good ack that arrives, the congestion window is increased 292 * by 1 (up to max size of logical infinity, which is to say, 293 * it doesn't wrap around). 294 * Source quench causes it to drop back to 1. 295 * tp_send() uses the smaller of (regular window, congestion window). 296 * One retransmission strategy option is to have any retransmission 297 * cause reset the congestion window back to 1. 298 * 299 * (cmd) is either PRC_QUENCH: source quench, or 300 * PRC_QUENCH2: dest. quench (dec bit) 301 * 302 * RETURNS: 303 * 304 * SIDE EFFECTS: 305 * 306 * NOTES: 307 */ 308 void 309 tp_quench( tpcb, cmd ) 310 struct tp_pcb *tpcb; 311 int cmd; 312 { 313 IFDEBUG(D_QUENCH) 314 printf("tp_quench tpcb 0x%x ref 0x%x sufx 0x%x\n", 315 tpcb, tpcb->tp_lref, *(u_short *)(tpcb->tp_lsuffix)); 316 printf("cong_win 0x%x decbit 0x%x \n", 317 tpcb->tp_cong_win, tpcb->tp_decbit); 318 ENDDEBUG 319 switch(cmd) { 320 case PRC_QUENCH: 321 tpcb->tp_cong_win = 1; 322 IncStat(ts_quench); 323 break; 324 case PRC_QUENCH2: 325 tpcb->tp_cong_win = 1; /* might as well quench source also */ 326 tpcb->tp_decbit = TP_DECBIT_CLEAR_COUNT; 327 IncStat(ts_rcvdecbit); 328 break; 329 } 330 } 331 332 333 /* 334 * NAME: tp_netcmd() 335 * 336 * CALLED FROM: 337 * 338 * FUNCTION and ARGUMENTS: 339 * 340 * RETURNS: 341 * 342 * SIDE EFFECTS: 343 * 344 * NOTES: 345 */ 346 tp_netcmd( tpcb, cmd ) 347 struct tp_pcb *tpcb; 348 int cmd; 349 { 350 #ifdef TPCONS 351 struct isopcb *isop; 352 struct pklcd *lcp; 353 354 if (tpcb->tp_netservice != ISO_CONS) 355 return; 356 isop = (struct isopcb *)tpcb->tp_npcb; 357 lcp = (struct pklcd *)isop->isop_chan; 358 switch (cmd) { 359 360 case CONN_CLOSE: 361 case CONN_REFUSE: 362 if (isop->isop_refcnt == 1) 363 pk_disconnect(lcp); 364 isop->isop_chan = 0; 365 isop->isop_refcnt = 0; 366 break; 367 368 default: 369 printf("tp_netcmd(0x%x, 0x%x) NOT IMPLEMENTED\n", tpcb, cmd); 370 break; 371 } 372 #else TPCONS 373 printf("tp_netcmd(): X25 NOT CONFIGURED!!\n"); 374 #endif 375 } 376 /* 377 * CALLED FROM: 378 * tp_ctloutput() and tp_emit() 379 * FUNCTION and ARGUMENTS: 380 * Convert a class mask to the highest numeric value it represents. 381 */ 382 383 int 384 tp_mask_to_num(x) 385 u_char x; 386 { 387 register int j; 388 389 for(j = 4; j>=0 ;j--) { 390 if(x & (1<<j)) 391 break; 392 } 393 ASSERT( (j == 4) || (j == 0) ); /* for now */ 394 if( (j != 4) && (j != 0) ) { 395 printf("ASSERTION ERROR: tp_mask_to_num: x 0x%x j %d\n", 396 x, j); 397 } 398 IFTRACE(D_TPINPUT) 399 tptrace(TPPTmisc, "tp_mask_to_num(x) returns j", x, j, 0, 0); 400 ENDTRACE 401 IFDEBUG(D_TPINPUT) 402 printf("tp_mask_to_num(0x%x) returns 0x%x\n", x, j); 403 ENDDEBUG 404 return j; 405 } 406 407 static 408 copyQOSparms(src, dst) 409 struct tp_conn_param *src, *dst; 410 { 411 /* copy all but the bits stuff at the end */ 412 #define COPYSIZE (12 * sizeof(short)) 413 414 bcopy((caddr_t)src, (caddr_t)dst, COPYSIZE); 415 dst->p_tpdusize = src->p_tpdusize; 416 dst->p_ack_strat = src->p_ack_strat; 417 dst->p_rx_strat = src->p_rx_strat; 418 #undef COPYSIZE 419 } 420 421 /* 422 * CALLED FROM: 423 * tp_usrreq on PRU_CONNECT and tp_input on receipt of CR 424 * 425 * FUNCTION and ARGUMENTS: 426 * route directly to x.25 if the address is type 37 - GROT. 427 * furthermore, let TP0 handle only type-37 addresses 428 * 429 * Since this assumes that its address argument is in a mbuf, the 430 * parameter was changed to reflect this assumtion. This also 431 * implies that an mbuf must be allocated when this is 432 * called from tp_input 433 * 434 * RETURNS: 435 * errno value : 436 * EAFNOSUPPORT if can't find an nl_protosw for x.25 (really could panic) 437 * ECONNREFUSED if trying to run TP0 with non-type 37 address 438 * possibly other E* returned from cons_netcmd() 439 * NOTE: 440 * Would like to eliminate as much of this as possible -- 441 * only one set of defaults (let the user set the parms according 442 * to parameters provided in the directory service). 443 * Left here for now 'cause we don't yet have a clean way to handle 444 * it on the passive end. 445 */ 446 int 447 tp_route_to( m, tpcb, channel) 448 struct mbuf *m; 449 register struct tp_pcb *tpcb; 450 caddr_t channel; 451 { 452 register struct sockaddr_iso *siso; /* NOTE: this may be a sockaddr_in */ 453 extern struct tp_conn_param tp_conn_param[]; 454 struct pklcd *lcp = (struct pklcd *)channel; 455 int error = 0; 456 457 siso = mtod(m, struct sockaddr_iso *); 458 IFTRACE(D_CONN) 459 tptraceTPCB(TPPTmisc, 460 "route_to: so afi netservice class", 461 tpcb->tp_sock, siso->siso_addr.isoa_genaddr[0], tpcb->tp_netservice, 462 tpcb->tp_class); 463 ENDTRACE 464 IFDEBUG(D_CONN) 465 printf("tp_route_to( m x%x, channel 0x%x, tpcb 0x%x netserv 0x%x)\n", 466 m, channel, tpcb, tpcb->tp_netservice); 467 printf("m->mlen x%x, m->m_data:\n", m->m_len); 468 dump_buf(mtod(m, caddr_t), m->m_len); 469 ENDDEBUG 470 if (siso->siso_family != tpcb->tp_domain) { 471 error = EAFNOSUPPORT; 472 goto done; 473 } 474 IFDEBUG(D_CONN) 475 printf("tp_route_to calling nlp_pcbconn, netserv %d\n", 476 tpcb->tp_netservice); 477 ENDDEBUG 478 #ifdef TPCONS 479 if (lcp) { 480 struct isopcb *isop = (struct isopcb *)lcp->lcd_upnext, 481 *isop_new = (struct isopcb *)tpcb->tp_sock->so_pcb; 482 remque(isop_new); 483 free(isop_new, M_PCB); 484 tpcb->tp_sock->so_pcb = (caddr_t)isop; 485 if (isop->isop_refcnt == 0) { 486 extern struct isopcb tp_isopcb; 487 remque(isop); 488 insque(isop, &tp_isopcb); 489 isop->isop_head = &tp_isopcb; 490 iso_putsufx(isop, tpcb->tp_lsuffix, tpcb->tp_lsuffixlen, TP_LOCAL); 491 } 492 /* else there are already connections sharing this */ 493 isop->isop_refcnt++; 494 } else 495 #endif 496 error = (tpcb->tp_nlproto->nlp_pcbconn)(tpcb->tp_sock->so_pcb, m); 497 if( error ) 498 goto done; 499 500 { 501 register int save_netservice = tpcb->tp_netservice; 502 503 switch(tpcb->tp_netservice) { 504 case ISO_COSNS: 505 case ISO_CLNS: 506 /* This is a kludge but seems necessary so the passive end 507 * can get long enough timers. sigh. 508 if( siso->siso_addr.osinet_idi[1] == (u_char)IDI_OSINET ) 509 */ 510 #define IDI_OSINET 0x0004 /* bcd of "0004" */ 511 if( siso->siso_addr.isoa_genaddr[2] == (char)IDI_OSINET ) { 512 if( tpcb->tp_dont_change_params == 0) { 513 copyQOSparms( &tp_conn_param[ISO_COSNS], 514 &tpcb->_tp_param); 515 } 516 tpcb->tp_flags |= TPF_NLQOS_PDN; 517 } 518 /* drop through to IN_CLNS*/ 519 case IN_CLNS: 520 if (iso_localifa(siso)) 521 tpcb->tp_flags |= TPF_PEER_ON_SAMENET; 522 if( (tpcb->tp_class & TP_CLASS_4)==0 ) { 523 error = EPROTOTYPE; 524 break; 525 } 526 tpcb->tp_class = TP_CLASS_4; /* IGNORE dont_change_parms */ 527 break; 528 529 case ISO_CONS: 530 #ifdef TPCONS 531 tpcb->tp_flags |= TPF_NLQOS_PDN; 532 if( tpcb->tp_dont_change_params == 0 ) { 533 copyQOSparms( &tp_conn_param[ISO_CONS], 534 &tpcb->_tp_param); 535 } 536 /* 537 * for use over x.25 really need a small receive window, 538 * need to start slowly, need small max negotiable tpdu size, 539 * and need to use the congestion window to the max 540 * IGNORES tp_dont_change_params for these! 541 */ 542 if( tpcb->tp_sock->so_snd.sb_hiwat > 512 ) { 543 (void) soreserve(tpcb->tp_sock, 512, 512 );/* GAG */ 544 } 545 tpcb->tp_rx_strat = TPRX_USE_CW; 546 547 if( (tpcb->tp_nlproto != &nl_protosw[ISO_CONS]) ) { 548 IFDEBUG(D_CONN) 549 printf( 550 "tp_route_to( CHANGING nlproto old 0x%x new 0x%x)\n", 551 tpcb->tp_nlproto , &nl_protosw[ISO_CONS]); 552 ENDDEBUG 553 tpcb->tp_nlproto = &nl_protosw[ISO_CONS]; 554 } 555 /* class 4 doesn't need to open a vc now - may use one already 556 * opened or may open one only when it sends a pkt. 557 */ 558 #else TPCONS 559 error = ECONNREFUSED; 560 #endif TPCONS 561 break; 562 default: 563 error = EPROTOTYPE; 564 } 565 566 ASSERT( save_netservice == tpcb->tp_netservice); 567 } 568 if (error) { 569 tp_netcmd( tpcb, CONN_CLOSE); 570 goto done; 571 } 572 { /* start with the global rtt, rtv stats */ 573 register int i = 574 (int) tpcb->tp_flags & (TPF_PEER_ON_SAMENET | TPF_NLQOS_PDN); 575 576 tpcb->tp_rtt = tp_stat.ts_rtt[i]; 577 tpcb->tp_rtv = tp_stat.ts_rtv[i]; 578 } 579 done: 580 IFDEBUG(D_CONN) 581 printf("tp_route_to returns 0x%x\n", error); 582 ENDDEBUG 583 IFTRACE(D_CONN) 584 tptraceTPCB(TPPTmisc, "route_to: returns: error netserv class", error, 585 tpcb->tp_netservice, tpcb->tp_class, 0); 586 ENDTRACE 587 return error; 588 } 589 590 #ifdef TP_PERF_MEAS 591 /* 592 * CALLED FROM: 593 * tp_ctloutput() when the user sets TPOPT_PERF_MEAS on 594 * and tp_newsocket() when a new connection is made from 595 * a listening socket with tp_perf_on == true. 596 * FUNCTION and ARGUMENTS: 597 * (tpcb) is the usual; this procedure gets a clear cluster mbuf for 598 * a tp_pmeas structure, and makes tpcb->tp_p_meas point to it. 599 * RETURN VALUE: 600 * ENOBUFS if it cannot get a cluster mbuf. 601 */ 602 603 int 604 tp_setup_perf(tpcb) 605 register struct tp_pcb *tpcb; 606 { 607 register struct mbuf *q; 608 609 if( tpcb->tp_p_meas == 0 ) { 610 MGET(q, M_WAITOK, MT_PCB); 611 if (q == 0) 612 return ENOBUFS; 613 MCLGET(q, M_WAITOK); 614 if ((q->m_flags & M_EXT) == 0) { 615 (void) m_free(q); 616 return ENOBUFS; 617 } 618 q->m_len = sizeof (struct tp_pmeas); 619 tpcb->tp_p_mbuf = q; 620 tpcb->tp_p_meas = mtod(q, struct tp_pmeas *); 621 bzero( (caddr_t)tpcb->tp_p_meas, sizeof (struct tp_pmeas) ); 622 IFDEBUG(D_PERF_MEAS) 623 printf( 624 "tpcb 0x%x so 0x%x ref 0x%x tp_p_meas 0x%x tp_perf_on 0x%x\n", 625 tpcb, tpcb->tp_sock, tpcb->tp_lref, 626 tpcb->tp_p_meas, tpcb->tp_perf_on); 627 ENDDEBUG 628 tpcb->tp_perf_on = 1; 629 } 630 return 0; 631 } 632 #endif TP_PERF_MEAS 633 634 #ifdef ARGO_DEBUG 635 dump_addr (addr) 636 register struct sockaddr *addr; 637 { 638 switch( addr->sa_family ) { 639 case AF_INET: 640 dump_inaddr((struct sockaddr_in *)addr); 641 break; 642 #ifdef ISO 643 case AF_ISO: 644 dump_isoaddr((struct sockaddr_iso *)addr); 645 break; 646 #endif ISO 647 default: 648 printf("BAD AF: 0x%x\n", addr->sa_family); 649 break; 650 } 651 } 652 653 #define MAX_COLUMNS 8 654 /* 655 * Dump the buffer to the screen in a readable format. Format is: 656 * 657 * hex/dec where hex is the hex format, dec is the decimal format. 658 * columns of hex/dec numbers will be printed, followed by the 659 * character representations (if printable). 660 */ 661 Dump_buf(buf, len) 662 caddr_t buf; 663 int len; 664 { 665 int i,j; 666 667 printf("Dump buf 0x%x len 0x%x\n", buf, len); 668 for (i = 0; i < len; i += MAX_COLUMNS) { 669 printf("+%d:\t", i); 670 for (j = 0; j < MAX_COLUMNS; j++) { 671 if (i + j < len) { 672 printf("%x/%d\t", buf[i+j]&0xff, buf[i+j]); 673 } else { 674 printf(" "); 675 } 676 } 677 678 for (j = 0; j < MAX_COLUMNS; j++) { 679 if (i + j < len) { 680 if (((buf[i+j]) > 31) && ((buf[i+j]) < 128)) 681 printf("%c", buf[i+j]&0xff); 682 else 683 printf("."); 684 } 685 } 686 printf("\n"); 687 } 688 } 689 690 691 #endif ARGO_DEBUG 692 693