util.c revision 205793
11539Srgrimes/* 21539Srgrimes * Copyright (c) 1980, 1993 31539Srgrimes * The Regents of the University of California. All rights reserved. 41539Srgrimes * 51539Srgrimes * Redistribution and use in source and binary forms, with or without 61539Srgrimes * modification, are permitted provided that the following conditions 71539Srgrimes * are met: 81539Srgrimes * 1. Redistributions of source code must retain the above copyright 91539Srgrimes * notice, this list of conditions and the following disclaimer. 101539Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111539Srgrimes * notice, this list of conditions and the following disclaimer in the 121539Srgrimes * documentation and/or other materials provided with the distribution. 131539Srgrimes * 3. All advertising materials mentioning features or use of this software 141539Srgrimes * must display the following acknowledgement: 151539Srgrimes * This product includes software developed by the University of 161539Srgrimes * California, Berkeley and its contributors. 171539Srgrimes * 4. Neither the name of the University nor the names of its contributors 181539Srgrimes * may be used to endorse or promote products derived from this software 191539Srgrimes * without specific prior written permission. 201539Srgrimes * 211539Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221539Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231539Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241539Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251539Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261539Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271539Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281539Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291539Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301539Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311539Srgrimes * SUCH DAMAGE. 321539Srgrimes */ 331539Srgrimes 341539Srgrimes#ifndef lint 351539Srgrimes#if 0 3623655Speterstatic char sccsid[] = "@(#)aux.c 8.1 (Berkeley) 6/6/93"; 3750473Speter#endif 381539Srgrimes#endif /* not lint */ 391539Srgrimes#include <sys/cdefs.h> 401539Srgrimes__FBSDID("$FreeBSD: head/usr.bin/mail/util.c 205793 2010-03-28 13:16:08Z ed $"); 411539Srgrimes 421539Srgrimes#include <sys/time.h> 431539Srgrimes 44123257Smarcel#include "rcv.h" 45102227Smike#include "extern.h" 461539Srgrimes 47104585Smike/* 48104585Smike * Mail -- a mail program 49102227Smike * 50102227Smike * Auxiliary functions. 51102227Smike */ 521539Srgrimes 531539Srgrimesstatic char *save2str(char *, char *); 54104585Smike 55104585Smike/* 56104585Smike * Return a pointer to a dynamic copy of the argument. 57104585Smike */ 58104585Smikechar * 59104585Smikesavestr(str) 60104585Smike char *str; 611539Srgrimes{ 621539Srgrimes char *new; 631539Srgrimes int size = strlen(str) + 1; 641539Srgrimes 651539Srgrimes if ((new = salloc(size)) != NULL) 661539Srgrimes bcopy(str, new, size); 671539Srgrimes return (new); 681539Srgrimes} 691539Srgrimes 701539Srgrimes/* 711539Srgrimes * Make a copy of new argument incorporating old one. 721539Srgrimes */ 731539Srgrimesstatic char * 741539Srgrimessave2str(str, old) 7572529Simp char *str, *old; 7673254Sdeischen{ 7772529Simp char *new; 781539Srgrimes int newsize = strlen(str) + 1; 791539Srgrimes int oldsize = old ? strlen(old) + 1 : 0; 801539Srgrimes 811539Srgrimes if ((new = salloc(newsize + oldsize)) != NULL) { 821539Srgrimes if (oldsize) { 831539Srgrimes bcopy(old, new, oldsize); 841539Srgrimes new[oldsize - 1] = ' '; 851539Srgrimes } 861539Srgrimes bcopy(str, new + oldsize, newsize); 871539Srgrimes } 881539Srgrimes return (new); 891539Srgrimes} 901539Srgrimes 911539Srgrimes/* 921539Srgrimes * Touch the named message by setting its MTOUCH flag. 931539Srgrimes * Touched messages have the effect of not being sent 941539Srgrimes * back to the system mailbox on exit. 951539Srgrimes */ 961539Srgrimesvoid 971539Srgrimestouch(mp) 981539Srgrimes struct message *mp; 991539Srgrimes{ 1001539Srgrimes 1011539Srgrimes mp->m_flag |= MTOUCH; 1021539Srgrimes if ((mp->m_flag & MREAD) == 0) 1031539Srgrimes mp->m_flag |= MREAD|MSTATUS; 1041539Srgrimes} 1051539Srgrimes 1061539Srgrimes/* 1071539Srgrimes * Test to see if the passed file name is a directory. 1081539Srgrimes * Return true if it is. 1091539Srgrimes */ 1101539Srgrimesint 1111539Srgrimesisdir(name) 1121539Srgrimes char name[]; 1131539Srgrimes{ 1141539Srgrimes struct stat sbuf; 11593032Simp 11693032Simp if (stat(name, &sbuf) < 0) 11793032Simp return (0); 11893032Simp return (S_ISDIR(sbuf.st_mode)); 1191539Srgrimes} 1201539Srgrimes 1211539Srgrimes/* 12272529Simp * Count the number of arguments in the given string raw list. 1231539Srgrimes */ 1241539Srgrimesint 1251539Srgrimesargcount(argv) 1261539Srgrimes char **argv; 1271539Srgrimes{ 1281539Srgrimes char **ap; 1291539Srgrimes 1301539Srgrimes for (ap = argv; *ap++ != NULL;) 1311539Srgrimes ; 1321539Srgrimes return (ap - argv - 1); 1331539Srgrimes} 1341539Srgrimes 1351539Srgrimes/* 1361539Srgrimes * Return the desired header line from the passed message 137129774Stjr * pointer (or NULL if the desired header field is not available). 1381539Srgrimes */ 13981600Speterchar * 14081600Speterhfield(field, mp) 14181600Speter const char *field; 1421539Srgrimes struct message *mp; 143129774Stjr{ 144129774Stjr FILE *ibuf; 1451539Srgrimes char linebuf[LINESIZE]; 1461539Srgrimes int lc; 1471539Srgrimes char *hfield; 1481539Srgrimes char *colon, *oldhfield = NULL; 1491539Srgrimes 1501539Srgrimes ibuf = setinput(mp); 1511539Srgrimes if ((lc = mp->m_lines - 1) < 0) 1521539Srgrimes return (NULL); 1531539Srgrimes if (readline(ibuf, linebuf, LINESIZE) < 0) 1541539Srgrimes return (NULL); 1551539Srgrimes while (lc > 0) { 1561539Srgrimes if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0) 15713771Smpp return (oldhfield); 15813771Smpp if ((hfield = ishfield(linebuf, colon, field)) != NULL) 1591539Srgrimes oldhfield = save2str(hfield, oldhfield); 1601539Srgrimes } 16137489Speter return (oldhfield); 16272372Sdeischen} 1631539Srgrimes 1641539Srgrimes/* 1651539Srgrimes * Return the next header field found in the given message. 1661539Srgrimes * Return >= 0 if something found, < 0 elsewise. 1671539Srgrimes * "colon" is set to point to the colon in the header. 1681539Srgrimes * Must deal with \ continuations & other such fraud. 1691539Srgrimes */ 1701539Srgrimesint 1711539Srgrimesgethfield(f, linebuf, rem, colon) 1721539Srgrimes FILE *f; 1731539Srgrimes char linebuf[]; 1741539Srgrimes int rem; 1751539Srgrimes char **colon; 1761539Srgrimes{ 1771539Srgrimes char line2[LINESIZE]; 1781539Srgrimes char *cp, *cp2; 1791539Srgrimes int c; 1801539Srgrimes 1811539Srgrimes for (;;) { 1821539Srgrimes if (--rem < 0) 1831539Srgrimes return (-1); 1841539Srgrimes if ((c = readline(f, linebuf, LINESIZE)) <= 0) 1851539Srgrimes return (-1); 1861539Srgrimes for (cp = linebuf; isprint((unsigned char)*cp) && *cp != ' ' && *cp != ':'; 1871539Srgrimes cp++) 1881539Srgrimes ; 1891539Srgrimes if (*cp != ':' || cp == linebuf) 190100133Swollman continue; 1911539Srgrimes /* 1921539Srgrimes * I guess we got a headline. 1931539Srgrimes * Handle wraparounding 1941539Srgrimes */ 1951539Srgrimes *colon = cp; 1961539Srgrimes cp = linebuf + c; 1971539Srgrimes for (;;) { 1981539Srgrimes while (--cp >= linebuf && (*cp == ' ' || *cp == '\t')) 1991539Srgrimes ; 2001539Srgrimes cp++; 2011539Srgrimes if (rem <= 0) 2021539Srgrimes break; 2031539Srgrimes ungetc(c = getc(f), f); 2041539Srgrimes if (c != ' ' && c != '\t') 2051539Srgrimes break; 20681600Speter if ((c = readline(f, line2, LINESIZE)) < 0) 20781600Speter break; 20881600Speter rem--; 2091539Srgrimes for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++) 210100133Swollman ; 2111539Srgrimes c -= cp2 - line2; 2121539Srgrimes if (cp + c >= linebuf + LINESIZE - 2) 2131539Srgrimes break; 21493032Simp *cp++ = ' '; 21593032Simp bcopy(cp2, cp, c); 21693032Simp cp += c; 21793032Simp } 21893032Simp *cp = 0; 21993032Simp return (rem); 220104989Smike } 221104989Smike /* NOTREACHED */ 222104989Smike} 223103012Stjr 22493032Simp/* 225104989Smike * Check whether the passed line is a header line of 226104989Smike * the desired breed. Return the field body, or 0. 227104989Smike */ 228104989Smike 22993032Simpchar* 23093032Simpishfield(linebuf, colon, field) 23193032Simp char linebuf[]; 232104989Smike char *colon; 23393032Simp const char *field; 23493032Simp{ 23593032Simp char *cp = colon; 23693032Simp 237103012Stjr *cp = 0; 23893032Simp if (strcasecmp(linebuf, field) != 0) { 23993032Simp *cp = ':'; 24093032Simp return (0); 24193032Simp } 24293032Simp *cp = ':'; 24393032Simp for (cp++; *cp == ' ' || *cp == '\t'; cp++) 244104989Smike ; 245103012Stjr return (cp); 246103012Stjr} 247103012Stjr 248104989Smike/* 24993032Simp * Copy a string and lowercase the result. 25093032Simp * dsize: space left in buffer (including space for NULL) 25193032Simp */ 252103012Stjrvoid 253102227Smikeistrncpy(dest, src, dsize) 254103012Stjr char *dest; 255103012Stjr const char *src; 256102227Smike size_t dsize; 2571539Srgrimes{ 258100133Swollman 259103012Stjr strlcpy(dest, src, dsize); 260101914Srobert while (*dest) 261105098Stjr *dest++ = tolower((unsigned char)*dest); 262105098Stjr} 263104989Smike 264103012Stjr/* 265102227Smike * The following code deals with input stacking to do source 266104989Smike * commands. All but the current file pointer are saved on 267104585Smike * the stack. 268100133Swollman */ 269100133Swollman 2701539Srgrimesstatic int ssp; /* Top of file stack */ 271100133Swollmanstruct sstack { 2721539Srgrimes FILE *s_file; /* File we were in. */ 273104585Smike int s_cond; /* Saved state of conditionals */ 27419211Swosch int s_loading; /* Loading .mailrc, etc. */ 275104585Smike}; 276104585Smike#define SSTACK_SIZE 64 /* XXX was NOFILE. */ 2771539Srgrimesstatic struct sstack sstack[SSTACK_SIZE]; 278104585Smike 27919211Swosch/* 28019211Swosch * Pushdown current input file and switch to a new one. 28193032Simp * Set the global flag "sourcing" so that others will realize 28293032Simp * that they are no longer reading from a tty (in all probability). 28393032Simp */ 284100133Swollmanint 285100133Swollmansource(arglist) 286100133Swollman char **arglist; 287100133Swollman{ 288100133Swollman FILE *fi; 289100133Swollman char *cp; 290100133Swollman 291100133Swollman if ((cp = expand(*arglist)) == NULL) 29293032Simp return (1); 29393032Simp if ((fi = Fopen(cp, "r")) == NULL) { 29493032Simp warn("%s", cp); 2951539Srgrimes return (1); 2961539Srgrimes } 297100133Swollman if (ssp >= SSTACK_SIZE - 1) { 298100133Swollman printf("Too much \"sourcing\" going on.\n"); 29924897Sbde (void)Fclose(fi); 300100133Swollman return (1); 301100133Swollman } 302100133Swollman sstack[ssp].s_file = input; 303100133Swollman sstack[ssp].s_cond = cond; 30424897Sbde sstack[ssp].s_loading = loading; 305109168Stjr ssp++; 306109168Stjr loading = 0; 307109168Stjr cond = CANY; 308109168Stjr input = fi; 309109168Stjr sourcing++; 310109168Stjr return (0); 311100133Swollman} 312100133Swollman 313102227Smike/* 314102227Smike * Pop the current input back to the previous level. 31524897Sbde * Update the "sourcing" flag as appropriate. 316100133Swollman */ 317100133Swollmanint 318100133Swollmanunstack() 319100133Swollman{ 320100133Swollman if (ssp <= 0) { 321100133Swollman printf("\"Source\" stack over-pop.\n"); 322100133Swollman sourcing = 0; 323100133Swollman return (1); 32424897Sbde } 32524897Sbde (void)Fclose(input); 32624897Sbde if (cond != CANY) 3271539Srgrimes printf("Unmatched \"if\"\n"); 3281539Srgrimes ssp--; 329100133Swollman cond = sstack[ssp].s_cond; 33093032Simp loading = sstack[ssp].s_loading; 33193032Simp input = sstack[ssp].s_file; 33293032Simp if (ssp == 0) 33387369Sobrien sourcing = loading; 33487369Sobrien return (0); 33587369Sobrien} 33687369Sobrien 33787369Sobrien/* 33893032Simp * Touch the indicated file. 33993032Simp * This is nifty for the shell. 34093032Simp */ 34193032Simpvoid 342102227Smikealter(name) 34337614Sbde char *name; 3441539Srgrimes{ 3451539Srgrimes struct stat sb; 346100133Swollman struct timeval tv[2]; 347100133Swollman 348100133Swollman if (stat(name, &sb)) 349100133Swollman return; 350100133Swollman (void)gettimeofday(&tv[0], (struct timezone *)NULL); 351100133Swollman tv[0].tv_sec++; 352100133Swollman TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtim); 353100133Swollman (void)utimes(name, tv); 3541539Srgrimes} 3551539Srgrimes 35693032Simp/* 35775818Sobrien * Get sender's name from this message. If the message has 35875818Sobrien * a bunch of arpanet stuff in it, we may have to skin the name 35975818Sobrien * before returning it. 36093032Simp */ 3611539Srgrimeschar * 3621539Srgrimesnameof(mp, reptype) 3631539Srgrimes struct message *mp; 3641539Srgrimes int reptype; 365100133Swollman{ 366100133Swollman char *cp, *cp2; 367100133Swollman 368100133Swollman cp = skin(name1(mp, reptype)); 369102227Smike if (reptype != 0 || charcount(cp, '!') < 2) 370100133Swollman return (cp); 371100133Swollman cp2 = strrchr(cp, '!'); 372100133Swollman cp2--; 373102227Smike while (cp2 > cp && *cp2 != '!') 374100133Swollman cp2--; 375100133Swollman if (*cp2 == '!') 376100133Swollman return (cp2 + 1); 377102227Smike return (cp); 378100133Swollman} 379100133Swollman 380100133Swollman/* 381102227Smike * Start of a "comment". 382100133Swollman * Ignore it. 383100133Swollman */ 384100133Swollmanchar * 385100133Swollmanskip_comment(cp) 3861539Srgrimes char *cp; 3871539Srgrimes{ 38893032Simp int nesting = 1; 38993032Simp 3901539Srgrimes for (; nesting > 0 && *cp; cp++) { 3911539Srgrimes switch (*cp) { 3928858Srgrimes case '\\': 3931539Srgrimes if (cp[1]) 3941539Srgrimes cp++; 3951539Srgrimes break; 3961539Srgrimes case '(': 3971539Srgrimes nesting++; 3981539Srgrimes break; 3991539Srgrimes case ')': 4001539Srgrimes nesting--; 4011539Srgrimes break; 4021539Srgrimes } 4031539Srgrimes } 4041539Srgrimes return (cp); 4051539Srgrimes} 4061539Srgrimes 4071539Srgrimes/* 4081539Srgrimes * Skin an arpa net address according to the RFC 822 interpretation 4091539Srgrimes * of "host-phrase." 4101539Srgrimes */ 4111539Srgrimeschar * 4121539Srgrimesskin(name) 4131539Srgrimes char *name; 4141539Srgrimes{ 4151539Srgrimes char *nbuf, *bufend, *cp, *cp2; 4161539Srgrimes int c, gotlt, lastsp; 4171539Srgrimes 4181539Srgrimes if (name == NULL) 4191539Srgrimes return (NULL); 4201539Srgrimes if (strchr(name, '(') == NULL && strchr(name, '<') == NULL 4211539Srgrimes && strchr(name, ' ') == NULL) 422127100Stjr return (name); 423127100Stjr 424127230Stjr /* We assume that length(input) <= length(output) */ 425127230Stjr if ((nbuf = malloc(strlen(name) + 1)) == NULL) 426127230Stjr err(1, "Out of memory"); 427127100Stjr gotlt = 0; 428127100Stjr lastsp = 0; 429127230Stjr bufend = nbuf; 430127100Stjr for (cp = name, cp2 = bufend; (c = *cp++) != '\0'; ) { 431127100Stjr switch (c) { 432127230Stjr case '(': 433127230Stjr cp = skip_comment(cp); 434127100Stjr lastsp = 0; 435127100Stjr break; 436127100Stjr 437127100Stjr case '"': 438104585Smike /* 43935127Sjb * Start of a "quoted-string". 44035127Sjb * Copy it in its entirety. 44135127Sjb */ 44235127Sjb while ((c = *cp) != '\0') { 44335127Sjb cp++; 44435127Sjb if (c == '"') 44535127Sjb break; 44635127Sjb if (c != '\\') 447104585Smike *cp2++ = c; 448104585Smike else if ((c = *cp) != '\0') { 44935127Sjb *cp2++ = c; 45071580Sdeischen cp++; 4511539Srgrimes } 45235127Sjb } 45335127Sjb lastsp = 0; 454104585Smike break; 45524897Sbde 456100133Swollman case ' ': 45724897Sbde if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ') 458 cp += 3, *cp2++ = '@'; 459 else 460 if (cp[0] == '@' && cp[1] == ' ') 461 cp += 2, *cp2++ = '@'; 462 else 463 lastsp = 1; 464 break; 465 466 case '<': 467 cp2 = bufend; 468 gotlt++; 469 lastsp = 0; 470 break; 471 472 case '>': 473 if (gotlt) { 474 gotlt = 0; 475 while ((c = *cp) != '\0' && c != ',') { 476 cp++; 477 if (c == '(') 478 cp = skip_comment(cp); 479 else if (c == '"') 480 while ((c = *cp) != '\0') { 481 cp++; 482 if (c == '"') 483 break; 484 if (c == '\\' && *cp != '\0') 485 cp++; 486 } 487 } 488 lastsp = 0; 489 break; 490 } 491 /* FALLTHROUGH */ 492 493 default: 494 if (lastsp) { 495 lastsp = 0; 496 *cp2++ = ' '; 497 } 498 *cp2++ = c; 499 if (c == ',' && *cp == ' ' && !gotlt) { 500 *cp2++ = ' '; 501 while (*++cp == ' ') 502 ; 503 lastsp = 0; 504 bufend = cp2; 505 } 506 } 507 } 508 *cp2 = '\0'; 509 510 if ((cp = realloc(nbuf, strlen(nbuf) + 1)) != NULL) 511 nbuf = cp; 512 return (nbuf); 513} 514 515/* 516 * Fetch the sender's name from the passed message. 517 * Reptype can be 518 * 0 -- get sender's name for display purposes 519 * 1 -- get sender's name for reply 520 * 2 -- get sender's name for Reply 521 */ 522char * 523name1(mp, reptype) 524 struct message *mp; 525 int reptype; 526{ 527 char namebuf[LINESIZE]; 528 char linebuf[LINESIZE]; 529 char *cp, *cp2; 530 FILE *ibuf; 531 int first = 1; 532 533 if ((cp = hfield("from", mp)) != NULL) 534 return (cp); 535 if (reptype == 0 && (cp = hfield("sender", mp)) != NULL) 536 return (cp); 537 ibuf = setinput(mp); 538 namebuf[0] = '\0'; 539 if (readline(ibuf, linebuf, LINESIZE) < 0) 540 return (savestr(namebuf)); 541newname: 542 for (cp = linebuf; *cp != '\0' && *cp != ' '; cp++) 543 ; 544 for (; *cp == ' ' || *cp == '\t'; cp++) 545 ; 546 for (cp2 = &namebuf[strlen(namebuf)]; 547 *cp != '\0' && *cp != ' ' && *cp != '\t' && 548 cp2 < namebuf + LINESIZE - 1;) 549 *cp2++ = *cp++; 550 *cp2 = '\0'; 551 if (readline(ibuf, linebuf, LINESIZE) < 0) 552 return (savestr(namebuf)); 553 if ((cp = strchr(linebuf, 'F')) == NULL) 554 return (savestr(namebuf)); 555 if (strncmp(cp, "From", 4) != 0) 556 return (savestr(namebuf)); 557 while ((cp = strchr(cp, 'r')) != NULL) { 558 if (strncmp(cp, "remote", 6) == 0) { 559 if ((cp = strchr(cp, 'f')) == NULL) 560 break; 561 if (strncmp(cp, "from", 4) != 0) 562 break; 563 if ((cp = strchr(cp, ' ')) == NULL) 564 break; 565 cp++; 566 if (first) { 567 cp2 = namebuf; 568 first = 0; 569 } else 570 cp2 = strrchr(namebuf, '!') + 1; 571 strlcpy(cp2, cp, sizeof(namebuf) - (cp2 - namebuf) - 1); 572 strcat(namebuf, "!"); 573 goto newname; 574 } 575 cp++; 576 } 577 return (savestr(namebuf)); 578} 579 580/* 581 * Count the occurances of c in str 582 */ 583int 584charcount(str, c) 585 char *str; 586 int c; 587{ 588 char *cp; 589 int i; 590 591 for (i = 0, cp = str; *cp != '\0'; cp++) 592 if (*cp == c) 593 i++; 594 return (i); 595} 596 597/* 598 * See if the given header field is supposed to be ignored. 599 */ 600int 601isign(field, ignore) 602 const char *field; 603 struct ignoretab ignore[2]; 604{ 605 char realfld[LINESIZE]; 606 607 if (ignore == ignoreall) 608 return (1); 609 /* 610 * Lower-case the string, so that "Status" and "status" 611 * will hash to the same place. 612 */ 613 istrncpy(realfld, field, sizeof(realfld)); 614 if (ignore[1].i_count > 0) 615 return (!member(realfld, ignore + 1)); 616 else 617 return (member(realfld, ignore)); 618} 619 620int 621member(realfield, table) 622 char *realfield; 623 struct ignoretab *table; 624{ 625 struct ignore *igp; 626 627 for (igp = table->i_head[hash(realfield)]; igp != NULL; igp = igp->i_link) 628 if (*igp->i_field == *realfield && 629 equal(igp->i_field, realfield)) 630 return (1); 631 return (0); 632} 633