A template-based license generator script written in Perl
1#!/usr/bin/env perl
2use strict;
3use warnings;
4use File::Spec;
5use File::Basename;
6
7my $VERSION = "0.1";
8
9sub usage {
10 print "Usage: lic <license> [OPTIONS] ... [KEY=VALUE] ...\n";
11 print "See 'lic --help' for more information.\n";
12}
13
14sub help {
15 print "Usage: lic <license> [OPTIONS] ... [KEY=VALUE] ...\n";
16 print "\nGenerate license text from templates.\n";
17 print "\n<license> may be either:\n";
18 print " a path to a license template file, or\n";
19 print " the name of a template in a standard location\n";
20 print "\nStandard locations include:\n";
21 print " directories listed in \$LICPATH (colon-separated)\n";
22 print " ~/.local/share/lic\n";
23 print " /usr/share/lic\n";
24 print "\nOptions may include:\n";
25 print " -H, --head\n use header variant of <license>\n";
26 print " -s, --spdx\n use shorter SPDX license header\n";
27 print " -v, --vars\n display variables defined in <license>\n";
28 print " -h, --help\n display this help message\n";
29 print " -V, --version\n display version information\n";
30 print "\nEach KEY=VALUE replaces all occurrences of {{KEY}} with VALUE in the template.\n";
31 print "Whitespace inside {{ }} is ignored.\n";
32 print "\nOuput is written to standard output.\n";
33}
34
35sub version {
36 print "lic version $VERSION\n";
37 print "License: BSD 2-Clause\n";
38}
39
40my $license;
41my $opt_head = 0;
42my $opt_spdx = 0;
43my $opt_vars = 0;
44my @substs = ();
45my @vars = ();
46my %vars_seen = ();
47my $file;
48
49for my $arg (@ARGV) {
50 if ($arg eq "-h" || $arg eq "--help") {
51 help;
52 exit;
53 }
54 elsif ($arg eq "-V" || $arg eq "--version") {
55 version;
56 exit;
57 }
58 elsif ($arg eq "-H" || $arg eq "--head") {
59 $opt_head = 1;
60 }
61 elsif ($arg eq "-s" || $arg eq "--spdx") {
62 $opt_spdx = 1;
63 }
64 elsif ($arg eq "-v" || $arg eq "--vars") {
65 $opt_vars = 1;
66 }
67 elsif (!defined $license) {
68 $license = $arg;
69 }
70 elsif ($arg =~ /([^=]+)=([^=]+)/) {
71 push @substs, [$1, $2];
72 }
73}
74
75unless (defined $license) {
76 usage;
77 exit;
78}
79
80if ($opt_head == 1 && $opt_spdx == 0) {
81 # license headers use .head extension
82 $license .= ".head";
83}
84if ($opt_spdx == 1) {
85 # spdx headers use .head.spdx
86 $license .= ".head.spdx"
87}
88
89if (-f $license) {
90 $file = $license;
91}
92else {
93 my @search_dirs;
94
95 if (defined $ENV{LICPATH}) {
96 push @search_dirs, split /:/, $ENV{LICPATH};
97 }
98
99 push @search_dirs,
100 File::Spec->catdir($ENV{HOME}, ".local", "share", "lic")
101 if defined $ENV{HOME};
102
103 push @search_dirs, "/usr/share/lic";
104
105SCAN_DIRS:
106 for my $dir (@search_dirs) {
107 my $candidate = File::Spec->catfile($dir, $license);
108 if (-f $candidate) {
109 $file = $candidate;
110 last;
111 }
112 }
113
114 if (!$file && $opt_spdx == 0) {
115 $license .= ".spdx";
116 $opt_spdx = 1;
117 goto SCAN_DIRS;
118 }
119
120 die "license template $license not found in:\n "
121 . join("\n ", @search_dirs) . "\n"
122 unless defined $file;
123}
124
125open my $input, '<', $file or die "can't open file $file: $!\n";
126
127while (<$input>) {
128 chomp;
129 my $line = $_;
130
131 if ($opt_vars == 1) {
132 for my $key ($line =~ /\{\{\s*([^}]+)\s*\}\}/g) {
133 # Only push new varaible names
134 push @vars, $key unless $vars_seen{$key}++;
135 }
136 next;
137 }
138
139 foreach my $subst (@substs) {
140 my ($key, $val) = @$subst;
141 $line =~ s/\{\{\s*\Q$key\E\s*\}\}/$val/g;
142 }
143
144 print $line, "\n";
145}
146
147close $input or die "can't close file $file: $!\n";
148
149if ($opt_vars == 1) {
150 for my $var (@vars) {
151 print $var, "\n";
152 }
153}
154