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