Home | History | Annotate | Download | only in awk
      1 # Copyright (C) 2010 The Android Open Source Project
      2 #
      3 # Licensed under the Apache License, Version 2.0 (the "License");
      4 # you may not use this file except in compliance with the License.
      5 # You may obtain a copy of the License at
      6 #
      7 #      http://www.apache.org/licenses/LICENSE-2.0
      8 #
      9 # Unless required by applicable law or agreed to in writing, software
     10 # distributed under the License is distributed on an "AS IS" BASIS,
     11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 # See the License for the specific language governing permissions and
     13 # limitations under the License.
     14 #
     15 # A nawk/gawk script used to extract the list of launchable activities
     16 # from an application's manifest (i.e. AndroidManifest.xml). Usage:
     17 #
     18 #   awk -f <this-script> AndroidManifest.xml
     19 #
     20 
     21 #
     22 # Explanation:
     23 #
     24 # A given application can have several activities, and each activity
     25 # can have several intent filters. We want to only list, in the final
     26 # output, the activities which have a intent-filter that contains the
     27 # following elements:
     28 #
     29 #   <action android:name="android.intent.action.MAIN" />
     30 #   <category android:name="android.intent.category.LAUNCHER" />
     31 #
     32 # To do this, we need hooks called when entering and exiting <activity>
     33 # and <intent-filter> elements.
     34 #
     35 
     36 BEGIN {
     37     while ( xml_event() ) {
     38         # concat xml event type and tag for simpler comparisons
     39         event = XML_TYPE "-" XML_TAG;
     40         # When entering a new <activity>, extract its name and set
     41         # the 'launchable' flag to false.
     42         if ( event == "BEGIN-ACTIVITY" && 
     43              XML_RPATH == "ACTIVITY/APPLICATION/MANIFEST/" ) {
     44             name = XML_ATTR["android:name"];
     45             launchable = 0;
     46         }
     47         # When exiting an <activity>, check that it has a name and
     48         # is launchable. If so, print its name to the output
     49         else if ( event == "END-ACTIVITY" &&
     50                   XML_RPATH == "APPLICATION/MANIFEST/" ) {
     51             if ( name && launchable ) {
     52                 # If the name doesn't contain any dot, we consider
     53                 # that it is just missing the initial one.
     54                 if (index(name, ".") == 0) {
     55                     name = "." name
     56                 }
     57                 print name;
     58             }
     59         }
     60         # When entering an <intent-filter> inside an <activity>, clear
     61         # the 'action' and 'category' variables. They are updated when
     62         # we enter the corresponding elements within the intent-filter.
     63         else if ( event == "BEGIN-INTENT-FILTER" &&
     64                  XML_RPATH == "INTENT-FILTER/ACTIVITY/APPLICATION/MANIFEST/" ) {
     65             action_main = 0;
     66             category_launcher = 0;
     67         }
     68         # When exiting an <intent-filter>, set the 'launchable' flag to true
     69         # for the current activity if both 'action' and 'category' have the
     70         # correct name.
     71         else if ( event == "END-INTENT-FILTER" &&
     72                   XML_RPATH == "ACTIVITY/APPLICATION/MANIFEST/" ) {
     73             if ( category_launcher ) {
     74                 launchable = 1;
     75             }
     76         }
     77         # When entering an <action> element inside an <intent-filter>, record
     78         # its name.
     79         else if ( event == "BEGIN-ACTION" &&
     80                   XML_RPATH == "ACTION/INTENT-FILTER/ACTIVITY/APPLICATION/MANIFEST/" ) {
     81             action_main = 0;
     82             if ( XML_ATTR["android:name"] == "android.intent.action.MAIN" ) {
     83                 action_main = 1;
     84             }
     85         }
     86         # When entering a <category> element inside an <intent-filter>, record
     87         # its name.
     88         else if ( event == "BEGIN-CATEGORY" &&
     89                   XML_RPATH == "CATEGORY/INTENT-FILTER/ACTIVITY/APPLICATION/MANIFEST/" ) {
     90             if ( action_main && XML_ATTR["android:name"] == "android.intent.category.LAUNCHER" ) {
     91                 category_launcher = 1;
     92             }
     93         }
     94     }
     95 }
     96 
     97 
     98 #
     99 # the following is copied directly from xml.awk - see this file for
    100 # usage and implementation details.
    101 #
    102 function xml_event () {
    103     RS=">";
    104     XML_TAG=XML_TYPE="";
    105     split("", XML_ATTR);
    106     while ( 1 ) {
    107         if (_xml_closing) { # delayed direct tag closure
    108             XML_TAG = _xml_closing;
    109             XML_TYPE = "END";
    110             _xml_closing = "";
    111             _xml_exit(XML_TAG);
    112             return 1;
    113         }
    114         if (getline <= 0) return 0; # read new input line
    115         _xml_p = index($0, "<"); # get start marker
    116         if (_xml_p == 0) return 0; # end of file (or malformed input)
    117         $0 = substr($0, _xml_p) # remove anything before '<'
    118         # ignore CData / Comments / Processing instructions / Declarations
    119         if (_xml_in_section("<!\\[[Cc][Dd][Aa][Tt][Aa]\\[", "]]") ||
    120             _xml_in_section("<!--", "--") ||
    121             _xml_in_section("<\\?", "\\?") ||
    122             _xml_in_section("<!", "")) {
    123             continue;
    124         }
    125         if (substr($0, 1, 2) == "</") { # is it a closing tag ?
    126             XML_TYPE = "END";
    127             $0 = substr($0, 3);
    128         } else { # nope, it's an opening one
    129             XML_TYPE = "BEGIN";
    130             $0 = substr($0, 2);
    131         }
    132         XML_TAG = $0
    133         sub("[ \r\n\t/].*$", "", XML_TAG);  # extract tag name
    134         XML_TAG = toupper(XML_TAG);       # uppercase it
    135         if ( XML_TAG !~ /^[A-Z][-+_.:0-9A-Z]*$/ )  # validate it
    136             _xml_panic("Invalid tag name: " XML_TAG);
    137         if (XML_TYPE == "BEGIN") {  # update reverse path
    138             _xml_enter(XML_TAG);
    139         } else {
    140             _xml_exit(XML_TAG);
    141         }
    142         sub("[^ \r\n\t]*[ \r\n\t]*", "", $0); # get rid of tag and spaces
    143         while ($0) { # process attributes
    144             if ($0 == "/") {  # deal with direct closing tag, e.g. </foo>
    145                 _xml_closing = XML_TAG; # record delayed tag closure.
    146                 break
    147             }
    148             _xml_attrib = $0;
    149             sub(/=.*$/,"",_xml_attrib);  # extract attribute name
    150             sub(/^[^=]*/,"",$0);         # remove it from record
    151             _xml_attrib = tolower(_xml_attrib);
    152             if ( _xml_attrib !~ /^[a-z][-+_0-9a-z:]*$/ ) # validate it
    153                 _xml_panic("Invalid attribute name: " _xml_attrib);
    154             if (substr($0,1,2) == "=\"") { # value is ="something"
    155                 _xml_value = substr($0,3);
    156                 sub(/".*$/,"",_xml_value);
    157                 sub(/^="[^"]*"/,"",$0);
    158             } else if (substr($0,1,2) == "='") { # value is ='something'
    159                 _xml_value = substr($0,3);
    160                 sub(/'.*$/,"",_xml_value);
    161                 sub(/^='[^']*'/,"",$0);
    162             } else {
    163                 _xml_panic("Invalid attribute value syntax for " _xml_attrib ": " $0);
    164             }
    165             XML_ATTR[_xml_attrib] = _xml_value;  # store attribute name/value
    166             sub(/^[ \t\r\n]*/,"",$0); # get rid of remaining leading spaces
    167         }
    168         return 1; # now return, XML_TYPE/TAG/ATTR/RPATH are set
    169     }
    170 }
    171 
    172 function _xml_panic (msg) {
    173     print msg > "/dev/stderr"
    174     exit(1)
    175 }
    176 
    177 function _xml_in_section (sec_begin, sec_end) {
    178     if (!match( $0, "^" sec_begin )) return 0;
    179     while (!match($0, sec_end "$")) {
    180         if (getline <= 0) _xml_panic("Unexpected EOF: " ERRNO);
    181     }
    182     return 1;
    183 }
    184 
    185 function _xml_enter (tag) {
    186     XML_RPATH = tag "/" XML_RPATH;
    187 }
    188 
    189 function _xml_exit (tag) {
    190     _xml_p = index(XML_RPATH, "/");
    191     _xml_expected = substr(XML_RPATH, 1, _xml_p-1);
    192     if (_xml_expected != XML_TAG)
    193         _xml_panic("Unexpected close tag: " XML_TAG ", expecting " _xml_expected);
    194     XML_RPATH = substr(XML_RPATH, _xml_p+1);
    195 }
    196