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