xref: /titanic_52/usr/src/cmd/genmsg/util.c (revision 10db1377dafab8ba3feedef26db9c5d8539a5cd1)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 1995 by Sun Microsystems, Inc.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <stdio.h>
29 #include <limits.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdarg.h>
33 #include <libintl.h>
34 #include <locale.h>
35 #include <libgen.h>
36 #include <ctype.h>
37 #include <unistd.h>
38 #include <signal.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 
42 #include "genmsg.h"
43 
44 #define	SET_TOKEN	"$set"
45 #define	DELSET_TOKEN	"$delset"
46 #define	QUOTE_TOKEN	"$quote"
47 
48 #define	SkipSpace(s)	while (*(s) == ' ' || *(s) == '\t') s++
49 
50 extern char *program;		/* from main.c */
51 extern char *mctag;		/* from main.c */
52 extern char *sctag;		/* from main.c */
53 extern char *premsg;		/* from main.c */
54 extern char *sufmsg;		/* from main.c */
55 extern int suppress_error;	/* from main.c */
56 extern void warning(char *);	/* from genmsg.l */
57 
58 typedef struct _SetID *SetID;
59 typedef struct _MsgID *MsgID;
60 
61 typedef struct _SetID SetIDRec;
62 struct _SetID {
63 	int id;
64 	char *comment;
65 	MsgID top;
66 	SetID next;
67 };
68 
69 typedef struct _MsgID MsgIDRec;
70 struct _MsgID {
71 	int no_write;
72 	int id;
73 	char *msg;
74 	int line;
75 	char *file;
76 	char *comment;
77 	MsgID next;
78 };
79 
80 
81 /* Top pointer of the setid list. */
82 static SetID setid_top;
83 
84 /* comment for messages. */
85 static char *msg_comment;
86 
87 /* comment for set numbers. */
88 static char *set_comment;
89 
90 /* List of set number's maximum message numbers. */
91 static int msgid_table[NL_SETMAX+1];
92 
93 /* Quote character to surround messages. */
94 static char quote = QUOTE;
95 
96 /* Internal functions. */
97 static void add_msgid(SetID, int, char *, char *, int, int);
98 static void add_setid(int, int, char *, char *, int, int);
99 static SetID lookup_setid(int);
100 static MsgID lookup_msgid(SetID, int, char *, char *, int);
101 static void print_prefix(FILE *, char *, int, char *);
102 static int is_bs_terminated(char *);
103 static char *ustrdup(char *);
104 static void cat_msg(char *, char *, int);
105 static void makeup_msg(char **);
106 
107 void
108 add_msg(int setid, int msgid, char *msg, char *file, int line, int no_write)
109 {
110 	SetID si;
111 
112 	if (si = lookup_setid(setid)) {
113 		if (lookup_msgid(si, msgid, msg, file, line)) {
114 			return; /* we already have the one. */
115 		} else {
116 			add_msgid(si, msgid, msg, file, line, no_write);
117 		}
118 	} else {
119 		add_setid(setid, msgid, msg, file, line, no_write);
120 	}
121 }
122 
123 int
124 is_writable(char *file)
125 {
126 	struct stat buf;
127 
128 	if (stat(file, &buf) == -1)
129 		return (TRUE);
130 
131 	if (access(file, W_OK) == 0)
132 		return (TRUE);
133 
134 	return (FALSE);
135 }
136 
137 void
138 write_msgfile(char *file)
139 {
140 	FILE *fp;
141 	SetID si = setid_top;
142 	char *mode = "w";
143 	char pquote[2];
144 
145 	if (is_writable(file) == FALSE) {
146 		prg_err(gettext("cannot create \"%s\": permission denied"),
147 			file);
148 		return;
149 	}
150 
151 	if (IsActiveMode(AppendMode)) {
152 		mode = "a";
153 	}
154 
155 	if ((fp = fopen(file, mode)) == NULL) {
156 		prg_err(gettext("cannot create \"%s\""), file);
157 		return;
158 	}
159 
160 	if (quote) {
161 		(void) sprintf(pquote, "%c", quote);
162 	} else {
163 		(void) sprintf(pquote, "");
164 	}
165 
166 	/* AppendMode is already turned off if the file doesn't exist. */
167 	if (!IsActiveMode(AppendMode)) {
168 		(void) fprintf(fp, "\n$quote %s\n\n", pquote);
169 	}
170 
171 	while (si) {
172 		int is_set = FALSE;
173 		MsgID mi = si->top;
174 		while (mi) {
175 			char msg[NL_TEXTMAX+32]; /* 32 is some other stuff. */
176 
177 			if (mi->no_write) {
178 				mi = mi->next;
179 				continue;
180 			}
181 			if (is_set == FALSE) {
182 				if (si->comment &&
183 					!IsActiveMode(BackCommentMode)) {
184 					(void) fprintf(fp, "\n");
185 					print_prefix(fp, "$ ", TRUE,
186 							si->comment);
187 					(void) fprintf(fp, "$set\t%d\n",
188 							si->id);
189 				} else {
190 					(void) fprintf(fp, "\n$set\t%d\n",
191 							si->id);
192 				}
193 				if (si->comment &&
194 					IsActiveMode(BackCommentMode)) {
195 					print_prefix(fp, "$ ", TRUE,
196 							si->comment);
197 				}
198 				(void) fprintf(fp, "\n");
199 				is_set = TRUE;
200 			}
201 
202 			makeup_msg(&(mi->msg));
203 
204 			(void) sprintf(msg, "%d\t%s%s%s\n",
205 				mi->id, pquote, mi->msg, pquote);
206 
207 			if (!IsActiveMode(BackCommentMode)) {
208 				if (mi->line && mi->file &&
209 					IsActiveMode(LineInfoMode)) {
210 					(void) fprintf(fp,
211 						"$ File:%s, line:%d\n",
212 						basename(mi->file), mi->line);
213 				}
214 
215 				if (mi->comment) {
216 					print_prefix(fp, "$ ", TRUE,
217 						mi->comment);
218 				}
219 
220 				if (IsActiveMode(DoubleLineMode)) {
221 					print_prefix(fp, "$ ", FALSE, msg);
222 				}
223 			}
224 
225 			(void) fprintf(fp, "%s", msg);
226 
227 			if (IsActiveMode(BackCommentMode)) {
228 				if (mi->line && mi->file &&
229 					IsActiveMode(LineInfoMode)) {
230 					(void) fprintf(fp,
231 						"$ File:%s, line:%d\n",
232 						basename(mi->file), mi->line);
233 				}
234 
235 				if (mi->comment) {
236 					print_prefix(fp, "$ ", TRUE,
237 						mi->comment);
238 				}
239 
240 				if (IsActiveMode(DoubleLineMode)) {
241 					print_prefix(fp, "$ ", FALSE, msg);
242 				}
243 			}
244 
245 			(void) fprintf(fp, "\n");
246 
247 			mi = mi->next;
248 		}
249 		si = si->next;
250 	}
251 
252 	(void) fclose(fp);
253 }
254 
255 static SetID
256 lookup_setid(int id)
257 {
258 	SetID si = setid_top;
259 	while (si) {
260 		if (si->id == id) {
261 			return (si);
262 		}
263 		si = si->next;
264 	}
265 	return (NULL);
266 }
267 
268 static MsgID
269 lookup_msgid(SetID si, int msgid, char *msg, char *file, int line)
270 {
271 	MsgID mi = si->top;
272 	while (mi) {
273 		if (mi->id == msgid) {
274 			/* same setid & msgid, but different msg. */
275 			if (strcmp(mi->msg, msg)) {
276 			src_err(file, line,
277 		gettext("multiple messages: set number %d, message number %d\n"
278 			"	current : \"%s\"\n"
279 			"	previous: \"%s\" : \"%s\", line %d"),
280 				si->id, mi->id,
281 				msg,
282 				mi->msg, mi->file, mi->line);
283 			}
284 			return (mi);
285 		}
286 		mi = mi->next;
287 	}
288 	return (NULL);
289 }
290 
291 static void
292 add_msgid(SetID si, int msgid, char *msg, char *file, int line, int no_write)
293 {
294 	MsgID mi = si->top, newmi, prev = NULL;
295 	int len = strlen(msg);
296 
297 	if (msgid == 0) {
298 		src_err(file, line, gettext("improper message number: %d"),
299 			msgid);
300 		return;
301 	}
302 
303 	if (msgid > NL_MSGMAX) {
304 		src_err(file, line, gettext("too large message number: %d"),
305 			msgid);
306 		return;
307 	}
308 
309 	if (len > NL_TEXTMAX) {
310 		src_err(file, line, gettext("too long message text"));
311 		return;
312 	}
313 
314 	while (mi) {
315 		if (mi->id > msgid) {
316 			break;
317 		}
318 		prev = mi;
319 		mi = mi->next;
320 	}
321 
322 	if ((newmi = (MsgID) malloc(sizeof (MsgIDRec))) == NULL) {
323 		prg_err(gettext("fatal: out of memory"));
324 		exit(EXIT_FAILURE);
325 	}
326 
327 	newmi->no_write = no_write;
328 	newmi->id = msgid;
329 	newmi->msg = (char *) ustrdup(msg);
330 	newmi->file = (char *) ustrdup(file);
331 	newmi->line = line;
332 	newmi->next = mi;
333 
334 	if (msg_comment) {
335 		newmi->comment = (char *) ustrdup(msg_comment);
336 		free(msg_comment);
337 		msg_comment = NULL;
338 	} else {
339 		newmi->comment = NULL;
340 	}
341 
342 	if (prev == NULL) {
343 		si->top = newmi;
344 	} else {
345 		prev->next = newmi;
346 	}
347 }
348 
349 static void
350 add_setid(int setid, int msgid, char *msg, char *file, int line, int no_write)
351 {
352 	SetID si = setid_top, newsi, prev = NULL;
353 
354 	while (si) {
355 		if (si->id > setid) {
356 			break;
357 		}
358 		prev = si;
359 		si = si->next;
360 	}
361 
362 	if ((newsi = (SetID) malloc(sizeof (SetIDRec))) == NULL) {
363 		prg_err(gettext("fatal: out of memory"));
364 		exit(EXIT_FAILURE);
365 	}
366 
367 	newsi->id = setid;
368 	newsi->top = NULL;
369 	newsi->next = si;
370 
371 	if (set_comment) {
372 		newsi->comment = (char *) ustrdup(set_comment);
373 		free(set_comment);
374 		set_comment = NULL;
375 	} else {
376 		newsi->comment = NULL;
377 	}
378 
379 	if (prev == NULL) {
380 		setid_top = newsi;
381 	} else {
382 		prev->next = newsi;
383 	}
384 
385 	add_msgid(newsi, msgid, msg, file, line, no_write);
386 }
387 
388 static void
389 print_prefix(FILE *fp, char *prefix, int rm_blank, char *str)
390 {
391 	(void) fprintf(fp, "%s", prefix);
392 	while (*str) {
393 		(void) fputc(*str, fp);
394 		if (*str == '\n' && *(str+1) != '\0') {
395 			(void) fprintf(fp, "%s", prefix);
396 			if (rm_blank == TRUE) {
397 				str++;
398 				SkipSpace(str);
399 				continue;
400 			}
401 		}
402 		str++;
403 	}
404 	if (*(str-1) != '\n') {
405 		(void) fputc('\n', fp);
406 	}
407 }
408 
409 int
410 read_projfile(char *file)
411 {
412 	FILE *fp;
413 	char line[LINE_MAX];
414 
415 	if (!file) {
416 		return (0);
417 	}
418 
419 	if ((fp = fopen(file, "r")) == NULL) {
420 		return (0);
421 	}
422 
423 	while (fgets(line, LINE_MAX, fp) != NULL) {
424 		char *p = line;
425 		int n, setid, msgid;
426 
427 		SkipSpace(p);
428 
429 		if (*p == '#' || *p == '\n') {
430 			continue;
431 		}
432 
433 		n = sscanf(p, "%d %d", &setid, &msgid);
434 
435 		if (n == 2) {
436 			if (setid > NL_SETMAX) {
437 			prg_err(gettext("%s: too large set number: %d"),
438 					file, setid);
439 				continue;
440 			}
441 			msgid_table[setid] = msgid;
442 		} else {
443 			prg_err(
444 			/* for stupid cstyle */
445 			gettext("warning: %s: missing or invalid entry"),
446 				file);
447 		}
448 	}
449 
450 	(void) fclose(fp);
451 
452 	return (1);
453 }
454 
455 void
456 write_projfile(char *file)
457 {
458 	FILE *fp;
459 	register int i;
460 
461 	if (is_writable(file) == FALSE) {
462 		prg_err(gettext("cannot create \"%s\": permission denied"),
463 			file);
464 		return;
465 	}
466 
467 	if ((fp = fopen(file, "w")) == NULL) {
468 		prg_err(gettext("cannot create \"%s\""), file);
469 		return;
470 	}
471 
472 	for (i = 1; i <= NL_SETMAX; i++) {
473 		if (msgid_table[i] > 0) {
474 			SetID si;
475 			char *com = NULL;
476 
477 			if (IsActiveMode(SetCommentMode) &&
478 				(si = lookup_setid(i)) && si->comment) {
479 				com = si->comment;
480 			}
481 
482 			if (com && !IsActiveMode(BackCommentMode)) {
483 				print_prefix(fp, "# ", TRUE, com);
484 			}
485 
486 			(void) fprintf(fp, "%d\t%d\n", i, msgid_table[i]);
487 
488 			if (com && IsActiveMode(BackCommentMode)) {
489 				print_prefix(fp, "# ", TRUE, com);
490 			}
491 		}
492 	}
493 
494 	(void) fclose(fp);
495 }
496 
497 int
498 get_msgid(char *file, int line, int setid, char *str)
499 {
500 	SetID si = setid_top;
501 	int id = msgid_table[setid];
502 
503 	while (si) {
504 		if (si->id == setid) {
505 			MsgID mi = si->top;
506 			while (mi) {
507 				if (strcmp(mi->msg, str) == 0) {
508 					return (mi->id);
509 				}
510 				mi = mi->next;
511 			}
512 		}
513 		si = si->next;
514 	}
515 
516 	id++;
517 
518 	if (id > NL_MSGMAX) {
519 		src_err(file, line,
520 			gettext("run out of message number in set number: %d"),
521 			setid);
522 		return (NOMSGID);
523 	}
524 
525 	return (msgid_table[setid] = id);
526 }
527 
528 void
529 set_msgid(int setid, int msgid)
530 {
531 	if (msgid_table[setid] < msgid) {
532 		msgid_table[setid] = msgid;
533 	}
534 }
535 
536 void
537 add_comment(Mode mode, char *str)
538 {
539 	char *tag = (mode == MsgCommentMode) ? mctag : sctag;
540 	char **comment = (mode == MsgCommentMode)
541 				? &msg_comment : &set_comment;
542 
543 	if (!strstr(str, tag)) {
544 		return;
545 	}
546 
547 	if (*comment) {
548 		free(*comment);
549 	}
550 
551 	*comment = (char *) ustrdup(str);
552 }
553 
554 void
555 read_msgfile(char *file)
556 {
557 	FILE *fp;
558 	char c = 0;
559 	int line = 0;
560 	int inmsg = FALSE;
561 	int setid = 0, unsetid = -1, msgid = 0;
562 	struct stat buf;
563 
564 	if ((fp = fopen(file, "r")) == NULL) {
565 		prg_err(gettext("cannot open \"%s\""), file);
566 		ResetActiveMode(AppendMode);
567 		return;
568 	}
569 
570 	if (stat(file, &buf) == -1 && buf.st_size == 0) {
571 		ResetActiveMode(AppendMode);
572 		return;
573 	}
574 
575 	quote = c;
576 
577 	/*CONSTCOND*/
578 	while (1) {
579 		char buf[LINE_MAX];
580 		char *ptr;
581 		char msg[NL_TEXTMAX];
582 
583 		if (fgets(buf, sizeof (buf), fp) == NULL) {
584 			break;
585 		}
586 
587 		line++;
588 
589 		ptr = &buf[0];
590 
591 		SkipSpace(ptr);
592 
593 		if ((*ptr == '$' && (*(ptr+1) == ' ' || *(ptr+1) == '\t')) ||
594 			((*ptr == '\n') && inmsg == FALSE)) {
595 			inmsg = FALSE;
596 			continue;
597 		}
598 
599 		if (strncmp(ptr, SET_TOKEN, sizeof (SET_TOKEN) - 1) == 0) {
600 			if (sscanf(ptr, "%*s %d", &setid) != 1) {
601 				setid = 0;
602 			}
603 			inmsg = FALSE;
604 			continue;
605 		} else if (strncmp(ptr, DELSET_TOKEN,
606 			sizeof (DELSET_TOKEN) - 1) == 0) {
607 			if (sscanf(ptr, "%*s %d", &unsetid) != 1) {
608 				unsetid = -1;
609 			}
610 			inmsg = FALSE;
611 			continue;
612 		} else if (strncmp(ptr, QUOTE_TOKEN,
613 			sizeof (QUOTE_TOKEN) - 1) == 0) {
614 			if (sscanf(ptr, "%*s %c", &c) != 1) {
615 				c = 0;
616 			}
617 			quote = c;
618 			inmsg = FALSE;
619 			continue;
620 		}
621 
622 		if (setid == unsetid) {
623 			continue;
624 		}
625 
626 		if (inmsg) {
627 			if (is_bs_terminated(ptr)) {
628 				(void) strcat(msg, ptr);
629 				inmsg = TRUE;
630 			} else {
631 				int len = strlen(ptr);
632 				*(ptr + len - 1) = '\0';
633 				if (c && (*(ptr + len - 2) == c)) {
634 					*(ptr + len - 2) = '\0';
635 				}
636 				(void) strcat(msg, ptr);
637 				add_msg(setid, msgid, msg, file, line, TRUE);
638 				inmsg = FALSE;
639 			}
640 			continue;
641 		}
642 
643 		if (isdigit(*ptr)) {
644 			char tmp[32];
645 			int i = 0;
646 
647 			SkipSpace(ptr);
648 
649 			while (isdigit(*ptr)) {
650 				tmp[i++] = *ptr++;
651 			}
652 			tmp[i] = '\0';
653 			msgid = atoi(tmp);
654 
655 			SkipSpace(ptr);
656 
657 			if (is_bs_terminated(ptr)) {
658 				(void) memset(msg, 0, NL_TEXTMAX);
659 				if (c && (*ptr == c)) {
660 					ptr++;
661 				}
662 				(void) strcpy(msg, ptr);
663 				inmsg = TRUE;
664 			} else {
665 				int len = strlen(ptr);
666 				*(ptr + len - 1) = '\0';
667 				if (c && ((*ptr == c) &&
668 					(*(ptr + len - 2) == c))) {
669 					*(ptr + len - 2) = '\0';
670 					ptr++;
671 				}
672 				add_msg(setid, msgid, ptr, file, line, TRUE);
673 				inmsg = FALSE;
674 			}
675 		}
676 	}
677 
678 	(void) fclose(fp);
679 }
680 
681 static int
682 is_bs_terminated(char *msg)
683 {
684 	int len = strlen(msg);
685 
686 	while (--len >= 0) {
687 		if (msg[len] == ' ' || msg[len] == '\t' || msg[len] == '\n') {
688 			continue;
689 		} else if (msg[len] == '\\') {
690 			len--;
691 			if (len >= 0 && msg[len] == '\\')
692 				return (0);
693 			return (1);
694 		} else {
695 			return (0);
696 		}
697 	}
698 	return (0);
699 }
700 
701 static char *
702 ustrdup(char *str)
703 {
704 	char *tmp = strdup(str);
705 	if (!tmp) {
706 		prg_err(gettext("fatal: out of memory"));
707 		exit(EXIT_FAILURE);
708 	}
709 	return (tmp);
710 }
711 
712 int
713 file_copy(char *in, char *out)
714 {
715 	int ret = TRUE;
716 	FILE *fin, *fout;
717 	int c;
718 	sigset_t newmask, oldmask;
719 
720 	(void) sigemptyset(&newmask);
721 	(void) sigaddset(&newmask, SIGQUIT);
722 	(void) sigaddset(&newmask, SIGINT);
723 	(void) sigaddset(&newmask, SIGHUP);
724 	(void) sigaddset(&newmask, SIGTERM);
725 	(void) sigprocmask(SIG_BLOCK, &newmask, &oldmask);
726 
727 	if ((fin = fopen(in, "r")) == NULL) {
728 		prg_err(gettext("cannot open \"%s\""), in);
729 		ret = FALSE;
730 		goto done;
731 	}
732 
733 	if ((fout = fopen(out, "w")) == NULL) {
734 		prg_err(gettext("cannot create \"%s\""), out);
735 		ret = FALSE;
736 		goto done;
737 	}
738 
739 	while ((c = getc(fin)) != EOF)
740 		(void) putc(c, fout);
741 
742 	(void) fclose(fin);
743 	(void) fclose(fout);
744 
745 done:
746 	(void) sigprocmask(SIG_SETMASK, &oldmask, NULL);
747 	return (ret);
748 }
749 
750 static void
751 cat_msg(char *out, char *msg, int max)
752 {
753 	if (strlen(out) > max) {
754 		return;
755 	}
756 
757 	(void) strncat(out, msg, max);
758 
759 	if (strlen(out) > max) {
760 		out[max] = '\0';
761 	}
762 }
763 
764 static void
765 makeup_msg(char **pmsg)
766 {
767 	char buf[NL_TEXTMAX];
768 	char *msg;
769 
770 	msg = *pmsg;
771 	(void) memset((void *) buf, 0, NL_TEXTMAX);
772 
773 	if (IsActiveMode(TripleMode) &&	!strchr(msg, '%')) {
774 		/* there is no '%' in message. */
775 		int len = strlen(msg);
776 
777 		if (msg[len-2] == '\\' && msg[len-1] == 'n') {
778 			msg[len-2] = '\0';
779 			cat_msg(buf, msg, (NL_TEXTMAX - 2));
780 			cat_msg(buf, msg, (NL_TEXTMAX - 2));
781 			cat_msg(buf, msg, (NL_TEXTMAX - 2));
782 			cat_msg(buf, "\\n", NL_TEXTMAX);
783 		} else {
784 			cat_msg(buf, msg, NL_TEXTMAX);
785 			cat_msg(buf, msg, NL_TEXTMAX);
786 			cat_msg(buf, msg, NL_TEXTMAX);
787 		}
788 		free(msg);
789 		*pmsg = (char *) ustrdup(buf);
790 	}
791 
792 	msg = *pmsg;
793 	(void) memset((void *) buf, 0, NL_TEXTMAX);
794 
795 	if (IsActiveMode(PrefixMode)) {
796 		cat_msg(buf, premsg, NL_TEXTMAX);
797 		cat_msg(buf, msg, NL_TEXTMAX);
798 		free(msg);
799 		*pmsg = (char *) ustrdup(buf);
800 	}
801 
802 	msg = *pmsg;
803 	(void) memset((void *) buf, 0, NL_TEXTMAX);
804 
805 	if (IsActiveMode(SuffixMode)) {
806 		int len = strlen(msg);
807 
808 		if (msg[len-2] == '\\' && msg[len-1] == 'n') {
809 			msg[len-2] = '\0';
810 			cat_msg(buf, msg, (NL_TEXTMAX - 2));
811 			cat_msg(buf, sufmsg, (NL_TEXTMAX - 2));
812 			cat_msg(buf, "\\n", NL_TEXTMAX);
813 		} else {
814 			cat_msg(buf, msg, NL_TEXTMAX);
815 			cat_msg(buf, sufmsg, NL_TEXTMAX);
816 		}
817 		free(msg);
818 		*pmsg = (char *) ustrdup(buf);
819 	}
820 }
821 
822 void
823 prg_err(char *fmt, ...)
824 {
825 	va_list ap;
826 	char buf[BUFSIZ];
827 
828 	va_start(ap, fmt);
829 
830 	(void) vsprintf(buf, fmt, ap);
831 
832 	(void) fprintf(stderr, "%s: %s\n", program, buf);
833 
834 	va_end(ap);
835 }
836 
837 void
838 src_err(char *file, int line, char *fmt, ...)
839 {
840 	va_list ap;
841 	char buf[BUFSIZ];
842 	char sbuf[BUFSIZ/2];
843 	char vbuf[BUFSIZ/2];
844 
845 	if (suppress_error == TRUE) {
846 		return;
847 	}
848 
849 	va_start(ap, fmt);
850 
851 	(void) sprintf(sbuf, gettext("\"%s\", line %d: "), file, line);
852 	(void) vsprintf(vbuf, fmt, ap);
853 
854 	(void) sprintf(buf, "%s%s\n", sbuf, vbuf);
855 	(void) fputs(buf, stderr);
856 
857 	va_end(ap);
858 }
859