Blob Blame History Raw
;; Fix comma/struct member parser collision
;; Tom Tromey and Leszek Swirski (RH BZ 18156900

commit b2f83c08372136fe9fe7d1df2feb5566c8b883fb
Author: Tom Tromey <tromey@redhat.com>
Date:   Thu Mar 21 15:19:33 2013 +0000

            PR exp/15109:
            * c-exp.y (yylex): Rewrite to push all tokens onto the FIFO.
            Handle FILENAME token.
    gdb/testsuite
            * gdb.cp/cpexprs.exp: Add test for FILENAME:: case.
            * gdb.cp/misc.exp: Add test for FILENAME:: case.

commit 805e1f190887b3b7dea3fd157d58bc25effcf688
Author: Tom Tromey <tromey@redhat.com>
Date:   Mon Nov 4 12:38:10 2013 -0700

    fix PR c++/16117
    
    This patch fixes PR c++/16117.
    
    gdb has an extension so that users can use expressions like FILE::NAME
    to choose a variable of the given name from the given file.  The bug
    is that this extension takes precedence over ordinary C++ expressions
    of the same form.  You might think this is merely hypothetical, but
    now that C++ headers commonly do not use an extension, it is more
    common.
    
    This patch fixes the bug by making two related changes.  First, it
    changes gdb to prefer the ordinary C++ meaning of a symbol over the
    extended meaning.  Second, it arranges for single-quoting of the
    symbol to indicate a preference for the extension.
    
    Built and regtested on x86-64 Fedora 18.
    New test case included.

    
    2013-11-15  Tom Tromey  <tromey@redhat.com>
    
            PR c++/16117:
            * c-exp.y (lex_one_token): Add "is_quoted_name" argument.
            (classify_name): Likewise.  Prefer a field of "this" over a
            filename.
            (classify_inner_name, yylex): Update.
    
    2013-11-15  Tom Tromey  <tromey@redhat.com>
    
            * gdb.texinfo (Variables): Note gdb rules for ambiguous cases.
            Add example.
    
    2013-11-15  Tom Tromey  <tromey@redhat.com>
    
            * gdb.cp/includefile: New file.
            * gdb.cp/filename.exp: New file.
            * gdb.cp/filename.cc: New file.

commit 59498c305e6f1db2a1ed8d44cb58f0d24ec092fe
Author: Leszek Swirski <leszeks@google.com>
Date:   Thu Jan 25 16:20:47 2018 +0000

    Do not classify C struct members as a filename
    
    There is existing logic in C/C++ expression parsing to avoid classifying
    names as a filename when they are a field on the this object. This
    change extends this logic to also avoid classifying names after a
    struct-op (-> or .) as a filename, which otherwise causes a syntax
    error.
    
    Thus, it is now possible in the file
    
        #include <map>
        struct D {
            void map();
        }
        D d;
    
    to call
    
        (gdb) print d.map()
    
    where previously this would have been a syntax error.
    
    Tested on gdb.cp/*.exp
    
    gdb/ChangeLog:
    
            * c-exp.y (lex_one_token, classify_name, yylex): Don't classify
            names after a structop as a filename
    
    gdb/testsuite/ChangeLog:
    
            * gdb.cp/filename.cc, gdb.cp/filename.exp: Test that member
            functions with the same name as an include file are parsed
            correctly.

*** gdb-7.6.1-orig/gdb/c-exp.y	2020-04-21 16:59:20.636119459 -0400
--- gdb-7.6.1/gdb/c-exp.y	2020-04-22 16:41:04.560602687 -0400
*************** static int last_was_structop;
*** 2352,2358 ****
  /* Read one token, getting characters through lexptr.  */
  
  static int
! lex_one_token (void)
  {
    int c;
    int namelen;
--- 2352,2358 ----
  /* Read one token, getting characters through lexptr.  */
  
  static int
! lex_one_token (int *is_quoted_name)
  {
    int c;
    int namelen;
*************** lex_one_token (void)
*** 2362,2367 ****
--- 2362,2368 ----
    char *copy;
  
    last_was_structop = 0;
+   *is_quoted_name = 0;
  
   retry:
  
*************** lex_one_token (void)
*** 2402,2408 ****
  
  	lexptr += 2;
  	yylval.opcode = tokentab2[i].opcode;
! 	if (parse_completion && tokentab2[i].token == ARROW)
  	  last_was_structop = 1;
  	return tokentab2[i].token;
        }
--- 2403,2409 ----
  
  	lexptr += 2;
  	yylval.opcode = tokentab2[i].opcode;
! 	if (tokentab2[i].token == ARROW)
  	  last_was_structop = 1;
  	return tokentab2[i].token;
        }
*************** lex_one_token (void)
*** 2426,2432 ****
  	  saw_name_at_eof = 0;
  	  return COMPLETE;
  	}
!       else if (saw_structop)
  	return COMPLETE;
        else
          return 0;
--- 2427,2433 ----
  	  saw_name_at_eof = 0;
  	  return COMPLETE;
  	}
!       else if (parse_completion && saw_structop)
  	return COMPLETE;
        else
          return 0;
*************** lex_one_token (void)
*** 2465,2472 ****
        /* Might be a floating point number.  */
        if (lexptr[1] < '0' || lexptr[1] > '9')
  	{
! 	  if (parse_completion)
! 	    last_was_structop = 1;
  	  goto symbol;		/* Nope, must be a symbol. */
  	}
        /* FALL THRU into number case.  */
--- 2466,2472 ----
        /* Might be a floating point number.  */
        if (lexptr[1] < '0' || lexptr[1] > '9')
  	{
! 	  last_was_structop = 1;
  	  goto symbol;		/* Nope, must be a symbol. */
  	}
        /* FALL THRU into number case.  */
*************** lex_one_token (void)
*** 2606,2611 ****
--- 2606,2613 ----
  	      {
  		++tokstart;
  		namelen = lexptr - tokstart - 1;
+ 		*is_quoted_name = 1;
+ 
  		goto tryname;
  	      }
  	    else if (host_len > 1)
*************** static struct obstack name_obstack;
*** 2748,2757 ****
  
  /* Classify a NAME token.  The contents of the token are in `yylval'.
     Updates yylval and returns the new token type.  BLOCK is the block
!    in which lookups start; this can be NULL to mean the global
!    scope.  */
  static int
! classify_name (const struct block *block)
  {
    struct symbol *sym;
    char *copy;
--- 2750,2763 ----
  
  /* Classify a NAME token.  The contents of the token are in `yylval'.
     Updates yylval and returns the new token type.  BLOCK is the block
!    in which lookups start; this can be NULL to mean the global scope.
!    IS_QUOTED_NAME is non-zero if the name token was originally quoted
!    in single quotes.  IS_AFTER_STRUCTOP is true if this name follows
!    a structure operator -- either '.' or ARROW.  */
! 
  static int
! classify_name (const struct block *block, int is_quoted_name,
! 	       int is_after_structop)
  {
    struct symbol *sym;
    char *copy;
*************** classify_name (const struct block *block
*** 2775,2790 ****
      }
    else if (!sym)
      {
-       /* See if it's a file name. */
-       struct symtab *symtab;
- 
-       symtab = lookup_symtab (copy);
-       if (symtab)
- 	{
- 	  yylval.bval = BLOCKVECTOR_BLOCK (BLOCKVECTOR (symtab), STATIC_BLOCK);
- 	  return FILENAME;
- 	}
- 
        /* If we found a field of 'this', we might have erroneously
  	 found a constructor where we wanted a type name.  Handle this
  	 case by noticing that we found a constructor and then look up
--- 2781,2786 ----
*************** classify_name (const struct block *block
*** 2804,2809 ****
--- 2800,2825 ----
  	      return TYPENAME;
  	    }
  	}
+ 
+       /* If we found a field on the "this" object, or we are looking
+ 	 up a field on a struct, then we want to prefer it over a
+ 	 filename.  However, if the name was quoted, then it is better
+ 	 to check for a filename or a block, since this is the only
+ 	 way the user has of requiring the extension to be used.  */
+       if ((is_a_field_of_this.type == NULL && !is_after_structop) 
+ 	  || is_quoted_name)
+ 	{
+ 	  /* See if it's a file name. */
+ 	  struct symtab *symtab;
+ 
+ 	  symtab = lookup_symtab (copy);
+ 	  if (symtab)
+ 	    {
+ 	      yylval.bval = BLOCKVECTOR_BLOCK (BLOCKVECTOR (symtab),
+ 					       STATIC_BLOCK);
+ 	      return FILENAME;
+ 	    }
+ 	}
      }
  
    if (sym && SYMBOL_CLASS (sym) == LOC_TYPEDEF)
*************** classify_inner_name (const struct block
*** 2873,2879 ****
    char *copy;
  
    if (context == NULL)
!     return classify_name (block);
  
    type = check_typedef (context);
    if (TYPE_CODE (type) != TYPE_CODE_STRUCT
--- 2889,2895 ----
    char *copy;
  
    if (context == NULL)
!     return classify_name (block, 0, 0);
  
    type = check_typedef (context);
    if (TYPE_CODE (type) != TYPE_CODE_STRUCT
*************** static int
*** 2941,2998 ****
  yylex (void)
  {
    token_and_value current;
!   int first_was_coloncolon, last_was_coloncolon, first_iter;
    struct type *context_type = NULL;
  
    if (popping && !VEC_empty (token_and_value, token_fifo))
!     {
!       token_and_value tv = *VEC_index (token_and_value, token_fifo, 0);
!       VEC_ordered_remove (token_and_value, token_fifo, 0);
!       yylval = tv.value;
!       return tv.token;
!     }
    popping = 0;
  
!   current.token = lex_one_token ();
    if (current.token == NAME)
!     current.token = classify_name (expression_context_block);
    if (parse_language->la_language != language_cplus
!       || (current.token != TYPENAME && current.token != COLONCOLON))
      return current.token;
  
!   first_was_coloncolon = current.token == COLONCOLON;
!   last_was_coloncolon = first_was_coloncolon;
!   obstack_free (&name_obstack, obstack_base (&name_obstack));
!   if (!last_was_coloncolon)
!     {
!       obstack_grow (&name_obstack, yylval.sval.ptr, yylval.sval.length);
!       context_type = yylval.tsym.type;
!     }
    current.value = yylval;
!   first_iter = 1;
    while (1)
      {
!       token_and_value next;
  
!       next.token = lex_one_token ();
!       next.value = yylval;
  
!       if (next.token == NAME && last_was_coloncolon)
  	{
  	  int classification;
  
! 	  classification = classify_inner_name (first_was_coloncolon
! 						? NULL
! 						: expression_context_block,
! 						context_type);
  	  /* We keep going until we either run out of names, or until
  	     we have a qualified name which is not a type.  */
  	  if (classification != TYPENAME && classification != NAME)
! 	    {
! 	      /* Push the final component and leave the loop.  */
! 	      VEC_safe_push (token_and_value, token_fifo, &next);
! 	      break;
! 	    }
  
  	  /* Update the partial name we are constructing.  */
  	  if (context_type != NULL)
--- 2957,3055 ----
  yylex (void)
  {
    token_and_value current;
!   int first_was_coloncolon, last_was_coloncolon;
    struct type *context_type = NULL;
+   int last_to_examine, next_to_examine, checkpoint;
+   const struct block *search_block;
+   int is_quoted_name, last_lex_was_structop;
  
    if (popping && !VEC_empty (token_and_value, token_fifo))
!     goto do_pop;
    popping = 0;
  
!   last_lex_was_structop = last_was_structop;
! 
!   /* Read the first token and decide what to do.  Most of the
!      subsequent code is C++-only; but also depends on seeing a "::" or
!      name-like token.  */
!   current.token = lex_one_token (&is_quoted_name);
    if (current.token == NAME)
!     current.token = classify_name (expression_context_block, is_quoted_name,
! 				   last_lex_was_structop);
    if (parse_language->la_language != language_cplus
!       || (current.token != TYPENAME && current.token != COLONCOLON
! 	  && current.token != FILENAME))
      return current.token;
  
!   /* Read any sequence of alternating "::" and name-like tokens into
!      the token FIFO.  */
    current.value = yylval;
!   VEC_safe_push (token_and_value, token_fifo, &current);
!   last_was_coloncolon = current.token == COLONCOLON;
    while (1)
      {
!       int ignore;
! 
!       /* We ignore quoted names other than the very first one.
! 	 Subsequent ones do not have any special meaning.  */
!       current.token = lex_one_token (&ignore);
!       current.value = yylval;
!       VEC_safe_push (token_and_value, token_fifo, &current);
  
!       if ((last_was_coloncolon && current.token != NAME)
! 	  || (!last_was_coloncolon && current.token != COLONCOLON))
! 	break;
!       last_was_coloncolon = !last_was_coloncolon;
!     }
!   popping = 1;
  
!   /* We always read one extra token, so compute the number of tokens
!      to examine accordingly.  */
!   last_to_examine = VEC_length (token_and_value, token_fifo) - 2;
!   next_to_examine = 0;
! 
!   current = *VEC_index (token_and_value, token_fifo, next_to_examine);
!   ++next_to_examine;
! 
!   obstack_free (&name_obstack, obstack_base (&name_obstack));
!   checkpoint = 0;
!   if (current.token == FILENAME)
!     search_block = current.value.bval;
!   else if (current.token == COLONCOLON)
!     search_block = NULL;
!   else
!     {
!       gdb_assert (current.token == TYPENAME);
!       search_block = expression_context_block;
!       obstack_grow (&name_obstack, current.value.sval.ptr,
! 		    current.value.sval.length);
!       context_type = current.value.tsym.type;
!       checkpoint = 1;
!     }
! 
!   first_was_coloncolon = current.token == COLONCOLON;
!   last_was_coloncolon = first_was_coloncolon;
! 
!   while (next_to_examine <= last_to_examine)
!     {
!       token_and_value *next;
! 
!       next = VEC_index (token_and_value, token_fifo, next_to_examine);
!       ++next_to_examine;
! 
!       if (next->token == NAME && last_was_coloncolon)
  	{
  	  int classification;
  
! 	  yylval = next->value;
! 	  classification = classify_inner_name (search_block, context_type);
  	  /* We keep going until we either run out of names, or until
  	     we have a qualified name which is not a type.  */
  	  if (classification != TYPENAME && classification != NAME)
! 	    break;
! 
! 	  /* Accept up to this token.  */
! 	  checkpoint = next_to_examine;
  
  	  /* Update the partial name we are constructing.  */
  	  if (context_type != NULL)
*************** yylex (void)
*** 3000,3007 ****
  	      /* We don't want to put a leading "::" into the name.  */
  	      obstack_grow_str (&name_obstack, "::");
  	    }
! 	  obstack_grow (&name_obstack, next.value.sval.ptr,
! 			next.value.sval.length);
  
  	  yylval.sval.ptr = obstack_base (&name_obstack);
  	  yylval.sval.length = obstack_object_size (&name_obstack);
--- 3057,3064 ----
  	      /* We don't want to put a leading "::" into the name.  */
  	      obstack_grow_str (&name_obstack, "::");
  	    }
! 	  obstack_grow (&name_obstack, next->value.sval.ptr,
! 			next->value.sval.length);
  
  	  yylval.sval.ptr = obstack_base (&name_obstack);
  	  yylval.sval.length = obstack_object_size (&name_obstack);
*************** yylex (void)
*** 3015,3052 ****
  
  	  context_type = yylval.tsym.type;
  	}
!       else if (next.token == COLONCOLON && !last_was_coloncolon)
  	last_was_coloncolon = 1;
        else
  	{
  	  /* We've reached the end of the name.  */
- 	  VEC_safe_push (token_and_value, token_fifo, &next);
  	  break;
  	}
- 
-       first_iter = 0;
-     }
- 
-   popping = 1;
- 
-   /* If we ended with a "::", insert it too.  */
-   if (last_was_coloncolon)
-     {
-       token_and_value cc;
-       memset (&cc, 0, sizeof (token_and_value));
-       if (first_was_coloncolon && first_iter)
- 	{
- 	  yylval = cc.value;
- 	  return COLONCOLON;
- 	}
-       cc.token = COLONCOLON;
-       VEC_safe_insert (token_and_value, token_fifo, 0, &cc);
      }
  
    yylval = current.value;
-   yylval.sval.ptr = obstack_copy0 (&expansion_obstack,
- 				   yylval.sval.ptr,
- 				   yylval.sval.length);
    return current.token;
  }
  
--- 3072,3103 ----
  
  	  context_type = yylval.tsym.type;
  	}
!       else if (next->token == COLONCOLON && !last_was_coloncolon)
  	last_was_coloncolon = 1;
        else
  	{
  	  /* We've reached the end of the name.  */
  	  break;
  	}
      }
  
+   /* If we have a replacement token, install it as the first token in
+      the FIFO, and delete the other constituent tokens.  */
+   if (checkpoint > 0)
+     {
+       current.value.sval.ptr = obstack_copy0 (&expansion_obstack,
+ 					      current.value.sval.ptr,
+ 					      current.value.sval.length);
+ 
+       VEC_replace (token_and_value, token_fifo, 0, &current);
+       if (checkpoint > 1)
+ 	VEC_block_remove (token_and_value, token_fifo, 1, checkpoint - 1);
+     }
+ 
+  do_pop:
+   current = *VEC_index (token_and_value, token_fifo, 0);
+   VEC_ordered_remove (token_and_value, token_fifo, 0);
    yylval = current.value;
    return current.token;
  }
  
*** gdb-7.6.1-orig/gdb/doc/gdb.texinfo	2020-04-21 16:59:20.581119544 -0400
--- gdb-7.6.1/gdb/doc/gdb.texinfo	2020-04-22 16:26:14.350165928 -0400
*************** $4 = 0
*** 8219,8229 ****
  @end smallexample
  
  @cindex C@t{++} scope resolution
! These uses of @samp{::} are very rarely in conflict with the very similar
! use of the same notation in C@t{++}.  @value{GDBN} also supports use of the C@t{++}
! scope resolution operator in @value{GDBN} expressions.
! @c FIXME: Um, so what happens in one of those rare cases where it's in
! @c conflict??  --mew
  
  @cindex wrong values
  @cindex variable values, wrong
--- 8219,8242 ----
  @end smallexample
  
  @cindex C@t{++} scope resolution
! These uses of @samp{::} are very rarely in conflict with the very
! similar use of the same notation in C@t{++}.  When they are in
! conflict, the C@t{++} meaning takes precedence; however, this can be
! overridden by quoting the file or function name with single quotes.
! 
! For example, suppose the program is stopped in a method of a class
! that has a field named @code{includefile}, and there is also an
! include file named @file{includefile} that defines a variable,
! @code{some_global}.
! 
! @smallexample
! (@value{GDBP}) p includefile
! $1 = 23
! (@value{GDBP}) p includefile::some_global
! A syntax error in expression, near `'.
! (@value{GDBP}) p 'includefile'::some_global
! $2 = 27
! @end smallexample
  
  @cindex wrong values
  @cindex variable values, wrong
*** gdb-7.6.1-orig/gdb/testsuite/gdb.cp/cpexprs.exp	2020-04-21 16:59:20.413119804 -0400
--- gdb-7.6.1/gdb/testsuite/gdb.cp/cpexprs.exp	2020-04-22 16:18:30.938538563 -0400
*************** gdb_test "p CV_f(int)"   { = {int \(int\
*** 731,735 ****
--- 731,738 ----
  gdb_test "p CV_f(CV::t)" { = {int \(int\)} 0x[0-9a-f]+ <CV_f\(int\)>}
  gdb_test "p CV_f(CV::i)" " = 43"
  
+ gdb_test "p CV_f('cpexprs.cc'::CV::t)" \
+     { = {int \(int\)} 0x[0-9a-f]+ <CV_f\(int\)>}
+ 
  gdb_exit
  return 0
*** gdb-7.6.1-orig/gdb/testsuite/gdb.cp/misc.exp	2020-04-21 16:59:20.411119807 -0400
--- gdb-7.6.1/gdb/testsuite/gdb.cp/misc.exp	2020-04-22 16:18:30.939538558 -0400
*************** gdb_test "print (bool)17.93" "\\$\[0-9\]
*** 107,109 ****
--- 107,112 ----
  gdb_test "print (bool)0.0" "\\$\[0-9\]* = false" "(bool)0.0"
  gdb_test "print (int)true" "\\$\[0-9\]* = 1" "(int)true"
  gdb_test "print (int)false" "\\$\[0-9\]* = 0" "(int)false"
+ 
+ gdb_test "print 'misc.cc'::v_bool" " = true" \
+     "expression using block qualifier"
*** gdb-7.6.1-orig/gdb/testsuite/gdb.cp/filename.cc	1969-12-31 19:00:00.000000000 -0500
--- gdb-7.6.1/gdb/testsuite/gdb.cp/filename.cc	2020-04-22 16:54:00.973626918 -0400
***************
*** 0 ****
--- 1,57 ----
+ /* This testcase is part of GDB, the GNU debugger.
+ 
+    Copyright 2013 Free Software Foundation, Inc.
+ 
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+ 
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+ 
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+ 
+ #include "includefile"
+ 
+ class C {
+ public:
+   int includefile[1];
+ 
+   C() {
+     includefile[0] = 23;
+   }
+ 
+   void m() {
+     /* stop inside C */
+   }
+ };
+ 
+ class D {
+ public:
+   int includefile();
+ 
+   void m() {
+     /* stop inside D */
+   }
+ };
+ 
+ int D::includefile() {
+   return 24;
+ }
+ 
+ int main() {
+   C c;
+   C* pc = &c;
+   c.m();
+ 
+   D d;
+   D* pd = &d;
+   d.m();
+ 
+   /* stop outside */
+   return 0;
+ }
*** gdb-7.6.1-orig/gdb/testsuite/gdb.cp/filename.exp	1969-12-31 19:00:00.000000000 -0500
--- gdb-7.6.1/gdb/testsuite/gdb.cp/filename.exp	2020-04-22 16:34:25.685647330 -0400
***************
*** 0 ****
--- 1,49 ----
+ # Copyright 2013 Free Software Foundation, Inc.
+ 
+ # This program is free software; you can redistribute it and/or modify
+ # it under the terms of the GNU General Public License as published by
+ # the Free Software Foundation; either version 3 of the License, or
+ # (at your option) any later version.
+ #
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU General Public License for more details.
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ 
+ if { [skip_cplus_tests] } { continue }
+ 
+ standard_testfile .cc
+ 
+ if {[prepare_for_testing $testfile.exp $testfile $srcfile {debug c++}]} {
+     return -1
+ }
+ 
+ if ![runto_main] then {
+     perror "couldn't run to main"
+     continue
+ }
+ 
+ gdb_breakpoint [gdb_get_line_number "stop inside C"]
+ gdb_continue_to_breakpoint "stop inside C"
+ 
+ gdb_test "print includefile\[0\]" " = 23"
+ gdb_test "print this->includefile\[0\]" " = 23"
+ gdb_test "print 'includefile'::some_global" " = 27"
+ 
+ gdb_breakpoint [gdb_get_line_number "stop inside D"]
+ gdb_continue_to_breakpoint "stop inside D"
+ 
+ gdb_test "print includefile()" " = 24"
+ gdb_test "print this->includefile()" " = 24"
+ gdb_test "print 'includefile'::some_global" " = 27"
+ 
+ gdb_breakpoint [gdb_get_line_number "stop outside"]
+ gdb_continue_to_breakpoint "stop outside"
+ 
+ gdb_test "print c.includefile\[0\]" " = 23"
+ gdb_test "print pc->includefile\[0\]" " = 23"
+ gdb_test "print d.includefile()" " = 24"
+ gdb_test "print pd->includefile()" " = 24"
*** gdb-7.6.1-orig/gdb/testsuite/gdb.cp/includefile	1969-12-31 19:00:00.000000000 -0500
--- gdb-7.6.1/gdb/testsuite/gdb.cp/includefile	2020-04-22 16:26:14.352165918 -0400
***************
*** 0 ****
--- 1,18 ----
+ /* This testcase is part of GDB, the GNU debugger.
+ 
+    Copyright 2013 Free Software Foundation, Inc.
+ 
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+ 
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+ 
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+ 
+ int some_global = 27;