chooser.c revision 1d778f8e
1/* 2 * 3Copyright 1990, 1998 The Open Group 4 5Permission to use, copy, modify, distribute, and sell this software and its 6documentation for any purpose is hereby granted without fee, provided that 7the above copyright notice appear in all copies and that both that 8copyright notice and this permission notice appear in supporting 9documentation. 10 11The above copyright notice and this permission notice shall be included in 12all copies or substantial portions of the Software. 13 14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 21Except as contained in this notice, the name of The Open Group shall not be 22used in advertising or otherwise to promote the sale, use or other dealings 23in this Software without prior written authorization from The Open Group. 24 * 25 * Author: Keith Packard, MIT X Consortium 26 */ 27 28 29/* 30 * Chooser - display a menu of names and let the user select one 31 */ 32 33/* 34 * Layout: 35 * 36 * +--------------------------------------------------+ 37 * | +------------------+ | 38 * | | Label | | 39 * | +------------------+ | 40 * | +-+--------------+ | 41 * | |^| name-1 | | 42 * | ||| name-2 | | 43 * | |v| name-3 | | 44 * | | | name-4 | | 45 * | | | name-5 | | 46 * | | | name-6 | | 47 * | +----------------+ | 48 * | cancel accept ping | 49 * +--------------------------------------------------+ 50 */ 51 52#include <X11/Intrinsic.h> 53#include <X11/StringDefs.h> 54#include <X11/Xatom.h> 55 56#include <X11/Xaw/Paned.h> 57#include <X11/Xaw/Label.h> 58#include <X11/Xaw/Viewport.h> 59#include <X11/Xaw/List.h> 60#include <X11/Xaw/Box.h> 61#include <X11/Xaw/Command.h> 62 63#include "dm.h" 64 65#include <X11/Xdmcp.h> 66 67#include <sys/types.h> 68#include <stdio.h> 69#include <ctype.h> 70 71#ifdef USE_XINERAMA 72# include <X11/extensions/Xinerama.h> 73#endif 74 75#if defined(SVR4) 76# include <sys/sockio.h> 77#endif 78 79#include "dm_socket.h" 80 81#include <arpa/inet.h> 82 83#include <sys/ioctl.h> 84 85#ifdef HAVE_SYS_PARAM_H 86# include <sys/param.h> 87# ifdef BSD 88# if (BSD >= 199103) 89# define VARIABLE_IFREQ 90# endif 91# endif 92#endif 93 94#ifdef XKB 95# include <X11/extensions/XKBbells.h> 96#endif 97 98#define BROADCAST_HOSTNAME "BROADCAST" 99 100#ifndef ishexdigit 101# define ishexdigit(c) (isdigit(c) || ('a' <= (c) && (c) <= 'f')) 102#endif 103 104#include <net/if.h> 105#include <netdb.h> 106#include <X11/keysym.h> 107 108#if defined(IPv6) && !defined(AF_INET6) 109#error "Cannot build IPv6 support without AF_INET6" 110#endif 111 112static int FromHex (char *s, char *d, int len); 113static int oldline; 114 115static Widget toplevel, label, viewport, paned, list, box, cancel, acceptit, ping; 116 117static void CvtStringToARRAY8( 118 XrmValuePtr args, 119 Cardinal *num_args, 120 XrmValuePtr fromVal, 121 XrmValuePtr toVal); 122 123static struct _app_resources { 124 ARRAY8Ptr xdmAddress; 125 ARRAY8Ptr clientAddress; 126 int connectionType; 127} app_resources; 128 129#define offset(field) XtOffsetOf(struct _app_resources, field) 130 131#define XtRARRAY8 "ARRAY8" 132 133static XtResource resources[] = { 134 {"xdmAddress", "XdmAddress", XtRARRAY8, sizeof (ARRAY8Ptr), 135 offset (xdmAddress), XtRString, NULL }, 136 {"clientAddress", "ClientAddress", XtRARRAY8, sizeof (ARRAY8Ptr), 137 offset (clientAddress), XtRString, NULL }, 138 {"connectionType", "ConnectionType", XtRInt, sizeof (int), 139 offset (connectionType), XtRImmediate, (XtPointer) 0 } 140}; 141#undef offset 142 143static XrmOptionDescRec options[] = { 144 { "-xdmaddress", "*xdmAddress", XrmoptionSepArg, NULL }, 145 { "-clientaddress", "*clientAddress", XrmoptionSepArg, NULL }, 146 { "-connectionType","*connectionType", XrmoptionSepArg, NULL }, 147}; 148 149typedef struct _hostAddr { 150 struct _hostAddr *next; 151 struct sockaddr *addr; 152 int addrlen; 153 xdmOpCode type; 154} HostAddr; 155 156static HostAddr *hostAddrdb; 157 158typedef struct _hostName { 159 struct _hostName *next; 160 char *fullname; 161 int willing; 162 ARRAY8 hostname, status; 163 CARD16 connectionType; 164 ARRAY8 hostaddr; 165} HostName; 166 167static HostName *hostNamedb; 168 169static int socketFD; 170#ifdef IPv6 171static int socket6FD; 172#endif 173 174static int pingTry; 175 176#define PING_INTERVAL 2000 177#define TRIES 3 178 179static XdmcpBuffer directBuffer, broadcastBuffer; 180static XdmcpBuffer buffer; 181 182/* ARGSUSED */ 183static void 184PingHosts (XtPointer closure, XtIntervalId *id) 185{ 186 HostAddr *hosts; 187 int sfd = socketFD; 188 189 for (hosts = hostAddrdb; hosts; hosts = hosts->next) 190 { 191#ifdef IPv6 192 if ( ((struct sockaddr *) hosts->addr)->sa_family == AF_INET6 ) 193 sfd = socket6FD; 194 else 195 sfd = socketFD; 196#endif 197 if (hosts->type == QUERY) 198 XdmcpFlush (sfd, &directBuffer, 199 (XdmcpNetaddr) hosts->addr, hosts->addrlen); 200 else 201 XdmcpFlush (sfd, &broadcastBuffer, 202 (XdmcpNetaddr) hosts->addr, hosts->addrlen); 203 } 204 if (++pingTry < TRIES) 205 XtAddTimeOut (PING_INTERVAL, PingHosts, (XtPointer) 0); 206} 207 208char **NameTable; 209int NameTableSize; 210 211static int 212HostnameCompare (const void *a, const void *b) 213{ 214 return strcmp (*(char **)a, *(char **)b); 215} 216 217static void 218RebuildTable (int size) 219{ 220 char **newTable = NULL; 221 HostName *names; 222 int i; 223 224 if (size) 225 { 226 newTable = malloc (size * sizeof (char *)); 227 if (!newTable) 228 return; 229 for (names = hostNamedb, i = 0; names; names = names->next, i++) 230 newTable[i] = names->fullname; 231 qsort (newTable, size, sizeof (char *), HostnameCompare); 232 } 233 XawListChange (list, (_Xconst char **) newTable, size, 0, TRUE); 234 free (NameTable); 235 NameTable = newTable; 236 NameTableSize = size; 237} 238 239static int 240AddHostname (ARRAY8Ptr hostname, ARRAY8Ptr status, struct sockaddr *addr, int willing) 241{ 242 HostName *new, **names, *name; 243 ARRAY8 hostAddr = {0, NULL}; 244 CARD16 connectionType; 245 int fulllen; 246 247 switch (addr->sa_family) 248 { 249 case AF_INET: 250 hostAddr.data = (CARD8 *) &((struct sockaddr_in *) addr)->sin_addr; 251 hostAddr.length = 4; 252 connectionType = FamilyInternet; 253 break; 254#ifdef IPv6 255 case AF_INET6: 256 hostAddr.data = (CARD8 *) &((struct sockaddr_in6 *) addr)->sin6_addr; 257 hostAddr.length = 16; 258 connectionType = FamilyInternet6; 259 break; 260#endif 261 default: 262 hostAddr.data = (CARD8 *) ""; 263 hostAddr.length = 0; 264 connectionType = FamilyLocal; 265 break; 266 } 267 for (names = &hostNamedb; *names; names = & (*names)->next) 268 { 269 name = *names; 270 if (connectionType == name->connectionType && 271 XdmcpARRAY8Equal (&hostAddr, &name->hostaddr)) 272 { 273 if (XdmcpARRAY8Equal (status, &name->status)) 274 { 275 return 0; 276 } 277 break; 278 } 279 } 280 if (!*names) 281 { 282 new = calloc (1, sizeof (HostName)); 283 if (!new) 284 return 0; 285 if (hostname->length) 286 { 287 switch (addr->sa_family) 288 { 289 case AF_INET: 290#ifdef IPv6 291 case AF_INET6: 292#endif 293 { 294 struct hostent *hostent; 295 char *host; 296 297 hostent = gethostbyaddr ((char *)hostAddr.data, hostAddr.length, addr->sa_family); 298 if (hostent) 299 { 300 XdmcpDisposeARRAY8 (hostname); 301 host = (char *)hostent->h_name; 302 XdmcpAllocARRAY8 (hostname, strlen (host)); 303 memcpy(hostname->data, host, hostname->length); 304 } 305 } 306 } 307 } 308 if (!XdmcpAllocARRAY8 (&new->hostaddr, hostAddr.length)) 309 { 310 free (new); 311 return 0; 312 } 313 memcpy(new->hostaddr.data, hostAddr.data, hostAddr.length); 314 new->connectionType = connectionType; 315 new->hostname = *hostname; 316 317 *names = new; 318 new->next = NULL; 319 NameTableSize++; 320 } 321 else 322 { 323 new = *names; 324 free (new->fullname); 325 XdmcpDisposeARRAY8 (&new->status); 326 XdmcpDisposeARRAY8 (hostname); 327 } 328 new->willing = willing; 329 new->status = *status; 330 331 hostname = &new->hostname; 332 fulllen = hostname->length; 333 if (fulllen < 30) 334 fulllen = 30; 335 fulllen += status->length + 10; 336 new->fullname = malloc (fulllen); 337 if (!new->fullname) 338 { 339 new->fullname = "Unknown"; 340 } 341 else 342 { 343 snprintf(new->fullname, fulllen, "%-30.*s %*.*s", 344 hostname->length, hostname->data, 345 status->length, status->length, status->data); 346 } 347 RebuildTable (NameTableSize); 348 return 1; 349} 350 351static void 352DisposeHostname (HostName *host) 353{ 354 XdmcpDisposeARRAY8 (&host->hostname); 355 XdmcpDisposeARRAY8 (&host->hostaddr); 356 XdmcpDisposeARRAY8 (&host->status); 357 free (host->fullname); 358 free (host); 359} 360 361static void 362EmptyHostnames (void) 363{ 364 HostName *hosts, *next; 365 366 for (hosts = hostNamedb; hosts; hosts = next) 367 { 368 next = hosts->next; 369 DisposeHostname (hosts); 370 } 371 NameTableSize = 0; 372 hostNamedb = NULL; 373 RebuildTable (NameTableSize); 374} 375 376/* ARGSUSED */ 377static void 378ReceivePacket (XtPointer closure, int *source, XtInputId *id) 379{ 380 XdmcpHeader header; 381 ARRAY8 authenticationName = {0, NULL}; 382 ARRAY8 hostname = {0, NULL}; 383 ARRAY8 status = {0, NULL}; 384 int saveHostname = 0; 385#ifdef HAVE_STRUCT_SOCKADDR_STORAGE 386 struct sockaddr_storage addr; 387#else 388 struct sockaddr addr; 389#endif 390 int addrlen; 391 int sfd = * (int *) closure; 392 393 addrlen = sizeof (addr); 394 if (!XdmcpFill (sfd, &buffer, (XdmcpNetaddr) &addr, &addrlen)) 395 return; 396 if (!XdmcpReadHeader (&buffer, &header)) 397 return; 398 if (header.version != XDM_PROTOCOL_VERSION) 399 return; 400 switch (header.opcode) { 401 case WILLING: 402 if (XdmcpReadARRAY8 (&buffer, &authenticationName) && 403 XdmcpReadARRAY8 (&buffer, &hostname) && 404 XdmcpReadARRAY8 (&buffer, &status)) 405 { 406 if (header.length == 6 + authenticationName.length + 407 hostname.length + status.length) 408 { 409 if (AddHostname (&hostname, &status, (struct sockaddr *) &addr, 410 header.opcode == (int) WILLING)) 411 saveHostname = 1; 412 } 413 } 414 XdmcpDisposeARRAY8 (&authenticationName); 415 break; 416 case UNWILLING: 417 if (XdmcpReadARRAY8 (&buffer, &hostname) && 418 XdmcpReadARRAY8 (&buffer, &status)) 419 { 420 if (header.length == 4 + hostname.length + status.length) 421 { 422 if (AddHostname (&hostname, &status, (struct sockaddr *) &addr, 423 header.opcode == (int) WILLING)) 424 saveHostname = 1; 425 426 } 427 } 428 break; 429 default: 430 break; 431 } 432 if (!saveHostname) 433 { 434 XdmcpDisposeARRAY8 (&hostname); 435 XdmcpDisposeARRAY8 (&status); 436 } 437} 438 439static void 440RegisterHostaddr (struct sockaddr *addr, int len, xdmOpCode type) 441{ 442 HostAddr *host, **prev; 443 444 host = malloc (sizeof (HostAddr)); 445 if (!host) 446 return; 447 host->addr = malloc (len); 448 if (!host->addr) 449 { 450 free (host); 451 return; 452 } 453 memcpy(host->addr, addr, len); 454 host->addrlen = len; 455 host->type = type; 456 for (prev = &hostAddrdb; *prev; prev = &(*prev)->next) 457 ; 458 *prev = host; 459 host->next = NULL; 460} 461 462/* 463 * Register the address for this host. 464 * Called with each of the names on the command line. 465 * The special name "BROADCAST" looks up all the broadcast 466 * addresses on the local host. 467 */ 468 469/* Handle variable length ifreq in BNR2 and later */ 470#ifdef VARIABLE_IFREQ 471# define ifr_size(p) (sizeof (struct ifreq) + \ 472 (p->ifr_addr.sa_len > sizeof (p->ifr_addr) ? \ 473 p->ifr_addr.sa_len - sizeof (p->ifr_addr) : 0)) 474#else 475# define ifr_size(p) (sizeof (struct ifreq)) 476#endif 477 478static void 479RegisterHostname (char *name) 480{ 481 struct sockaddr_in in_addr; 482 struct ifconf ifc; 483 register struct ifreq *ifr; 484 struct sockaddr broad_addr; 485 char buf[2048], *cp, *cplim; 486 487 if (!strcmp (name, BROADCAST_HOSTNAME)) 488 { 489 ifc.ifc_len = sizeof (buf); 490 ifc.ifc_buf = buf; 491 if (ioctl (socketFD, (int) SIOCGIFCONF, (char *) &ifc) < 0) 492 return; 493 494 cplim = (char *) ifc.ifc_req + ifc.ifc_len; 495 496 for (cp = (char *) ifc.ifc_req; cp < cplim; cp += ifr_size (ifr)) 497 { 498 ifr = (struct ifreq *) cp; 499 if (ifr->ifr_addr.sa_family != AF_INET) 500 continue; 501 502 broad_addr = ifr->ifr_addr; 503 ((struct sockaddr_in *) &broad_addr)->sin_addr.s_addr = 504 htonl (INADDR_BROADCAST); 505#ifdef SIOCGIFBRDADDR 506 { 507 struct ifreq broad_req; 508 509 broad_req = *ifr; 510 if (ioctl (socketFD, SIOCGIFFLAGS, (char *) &broad_req) != -1 && 511 (broad_req.ifr_flags & IFF_BROADCAST) && 512 (broad_req.ifr_flags & IFF_UP) 513 ) 514 { 515 broad_req = *ifr; 516 if (ioctl (socketFD, SIOCGIFBRDADDR, &broad_req) != -1) 517 broad_addr = broad_req.ifr_addr; 518 else 519 continue; 520 } 521 else 522 continue; 523 } 524#endif 525 in_addr = *((struct sockaddr_in *) &broad_addr); 526 in_addr.sin_port = htons (XDM_UDP_PORT); 527#ifdef BSD44SOCKETS 528 in_addr.sin_len = sizeof(in_addr); 529#endif 530 RegisterHostaddr ((struct sockaddr *)&in_addr, sizeof (in_addr), 531 BROADCAST_QUERY); 532 } 533 } 534 else 535 { 536 /* address as hex string, e.g., "12180022" (deprecated) */ 537 if (strlen(name) == 8 && 538 FromHex(name, (char *)&in_addr.sin_addr, strlen(name)) == 0) 539 { 540 in_addr.sin_family = AF_INET; 541 in_addr.sin_port = htons (XDM_UDP_PORT); 542#ifdef BSD44SOCKETS 543 in_addr.sin_len = sizeof(in_addr); 544#endif 545 RegisterHostaddr ((struct sockaddr *)&in_addr, sizeof (in_addr), 546 QUERY); 547 } 548#ifdef HAVE_GETADDRINFO 549 else { 550 char sport[8]; 551 struct addrinfo *ai, *nai, hints; 552 bzero(&hints,sizeof(hints)); 553# ifdef IPv6 554 hints.ai_family = AF_UNSPEC; 555# else 556 hints.ai_family = AF_INET; 557# endif 558 hints.ai_socktype = SOCK_DGRAM; 559 snprintf(sport, sizeof(sport), "%d", XDM_UDP_PORT); 560 if (getaddrinfo(name, sport, &hints, &ai) == 0) { 561 for (nai = ai ; nai != NULL ; nai = nai->ai_next) { 562 if ((nai->ai_family == AF_INET) 563#ifdef IPv6 564 || (nai->ai_family == AF_INET6) 565#endif 566 ) { 567 if (((nai->ai_family == AF_INET) && 568 IN_MULTICAST(((struct sockaddr_in *) nai->ai_addr) 569 ->sin_addr.s_addr)) 570#ifdef IPv6 571 || ((nai->ai_family == AF_INET6) && 572 IN6_IS_ADDR_MULTICAST( 573 &((struct sockaddr_in6 *) nai->ai_addr) 574 ->sin6_addr)) 575#endif 576 ) 577 { 578 RegisterHostaddr(nai->ai_addr, nai->ai_addrlen, 579 BROADCAST_QUERY); 580 } else { 581 RegisterHostaddr(nai->ai_addr, nai->ai_addrlen, 582 QUERY); 583 } 584 } 585 } 586 freeaddrinfo(ai); 587 } 588 } 589#else /* !HAVE_GETADDRINFO */ 590 /* Per RFC 1123, check first for IP address in dotted-decimal form */ 591 else if ((in_addr.sin_addr.s_addr = inet_addr(name)) != -1) 592 in_addr.sin_family = AF_INET; 593 else 594 { 595 struct hostent *hostent; 596 597 hostent = gethostbyname (name); 598 if (!hostent) 599 return; 600 if (hostent->h_addrtype != AF_INET || hostent->h_length != 4) 601 return; 602 in_addr.sin_family = hostent->h_addrtype; 603 memcpy(&in_addr.sin_addr, hostent->h_addr, 4); 604 } 605 in_addr.sin_port = htons (XDM_UDP_PORT); 606# ifdef BSD44SOCKETS 607 in_addr.sin_len = sizeof(in_addr); 608# endif 609 RegisterHostaddr ((struct sockaddr *)&in_addr, sizeof (in_addr), 610 QUERY); 611#endif /* HAVE_GETADDRINFO */ 612 } 613} 614 615static ARRAYofARRAY8 AuthenticationNames; 616 617static int 618InitXDMCP (char **argv) 619{ 620 int soopts = 1; 621 XdmcpHeader header; 622 int i; 623 624 header.version = XDM_PROTOCOL_VERSION; 625 header.opcode = (CARD16) BROADCAST_QUERY; 626 header.length = 1; 627 for (i = 0; i < (int)AuthenticationNames.length; i++) 628 header.length += 2 + AuthenticationNames.data[i].length; 629 XdmcpWriteHeader (&broadcastBuffer, &header); 630 XdmcpWriteARRAYofARRAY8 (&broadcastBuffer, &AuthenticationNames); 631 632 header.version = XDM_PROTOCOL_VERSION; 633 header.opcode = (CARD16) QUERY; 634 header.length = 1; 635 for (i = 0; i < (int)AuthenticationNames.length; i++) 636 header.length += 2 + AuthenticationNames.data[i].length; 637 XdmcpWriteHeader (&directBuffer, &header); 638 XdmcpWriteARRAYofARRAY8 (&directBuffer, &AuthenticationNames); 639 if ((socketFD = socket (AF_INET, SOCK_DGRAM, 0)) < 0) 640 return 0; 641#ifdef IPv6 642 socket6FD = socket (AF_INET6, SOCK_DGRAM, 0); 643#endif 644#ifdef SO_BROADCAST 645 soopts = 1; 646 if (setsockopt (socketFD, SOL_SOCKET, SO_BROADCAST, (char *)&soopts, sizeof (soopts)) < 0) 647 perror ("setsockopt"); 648#endif 649 650 XtAddInput (socketFD, (XtPointer) XtInputReadMask, ReceivePacket, 651 (XtPointer) &socketFD); 652#ifdef IPv6 653 if (socket6FD != -1) 654 XtAddInput (socket6FD, (XtPointer) XtInputReadMask, ReceivePacket, 655 (XtPointer) &socket6FD); 656#endif 657 while (*argv) 658 { 659 RegisterHostname (*argv); 660 ++argv; 661 } 662 pingTry = 0; 663 PingHosts ((XtPointer)NULL, (XtIntervalId *)NULL); 664 return 1; 665} 666 667static void 668Choose (HostName *h) 669{ 670 if (app_resources.xdmAddress) 671 { 672 struct sockaddr_in in_addr; 673#ifdef IPv6 674 struct sockaddr_in6 in6_addr; 675#endif 676 struct sockaddr *addr = NULL; 677 int family; 678 int len = 0; 679 int fd; 680 char buf[1024]; 681 XdmcpBuffer buffer; 682 char *xdm; 683 684 xdm = (char *) app_resources.xdmAddress->data; 685 family = (xdm[0] << 8) + xdm[1]; 686 switch (family) { 687 case AF_INET: 688#ifdef BSD44SOCKETS 689 in_addr.sin_len = sizeof(in_addr); 690#endif 691 in_addr.sin_family = family; 692 memcpy(&in_addr.sin_port, xdm + 2, 2); 693 memcpy(&in_addr.sin_addr, xdm + 4, 4); 694 addr = (struct sockaddr *) &in_addr; 695 len = sizeof (in_addr); 696 break; 697#ifdef IPv6 698 case AF_INET6: 699 bzero(&in6_addr, sizeof(in6_addr)); 700# ifdef SIN6_LEN 701 in6_addr.sin6_len = sizeof(in6_addr); 702# endif 703 in6_addr.sin6_family = family; 704 memcpy(&in6_addr.sin6_port, xdm + 2, 2); 705 memcpy(&in6_addr.sin6_addr, xdm + 4, 16); 706 addr = (struct sockaddr *) &in6_addr; 707 len = sizeof (in6_addr); 708 break; 709#endif 710 } 711 if ((fd = socket (family, SOCK_STREAM, 0)) == -1) 712 { 713 fprintf (stderr, "Cannot create response socket\n"); 714 exit (REMANAGE_DISPLAY); 715 } 716 if (connect (fd, addr, len) == -1) 717 { 718 fprintf (stderr, "Cannot connect to xdm\n"); 719 exit (REMANAGE_DISPLAY); 720 } 721 buffer.data = (BYTE *) buf; 722 buffer.size = sizeof (buf); 723 buffer.pointer = 0; 724 buffer.count = 0; 725 XdmcpWriteARRAY8 (&buffer, app_resources.clientAddress); 726 XdmcpWriteCARD16 (&buffer, (CARD16) app_resources.connectionType); 727 XdmcpWriteARRAY8 (&buffer, &h->hostaddr); 728 write (fd, (char *)buffer.data, buffer.pointer); 729 close (fd); 730 } 731 else 732 { 733 int i; 734 735 printf ("%u\n", h->connectionType); 736 for (i = 0; i < (int)h->hostaddr.length; i++) 737 printf ("%u%s", h->hostaddr.data[i], 738 i == h->hostaddr.length - 1 ? "\n" : " "); 739 } 740} 741 742/* 743 next_line returns the next line in a list 744 across the list end. 745 (0, 1, 2, 3, 0, 1, 2, 3 ....) 746*/ 747 748static int 749next_line(unsigned int current, unsigned int size, int event) 750{ 751 switch(event) { 752 case Button5: 753 return (current + 1) % size; 754 case Button4: 755 return (current + size - 1) % size; 756 case XK_Down: 757 return (current + 1) % size; 758 case XK_Up: 759 return (current + size - 1) % size; 760 } 761 return -1; 762} 763 764/* 765 Hostselect selects a given chooser line. 766 Returns 1 when host is willing and 0 if not 767*/ 768 769static int 770Hostselect (int line) 771{ 772 XawListReturnStruct *r; 773 HostName *h; 774 775 /* Assume the next host is willing */ 776 XawListHighlight (list,line); 777 r = XawListShowCurrent (list); 778 /* copied from DoCheckWilling */ 779 for (h = hostNamedb; h; h = h->next) 780 { 781 if (!strcmp (r->string, h->fullname)) 782 { 783 if (!h->willing) 784 XawListUnhighlight (list); 785 else 786 { 787 /* Scroll viewport to make sure new selection is visible */ 788 Arg size[2]; 789 Dimension height, portheight; 790 Position y; 791 int lineheight, liney; 792 793 XtSetArg (size[0], XtNheight, &height); 794 XtSetArg (size[1], XtNy, &y); 795 XtGetValues (list, size, (Cardinal) 2); 796 797 XtSetArg (size[0], XtNheight, &portheight); 798 XtGetValues (viewport, size, (Cardinal) 1); 799 800 lineheight = height / NameTableSize; 801 liney = lineheight * line; 802 803 if ((y + liney) < 0) { 804 XawViewportSetCoordinates(viewport, 0, liney); 805 } else if ((y + liney + lineheight) > portheight) { 806 XawViewportSetCoordinates(viewport, 0, 807 (liney + lineheight) - portheight); 808 } 809 810 XtFree((char *) r); 811 return 1; 812 } 813 } 814 } 815 XtFree((char *) r); 816 return 0; 817} 818 819/* 820 Selectfirst selects the first willing host 821 in the chooser list (so you can select a host without 822 presence of mouse, but with pressing space or return, 823 or XK_Down / XK_Up 824*/ 825 826static void 827Selectfirst (void) 828{ 829 int line; 830 831 for (line = 0; line < NameTableSize; line++) 832 { 833 if (Hostselect(line)) 834 return; 835 } 836 return; 837} 838 839/* 840 Storeold stores the selected line into global int oldentry 841*/ 842 843static void 844Storeold (Widget w, XEvent *event, String *params, Cardinal *num_params) 845{ 846 XawListReturnStruct *r = XawListShowCurrent(list); 847 848 oldline = r->list_index; 849 XtFree((char *) r); 850} 851 852/* 853 Setold restores the global int oldentry 854 when you try to select a host with your mouse 855 who is unwilling as follows: 856 <Btn1Down>: Store() Set() CheckWilling() Setold() \n\ 857*/ 858 859static void 860Setold (Widget w, XEvent *event, String *params, Cardinal *num_params) 861{ 862 863 if ( (XawListShowCurrent(list))->list_index == XAW_LIST_NONE && oldline != XAW_LIST_NONE) 864 XawListHighlight (list, oldline); 865} 866 867/* 868 HostCycle tries to select the next willing host across 869 the list end and will stop at the first found willing host 870 or after trying all entries. 871*/ 872 873static void 874HostCycle(unsigned int line, unsigned int size, KeySym keysym) 875{ 876 unsigned int newline = line; 877 /* Do it only once around the chooser list, either direction */ 878 while ( (newline = next_line(newline,size,keysym)) != line && newline != -1) 879 { 880 if (Hostselect(newline)) 881 return; 882 } 883 /* No other willing host could be found, stay at the old one*/ 884 XawListHighlight (list, line); 885 return; 886} 887 888/* 889 Switch_Key handles XK_Up and XK_Down 890 and highlights an appropriate line in the chooser list. 891*/ 892 893static void 894Switch_Key (Widget w, XEvent *event, String *params, Cardinal *num_params) 895{ 896 char strbuf[128]; 897 KeySym keysym = 0; 898 static XComposeStatus compose_status = {NULL, 0}; 899 XawListReturnStruct *r; 900 901 XLookupString (&event->xkey, strbuf, sizeof (strbuf), 902 &keysym, &compose_status); 903 904 if (keysym != XK_Up && keysym != XK_Down) 905 return; 906 907 r = XawListShowCurrent (list); 908 909 if (r->list_index == XAW_LIST_NONE) 910 Selectfirst(); 911 else 912 HostCycle(r->list_index,NameTableSize,keysym); 913 914 XtFree((char *) r); 915 return; 916} 917 918 919 920/* 921 Switch_Btn handles ScrollWheel Forward (Button5) and Backward 922 (Button4) and highlights an appropriate line in the chooser list. 923*/ 924 925static void 926Switch_Btn (Widget w, XEvent *event, String *params, Cardinal *num_params) 927{ 928 XawListReturnStruct *r; 929 r = XawListShowCurrent (list); 930 931 if (r->list_index == XAW_LIST_NONE) 932 Selectfirst(); 933 else 934 HostCycle(r->list_index,NameTableSize,event->xbutton.button); 935 936 XtFree((char *) r); 937 return; 938} 939 940 941/* ARGSUSED */ 942static void 943DoAccept (Widget w, XEvent *event, String *params, Cardinal *num_params) 944{ 945 XawListReturnStruct *r; 946 HostName *h; 947 948 r = XawListShowCurrent (list); 949 if (r->list_index == XAW_LIST_NONE) 950#ifdef XKB 951 XkbStdBell(XtDisplay(toplevel),XtWindow(w),0,XkbBI_MinorError); 952#else 953 XBell (XtDisplay (toplevel), 0); 954#endif 955 else 956 { 957 for (h = hostNamedb; h; h = h->next) 958 if (!strcmp (r->string, h->fullname)) 959 { 960 Choose (h); 961 } 962 exit (OBEYSESS_DISPLAY); 963 } 964 XtFree((char *) r); 965} 966 967/* ARGSUSED */ 968static void 969DoCheckWilling (Widget w, XEvent *event, String *params, Cardinal *num_params) 970{ 971 XawListReturnStruct *r; 972 HostName *h; 973 974 r = XawListShowCurrent (list); 975 if (r->list_index != XAW_LIST_NONE) { 976 for (h = hostNamedb; h; h = h->next) 977 if (!strcmp (r->string, h->fullname)) 978 if (!h->willing) 979 XawListUnhighlight (list); 980 } 981 XtFree((char *) r); 982} 983 984/* ARGSUSED */ 985_X_NORETURN 986static void 987DoCancel (Widget w, XEvent *event, String *params, Cardinal *num_params) 988{ 989 exit (OBEYSESS_DISPLAY); 990} 991 992/* ARGSUSED */ 993static void 994DoPing (Widget w, XEvent *event, String *params, Cardinal *num_params) 995{ 996 EmptyHostnames (); 997 pingTry = 0; 998 PingHosts ((XtPointer)NULL, (XtIntervalId *)NULL); 999} 1000 1001static XtActionsRec app_actions[] = { 1002 { "Accept", DoAccept }, 1003 { "Cancel", DoCancel }, 1004 { "CheckWilling", DoCheckWilling }, 1005 { "Ping", DoPing }, 1006 { "KeySwitch", Switch_Key }, 1007 { "BtnSwitch", Switch_Btn }, 1008 { "Store", Storeold }, 1009 { "Setold", Setold }, 1010}; 1011 1012int 1013main (int argc, char **argv) 1014{ 1015 Arg position[3]; 1016 Dimension width, height; 1017 Position x, y; 1018#ifdef USE_XINERAMA 1019 XineramaScreenInfo *screens; 1020 int s_num; 1021#endif 1022 1023 1024 toplevel = XtInitialize (argv[0], "Chooser", options, XtNumber(options), &argc, argv); 1025 1026 XtAddConverter(XtRString, XtRARRAY8, CvtStringToARRAY8, NULL, 0); 1027 1028 XtGetApplicationResources (toplevel, (XtPointer) &app_resources, resources, 1029 XtNumber (resources), NULL, (Cardinal) 0); 1030 1031 XtAddActions (app_actions, XtNumber (app_actions)); 1032 paned = XtCreateManagedWidget ("paned", panedWidgetClass, toplevel, NULL, 0); 1033 label = XtCreateManagedWidget ("label", labelWidgetClass, paned, NULL, 0); 1034 viewport = XtCreateManagedWidget ("viewport", viewportWidgetClass, paned, NULL, 0); 1035 list = XtCreateManagedWidget ("list", listWidgetClass, viewport, NULL, 0); 1036 box = XtCreateManagedWidget ("box", boxWidgetClass, paned, NULL, 0); 1037 cancel = XtCreateManagedWidget ("cancel", commandWidgetClass, box, NULL, 0); 1038 acceptit = XtCreateManagedWidget ("accept", commandWidgetClass, box, NULL, 0); 1039 ping = XtCreateManagedWidget ("ping", commandWidgetClass, box, NULL, 0); 1040 1041 /* 1042 * center ourselves on the screen 1043 */ 1044 XtSetMappedWhenManaged(toplevel, FALSE); 1045 XtRealizeWidget (toplevel); 1046 1047 XtSetArg (position[0], XtNwidth, &width); 1048 XtSetArg (position[1], XtNheight, &height); 1049 XtGetValues (toplevel, position, (Cardinal) 2); 1050#ifdef USE_XINERAMA 1051 if ( 1052 XineramaIsActive(XtDisplay(toplevel)) && 1053 (screens = XineramaQueryScreens(XtDisplay(toplevel), &s_num)) != NULL 1054 ) 1055 { 1056 x = (Position)(screens[0].x_org + (screens[0].width - width) / 2); 1057 y = (Position)(screens[0].y_org + (screens[0].height - height) / 3); 1058 1059 XFree(screens); 1060 } 1061 else 1062#endif 1063 { 1064 x = (Position)(WidthOfScreen (XtScreen (toplevel)) - width) / 2; 1065 y = (Position)(HeightOfScreen (XtScreen (toplevel)) - height) / 3; 1066 } 1067 XtSetArg (position[0], XtNx, x); 1068 XtSetArg (position[1], XtNy, y); 1069 XtSetValues (toplevel, position, (Cardinal) 2); 1070 1071 /* 1072 * Run 1073 */ 1074 XtMapWidget(toplevel); 1075 InitXDMCP (argv + 1); 1076 XtMainLoop (); 1077 exit(0); 1078 /*NOTREACHED*/ 1079} 1080 1081/* Converts the hex string s of length len into the byte array d. 1082 Returns 0 if s was a legal hex string, 1 otherwise. 1083 */ 1084static int 1085FromHex (char *s, char *d, int len) 1086{ 1087 int t; 1088 int ret = len&1; /* odd-length hex strings are illegal */ 1089 while (len >= 2) 1090 { 1091#define HexChar(c) ('0' <= (c) && (c) <= '9' ? (c) - '0' : (c) - 'a' + 10) 1092 1093 if (!ishexdigit(*s)) 1094 ret = 1; 1095 t = HexChar (*s) << 4; 1096 s++; 1097 if (!ishexdigit(*s)) 1098 ret = 1; 1099 t += HexChar (*s); 1100 s++; 1101 *d++ = t; 1102 len -= 2; 1103 } 1104 return ret; 1105} 1106 1107/*ARGSUSED*/ 1108static void 1109CvtStringToARRAY8 (XrmValuePtr args, Cardinal *num_args, XrmValuePtr fromVal, XrmValuePtr toVal) 1110{ 1111 static ARRAY8Ptr dest; 1112 char *s; 1113 int len; 1114 1115 dest = (ARRAY8Ptr) XtMalloc (sizeof (ARRAY8)); 1116 len = fromVal->size; 1117 s = (char *) fromVal->addr; 1118 if (!XdmcpAllocARRAY8 (dest, len >> 1)) 1119 XtStringConversionWarning ((char *) fromVal->addr, XtRARRAY8); 1120 else 1121 { 1122 FromHex (s, (char *) dest->data, len); 1123 } 1124 toVal->addr = (caddr_t) &dest; 1125 toVal->size = sizeof (ARRAY8Ptr); 1126} 1127