10b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# Copyright (C) 2010 The Android Open Source Project 20b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# 30b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# Licensed under the Apache License, Version 2.0 (the "License"); 40b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# you may not use this file except in compliance with the License. 50b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# You may obtain a copy of the License at 60b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# 70b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# http://www.apache.org/licenses/LICENSE-2.0 80b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# 90b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# Unless required by applicable law or agreed to in writing, software 100b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# distributed under the License is distributed on an "AS IS" BASIS, 110b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 120b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# See the License for the specific language governing permissions and 130b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# limitations under the License. 140b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# 150b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# A nawk/gawk script used to extract the list of launchable activities 160b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# from an application's manifest (i.e. AndroidManifest.xml). Usage: 170b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# 180b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# awk -f <this-script> AndroidManifest.xml 190b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# 200b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner 210b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# 220b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# Explanation: 230b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# 240b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# A given application can have several activities, and each activity 250b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# can have several intent filters. We want to only list, in the final 260b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# output, the activities which have a intent-filter that contains the 270b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# following elements: 280b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# 290b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# <action android:name="android.intent.action.MAIN" /> 300b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# <category android:name="android.intent.category.LAUNCHER" /> 310b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# 320b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# To do this, we need hooks called when entering and exiting <activity> 330b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# and <intent-filter> elements. 340b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# 350b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner 360b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' TurnerBEGIN { 370b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner while ( xml_event() ) { 380b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner # concat xml event type and tag for simpler comparisons 390b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner event = XML_TYPE "-" XML_TAG; 400b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner # When entering a new <activity>, extract its name and set 410b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner # the 'launchable' flag to false. 420b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner if ( event == "BEGIN-ACTIVITY" && 430b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner XML_RPATH == "ACTIVITY/APPLICATION/MANIFEST/" ) { 440b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner name = XML_ATTR["android:name"]; 450b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner launchable = 0; 460b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner } 470b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner # When exiting an <activity>, check that it has a name and 480b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner # is launchable. If so, print its name to the output 490b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner else if ( event == "END-ACTIVITY" && 500b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner XML_RPATH == "APPLICATION/MANIFEST/" ) { 510b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner if ( name && launchable ) { 528c3d67c878fac8a5bef0458ef19ac06d959c7488David 'Digit' Turner # If the name doesn't contain any dot, we consider 538c3d67c878fac8a5bef0458ef19ac06d959c7488David 'Digit' Turner # that it is just missing the initial one. 548c3d67c878fac8a5bef0458ef19ac06d959c7488David 'Digit' Turner if (index(name, ".") == 0) { 558c3d67c878fac8a5bef0458ef19ac06d959c7488David 'Digit' Turner name = "." name 568c3d67c878fac8a5bef0458ef19ac06d959c7488David 'Digit' Turner } 570b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner print name; 580b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner } 590b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner } 600b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner # When entering an <intent-filter> inside an <activity>, clear 610b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner # the 'action' and 'category' variables. They are updated when 620b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner # we enter the corresponding elements within the intent-filter. 630b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner else if ( event == "BEGIN-INTENT-FILTER" && 640b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner XML_RPATH == "INTENT-FILTER/ACTIVITY/APPLICATION/MANIFEST/" ) { 65b2d2128c1cd0f92455b201bca17897ecf07b48d0David 'Digit' Turner action_main = 0; 66b2d2128c1cd0f92455b201bca17897ecf07b48d0David 'Digit' Turner category_launcher = 0; 670b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner } 680b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner # When exiting an <intent-filter>, set the 'launchable' flag to true 690b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner # for the current activity if both 'action' and 'category' have the 700b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner # correct name. 710b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner else if ( event == "END-INTENT-FILTER" && 720b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner XML_RPATH == "ACTIVITY/APPLICATION/MANIFEST/" ) { 73b2d2128c1cd0f92455b201bca17897ecf07b48d0David 'Digit' Turner if ( category_launcher ) { 74b2d2128c1cd0f92455b201bca17897ecf07b48d0David 'Digit' Turner launchable = 1; 750b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner } 760b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner } 770b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner # When entering an <action> element inside an <intent-filter>, record 780b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner # its name. 790b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner else if ( event == "BEGIN-ACTION" && 800b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner XML_RPATH == "ACTION/INTENT-FILTER/ACTIVITY/APPLICATION/MANIFEST/" ) { 81b2d2128c1cd0f92455b201bca17897ecf07b48d0David 'Digit' Turner action_main = 0; 82b2d2128c1cd0f92455b201bca17897ecf07b48d0David 'Digit' Turner if ( XML_ATTR["android:name"] == "android.intent.action.MAIN" ) { 83b2d2128c1cd0f92455b201bca17897ecf07b48d0David 'Digit' Turner action_main = 1; 84b2d2128c1cd0f92455b201bca17897ecf07b48d0David 'Digit' Turner } 850b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner } 860b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner # When entering a <category> element inside an <intent-filter>, record 870b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner # its name. 880b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner else if ( event == "BEGIN-CATEGORY" && 890b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner XML_RPATH == "CATEGORY/INTENT-FILTER/ACTIVITY/APPLICATION/MANIFEST/" ) { 90b2d2128c1cd0f92455b201bca17897ecf07b48d0David 'Digit' Turner if ( action_main && XML_ATTR["android:name"] == "android.intent.category.LAUNCHER" ) { 91b2d2128c1cd0f92455b201bca17897ecf07b48d0David 'Digit' Turner category_launcher = 1; 92b2d2128c1cd0f92455b201bca17897ecf07b48d0David 'Digit' Turner } 930b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner } 940b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner } 950b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner} 960b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner 970b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner 980b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# 990b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# the following is copied directly from xml.awk - see this file for 1000b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# usage and implementation details. 1010b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner# 1020b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turnerfunction xml_event () { 1030b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner RS=">"; 1040b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner XML_TAG=XML_TYPE=""; 1050b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner split("", XML_ATTR); 1060b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner while ( 1 ) { 1070b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner if (_xml_closing) { # delayed direct tag closure 1080b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner XML_TAG = _xml_closing; 1090b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner XML_TYPE = "END"; 1100b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner _xml_closing = ""; 1110b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner _xml_exit(XML_TAG); 1120b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner return 1; 1130b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner } 1140b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner if (getline <= 0) return 0; # read new input line 1150b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner _xml_p = index($0, "<"); # get start marker 1160b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner if (_xml_p == 0) return 0; # end of file (or malformed input) 1170b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner $0 = substr($0, _xml_p) # remove anything before '<' 1180b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner # ignore CData / Comments / Processing instructions / Declarations 1190b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner if (_xml_in_section("<!\\[[Cc][Dd][Aa][Tt][Aa]\\[", "]]") || 1200b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner _xml_in_section("<!--", "--") || 1210b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner _xml_in_section("<\\?", "\\?") || 1220b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner _xml_in_section("<!", "")) { 1230b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner continue; 1240b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner } 1250b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner if (substr($0, 1, 2) == "</") { # is it a closing tag ? 1260b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner XML_TYPE = "END"; 1270b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner $0 = substr($0, 3); 1280b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner } else { # nope, it's an opening one 1290b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner XML_TYPE = "BEGIN"; 1300b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner $0 = substr($0, 2); 1310b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner } 1320b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner XML_TAG = $0 1330b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner sub("[ \n\t/].*$", "", XML_TAG); # extract tag name 1340b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner XML_TAG = toupper(XML_TAG); # uppercase it 1350b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner if ( XML_TAG !~ /^[A-Z][-+_.:0-9A-Z]*$/ ) # validate it 1360b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner _xml_panic("Invalid tag name: " XML_TAG); 1370b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner if (XML_TYPE == "BEGIN") { # update reverse path 1380b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner _xml_enter(XML_TAG); 1390b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner } else { 1400b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner _xml_exit(XML_TAG); 1410b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner } 1420b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner sub("[^ \n\t]*[ \n\t]*", "", $0); # get rid of tag and spaces 1430b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner while ($0) { # process attributes 1440b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner if ($0 == "/") { # deal with direct closing tag, e.g. </foo> 1450b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner _xml_closing = XML_TAG; # record delayed tag closure. 1460b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner break 1470b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner } 1480b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner _xml_attrib = $0; 1490b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner sub(/=.*$/,"",_xml_attrib); # extract attribute name 1500b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner sub(/^[^=]*/,"",$0); # remove it from record 1510b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner _xml_attrib = tolower(_xml_attrib); 1520b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner if ( _xml_attrib !~ /^[a-z][-+_0-9a-z:]*$/ ) # validate it 1530b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner _xml_panic("Invalid attribute name: " _xml_attrib); 1540b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner if (substr($0,1,2) == "=\"") { # value is ="something" 1550b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner _xml_value = substr($0,3); 1560b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner sub(/".*$/,"",_xml_value); 1570b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner sub(/^="[^"]*"/,"",$0); 1580b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner } else if (substr($0,1,2) == "='") { # value is ='something' 1590b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner _xml_value = substr($0,3); 1600b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner sub(/'.*$/,"",_xml_value); 1610b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner sub(/^='[^']*'/,"",$0); 1620b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner } else { 1630b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner _xml_panic("Invalid attribute value syntax for " _xml_attrib ": " $0); 1640b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner } 1650b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner XML_ATTR[_xml_attrib] = _xml_value; # store attribute name/value 1660b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner sub(/^[ \t\n]*/,"",$0); # get rid of remaining leading spaces 1670b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner } 1680b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner return 1; # now return, XML_TYPE/TAG/ATTR/RPATH are set 1690b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner } 1700b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner} 1710b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner 1720b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turnerfunction _xml_panic (msg) { 1730b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner print msg > "/dev/stderr" 1740b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner exit(1) 1750b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner} 1760b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner 1770b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turnerfunction _xml_in_section (sec_begin, sec_end) { 1780b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner if (!match( $0, "^" sec_begin )) return 0; 1790b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner while (!match($0, sec_end "$")) { 1800b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner if (getline <= 0) _xml_panic("Unexpected EOF: " ERRNO); 1810b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner } 1820b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner return 1; 1830b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner} 1840b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner 1850b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turnerfunction _xml_enter (tag) { 1860b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner XML_RPATH = tag "/" XML_RPATH; 1870b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner} 1880b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner 1890b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turnerfunction _xml_exit (tag) { 1900b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner _xml_p = index(XML_RPATH, "/"); 1910b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner _xml_expected = substr(XML_RPATH, 1, _xml_p-1); 1920b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner if (_xml_expected != XML_TAG) 1930b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner _xml_panic("Unexpected close tag: " XML_TAG ", expecting " _xml_expected); 1940b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner XML_RPATH = substr(XML_RPATH, _xml_p+1); 1950b2676bac67c271de9989357f6e3b2e762a7adf1David 'Digit' Turner} 196