1
2 /**
3 * @file expFormat.c
4 *
5 * This module implements formatting expression functions.
6 *
7 * @addtogroup autogen
8 * @{
9 */
10 /*
11 * This file is part of AutoGen.
12 * AutoGen Copyright (C) 1992-2018 by Bruce Korb - all rights reserved
13 *
14 * AutoGen is free software: you can redistribute it and/or modify it
15 * under the terms of the GNU General Public License as published by the
16 * Free Software Foundation, either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * AutoGen is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
22 * See the GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License along
25 * with this program. If not, see <http://www.gnu.org/licenses/>.
26 */
27
28 typedef enum {
29 LSEG_INFO = 1,
30 LSEG_DESC = 2,
31 LSEG_FULL = 3,
32 LSEG_NAME = 4
33 } lic_segment_e_t;
34
35 /**
36 * Remove horizontal white space at the ends of lines.
37 * "dne" and licensing text passes through this before
38 * making an SCM out of the result.
39 *
40 * @param[in,out] text the text to work on.
41 */
42 static void
trim_trailing_white(char * text)43 trim_trailing_white(char * text)
44 {
45 char * start = text++;
46 if (*start == NUL)
47 return;
48
49 for (;;) {
50 switch (*text++) {
51 case NUL:
52 return;
53
54 case NL:
55 if (IS_HORIZ_WHITE_CHAR(text[-2]))
56 goto doit;
57 default:
58 break;
59 }
60 }
61
62 doit:
63 start = SPN_HORIZ_WHITE_BACK(start, text - 2);
64 *(start++) = NL;
65
66 char * dest = start;
67
68 for (;;) {
69 switch (*(dest++) = *(text++)) {
70 case NUL:
71 return;
72
73 case NL:
74 if (IS_HORIZ_WHITE_CHAR(dest[-2])) {
75 dest = SPN_HORIZ_WHITE_BACK(start, dest - 2);
76 start = dest;
77 *(dest++) = NL;
78 }
79
80 default:
81 break;
82 }
83 }
84 }
85
86 /*=gfunc dne
87 *
88 * what: '"Do Not Edit" warning'
89 *
90 * exparg: prefix, string for starting each output line
91 * exparg: first_prefix, for the first output line, opt
92 * exparg: optpfx, shifted prefix, opt
93 *
94 * doc:
95 * Generate a "DO NOT EDIT" or "EDIT WITH CARE" warning string.
96 * Which depends on whether or not the @code{--writable} command line
97 * option was set.
98 *
99 * The first argument may be an option: @samp{-D} or @samp{-d}, causing the
100 * second and (potentially) third arguments to be interpreted as the first
101 * and second arguments. The only useful option is @samp{-D}:
102 *
103 * @table @samp
104 * @item -D
105 * will add date, timestamp and version information.
106 * @item -d
107 * is ignored, but still accepted for compatibility with older versions
108 * of the "dne" function where emitting the date was the default.
109 * @end table
110 *
111 * If one of these options is specified, then the "prefix" and "first"
112 * arguments are obtained from the following arguments. The presence (or
113 * absence) of this option can be overridden with the environment variable,
114 * @samp{AUTOGEN_DNE_DATE}. The date is disabled if the value is empty or
115 * starts with one of the characters, @samp{0nNfF} -- zero or the first
116 * letter of "no" or "false".
117 *
118 * The @code{prefix} argument is a per-line string prefix. The optional
119 * second argument is a prefix for the first line only and, in read-only
120 * mode, activates editor hints.
121 *
122 * @example
123 * -*- buffer-read-only: t -*- vi: set ro:
124 * @end example
125 *
126 * @noindent
127 * The warning string also includes information about the template used
128 * to construct the file and the definitions used in its instantiation.
129 =*/
130 SCM
ag_scm_dne(SCM prefix,SCM first,SCM opt)131 ag_scm_dne(SCM prefix, SCM first, SCM opt)
132 {
133 char const * date_str;
134 char const * pzFirst;
135 char const * pzPrefix;
136
137 if (! scm_is_string(prefix))
138 return SCM_UNDEFINED;
139
140 date_str = zNil;
141 pzFirst = zNil;
142
143 {
144 size_t pfxLen = scm_c_string_length(prefix);
145 pzPrefix = ag_scm2zchars(prefix, "dne-prefix");
146
147 /*
148 * Check for a -d option (ignored) or a -D option (emit date)
149 * by default, "dne" will not emit a date in the output.
150 */
151 if ((pfxLen == 2) && (*pzPrefix == '-')) {
152 switch (pzPrefix[1]) {
153 case 'D':
154 date_str = NULL;
155 pzPrefix = ag_scm2zchars(first, "dne-prefix");
156 first = opt;
157 break;
158
159 case 'd':
160 pzPrefix = ag_scm2zchars(first, "dne-prefix");
161 first = opt;
162 break;
163 }
164 }
165 }
166
167 do {
168 char const * pz = getenv("AUTOGEN_DNE_DATE");
169 if (pz == NULL) break; /* use selection from template */
170
171 switch (*pz) {
172 case NUL:
173 case '0': /* zero */
174 case 'n':
175 case 'N': /* no */
176 case 'f':
177 case 'F': /* false */
178 date_str = zNil; /* template user says "no DNE date" */
179 break;
180
181 default:
182 date_str = NULL; /* template user says "INCLUDE DNE date" */
183 }
184 } while (0);
185
186 /*
187 * IF we also have a 'first' prefix string,
188 * THEN we set it to something other than ``zNil'' and deallocate later.
189 */
190 if (scm_is_string(first))
191 pzFirst = aprf(ENABLED_OPT(WRITABLE) ? "%s\n" : EXP_FMT_DNE1,
192 ag_scm2zchars(first, "pfx-1"), pzPrefix);
193
194 if (date_str == NULL) {
195 static char const tim_fmt[] =
196 " %B %e, %Y at %r by AutoGen " AUTOGEN_VERSION;
197
198 size_t const tsiz = sizeof(tim_fmt) + sizeof("september") * 2;
199 time_t curTime = time(NULL);
200 struct tm * pTime = localtime(&curTime);
201
202 date_str = scribble_get((ssize_t)tsiz);
203 strftime((char *)date_str, tsiz, tim_fmt, pTime);
204 }
205
206 {
207 char const * pz;
208 out_stack_t * pfp = cur_fpstack;
209 char const * tpl_name = strrchr(tpl_fname, DIRCH);
210 if (tpl_name == NULL)
211 tpl_name = tpl_fname;
212 else
213 tpl_name++;
214
215 while (pfp->stk_flags & FPF_UNLINK) pfp = pfp->stk_prev;
216 if (! ENABLED_OPT(DEFINITIONS))
217 pz = "<<no definitions>>";
218
219 else if (*oops_pfx != NUL)
220 pz = "<<CGI-definitions>>";
221
222 else {
223 pz = OPT_ARG(DEFINITIONS);
224 if (strcmp(pz, "-") == 0)
225 pz = "stdin";
226 }
227
228 pz = aprf(ENABLED_OPT(WRITABLE) ? EXP_FMT_DNE2 : EXP_FMT_DNE,
229 pzPrefix, pfp->stk_fname, date_str,
230 pz, tpl_name, pzFirst);
231 if (pz == NULL)
232 AG_ABEND("Allocating Do-Not-Edit string");
233 trim_trailing_white(C(char *, pz));
234 date_str = pz;
235 }
236
237 /*
238 * Deallocate any temporary buffers. pzFirst either points to
239 * the zNil string, or to an allocated buffer.
240 */
241 if (pzFirst != zNil)
242 AGFREE(pzFirst);
243 {
244 SCM res = scm_from_latin1_string(date_str);
245 AGFREE(date_str);
246
247 return res;
248 }
249 }
250
251
252 /*=gfunc warn
253 *
254 * what: display warning message and continue
255 *
256 * exparg: @ message @ message to display @@
257 * doc:
258 *
259 * The argument is a string that printed out to stderr.
260 * The message is formed from the formatting string:
261 *
262 * @example
263 * @code{WARNING:} %s\n
264 * @end example
265 *
266 * The template processing resumes after printing the message.
267 =*/
268 SCM
ag_scm_warn(SCM res)269 ag_scm_warn(SCM res)
270 {
271 char const * msg = ag_scm2zchars(res, "warn str");
272 if ((msg == NULL) || (*msg == NUL))
273 AG_ABEND("warn called without a message string");
274 fprintf(stderr, WARN_FMT, msg);
275 return SCM_UNDEFINED;
276 }
277
278
279 /*=gfunc error
280 *
281 * what: display message and exit
282 *
283 * exparg: @ message @ message to display before exiting @@
284 * doc:
285 *
286 * The argument is a string that printed out as part of an error
287 * message. The message is formed from the formatting string:
288 *
289 * @example
290 * DEFINITIONS ERROR in %s line %d for %s: %s\n
291 * @end example
292 *
293 * The first three arguments to this format are provided by the
294 * routine and are: The name of the template file, the line within
295 * the template where the error was found, and the current output
296 * file name.
297 *
298 * After displaying the message, the current output file is removed
299 * and autogen exits with the EXIT_FAILURE error code. IF, however,
300 * the argument begins with the number 0 (zero), or the string is the
301 * empty string, then processing continues with the next suffix.
302 =*/
303 SCM
ag_scm_error(SCM res)304 ag_scm_error(SCM res)
305 {
306 char const * msg;
307 tSuccess abrt = FAILURE;
308 char num_bf[16];
309 size_t msg_ln;
310
311 switch (ag_scm_type_e(res)) {
312 case GH_TYPE_BOOLEAN:
313 if (scm_is_false(res))
314 abrt = PROBLEM;
315 msg = zNil;
316 break;
317
318 case GH_TYPE_NUMBER:
319 {
320 unsigned long val = AG_SCM_TO_ULONG(res);
321 if (val == 0)
322 abrt = PROBLEM;
323 snprintf(num_bf, sizeof(num_bf), "%d", (int)val);
324 msg = num_bf;
325 break;
326 }
327
328 case GH_TYPE_CHAR:
329 num_bf[0] = (char)SCM_CHAR(res);
330 if ((num_bf[0] == NUL) || (num_bf[0] == '0'))
331 abrt = PROBLEM;
332 num_bf[1] = NUL;
333 msg = num_bf;
334 break;
335
336 case GH_TYPE_STRING:
337 msg = ag_scm2zchars(res, "error string");
338 msg = SPN_WHITESPACE_CHARS(msg);
339 msg_ln = strlen(msg);
340
341 /*
342 * IF the message starts with the number zero,
343 * OR the message is the empty string,
344 * THEN this is just a warning that is ignored
345 */
346 if (msg_ln == 0)
347 abrt = PROBLEM;
348 else if (IS_DEC_DIGIT_CHAR(*msg) && (strtol(msg, NULL, 0) == 0))
349 abrt = PROBLEM;
350 break;
351
352 default:
353 msg = BAD_MSG_STR;
354 }
355
356 /*
357 * IF there is a message,
358 * THEN print it.
359 */
360 if (*msg != NUL) {
361 char const * typ = (abrt != PROBLEM) ? ERROR_STR : WARN_STR;
362 char * pz = aprf(DEF_NOTE_FMT, typ,
363 current_tpl->td_file, cur_macro->md_line,
364 cur_fpstack->stk_fname, msg);
365 if (abrt != PROBLEM)
366 AG_ABEND(pz);
367 fputs(pz, trace_fp);
368 AGFREE(pz);
369 }
370
371 longjmp(abort_jmp_buf, abrt);
372 /* NOTREACHED */
373 return SCM_UNDEFINED;
374 }
375
376 /**
377 * Assemble the copyright preamble and long license description.
378 *
379 * @param txt a pointer to the first of two newlines separating
380 * copyright information from the description.
381 */
382 static void
assemble_full_desc(char * txt,char const * pfx)383 assemble_full_desc(char * txt, char const * pfx)
384 {
385 char * pd;
386 char * md;
387
388 size_t prefix_len = strlen(pfx) + 1;
389 while ( (prefix_len > 0)
390 && IS_WHITESPACE_CHAR(pfx[prefix_len - 2]))
391 prefix_len--;
392
393 /*
394 * Preserve the first newline. Set the move destination
395 * out past where we will be inserting the "<PFX>\n" marker.
396 */
397 pd = txt + 1; /* prefix destination */
398 md = pd + prefix_len; /* move destination */
399
400 while (*txt == NL) txt++;
401 /*
402 * Maybe there were exactly enough NL characters we don't need to move
403 */
404 if (md != txt)
405 memmove(md, txt, strlen(txt) + 1);
406 memmove(pd, pfx, --prefix_len);
407 pd[prefix_len] = NL;
408
409 /*
410 * Look for a trailing license name and trim it and trailing white space
411 */
412 txt = strstr(md, "\n\n");
413 if (txt == NULL)
414 txt = md + strlen(md);
415
416 while ( (txt > md)
417 && IS_WHITESPACE_CHAR(txt[-1])) txt--;
418 *txt = NUL;
419 }
420
421 /**
422 * Trim off the license name. It is the third double-newline stanza
423 * in the license file.
424 *
425 * @param p a pointer to the first of two newlines separating
426 * copyright information from the description.
427 * @return pointer to second stanza, sans the license name trailer.
428 */
429 static char *
trim_lic_name(char * p)430 trim_lic_name(char * p)
431 {
432 char * res;
433 /* skip the leading white space. It starts with NL. */
434 p = SPN_WHITESPACE_CHARS(p + 1);
435 if (*p == NUL)
436 return p;
437
438 res = p;
439
440 /*
441 * The last section ends with two consecutive new lines.
442 * All trailing newlines are trimmed (not all white space).
443 */
444 p = strstr(p, "\n\n");
445 if (p == NULL)
446 p = res + strlen(res);
447 while ( (p > res)
448 && IS_WHITESPACE_CHAR(p[-1])) p--;
449 *p = NUL;
450
451 return res;
452 }
453
454 /**
455 * Extract the license name. It is the third double-newline stanza
456 * in the license file.
457 *
458 * @param txt a pointer to the first of two newlines separating
459 * copyright information from the description.
460 * @return pointer to the license name trailer.
461 */
462 static char *
get_lic_name(char * p)463 get_lic_name(char * p)
464 {
465 char * scan = p;
466 while (*(++scan) == NL) ; /* skip the leading NL's. */
467
468 /*
469 * Find the third stanza. If there. If not, we supply some static
470 * text: "an unknown license"
471 */
472 scan = strstr(scan, "\n\n");
473 if (scan == NULL) {
474 strcpy(p, EXP_FMT_BAD_LIC);
475 return p;
476 }
477 while (*scan == NL) scan++;
478 return scan;
479 }
480
481 /**
482 * Find the kind of text being requested. It may be "full" (the first
483 * two stanzas), "info" (the first -- copyright info + license name),
484 * "description" (the second -- a one paragraph description), or
485 * "name" -- the third stanza.
486 *
487 * @param txt a pointer to the first of two newlines separating
488 * copyright information from the description.
489 * @return pointer to the requested text.
490 */
491 static char *
find_lic_text(lic_segment_e_t segment,SCM lic,ssize_t * txt_len,char const * pfx)492 find_lic_text(
493 lic_segment_e_t segment, SCM lic, ssize_t * txt_len, char const * pfx)
494 {
495 static char const * const lic_sfx[] = { FIND_LIC_TEXT_SFX, NULL };
496
497 char const * lic_pz = ag_scm2zchars(lic, "license");
498 char fname[ AG_PATH_MAX ];
499 char * ftext;
500 ssize_t flen;
501
502 /*
503 * auto-convert "bsd" into "mbsd" for compatibility.
504 */
505 if (strcmp(lic_pz, FIND_LIC_TEXT_MBSD+1) == 0)
506 lic_pz = FIND_LIC_TEXT_MBSD;
507
508 if (! SUCCESSFUL(find_file(lic_pz, fname, lic_sfx, NULL)))
509 return NULL;
510
511 {
512 struct stat stbf;
513 if (stat(fname, &stbf) != 0)
514 AG_CANT(FIND_LIC_TEXT_NO_LIC, fname);
515 if (! S_ISREG(stbf.st_mode)) {
516 errno = EINVAL;
517 AG_CANT(FIND_LIC_TEXT_BAD_FILE, fname);
518 }
519 flen = stbf.st_size;
520 }
521
522 ftext = scribble_get(flen + EXP_FMT_BAD_LIC_LEN + 1);
523 *txt_len = flen;
524
525 {
526 FILE * fp = fopen(fname, "r");
527
528 if (fp == NULL)
529 AG_CANT(FIND_LIC_TEXT_OPEN, fname);
530
531 if (fread(ftext, 1, (size_t)flen, fp) != (size_t)flen)
532 AG_CANT(FIND_LIC_TEXT_BAD_FILE, fname);
533
534 ftext[flen] = NUL;
535 fclose(fp);
536 }
537
538 if (dep_fp != NULL)
539 add_source_file(fname);
540
541 {
542 char * p = strstr(ftext, DOUBLE_NEWLINE);
543
544 if (p == NULL)
545 AG_ABEND(aprf(FIND_LIC_TEXT_INVAL, fname));
546
547 switch (segment) {
548 case LSEG_INFO: p[1] = NUL; break;
549 case LSEG_DESC: ftext = trim_lic_name(p); break;
550 case LSEG_NAME: ftext = get_lic_name(p); break;
551 case LSEG_FULL: assemble_full_desc(p, pfx); break;
552 }
553 }
554
555 return ftext;
556 }
557
558 /**
559 * Construct an SCM for the kind of text being requested.
560 *
561 * It may be "full" (the first two stanzas), "info" (the first -- copyright
562 * info + license name), "description" (the second -- a one paragraph
563 * description), or "name" -- the third stanza.
564 *
565 * @param seg which segment of license is desired
566 * @param lic The name of the license
567 * @param prog the name of the program
568 * @param pfx a per-line prefix
569 * @param owner who owns the copyright
570 * @param years the copyright years
571 *
572 * @return the SCM-ized string
573 */
574 static SCM
construct_license(lic_segment_e_t seg,SCM lic,SCM prog,SCM pfx,SCM owner,SCM years)575 construct_license(
576 lic_segment_e_t seg, SCM lic, SCM prog, SCM pfx, SCM owner, SCM years)
577 {
578 static SCM subs = SCM_UNDEFINED;
579 static SCM empty = SCM_UNDEFINED;
580
581 SCM vals = SCM_UNDEFINED;
582 char * lic_text;
583 ssize_t text_len;
584 char const * pfx_pz = ag_scm2zchars(pfx, "lic-prefix");
585
586 if (subs == SCM_UNDEFINED) {
587 static char const * const slst[] = {
588 MK_LIC_PROG, MK_LIC_PFX, MK_LIC_OWN, MK_LIC_YRS
589 };
590 subs = scm_gc_protect_object(
591 scm_list_4(scm_from_latin1_string(slst[0]),
592 scm_from_latin1_string(slst[1]),
593 scm_from_latin1_string(slst[2]),
594 scm_from_latin1_string(slst[3])));
595
596 empty = scm_gc_protect_object(scm_from_latin1_string(""));
597 }
598
599 if (! scm_is_string(lic))
600 AG_ABEND(MK_LIC_NOT_STR);
601
602 lic_text = find_lic_text(seg, lic, &text_len, pfx_pz);
603 if (lic_text == NULL)
604 AG_ABEND(aprf(MK_LIC_NO_LIC, ag_scm2zchars(lic, "lic")));
605
606 if (! scm_is_string(owner)) owner = empty;
607 if (! scm_is_string(years)) years = empty;
608 vals = scm_list_4(prog, pfx, owner, years);
609
610 do_multi_subs(&lic_text, &text_len, subs, vals);
611
612 trim_trailing_white(lic_text);
613 return scm_from_latin1_string(lic_text);
614 }
615
616 /*=gfunc license_full
617 *
618 * what: Emit the licensing information and description
619 * general_use:
620 *
621 * exparg: license, name of license type
622 * exparg: prog-name, name of the program under the GPL
623 * exparg: prefix, String for starting each output line
624 * exparg: owner, owner of the program, optional
625 * exparg: years, copyright years, optional
626 *
627 * doc:
628 *
629 * Emit all the text that @code{license-info} and @code{license-description}
630 * would emit (@pxref{SCM license-info, @code{license-info}},
631 * and @pxref{SCM license-description, @code{license-description}}),
632 * with all the same substitutions.
633 *
634 * All of these depend upon the existence of a license file named after the
635 * @code{license} argument with a @code{.lic} suffix. That file should
636 * contain three blocks of text, each separated by two or more consecutive
637 * newline characters (at least one completely blank line).
638 *
639 * The first section describes copyright attribution and the name of the usage
640 * licence. For GNU software, this should be the text that is to be displayed
641 * with the program version. Four text markers can be replaced: <PFX>,
642 * <program>, <years> and <owner>.
643 *
644 * The second section is a short description of the terms of the license.
645 * This is typically the kind of text that gets displayed in the header of
646 * source files. Only the <PFX>, <owner> and <program> markers are
647 * substituted.
648 *
649 * The third section is strictly the name of the license.
650 * No marker substitutions are performed.
651 *
652 * @example
653 * <PFX>Copyright (C) <years> <owner>, all rights reserved.
654 * <PFX>
655 * <PFX>This is free software. It is licensed for use,
656 * <PFX>modification and redistribution under the terms
657 * <PFX>of the GNU General Public License, version 3 or later
658 * <PFX> <http://gnu.org/licenses/gpl.html>
659 *
660 * <PFX><program> is free software: you can redistribute it
661 * <PFX>and/or modify it under the terms of the GNU General
662 * <PFX>Public License as published by the Free Software ...
663 *
664 * the GNU General Public License, version 3 or later
665 * @end example
666 =*/
667 SCM
ag_scm_license_full(SCM lic,SCM prog,SCM pfx,SCM owner,SCM years)668 ag_scm_license_full(SCM lic, SCM prog, SCM pfx, SCM owner, SCM years)
669 {
670 return construct_license(LSEG_FULL, lic, prog, pfx, owner, years);
671 }
672
673 /*=gfunc license_description
674 *
675 * what: Emit a license description
676 * general_use:
677 *
678 * exparg: license, name of license type
679 * exparg: prog-name, name of the program under the GPL
680 * exparg: prefix, String for starting each output line
681 * exparg: owner, owner of the program, optional
682 *
683 * doc:
684 *
685 * Emit a string that contains a detailed license description, with
686 * substitutions for program name, copyright holder and a per-line prefix.
687 * This is the text typically used as part of a source file header.
688 * For more details, @xref{SCM license-full, the license-full command}.
689 *
690 =*/
691 SCM
ag_scm_license_description(SCM lic,SCM prog,SCM pfx,SCM owner)692 ag_scm_license_description(SCM lic, SCM prog, SCM pfx, SCM owner)
693 {
694 return construct_license(LSEG_DESC, lic, prog, pfx, owner, SCM_UNDEFINED);
695 }
696
697 /*=gfunc license_info
698 *
699 * what: Emit the licensing information and copyright years
700 * general_use:
701 *
702 * exparg: license, name of license type
703 * exparg: prog-name, name of the program under the GPL
704 * exparg: prefix, String for starting each output line
705 * exparg: owner, owner of the program, optional
706 * exparg: years, copyright years, optional
707 *
708 * doc:
709 *
710 * Emit a string that contains the licensing description, with some
711 * substitutions for program name, copyright holder, a list of years when the
712 * source was modified, and a per-line prefix. This text typically includes a
713 * brief license description and is often printed out when a program starts
714 * running or as part of the @code{--version} output.
715 * For more details, @xref{SCM license-full, the license-full command}.
716 *
717 =*/
718 SCM
ag_scm_license_info(SCM lic,SCM prog,SCM pfx,SCM owner,SCM years)719 ag_scm_license_info(SCM lic, SCM prog, SCM pfx, SCM owner, SCM years)
720 {
721 return construct_license(LSEG_INFO, lic, prog, pfx, owner, years);
722 }
723
724 /*=gfunc license_name
725 *
726 * what: Emit the name of the license
727 * general_use:
728 *
729 * exparg: license, name of license type
730 *
731 * doc:
732 *
733 * Emit a string that contains the full name of the license.
734 =*/
735 SCM
ag_scm_license_name(SCM lic)736 ag_scm_license_name(SCM lic)
737 {
738 ssize_t text_len;
739 char * txt = find_lic_text(LSEG_NAME, lic, &text_len, "");
740 char * e;
741
742 if (txt != NULL) {
743 txt = SPN_WHITESPACE_CHARS(txt);
744 e = SPN_WHITESPACE_BACK(txt, txt);
745 *e = NUL;
746 lic = scm_from_latin1_string(txt);
747 }
748 return lic;
749 }
750
751 /*=gfunc gpl
752 *
753 * what: GNU General Public License
754 * general_use:
755 *
756 * exparg: prog-name, name of the program under the GPL
757 * exparg: prefix, String for starting each output line
758 *
759 * doc:
760 *
761 * Emit a string that contains the GNU General Public License.
762 * This function is now deprecated. Please @xref{SCM license-description}.
763 =*/
764 SCM
ag_scm_gpl(SCM prog_name,SCM prefix)765 ag_scm_gpl(SCM prog_name, SCM prefix)
766 {
767 static SCM lic = SCM_UNDEFINED;
768
769 if (lic == SCM_UNDEFINED)
770 lic = scm_gc_protect_object(
771 scm_from_latin1_string(FIND_LIC_TEXT_LGPL+1));
772 return ag_scm_license_description(lic, prog_name, prefix, SCM_UNDEFINED);
773 }
774
775 /*=gfunc agpl
776 *
777 * what: GNU Affero General Public License
778 * general_use:
779 *
780 * exparg: prog-name, name of the program under the GPL
781 * exparg: prefix, String for starting each output line
782 *
783 * doc:
784 *
785 * Emit a string that contains the GNU Affero General Public License.
786 * This function is now deprecated. Please @xref{SCM license-description}.
787 =*/
788 SCM
ag_scm_agpl(SCM prog_name,SCM prefix)789 ag_scm_agpl(SCM prog_name, SCM prefix)
790 {
791 static SCM lic = SCM_UNDEFINED;
792
793 if (lic == SCM_UNDEFINED)
794 lic = scm_gc_protect_object(
795 scm_from_latin1_string(FIND_LIC_TEXT_AGPL));
796 return ag_scm_license_description(lic, prog_name, prefix, SCM_UNDEFINED);
797 }
798
799 /*=gfunc lgpl
800 *
801 * what: GNU Library General Public License
802 * general_use:
803 *
804 * exparg: prog_name, name of the program under the LGPL
805 * exparg: owner, Grantor of the LGPL
806 * exparg: prefix, String for starting each output line
807 *
808 * doc:
809 *
810 * Emit a string that contains the GNU Library General Public License.
811 * This function is now deprecated. Please @xref{SCM license-description}.
812 =*/
813 SCM
ag_scm_lgpl(SCM prog_name,SCM owner,SCM prefix)814 ag_scm_lgpl(SCM prog_name, SCM owner, SCM prefix)
815 {
816 static SCM lic = SCM_UNDEFINED;
817
818 if (lic == SCM_UNDEFINED)
819 lic = scm_gc_protect_object(
820 scm_from_latin1_string(FIND_LIC_TEXT_LGPL));
821 return ag_scm_license_description(lic, prog_name, prefix, owner);
822 }
823
824 /*=gfunc bsd
825 *
826 * what: BSD Public License
827 * general_use:
828 *
829 * exparg: prog_name, name of the program under the BSD
830 * exparg: owner, Grantor of the BSD License
831 * exparg: prefix, String for starting each output line
832 *
833 * doc:
834 *
835 * Emit a string that contains the Free BSD Public License.
836 * This function is now deprecated. Please @xref{SCM license-description}.
837 *
838 =*/
839 SCM
ag_scm_bsd(SCM prog_name,SCM owner,SCM prefix)840 ag_scm_bsd(SCM prog_name, SCM owner, SCM prefix)
841 {
842 static SCM lic = SCM_UNDEFINED;
843
844 if (lic == SCM_UNDEFINED)
845 lic = scm_gc_protect_object(
846 scm_from_latin1_string(FIND_LIC_TEXT_MBSD));
847 return ag_scm_license_description(lic, prog_name, prefix, owner);
848 }
849
850 /*=gfunc license
851 *
852 * what: an arbitrary license
853 * general_use:
854 *
855 * exparg: lic_name, file name of the license
856 * exparg: prog_name, name of the licensed program or library
857 * exparg: owner, Grantor of the License
858 * exparg: prefix, String for starting each output line
859 *
860 * doc:
861 * Emit a string that contains the named license.
862 * This function is now deprecated. Please @xref{SCM license-description}.
863 =*/
864 SCM
ag_scm_license(SCM license,SCM prog_name,SCM owner,SCM prefix)865 ag_scm_license(SCM license, SCM prog_name, SCM owner, SCM prefix)
866 {
867 char const * prefx = ag_scm2zchars(prefix, "line pfx");
868 char const * pname = ag_scm2zchars(prog_name, "p name");
869 char const * ownrz = ag_scm2zchars(owner, "owner");
870 static struct {
871 char const * pzFN;
872 tmap_info_t mi;
873 } lic = { NULL, { NULL, 0, 0, 0, 0, 0, 0, 0 }};
874
875 char * pzRes;
876
877 if (! scm_is_string(license))
878 return SCM_UNDEFINED;
879
880 {
881 static char const * const apzSfx[] = { MK_LIC_SFX, NULL };
882 static char fname[ AG_PATH_MAX ];
883 char const * l_file = ag_scm2zchars(license, "lic file");
884
885 /*
886 * Find the template file somewhere
887 */
888 if (! SUCCESSFUL(find_file(l_file, fname, apzSfx, NULL))) {
889 errno = ENOENT;
890 AG_CANT(MK_LIC_NO_LIC, l_file);
891 }
892
893 if ((lic.pzFN != NULL) && (strcmp(fname, lic.pzFN) != 0)) {
894 text_munmap(&lic.mi);
895 AGFREE(lic.pzFN);
896 lic.pzFN = NULL;
897 }
898
899 if (lic.pzFN == NULL) {
900 text_mmap(fname, PROT_READ|PROT_WRITE, MAP_PRIVATE, &lic.mi);
901 if (TEXT_MMAP_FAILED_ADDR(lic.mi.txt_data))
902 AG_ABEND(aprf(MK_LIC_NO_OPEN, l_file));
903
904 if (dep_fp != NULL)
905 add_source_file(l_file);
906
907 AGDUPSTR(lic.pzFN, fname, "lic f name");
908 }
909 }
910
911 /*
912 * Trim trailing white space.
913 */
914 {
915 char * pz = (char *)lic.mi.txt_data + lic.mi.txt_size;
916 while ( (pz > (char *)lic.mi.txt_data)
917 && IS_WHITESPACE_CHAR(pz[-1]))
918 pz--;
919 *pz = NUL;
920 }
921
922 /*
923 * Get the addresses of the program name prefix and owner strings.
924 * Make sure they are reasonably sized (less than
925 * SCRIBBLE_SIZE). Copy them to the scratch buffer.
926 */
927 if (scm_c_string_length(prog_name) >= SCRIBBLE_SIZE)
928 AG_ABEND(aprf(MK_LIC_TOO_BIG_FMT, MK_LIC_BIG_PROG, SCRIBBLE_SIZE));
929
930 if (scm_c_string_length(prefix) >= SCRIBBLE_SIZE)
931 AG_ABEND(aprf(MK_LIC_TOO_BIG_FMT, MK_LIC_BIG_PFX, SCRIBBLE_SIZE));
932
933 if (scm_c_string_length(owner) >= SCRIBBLE_SIZE)
934 AG_ABEND(aprf(MK_LIC_TOO_BIG_FMT, MK_LIC_BIG_OWN, SCRIBBLE_SIZE));
935
936 /*
937 * Reformat the string with the given arguments
938 */
939 pzRes = aprf((char *)lic.mi.txt_data, pname, ownrz);
940 {
941 int pfx_size = (int)strlen(prefx);
942 char * pzScan = pzRes;
943 char * pzOut;
944 char * pzSaveRes;
945 ssize_t out_size = pfx_size;
946
947 /*
948 * Figure out how much space we need (text size plus
949 * a prefix size for each newline)
950 */
951 for (;;) {
952 switch (*(pzScan++)) {
953 case NUL:
954 goto exit_count;
955 case NL:
956 out_size += pfx_size;
957 /* FALLTHROUGH */
958 default:
959 out_size++;
960 }
961 } exit_count:;
962
963 /*
964 * Create our output buffer and insert the first prefix
965 */
966 pzOut = pzSaveRes = scribble_get(out_size);
967
968 strcpy(pzOut, prefx);
969 pzOut += pfx_size;
970 pzScan = pzRes;
971
972 for (;;) {
973 switch (*(pzOut++) = *(pzScan++)) {
974 case NUL:
975 goto exit_copy;
976
977 case NL:
978 strcpy(pzOut, prefx);
979 pzOut += pfx_size;
980 break;
981
982 default:
983 break;
984 }
985 }
986 exit_copy:;
987
988 /*
989 * We allocated a temporary buffer that has all the
990 * formatting done, but need the prefixes on each line.
991 */
992 AGFREE(pzRes);
993
994 return scm_from_latin1_stringn(
995 pzSaveRes, (size_t)((pzOut - pzSaveRes) - 1));
996 }
997 }
998 /**
999 * @}
1000 *
1001 * Local Variables:
1002 * mode: C
1003 * c-file-style: "stroustrup"
1004 * indent-tabs-mode: nil
1005 * End:
1006 * end of agen5/expFormat.c */
1007