1 
2 static char rcsid[] = "@(#)$Id: hdrconfg.c,v 1.5 1996/05/09 15:51:19 wfp5p Exp $";
3 
4 /*******************************************************************************
5  *  The Elm Mail System  -  $Revision: 1.5 $   $State: Exp $
6  *
7  *                      Copyright (c) 1988-1995 USENET Community Trust
8  *			Copyright (c) 1986,1987 Dave Taylor
9  *******************************************************************************
10  * Bug reports, patches, comments, suggestions should be sent to:
11  *
12  *      Bill Pemberton, Elm Coordinator
13  *      flash@virginia.edu
14  *
15  *******************************************************************************
16  * $Log: hdrconfg.c,v $
17  * Revision 1.5  1996/05/09  15:51:19  wfp5p
18  * Alpha 10
19  *
20  * Revision 1.4  1996/03/14  17:29:36  wfp5p
21  * Alpha 9
22  *
23  * Revision 1.3  1995/09/29  17:42:12  wfp5p
24  * Alpha 8 (Chip's big changes)
25  *
26  * Revision 1.2  1995/09/11  15:19:08  wfp5p
27  * Alpha 7
28  *
29  * Revision 1.1.1.1  1995/04/19  20:38:36  wfp5p
30  * Initial import of elm 2.4 PL0 as base for elm 2.5.
31  *
32  ******************************************************************************/
33 
34 /**   This file contains the routines necessary to be able to modify
35       the mail headers of messages on the way off the machine.  The
36       headers currently supported for modification are:
37 
38 	Subject:
39 	To:
40 	Cc:
41 	Bcc:
42 	Reply-To:
43 	Priority:
44 	Precedence:
45 	In-Reply-To:
46 	Action:
47 
48 	<user defined>
49 **/
50 
51 #include "elm_defs.h"
52 #include "elm_globals.h"
53 #include "sndhdrs.h"
54 #include "s_elm.h"
55 
56 /*
57  * Placement of prompts and messages at the bottom of the screen.
58  */
59 #define INSTRUCT_LINE           (LINES-4)
60 #define INPUT_LINE		(LINES-2)
61 #define ERROR_LINE		(LINES-1)
62 #define TOPMOST_PROMPTAREA_LINE INSTRUCT_LINE
63 
64 /*
65  * Option flags for the fields in a (struct hdr_menu_item).
66  */
67 #define HF_DISP_1ROW	0001	/* field is displayed on one line	*/
68 #define HF_DISP_2ROW	0002	/* field display spans two lines	*/
69 #define HF_DISP_3ROW	0003	/* field display spans three lines	*/
70 #define HF_DISP_LEFT	0004	/* field occupies left half of a line	*/
71 #define HF_DISP_RIGHT	0005	/* field occupies right half of a line	*/
72 #define HF_DISP_MASK	0007	/* -- mask to pull out display option	*/
73 #define HF_PROMPT_USR	0020	/* prompt for user defined hdr entry	*/
74 #define HF_PROMPT_MASK	0070	/* -- mask to pull out prompt option	*/
75 #define HF_APPENDENTRY	0100	/* append user entry to existing value	*/
76 
77 /*
78  * Structure to describe a header which can be edited in this menu.
79  */
80 struct hdr_menu_item {
81     int menucmd;	/* The single keystroke (lower-case letter) the	*/
82 			/*   user strikes to edit this menu item.	*/
83     char *hdrname;	/* Header name to display in the menu.  Parens	*/
84 			/*   should be used to bracket the "menucmd"	*/
85 			/*   char in the name, e.g. "S)ubject".  This	*/
86 			/*   will be NULL for the user-defined header.	*/
87     int lineno;		/* Screen line at which the field is displayed.	*/
88     int flags;		/* Various flags which effect the display and	*/
89 			/*   user entry of this item.			*/
90     char *inpval;	/* Pointer to the buffer to hold the value	*/
91 			/*   entered by the user.			*/
92     char *expval;	/* Pointer to the expanded header value to	*/
93 			/*   display.  If no special expansions are	*/
94 			/*   required then this will point to "inpval".	*/
95     int (*hdrproc)();	/* Pointer to a procedure which verifies the	*/
96 			/*   user data entry, and if required converts	*/
97 			/*   the "inpval" value to "expval" value.  If	*/
98 			/*   no verification or expansion is needed	*/
99 			/*   then this will be NULL.			*/
100 };
101 
102 /*
103  * Local procedures.
104  */
105 static void hdrmenu_clear_promptarea();
106 static int hdrmenu_get();
107 static void hdrmenu_put();
108 static int hdrproc_addr();
109 static int hdrproc_precedence();
110 static int hdrproc_userhdr();
111 static void domainize_submenu();
112 static void domainize();
113 static void domainize_addr();
114 
115 /*
116  * Buffer to hold the message header during editing.
117  *
118  * I'm sorry to report this buffer is not for clever reasons.  (Although
119  * it could be used as the basis of an "undo" capability.)  This buffer
120  * exists because this routine originally was written to work on global
121  * strings.  Using this buffer just made it easy to allow this routine
122  * to work now that sending header information now is contained in
123  * a SEND_HEADER datatype.
124  */
125 static SEND_HEADER H;
126 
127 /*
128  * Definition of all the header editing menu fields.
129  */
130 struct hdr_menu_item hmenu_item_list[] = {
131     { 't', "T)o",		 2, HF_DISP_3ROW|HF_APPENDENTRY,
132 					H.to, H.expanded_to, hdrproc_addr },
133     { 'c', "C)c",		 5, HF_DISP_3ROW|HF_APPENDENTRY,
134 					H.cc, H.expanded_cc, hdrproc_addr },
135     { 'b', "B)cc",		 8, HF_DISP_2ROW|HF_APPENDENTRY,
136 					H.bcc, H.expanded_bcc, hdrproc_addr },
137     { 's', "S)ubject",		10, HF_DISP_2ROW, H.subject, H.subject, NULL },
138     { 'r', "R)eply-to",		12, HF_DISP_1ROW, H.reply_to,
139 					H.expanded_reply_to, hdrproc_addr },
140     { 'a', "A)ction",		13, HF_DISP_LEFT, H.action, H.action, NULL },
141     { 'p', "P)riority",		14, HF_DISP_LEFT,
142 					H.priority, H.priority, NULL },
143     { 'n', "Precede(n)ce",	14, HF_DISP_RIGHT, H.precedence,
144 					H.precedence, hdrproc_precedence },
145     { 'i', "I)n-reply-to",	15, HF_DISP_2ROW,
146 					H.in_reply_to, H.in_reply_to, NULL },
147     { 'u', NULL,		17, HF_DISP_1ROW|HF_PROMPT_USR,
148 					H.user_defined_header,
149 					H.user_defined_header,
150 					hdrproc_userhdr },
151     { -1, NULL, -1, -1, NULL, NULL, NULL },
152 };
153 
154 /*
155  * Selection of individual fields.  The indices *must* correspond
156  * to the above "hmenu_item_list[]" list.
157  */
158 #define hmenu_to		(hmenu_item_list[0])
159 #define hmenu_cc		(hmenu_item_list[1])
160 #define hmenu_bcc		(hmenu_item_list[2])
161 #define hmenu_subject		(hmenu_item_list[3])
162 #define hmenu_replyto		(hmenu_item_list[4])
163 #define hmenu_action		(hmenu_item_list[5])
164 #define hmenu_priority		(hmenu_item_list[7])
165 #define hmenu_precedence	(hmenu_item_list[8])
166 #define hmenu_inreplyto		(hmenu_item_list[9])
167 #define hmenu_userdef		(hmenu_item_list[10])
168 
169 
edit_headers(shdr)170 PUBLIC void edit_headers(shdr)
171 SEND_HEADER *shdr;
172 {
173     int c, i, do_redraw;
174     struct hdr_menu_item *h;
175 
176     /* copy message header into private buffer */
177     bcopy((char *)shdr, (char *)&H, sizeof(SEND_HEADER));
178 
179     /* expand out all of the header values */
180     /* menu displays expanded values, user edits unexpended versions */
181     for (h = hmenu_item_list ; h->menucmd > 0 ; ++h) {
182 	if (h->hdrproc != NULL)
183 	    (*h->hdrproc)(h);
184     }
185 
186     clearerr(stdin);
187     do_redraw = TRUE;
188     while (TRUE) {	/* forever */
189 
190 	/* redraw the entire display if required */
191 	if (do_redraw) {
192 	    ClearScreen();
193 	    CenterLine(0, catgets(elm_msg_cat, ElmSet,
194 		ElmHdrmenuScreenTitle, "Message Header Edit Screen"));
195 	    for (h = hmenu_item_list ; h->menucmd > 0 ; ++h)
196 		hdrmenu_put(h, TRUE);
197 	    do_redraw = FALSE;
198 	}
199 
200 	/* display the instructions */
201 #ifdef ALLOW_SUBSHELL
202 	CenterLine(INSTRUCT_LINE, catgets(elm_msg_cat, ElmSet,
203 	    ElmHdrmenuInstruct,
204 	    "Choose header, u)ser defined header, d)omainize, !)shell, or <return>."));
205 #else
206 	CenterLine(INSTRUCT_LINE, catgets(elm_msg_cat, ElmSet,
207 	    ElmHdrmenuInstructNoShell,
208 	    "Choose header, u)ser defined header, d)omainize, or <return>."));
209 #endif
210 
211 	/* prompt for command */
212 	PutLine0(INPUT_LINE, 0, catgets(elm_msg_cat, ElmSet,
213 	    ElmHdrmenuPrompt, "Choice: "));
214 	if ((c = GetKey(0)) == KEY_REDRAW) {
215 	    do_redraw = TRUE;
216 	    continue;
217 	}
218 	hdrmenu_clear_promptarea();
219 
220 	/* execute the command */
221 	switch (c) {
222 
223 	case RETURN:
224 	case LINE_FEED:
225 	case 'q':
226 	    goto done;
227 
228 	case 'd':
229 	    domainize_submenu();
230 	    break;
231 
232 #ifdef ALLOW_SUBSHELL
233 	case '!':
234 	    if (subshell())
235 		do_redraw = TRUE;
236 	    break;
237 #endif
238 
239 	case ctrl('L'):
240 	    do_redraw = TRUE;
241 	    break;
242 
243 	default:
244 	    /*  Since headers are displayed with parenthesized letter
245 	     *  capitalized, allow user to hit the capital letter, too.
246 	     *  Shhh--don't tell anyone about Precede(n)ce. */
247 	    c = tolower(c);
248 
249 	    for (h = hmenu_item_list ; h->menucmd > 0 ; ++h) {
250 		if (h->menucmd == c) {
251 		    if (hdrmenu_get(h) != 0)
252 			goto done;
253 		    hdrmenu_put(h, FALSE);
254 		    break;
255 		}
256 	    }
257 	    if (h->menucmd <= 0) {
258 		CenterLine(ERROR_LINE, catgets(elm_msg_cat, ElmSet,
259 		    ElmHdrmenuBadChoice, "No such header!"));
260 		Beep();
261 	    }
262 	    break;
263 
264 	}
265 
266     }
267 
268 done:
269     /* copy private buffer out to message header */
270     bcopy((char *)&H, (char *)shdr, sizeof(SEND_HEADER));
271 }
272 
273 
274 /*
275  * Erase instructions, user input, left-over errors, etc.
276  * This should be run after every user input command in this module.
277  */
hdrmenu_clear_promptarea()278 static void hdrmenu_clear_promptarea()
279 {
280     clear_error();
281     MoveCursor(TOPMOST_PROMPTAREA_LINE, 0);
282     CleartoEOS();
283 }
284 
285 
286 /*
287  * Prompt the user for a header value, and do any required post-processing.
288  */
hdrmenu_get(h)289 static int hdrmenu_get(h)
290 struct hdr_menu_item *h;
291 {
292     char inpbuf[SLEN], *s;
293     int ret, emode;
294 
295     /* display the instructions */
296     switch (h->flags & HF_PROMPT_MASK) {
297     case HF_PROMPT_USR:
298 	CenterLine(INSTRUCT_LINE, catgets(elm_msg_cat, ElmSet,
299 	    ElmHdrmenuGetUserdefInstruct,
300 	    "Enter in the format \"HeaderName: HeaderValue\"."));
301 	break;
302     default:
303 	CenterLine(INSTRUCT_LINE, catgets(elm_msg_cat, ElmSet,
304 	    ElmHdrmenuGetInstruct, "Enter value for the header."));
305 	break;
306     }
307 
308     /* display a prompt */
309     MoveCursor(INPUT_LINE, 0);
310     if (h->hdrname != NULL) {
311 	for (s = h->hdrname ; *s != '\0' ; ++s) {
312 	    if (*s != '(' && *s != ')')
313 		WriteChar(*s);
314 	}
315 	WriteChar(':');
316 	WriteChar(' ');
317     }
318 
319     /* get input from the user */
320     (void) strfcpy(inpbuf, h->inpval, sizeof(inpbuf));
321     emode = ((h->flags & HF_APPENDENTRY) ? ESTR_UPDATE : ESTR_REPLACE);
322     if (enter_string(inpbuf, sizeof(inpbuf), -1, -1, emode) == 0)
323 	(void) strcpy(h->inpval, inpbuf);
324     hdrmenu_clear_promptarea();
325 
326     /* see if there is some processing required on this value */
327     if (h->hdrproc != NULL)
328 	(void) (*h->hdrproc)(h);
329 
330     return 0;
331 }
332 
333 
334 /*
335  * Dispay a header and its value in the appropriate field.
336  */
hdrmenu_put(h,already_clear)337 static void hdrmenu_put(h, already_clear)
338 struct hdr_menu_item *h;
339 int already_clear;
340 {
341     char    *p;
342     int     start_row, max_row, start_col, max_col, row, col;
343 
344     /* figure out the dimensions of the field */
345     switch (h->flags & HF_DISP_MASK) {
346     case HF_DISP_LEFT:
347 	start_row = h->lineno;		max_row = h->lineno;
348 	start_col = 0;			max_col = COLS/2 - 2;
349 	break;
350     case HF_DISP_RIGHT:
351 	start_row = h->lineno;		max_row = h->lineno;
352 	start_col = COLS/2 + 1;		max_col = COLS-1;
353 	break;
354     case HF_DISP_3ROW:
355 	start_row = h->lineno;		max_row = h->lineno+2;
356 	start_col = 0;			max_col = COLS-1;
357 	break;
358     case HF_DISP_2ROW:
359 	start_row = h->lineno;		max_row = h->lineno+1;
360 	start_col = 0;			max_col = COLS-1;
361 	break;
362     default:
363 	start_row = h->lineno;		max_row = h->lineno;
364 	start_col = 0;			max_col = COLS-1;
365 	break;
366     }
367 
368     /* display the header name */
369     MoveCursor(start_row, start_col);
370     if (h->hdrname != NULL) {
371 	PutLine0(-1, -1, h->hdrname);
372 	WriteChar(':');
373 	WriteChar(' ');
374     }
375 
376     /* display the header value */
377     GetCursorPos(&row, &col);
378     for (p = h->expval ; *p != '\0' && row <= max_row ; ++p) {
379 	if (row == max_row && col == max_col-4 && strlen(p) > 4)
380 	    p = " ...";		/* neat hack alert */
381 	WriteChar(*p);
382 	if (!isprint(*p) || ++col > max_col)
383 	    GetCursorPos(&row, &col);
384     }
385 
386     /* save some drawing if we know the screen is already empty */
387     if (!already_clear) {
388 
389 	/* clear out remaining space in this line of the field */
390 	if (max_col == COLS-1) {
391 	    /* people on slow terminals might appreciate doing it this way */
392 	    CleartoEOLN();
393 	} else {
394 	    while (col++ <= max_col)
395 		WriteChar(' ');
396 	}
397 
398 	/* for multi-line fields, clear out any unused lines */
399 	/* this assumes that multi-line fields span the entire screen width */
400 	while (++row <= max_row) {
401 	    /* grrrrrr -- this is a multi-statement macro */
402 	    ClearLine(row);
403 	}
404 
405     }
406 
407 }
408 
409 
410 /*
411  * Process the to, cc, and bcc headers.  The value entered by the
412  * user is expanded.  A successful status is always returned.
413  */
hdrproc_addr(h)414 static int hdrproc_addr(h)
415 struct hdr_menu_item *h;
416 {
417     (void) build_address(strip_commas(h->inpval), h->expval);
418     return 0;
419 }
420 
421 
422 
423 /*
424  * Process the precedence header.  The value entered by the user is
425  * checked against the list of allowed precedences, if one exists.  If
426  * the precedence has a priority assigned to it, then an empty priority
427  * field will be filled in with that value.  If an error occurs a message
428  * is printed, the precedence value is cleared out, and a -1 is returned.
429  */
hdrproc_precedence(h)430 static int hdrproc_precedence(h)
431 struct hdr_menu_item *h;
432 {
433     char buf[SLEN];	/* assumes sizeof(allowed_precedences) <= SLEN */
434     char *bp, *prec, *prio;
435 
436     /* empty is ok */
437     if (h->inpval[0] == '\0')
438 	return 0;
439 
440     /* if there are no restrictions on precedence then anything is ok */
441     if (allowed_precedences[0] == '\0')
442 	return 0;
443 
444     /* the "allowed_precedences[]" format is: */
445     /*   precedence[:priority-value] precedence[:priority-value] ... */
446     bp = strcpy(buf, allowed_precedences);
447     while ((prec = strtok(bp, " \t\n")) != NULL) {
448 	bp = NULL;
449 	if ((prio = index(prec, ':')) != NULL)
450 	    *prio++ = '\0';
451 	if (istrcmp(prec, h->inpval) == 0)
452 	    break;
453     }
454 
455     /* see if we reached the end of the list without a match */
456     if (prec == NULL) {
457 	CenterLine(ERROR_LINE, catgets(elm_msg_cat, ElmSet,
458 	    ElmHdrmenuPrecedenceBadValue,
459 	    "Unknown precedence value specified."));
460 	h->inpval[0] = '\0';
461 	return -1;
462     }
463 
464     /* see if this precedence has an associated priority */
465     if (prio != NULL && hmenu_priority.inpval[0] == '\0') {
466 	(void) strcpy(hmenu_priority.inpval, prio);
467 	hdrmenu_put(&hmenu_priority, FALSE);
468     }
469 
470     return 0;
471 }
472 
473 
474 /*
475  * Process the user-defined header.  The value entered by the user is
476  * verified for proper format.  If an error occurs a message is printed,
477  * the expanded value is cleared out, and a -1 is returned.
478  */
hdrproc_userhdr(h)479 static int hdrproc_userhdr(h)
480 struct hdr_menu_item *h;
481 {
482     char *s;
483 
484     /* empty is ok */
485     if (h->inpval[0] == '\0')
486 	return 0;
487 
488     /* make sure the header name doesn't begin with some strange character */
489     if (!isalnum(h->inpval[0])) {
490 	CenterLine(ERROR_LINE, catgets(elm_msg_cat, ElmSet,
491 	    ElmHdrmenuUserdefNotAlnum,
492 	    "The user-defined header must begin with a letter or number."));
493 	h->inpval[0] = '\0';
494 	return -1;
495     }
496 
497     /* locate the end of the header name */
498     for (s = h->inpval ; *s != ':' && isprint(*s) && !isspace(*s) ; ++s)
499 	;
500 
501     /* there needs to be a colon at the end of the header name */
502     if (*s != ':') {
503 	CenterLine(ERROR_LINE, catgets(elm_msg_cat, ElmSet,
504 	    ElmHdrmenuUserdefMissingColon,
505 	    "The user-defined header must have a colon after the field name."));
506 	h->inpval[0] = '\0';
507 	return -1;
508     }
509 
510     return 0;
511 }
512 
513 
514 /*
515  * Prompt the user to domainize a header.
516  */
domainize_submenu()517 static void domainize_submenu()
518 {
519     int c;
520     struct hdr_menu_item *h;
521 
522     CenterLine(INSTRUCT_LINE, catgets(elm_msg_cat, ElmSet,
523 	ElmHdrmenuDomInstruct,
524 	"Select header to domainize:  T)o, C)c, B)cc, or <return>."));
525     PutLine0(INPUT_LINE, 0, catgets(elm_msg_cat, ElmSet,
526 	ElmHdrmenuDomPrompt, "Domainize choice: "));
527 
528     for (;;) {
529 
530 	c = ReadCh();
531 
532 	switch (tolower(c)) {
533 	case 't':
534 	    h = &hmenu_to;
535 	    break;
536 	case 'c':
537 	    h = &hmenu_cc;
538 	    break;
539 	case 'b':
540 	    h = &hmenu_bcc;
541 	    break;
542 	case '\r':
543 	case '\n':
544 	    h = NULL;
545 	    break;
546 	default:
547 	    Beep();
548 	    continue;
549 	}
550 
551 	if (h != NULL) {
552 	    domainize(h->expval);
553 	    hdrmenu_put(h, FALSE);
554 	}
555 	hdrmenu_clear_promptarea();
556 	break;
557 
558     }
559 
560 }
561 
562 
563 
domainize(addresses)564 static void domainize(addresses)
565 char *addresses;
566 {
567 	/*** Convert the given addresses from bang paths to domain format.
568 	     This policy amounts to Rabid Rerouting.  However, since it's
569 	     under the sender's control, I don't mind. ***/
570 
571 	char buffer[VERY_LONG_STRING];
572 	char *a, *d;
573 	int how_many;
574 
575 	strcpy(buffer, addresses);
576 	a = buffer;
577 	d = addresses;
578 	how_many = 0;
579 
580 	for (;;) {
581 	  while (*a == ' ' || *a == ',')
582 	    ++a;
583 	  if (*a == '\0')
584 	    break;
585 
586 	  if (*a == '(' || *a == '"') {
587 	    int endch;
588 
589 	    if (d != addresses)
590 	      *d++ = ' ';
591 	    endch = (*a == '(') ? ')' : '"';
592 	    while (*a && *a != endch) {
593 	      if (*a == '\\' && *(a + 1))
594 		*d++ = *a++;
595 	      *d++ = *a++;
596 	    }
597 	    if (*a)
598 	      *d++ = *a++;
599 	  }
600 	  else {
601 	    char *addr;
602 
603 	    if (how_many) {
604 	      *d++ = ',';
605 	      *d++ = ' ';
606 	    }
607 	    ++how_many;
608 
609 	    if (*a == '<') {
610 	      *d++ = *a++;
611 	      addr = a;
612 	      while (*a && *a != '>') {
613 		if (*a == '\\' && *(a + 1))
614 		  ++a;
615 		++a;
616 	      }
617 	      if (*a)
618 		*a++ = '\0';
619 	      domainize_addr(addr, d);
620 	      d += strlen(d);
621 	      *d++ = '>';
622 	    }
623 	    else {
624 	      addr = a;
625 	      while (*a && *a != ' ' && *a != ',')
626 		++a;
627 	      if (*a)
628 		*a++ = '\0';
629 	      domainize_addr(addr, d);
630 	      d += strlen(d);
631 	    }
632 	  }
633 	}
634 
635 	*d = '\0';
636 }
637 
domainize_addr(src,dest)638 static void domainize_addr(src, dest)
639 char *src, *dest;
640 {
641 	/*** Convert one address to domain form. ***/
642 
643 	char *locpart, *host;
644 
645 	if (index(src, '@') != NULL
646 	 || (locpart = rindex(src, '!')) == NULL) {
647 	  strcpy(dest, src);
648 	  return;
649 	}
650 
651 	*locpart++ = '\0';
652 
653 	if ((host = rindex(src, '!')) != NULL)
654 	  ++host;
655 	else
656 	  host = src;
657 	sprintf(dest, "%s@%s", locpart, host);
658 	if (!index(host, '.'))
659 	  strcat(dest, ".uucp");
660 
661 	*--locpart = '!';
662 }
663 
664 
show_msg_headers(shdr,cmds)665 PUBLIC int show_msg_headers(shdr, cmds)
666 const SEND_HEADER *shdr;
667 const char *cmds;
668 {
669     int max_lineno, c, i;
670     struct hdr_menu_item *h;
671 
672     max_lineno = 0;
673 
674     /* copy message header into private buffer */
675     bcopy((char *)shdr, (char *)&H, sizeof(SEND_HEADER));
676 
677     /* expand out all of the header values */
678     /* menu displays expanded values, user edits unexpended versions */
679     for (h = hmenu_item_list ; h->menucmd > 0 ; ++h) {
680 	if (h->hdrproc != NULL)
681 	    (*h->hdrproc)(h);
682     }
683 
684     for (h = hmenu_item_list ; h->menucmd > 0 ; ++h) {
685 	if (index(cmds, h->menucmd) != NULL) {
686 	    hdrmenu_put(h, TRUE);
687 	    if (h->lineno > max_lineno)
688 		max_lineno = h->lineno;
689 	}
690     }
691 
692     return max_lineno;
693 }
694 
695 
edit_header_char(shdr,c)696 PUBLIC int edit_header_char(shdr, c)
697 SEND_HEADER *shdr;
698 int c;
699 {
700     struct hdr_menu_item *h;
701 
702     for (h = hmenu_item_list ; h->menucmd > 0 ; ++h) {
703 	if (h->menucmd == c) {
704 	    bcopy((char *)shdr, (char *)&H, sizeof(SEND_HEADER));
705 	    if (hdrmenu_get(h) != 0)
706 		return FALSE;
707 	    hdrmenu_put(h, FALSE);
708 	    bcopy((char *)&H, (char *)shdr, sizeof(SEND_HEADER));
709 	    return TRUE;
710 	}
711     }
712     return FALSE;
713 }
714