incorporate changes for new kvm interfaces from lbl
[unix-history] / usr / src / usr.bin / netstat / route.c
CommitLineData
5ff67f98 1/*
b36fc510 2 * Copyright (c) 1983, 1988 Regents of the University of California.
ee3f90a5
MK
3 * All rights reserved.
4 *
42841feb 5 * %sccs.include.redist.c%
5ff67f98
DF
6 */
7
3537c4ea 8#ifndef lint
6e549c8f 9static char sccsid[] = "@(#)route.c 5.23 (Berkeley) %G%";
b36fc510 10#endif /* not lint */
3537c4ea 11
6c5bed6c 12#include <sys/param.h>
6e549c8f 13#include <sys/protosw.h>
3537c4ea
SL
14#include <sys/socket.h>
15#include <sys/mbuf.h>
44906619 16
3537c4ea 17#include <net/if.h>
a6dd7faa
MK
18#include <net/if_dl.h>
19#include <net/if_types.h>
4b68c469 20#define KERNEL
3537c4ea 21#include <net/route.h>
4b68c469 22#undef KERNEL
44906619
SL
23#include <netinet/in.h>
24
f1fbb01b
KS
25#include <netns/ns.h>
26
4d9e18da 27#include <sys/kinfo.h>
3537c4ea 28
6e549c8f 29#include <netdb.h>
42841feb 30#include <stdio.h>
6e549c8f 31#include <stdlib.h>
42841feb 32#include <string.h>
6e549c8f 33#include "netstat.h"
42841feb 34
4b68c469 35#define kget(p, d) \
6e549c8f 36 (kread((off_t)(p), (char *)&(d), sizeof (d)))
3537c4ea
SL
37
38/*
39 * Definitions for showing gateway flags.
40 */
41struct bits {
42 short b_mask;
43 char b_val;
44} bits[] = {
45 { RTF_UP, 'U' },
46 { RTF_GATEWAY, 'G' },
47 { RTF_HOST, 'H' },
6c5bed6c 48 { RTF_DYNAMIC, 'D' },
3273bd86 49 { RTF_MODIFIED, 'M' },
56492e5a 50 { RTF_CLONING, 'C' },
b8442d18 51 { RTF_XRESOLVE, 'X' },
56492e5a 52 { RTF_LLINFO, 'L' },
b8442d18 53 { RTF_REJECT, 'R' },
4e64fa9b 54 { RTF_STATIC, 'S' },
3537c4ea
SL
55 { 0 }
56};
57
a6dd7faa
MK
58static union {
59 struct sockaddr u_sa;
60 u_short u_data[128];
61} pt_u;
62
63int do_rtent = 0;
64struct rtentry rtentry;
65struct radix_node rnode;
66struct radix_mask rmask;
67
68int NewTree = 0;
69
6e549c8f
KS
70static struct sockaddr *kgetsa __P((struct sockaddr *));
71static void p_tree __P((struct radix_node *));
72static void p_rtnode __P(());
73static void ntreestuff __P(());
74static void np_rtentry __P((struct rt_msghdr *));
75static void p_sockaddr __P((struct sockaddr *, int, int));
76static void p_flags __P((int, char *));
77static void p_rtentry __P((struct rtentry *));
78
3537c4ea
SL
79/*
80 * Print routing tables.
81 */
6e549c8f 82void
a6dd7faa
MK
83routepr(rtree)
84 off_t rtree;
3537c4ea 85{
a6dd7faa 86 struct radix_node_head *rnh, head;
4e64fa9b 87 int i;
3537c4ea 88
4b68c469 89 printf("Routing tables\n");
a6dd7faa
MK
90
91 if (Aflag == 0 && NewTree)
92 ntreestuff();
93 else {
94 if (rtree == 0) {
4e64fa9b 95 printf("rt_tables: symbol not in namelist\n");
a6dd7faa
MK
96 return;
97 }
98
4e64fa9b
KS
99 kget(rtree, rt_tables);
100 for (i = 0; i <= AF_MAX; i++) {
101 if ((rnh = rt_tables[i]) == 0)
102 continue;
a6dd7faa 103 kget(rnh, head);
4e64fa9b 104 if (i == AF_UNSPEC) {
6e549c8f 105 if (Aflag && af == 0) {
a6dd7faa
MK
106 printf("Netmasks:\n");
107 p_tree(head.rnh_treetop);
108 }
4e64fa9b
KS
109 } else if (af == AF_UNSPEC || af == i) {
110 pr_family(i);
a6dd7faa
MK
111 do_rtent = 1;
112 pr_rthdr();
113 p_tree(head.rnh_treetop);
114 }
3537c4ea
SL
115 }
116 }
4b68c469
MK
117}
118
a6dd7faa
MK
119/*
120 * Print address family header before a section of the routing table.
121 */
6e549c8f 122void
a6dd7faa
MK
123pr_family(af)
124 int af;
4b68c469 125{
a6dd7faa 126 char *afname;
56492e5a 127
a6dd7faa
MK
128 switch (af) {
129 case AF_INET:
130 afname = "Internet";
131 break;
132 case AF_NS:
133 afname = "XNS";
134 break;
135 case AF_ISO:
136 afname = "ISO";
137 break;
4e64fa9b
KS
138 case AF_CCITT:
139 afname = "X.25";
140 break;
a6dd7faa
MK
141 default:
142 afname = NULL;
143 break;
4b68c469 144 }
a6dd7faa
MK
145 if (afname)
146 printf("\n%s:\n", afname);
147 else
148 printf("\nProtocol Family %d:\n", af);
149}
150
151/* column widths; each followed by one space */
152#define WID_DST 16 /* width of destination column */
153#define WID_GW 18 /* width of gateway column */
154
155/*
156 * Print header for routing table columns.
157 */
6e549c8f 158void
a6dd7faa
MK
159pr_rthdr()
160{
161
162 if (Aflag)
163 printf("%-8.8s ","Address");
164 printf("%-*.*s %-*.*s %-6.6s %6.6s%8.8s %s\n",
165 WID_DST, WID_DST, "Destination",
166 WID_GW, WID_GW, "Gateway",
167 "Flags", "Refs", "Use", "Interface");
4b68c469
MK
168}
169
6e549c8f 170static struct sockaddr *
4d9e18da 171kgetsa(dst)
a6dd7faa 172 register struct sockaddr *dst;
4d9e18da 173{
a6dd7faa 174
4d9e18da 175 kget(dst, pt_u.u_sa);
a6dd7faa 176 if (pt_u.u_sa.sa_len > sizeof (pt_u.u_sa))
6e549c8f 177 kread((off_t)dst, (char *)pt_u.u_data, pt_u.u_sa.sa_len);
4d9e18da
KS
178 return (&pt_u.u_sa);
179}
180
6e549c8f 181static void
b8442d18 182p_tree(rn)
a6dd7faa 183 struct radix_node *rn;
4b68c469 184{
4b68c469
MK
185
186again:
187 kget(rn, rnode);
188 if (rnode.rn_b < 0) {
b8442d18
KS
189 if (Aflag)
190 printf("%-8.8x ", rn);
a6dd7faa
MK
191 if (rnode.rn_flags & RNF_ROOT) {
192 if (Aflag)
193 printf("(root node)%s",
b8442d18 194 rnode.rn_dupedkey ? " =>\n" : "\n");
a6dd7faa 195 } else if (do_rtent) {
4b68c469
MK
196 kget(rn, rtentry);
197 p_rtentry(&rtentry);
b8442d18
KS
198 if (Aflag)
199 p_rtnode();
4b68c469 200 } else {
4d9e18da 201 p_sockaddr(kgetsa((struct sockaddr *)rnode.rn_key),
a6dd7faa 202 0, 44);
4b68c469
MK
203 putchar('\n');
204 }
205 if (rn = rnode.rn_dupedkey)
206 goto again;
207 } else {
b8442d18
KS
208 if (Aflag && do_rtent) {
209 printf("%-8.8x ", rn);
210 p_rtnode();
211 }
212 rn = rnode.rn_r;
213 p_tree(rnode.rn_l);
214 p_tree(rn);
215 }
216}
a6dd7faa
MK
217
218char nbuf[20];
b8442d18 219
6e549c8f 220static void
b8442d18
KS
221p_rtnode()
222{
b8442d18 223 struct radix_mask *rm = rnode.rn_mklist;
a6dd7faa 224
b8442d18
KS
225 if (rnode.rn_b < 0) {
226 if (rnode.rn_mask) {
227 printf("\t mask ");
228 p_sockaddr(kgetsa((struct sockaddr *)rnode.rn_mask),
229 0, -1);
230 } else if (rm == 0)
231 return;
232 } else {
233 sprintf(nbuf, "(%d)", rnode.rn_b);
234 printf("%6.6s %8.8x : %8.8x", nbuf, rnode.rn_l, rnode.rn_r);
4b68c469 235 }
b8442d18
KS
236 while (rm) {
237 kget(rm, rmask);
238 sprintf(nbuf, " %d refs, ", rmask.rm_refs);
239 printf(" mk = %8.8x {(%d),%s",
240 rm, -1 - rmask.rm_b, rmask.rm_refs ? nbuf : " ");
241 p_sockaddr(kgetsa((struct sockaddr *)rmask.rm_mask), 0, -1);
242 putchar('}');
243 if (rm = rmask.rm_mklist)
244 printf(" ->");
245 }
246 putchar('\n');
4b68c469
MK
247}
248
6e549c8f 249static void
4d9e18da 250ntreestuff()
4b68c469 251{
4d9e18da
KS
252 int needed;
253 char *buf, *next, *lim;
254 register struct rt_msghdr *rtm;
255
256 if ((needed = getkerninfo(KINFO_RT_DUMP, 0, 0, 0)) < 0)
257 { perror("route-getkerninfo-estimate"); exit(1);}
258 if ((buf = malloc(needed)) == 0)
56492e5a 259 { printf("out of space\n"); exit(1);}
4d9e18da 260 if (getkerninfo(KINFO_RT_DUMP, buf, &needed, 0) < 0)
a6dd7faa 261 { perror("getkerninfo of routing table"); exit(1);}
4d9e18da
KS
262 lim = buf + needed;
263 for (next = buf; next < lim; next += rtm->rtm_msglen) {
264 rtm = (struct rt_msghdr *)next;
265 np_rtentry(rtm);
4b68c469 266 }
4b68c469
MK
267}
268
6e549c8f 269static void
4d9e18da 270np_rtentry(rtm)
a6dd7faa 271 register struct rt_msghdr *rtm;
4b68c469 272{
4d9e18da 273 register struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
6e549c8f
KS
274#ifdef notdef
275 static int masks_done, banner_printed;
276#endif
277 static int old_af;
4d9e18da 278 int af = 0, interesting = RTF_UP | RTF_GATEWAY | RTF_HOST;
4b68c469 279
4d9e18da
KS
280#ifdef notdef
281 /* for the moment, netmasks are skipped over */
282 if (!banner_printed) {
283 printf("Netmasks:\n");
284 banner_printed = 1;
285 }
286 if (masks_done == 0) {
287 if (rtm->rtm_addrs != RTA_DST ) {
288 masks_done = 1;
289 af = sa->sa_family;
290 }
291 } else
292#endif
293 af = sa->sa_family;
294 if (af != old_af) {
a6dd7faa 295 pr_family(af);
4d9e18da
KS
296 old_af = af;
297 }
298 if (rtm->rtm_addrs == RTA_DST)
56492e5a 299 p_sockaddr(sa, 0, 36);
4d9e18da 300 else {
56492e5a 301 p_sockaddr(sa, rtm->rtm_flags, 16);
4d9e18da
KS
302 if (sa->sa_len == 0)
303 sa->sa_len = sizeof(long);
304 sa = (struct sockaddr *)(sa->sa_len + (char *)sa);
56492e5a 305 p_sockaddr(sa, 0, 18);
4d9e18da
KS
306 }
307 p_flags(rtm->rtm_flags & interesting, "%-6.6s ");
308 putchar('\n');
309}
310
6e549c8f 311static void
56492e5a 312p_sockaddr(sa, flags, width)
a6dd7faa
MK
313 struct sockaddr *sa;
314 int flags, width;
4d9e18da 315{
6e549c8f 316 char workbuf[128], *cplim;
a6dd7faa 317 register char *cp = workbuf;
4d9e18da
KS
318
319 switch(sa->sa_family) {
4b68c469 320 case AF_INET:
4d9e18da
KS
321 {
322 register struct sockaddr_in *sin = (struct sockaddr_in *)sa;
323
324 cp = (sin->sin_addr.s_addr == 0) ? "default" :
325 ((flags & RTF_HOST) ?
6e549c8f
KS
326 routename(sin->sin_addr.s_addr) :
327 netname(sin->sin_addr.s_addr, 0L));
4b68c469 328 break;
a6dd7faa 329 }
4d9e18da 330
4b68c469 331 case AF_NS:
6e549c8f 332 cp = ns_print(sa);
4b68c469 333 break;
4d9e18da 334
a6dd7faa
MK
335 case AF_LINK:
336 {
337 register struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
a6dd7faa
MK
338
339 if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 &&
340 sdl->sdl_slen == 0)
341 (void) sprintf(workbuf, "link#%d", sdl->sdl_index);
342 else switch (sdl->sdl_type) {
343 case IFT_ETHER:
344 {
345 register int i;
346 register u_char *lla = (u_char *)sdl->sdl_data +
347 sdl->sdl_nlen;
348
349 cplim = "";
350 for (i = 0; i < sdl->sdl_alen; i++, lla++) {
351 cp += sprintf(cp, "%s%x", cplim, *lla);
352 cplim = ":";
353 }
354 cp = workbuf;
355 break;
356 }
357 default:
358 cp = link_ntoa(sdl);
359 break;
360 }
361 break;
362 }
363
4b68c469 364 default:
4d9e18da 365 {
b8442d18 366 register u_short *s = ((u_short *)sa->sa_data), *slim;
4d9e18da 367
b8442d18
KS
368 slim = (u_short *) sa + ((sa->sa_len + sizeof(u_short) - 1) /
369 sizeof(u_short));
4d9e18da
KS
370 cplim = cp + sizeof(workbuf) - 6;
371 cp += sprintf(cp, "(%d)", sa->sa_family);
372 while (s < slim && cp < cplim)
b8442d18 373 cp += sprintf(cp, " %x", *s++);
4d9e18da
KS
374 cp = workbuf;
375 }
4b68c469 376 }
b8442d18
KS
377 if (width < 0 )
378 printf("%s ", cp);
379 else {
380 if (nflag)
381 printf("%-*s ", width, cp);
382 else
383 printf("%-*.*s ", width, width, cp);
384 }
4d9e18da
KS
385}
386
6e549c8f 387static void
4d9e18da 388p_flags(f, format)
a6dd7faa
MK
389 register int f;
390 char *format;
4d9e18da
KS
391{
392 char name[33], *flags;
393 register struct bits *p = bits;
a6dd7faa 394
4d9e18da
KS
395 for (flags = name; p->b_mask; p++)
396 if (p->b_mask & f)
4b68c469
MK
397 *flags++ = p->b_val;
398 *flags = '\0';
4d9e18da
KS
399 printf(format, name);
400}
401
6e549c8f 402static void
4d9e18da 403p_rtentry(rt)
6e549c8f 404 register struct rtentry *rt;
4d9e18da 405{
a6dd7faa
MK
406 static struct ifnet ifnet, *lastif;
407 static char name[16];
4d9e18da 408
a6dd7faa
MK
409 p_sockaddr(kgetsa(rt_key(rt)), rt->rt_flags, WID_DST);
410 p_sockaddr(kgetsa(rt->rt_gateway), 0, WID_GW);
4d9e18da
KS
411 p_flags(rt->rt_flags, "%-6.6s ");
412 printf("%6d %8d ", rt->rt_refcnt, rt->rt_use);
a6dd7faa
MK
413 if (rt->rt_ifp) {
414 if (rt->rt_ifp != lastif) {
415 kget(rt->rt_ifp, ifnet);
6e549c8f 416 kread((off_t)ifnet.if_name, name, 16);
a6dd7faa
MK
417 lastif = rt->rt_ifp;
418 }
419 printf(" %.15s%d%s", name, ifnet.if_unit,
420 rt->rt_nodes[0].rn_dupedkey ? " =>" : "");
4b68c469 421 }
a6dd7faa 422 putchar('\n');
3537c4ea
SL
423}
424
425char *
426routename(in)
6e549c8f 427 u_long in;
3537c4ea 428{
6b9930b0 429 register char *cp;
cb646d4b 430 static char line[MAXHOSTNAMELEN + 1];
40f48772 431 struct hostent *hp;
6c5bed6c
MK
432 static char domain[MAXHOSTNAMELEN + 1];
433 static int first = 1;
3537c4ea 434
6c5bed6c
MK
435 if (first) {
436 first = 0;
437 if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
438 (cp = index(domain, '.')))
439 (void) strcpy(domain, cp + 1);
440 else
441 domain[0] = 0;
442 }
6b9930b0 443 cp = 0;
3537c4ea 444 if (!nflag) {
f7c99b06 445 hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
cab3a575 446 AF_INET);
6c5bed6c
MK
447 if (hp) {
448 if ((cp = index(hp->h_name, '.')) &&
449 !strcmp(cp + 1, domain))
450 *cp = 0;
cab3a575 451 cp = hp->h_name;
6c5bed6c 452 }
3537c4ea
SL
453 }
454 if (cp)
cb646d4b 455 strncpy(line, cp, sizeof(line) - 1);
3537c4ea 456 else {
cab3a575 457#define C(x) ((x) & 0xff)
6e549c8f
KS
458 in = ntohl(in);
459 sprintf(line, "%u.%u.%u.%u",
460 C(in >> 24), C(in >> 16), C(in >> 8), C(in));
cab3a575
MK
461 }
462 return (line);
463}
464
465/*
466 * Return the name of the network whose address is given.
467 * The address is assumed to be that of a net or subnet, not a host.
468 */
469char *
470netname(in, mask)
6e549c8f 471 u_long in, mask;
cab3a575
MK
472{
473 char *cp = 0;
cb646d4b 474 static char line[MAXHOSTNAMELEN + 1];
cab3a575
MK
475 struct netent *np = 0;
476 u_long net;
6e549c8f 477 register int i;
6c5bed6c 478 int subnetshift;
cab3a575 479
6e549c8f 480 i = ntohl(in);
a83ade01 481 if (!nflag && i) {
6c5bed6c
MK
482 if (mask == 0) {
483 if (IN_CLASSA(i)) {
484 mask = IN_CLASSA_NET;
485 subnetshift = 8;
486 } else if (IN_CLASSB(i)) {
487 mask = IN_CLASSB_NET;
488 subnetshift = 8;
489 } else {
490 mask = IN_CLASSC_NET;
491 subnetshift = 4;
492 }
cab3a575 493 /*
6c5bed6c
MK
494 * If there are more bits than the standard mask
495 * would suggest, subnets must be in use.
496 * Guess at the subnet mask, assuming reasonable
497 * width subnet fields.
cab3a575 498 */
a83ade01 499 while (i &~ mask)
6c5bed6c 500 mask = (long)mask >> subnetshift;
cab3a575 501 }
a83ade01 502 net = i & mask;
6c5bed6c
MK
503 while ((mask & 1) == 0)
504 mask >>= 1, net >>= 1;
505 np = getnetbyaddr(net, AF_INET);
cab3a575
MK
506 if (np)
507 cp = np->n_name;
6e549c8f 508 }
cab3a575 509 if (cp)
cb646d4b 510 strncpy(line, cp, sizeof(line) - 1);
a83ade01
MK
511 else if ((i & 0xffffff) == 0)
512 sprintf(line, "%u", C(i >> 24));
513 else if ((i & 0xffff) == 0)
514 sprintf(line, "%u.%u", C(i >> 24) , C(i >> 16));
515 else if ((i & 0xff) == 0)
516 sprintf(line, "%u.%u.%u", C(i >> 24), C(i >> 16), C(i >> 8));
cab3a575 517 else
a83ade01
MK
518 sprintf(line, "%u.%u.%u.%u", C(i >> 24),
519 C(i >> 16), C(i >> 8), C(i));
3537c4ea
SL
520 return (line);
521}
6c5bed6c 522
bbd2d21e
SL
523/*
524 * Print routing statistics
525 */
6e549c8f 526void
bbd2d21e
SL
527rt_stats(off)
528 off_t off;
529{
530 struct rtstat rtstat;
531
532 if (off == 0) {
533 printf("rtstat: symbol not in namelist\n");
534 return;
535 }
6e549c8f 536 kread(off, (char *)&rtstat, sizeof (rtstat));
bbd2d21e 537 printf("routing:\n");
bd91d3f9 538 printf("\t%u bad routing redirect%s\n",
bbd2d21e 539 rtstat.rts_badredirect, plural(rtstat.rts_badredirect));
bd91d3f9 540 printf("\t%u dynamically created route%s\n",
bbd2d21e 541 rtstat.rts_dynamic, plural(rtstat.rts_dynamic));
bd91d3f9 542 printf("\t%u new gateway%s due to redirects\n",
bbd2d21e 543 rtstat.rts_newgateway, plural(rtstat.rts_newgateway));
bd91d3f9 544 printf("\t%u destination%s found unreachable\n",
bbd2d21e 545 rtstat.rts_unreach, plural(rtstat.rts_unreach));
bd91d3f9 546 printf("\t%u use%s of a wildcard route\n",
bbd2d21e
SL
547 rtstat.rts_wildcard, plural(rtstat.rts_wildcard));
548}
2ff355d6 549short ns_nullh[] = {0,0,0};
f1fbb01b
KS
550short ns_bh[] = {-1,-1,-1};
551
552char *
6e549c8f
KS
553ns_print(sa)
554 register struct sockaddr *sa;
f1fbb01b 555{
6e549c8f 556 register struct sockaddr_ns *sns = (struct sockaddr_ns*)sa;
2ff355d6
MK
557 struct ns_addr work;
558 union { union ns_net net_e; u_long long_e; } net;
559 u_short port;
560 static char mybuf[50], cport[10], chost[25];
561 char *host = "";
f7c99b06 562 register char *p; register u_char *q;
f1fbb01b 563
2ff355d6
MK
564 work = sns->sns_addr;
565 port = ntohs(work.x_port);
566 work.x_port = 0;
567 net.net_e = work.x_net;
568 if (ns_nullhost(work) && net.long_e == 0) {
569 if (port ) {
570 sprintf(mybuf, "*.%xH", port);
571 upHex(mybuf);
572 } else
573 sprintf(mybuf, "*.*");
574 return (mybuf);
575 }
f1fbb01b 576
56492e5a 577 if (bcmp(ns_bh, work.x_host.c_host, 6) == 0) {
2ff355d6
MK
578 host = "any";
579 } else if (bcmp(ns_nullh, work.x_host.c_host, 6) == 0) {
580 host = "*";
581 } else {
582 q = work.x_host.c_host;
583 sprintf(chost, "%02x%02x%02x%02x%02x%02xH",
584 q[0], q[1], q[2], q[3], q[4], q[5]);
6e549c8f
KS
585 for (p = chost; *p == '0' && p < chost + 12; p++)
586 continue;
2ff355d6 587 host = p;
f1fbb01b 588 }
2ff355d6
MK
589 if (port)
590 sprintf(cport, ".%xH", htons(port));
591 else
592 *cport = 0;
593
594 sprintf(mybuf,"%xH.%s%s", ntohl(net.long_e), host, cport);
595 upHex(mybuf);
f1fbb01b
KS
596 return(mybuf);
597}
2ff355d6 598
f1fbb01b 599char *
6e549c8f
KS
600ns_phost(sa)
601 struct sockaddr *sa;
f1fbb01b 602{
6e549c8f 603 register struct sockaddr_ns *sns = (struct sockaddr_ns *)sa;
2ff355d6
MK
604 struct sockaddr_ns work;
605 static union ns_net ns_zeronet;
606 char *p;
6e549c8f 607
2ff355d6
MK
608 work = *sns;
609 work.sns_addr.x_port = 0;
610 work.sns_addr.x_net = ns_zeronet;
611
6e549c8f 612 p = ns_print((struct sockaddr *)&work);
2ff355d6
MK
613 if (strncmp("0H.", p, 3) == 0) p += 3;
614 return(p);
615}
6e549c8f
KS
616
617void
2ff355d6 618upHex(p0)
6e549c8f 619 char *p0;
2ff355d6
MK
620{
621 register char *p = p0;
622 for (; *p; p++) switch (*p) {
623
624 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
625 *p += ('A' - 'a');
626 }
f1fbb01b 627}