• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/bin/sh
2
3set -u
4
5ME=$(basename $0)
6
7USAGE="Usage: ${ME} {options}
8
9Builds a license metadata specification and outputs it to stdout or {outfile}.
10
11The available options are:
12
13-k kind...              license kinds
14-c condition...         license conditions
15-p package...           license package name
16-n notice...            license notice file
17-d dependency...        license metadata file dependency
18-t target...            targets
19-m target:installed...  map dependent targets to their installed names
20-is_container           preserved dependent target name when given
21-o outfile              output file
22"
23
24# Global flag variables
25license_kinds=
26license_conditions=
27license_package_name=
28license_notice=
29license_deps=
30targets=
31installmap=
32is_container=false
33ofile=
34
35# Global variables
36depfiles=" "
37effective_conditions=
38
39
40# Exits with a message.
41#
42# When the exit status is 2, assumes a usage error and outputs the usage message
43# to stderr before outputting the specific error message to stderr.
44#
45# Parameters:
46#   Optional numeric exit status (defaults to 2, i.e. a usage error.)
47#   Remaining args treated as an error message sent to stderr.
48die() {
49  lstatus=2
50  case "${1:-}" in *[^0-9]*) ;; *) lstatus="$1"; shift ;; esac
51  case "${lstatus}" in 2) echo "${USAGE}" >&2; echo >&2 ;; esac
52  if [ -n "$*" ]; then
53    echo -e "$*\n" >&2
54  fi
55  exit $lstatus
56}
57
58
59# Sets the flag variables based on the command-line.
60#
61# invoke with: process_args "$@"
62process_args() {
63  lcurr_flag=
64  while [ "$#" -gt '0' ]; do
65    case "${1}" in
66      -h)
67        echo "${USAGE}"
68        exit 0
69        ;;
70      -k)
71        lcurr_flag=kind
72        ;;
73      -c)
74        lcurr_flag=condition
75        ;;
76      -p)
77        lcurr_flag=package
78        ;;
79      -n)
80        lcurr_flag=notice
81        ;;
82      -d)
83        lcurr_flag=dependency
84        ;;
85      -t)
86        lcurr_flag=target
87        ;;
88      -m)
89        lcurr_flag=installmap
90        ;;
91      -o)
92        lcurr_flag=ofile
93        ;;
94      -is_container)
95        lcurr_flag=
96        is_container=true
97        ;;
98      -*)
99        die "Unknown flag: \"${1}\""
100        ;;
101      *)
102        case "${lcurr_flag}" in
103          kind)
104            license_kinds="${license_kinds}${license_kinds:+ }${1}"
105            ;;
106          condition)
107            license_conditions="${license_conditions}${license_conditions:+ }${1}"
108            ;;
109          package)
110            license_package_name="${license_package_name}${license_package_name:+ }${1}"
111            ;;
112          notice)
113            license_notice="${license_notice}${license_notice:+ }${1}"
114            ;;
115          dependency)
116            license_deps="${license_deps}${license_deps:+ }${1}"
117            ;;
118          target)
119            targets="${targets}${targets:+ }${1}"
120            ;;
121          installmap)
122            installmap="${installmap}${installmap:+ }${1}"
123            ;;
124          ofile)
125            if [ -n "${ofile}" ]; then
126              die "Output file -o appears twice as \"${ofile}\" and \"${1}\""
127            fi
128            ofile="${1}"
129            ;;
130          *)
131            die "Must precede argument \"${1}\" with type flag."
132            ;;
133        esac
134        ;;
135    esac
136    shift
137  done
138}
139
140# Reads a license metadata file from stdin, and outputs the named dependencies.
141#
142# No parameters.
143extract_deps() {
144  awk '$1 == "dep_name:" { sub(/^"/, "", $2); sub(/"$/, "", $2); print $2; }'
145}
146
147# Populates the depfiles variable identifying dependency files.
148#
149# Starting with the dependencies enumerated in license_deps, calculates the
150# transitive closure of all dependencies.
151#
152# Dependency names ending in .meta_module indirectly reference license
153# metadata with 1 license metadata filename per line.
154#
155# No parameters; no output.
156read_deps() {
157  lnewdeps=
158  for d in ${license_deps}; do
159    case "${d}" in
160      *.meta_module)
161        lnewdeps="${lnewdeps}${lnewdeps:+ }"$(cat "${d}") ;;
162      *)
163        lnewdeps="${lnewdeps}${lnewdeps:+ }${d}" ;;
164    esac
165  done
166  lnewdeps=$(echo "${lnewdeps}" | tr ' ' '\n' | sort -u)
167  lalldeps=
168  ldeps=
169  lmod=
170  ldep=
171  while [ "${#lnewdeps}" -gt '0' ]; do
172    ldeps="${lnewdeps}"
173    lnewdeps=
174    for ldep in ${ldeps}; do
175      depfiles="${depfiles}${ldep} "
176      lalldeps="${lalldeps}${lalldeps:+ }"$(cat "${ldep}" | extract_deps)
177    done
178    lalldeps=$(for d in ${lalldeps}; do echo "${d}"; done | sort -u)
179    for d in ${lalldeps}; do
180      ldeps="${d}"
181      case "${d}" in *.meta_module) ldeps=$(cat "${d}") ;; esac
182      for lmod in ${ldeps}; do
183        if ! expr "${depfiles}" : ".* ${lmod} .*" >/dev/null 2>&1; then
184          lnewdeps="${lnewdeps}${lnewdeps:+ }${lmod}"
185        fi
186      done
187    done
188    lalldeps=
189  done
190}
191
192# Returns the effective license conditions for the current license metadata.
193#
194# If a module is restricted or links in a restricted module, the effective
195# license has a restricted condition.
196calculate_effective_conditions() {
197  lconditions="${license_conditions}"
198  case "${license_conditions}" in
199    *restricted*) : do nothing ;;
200    *)
201       for d in ${depfiles}; do
202         if cat "${d}" | egrep -q 'effective_condition\s*:.*restricted' ; then
203           lconditions="${lconditions}${lconditions:+ }restricted"
204           break
205         fi
206       done
207     ;;
208  esac
209  echo "${lconditions}"
210}
211
212
213process_args "$@"
214
215if [ -n "${ofile}" ]; then
216  # truncate the output file before appending results
217  : >"${ofile}"
218else
219  ofile=/dev/stdout
220fi
221
222# spit out the license metadata file content
223(
224  echo 'license_package_name: "'${license_package_name}'"'
225  for kind in ${license_kinds}; do
226    echo 'license_kind: "'${kind}'"'
227  done
228  for condition in ${license_conditions}; do
229    echo 'license_condition: "'${condition}'"'
230  done
231  for f in ${license_notice}; do
232    echo 'license_text: "'${f}'"'
233  done
234  echo "is_container: ${is_container}"
235  for t in ${targets}; do
236    echo 'target: "'${t}'"'
237  done
238  for m in ${installmap}; do
239    echo 'install_map: "'${m}'"'
240  done
241) >>"${ofile}"
242read_deps
243effective_conditions=$(calculate_effective_conditions)
244for condition in ${effective_conditions}; do
245  echo 'effective_condition: "'${condition}'"'
246done >>"${ofile}"
247for dep in ${depfiles}; do
248  echo 'dep {'
249  cat "${dep}" | \
250    awk -v name="${dep}" '
251      function strip_type() {
252        $1 = ""
253        sub(/^\s*/, "")
254      }
255      BEGIN {
256        print "  dep_name: " name
257      }
258      $1 == "license_package_name:" {
259        strip_type()
260        print "  dep_package_name: "$0
261      }
262      $1 == "dep_name:" {
263        print "  dep_sub_dep: "$2
264      }
265      $1 == "license_kind:" {
266        print "  dep_license_kind: "$2
267      }
268      $1 == "license_condition:" {
269        print "  dep_license_condition: "$2
270      }
271      $1 == "is_container:" {
272        print "  dep_is_container: "$2
273      }
274      $1 == "license_text:" {
275        strip_type()
276        print "  dep_license_text: "$0
277      }
278      $1 == "target:" {
279        print "  dep_target: "$2
280      }
281      $1 == "install_map:" {
282        print "  dep_install_map: "$2
283      }
284  '
285  # The restricted license kind is contagious to all linked dependencies.
286  dep_conditions=$(echo $(
287      cat "${dep}" | awk '
288        $1 == "effective_condition:" {
289          $1 = ""
290          sub(/^\s*/, "")
291          gsub(/"/, "")
292          print
293        }
294      '
295  ))
296  for condition in ${dep_conditions}; do
297    echo '  dep_effective_condition: "'${condition}'"'
298  done
299  if ! ${is_container}; then
300    case "${dep_conditions}" in
301      *restricted*) : already restricted -- nothing to inherit ;;
302      *)
303        case "${effective_conditions}" in
304          *restricted*)
305            # "contagious" restricted infects everything linked to restricted
306            echo '  dep_effective_condition: "restricted"'
307            ;;
308        esac
309        ;;
310    esac
311  fi
312  echo '}'
313done >>"${ofile}"
314