about summary refs log blame commit diff stats
path: root/html/subx/001help.cc.html
blob: 2aef411f2d1c00fc167c682e57a8bae586986e9b (plain) (tree)
1
2
3
4
5
6
7
8
9




                                                                                          
                                         


                                                                                                               
                                                 

                       

                                                                                                 
                    
                                     







                                                                           
                             
































                                                                                 
                                                                                                                                   




                                                                                                                                                  
                                                                                                                                                                                                                                                                                                                                                                                                                       

                                                                                                                                  
                                                                                                                                                                                                                                                                               






                                                                                                                                                                                              
                                                                                                                                                                                                                                                                                                                                                                                                 







                                                                                                                                                                                                                       
                                                                                                                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                  
                                                                                                                                                    
                                                                                                                              



                                                                                                                                                           
                                                                                                                                                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                      


                                                                                                                                                   
                                                                                                                                                                                                                            
                                                                                                                              
                                                                                                                                                                           




                                                                                                                                                   
                                                                                                                                                                                           

                                                                                                                                                                



                                                                                                         
                                                                                                                                                                                                     



                                                                                                                   
                                                                                                                      
                                                                               
                                                                                                                                                                                       
                                                                                                                                                                                                                                                                                                    
















                                                                                                                                                                                                       
















































                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
                                                                          







                                                                                                                                                                                                       
                                                                          
                                                                                                                                               
                                                                          


                                                                                                                                                   
                                                                          







































































































































                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
                                          















                                                                                                                         



                                     
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Mu - subx/001help.cc</title>
<meta name="Generator" content="Vim/8.0">
<meta name="plugin-version" content="vim7.4_v2">
<meta name="syntax" content="cpp">
<meta name="settings" content="number_lines,use_css,pre_wrap,no_foldcolumn,expand_tabs,line_ids,prevent_copy=">
<meta name="colorscheme" content="minimal-light">
<style type="text/css">
<!--
pre { white-space: pre-wrap; font-family: monospace; color: #000000; background-color: #c6c6c6; }
body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; }
a { color:inherit; }
* { font-size:12pt; font-size: 1em; }
.CommentedCode { color: #8a8a8a; }
.LineNr { }
.Constant { color: #008787; }
.Delimiter { color: #c000c0; }
.Special { color: #d70000; }
.Identifier { color: #af5f00; }
.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; }
.Comment { color: #005faf; }
.cSpecial { color: #008000; }
-->
</style>

<script type='text/javascript'>
<!--

/* function to open any folds containing a jumped-to line before jumping to it */
function JumpToLine()
{
  var lineNum;
  lineNum = window.location.hash;
  lineNum = lineNum.substr(1); /* strip off '#' */

  if (lineNum.indexOf('L') == -1) {
    lineNum = 'L'+lineNum;
  }
  lineElem = document.getElementById(lineNum);
  /* Always jump to new location even if the line was hidden inside a fold, or
   * we corrected the raw number to a line ID.
   */
  if (lineElem) {
    lineElem.scrollIntoView(true);
  }
  return true;
}
if ('onhashchange' in window) {
  window.onhashchange = JumpToLine;
}

-->
</script>
</head>
<body onload='JumpToLine();'>
<a href='https://github.com/akkartik/mu/blob/master/subx/001help.cc'>https://github.com/akkartik/mu/blob/master/subx/001help.cc</a>
<pre id='vimCodeElement'>
<span id="L1" class="LineNr">  1 </span><span class="Comment">//: Everything this project/binary supports.</span>
<span id="L2" class="LineNr">  2 </span><span class="Comment">//: This should give you a sense for what to look forward to in later layers.</span>
<span id="L3" class="LineNr">  3 </span>
<span id="L4" class="LineNr">  4 </span><span class="Delimiter">:(before &quot;End Commandline Parsing&quot;)</span>
<span id="L5" class="LineNr">  5 </span><span class="Normal">if</span> <span class="Delimiter">(</span>argc &lt;= <span class="Constant">1</span> || <a href='001help.cc.html#L98'>is_equal</a><span class="Delimiter">(</span>argv[<span class="Constant">1</span>]<span class="Delimiter">,</span> <span class="Constant">&quot;--help&quot;</span><span class="Delimiter">))</span> <span class="Delimiter">{</span>
<span id="L6" class="LineNr">  6 </span>  <span class="Comment">//: this is the functionality later layers will provide</span>
<span id="L7" class="LineNr">  7 </span>  <span class="Comment">// currently no automated tests for commandline arg parsing</span>
<span id="L8" class="LineNr">  8 </span>  cerr &lt;&lt; get<span class="Delimiter">(</span><span class="Special"><a href='001help.cc.html#L53'>Help</a></span><span class="Delimiter">,</span> <span class="Constant">&quot;usage&quot;</span><span class="Delimiter">);</span>
<span id="L9" class="LineNr">  9 </span>  <span class="Identifier">return</span> <span class="Constant">0</span><span class="Delimiter">;</span>
<span id="L10" class="LineNr"> 10 </span><span class="Delimiter">}</span>
<span id="L11" class="LineNr"> 11 </span>
<span id="L12" class="LineNr"> 12 </span><span class="Comment">//: Support for option parsing.</span>
<span id="L13" class="LineNr"> 13 </span><span class="Comment">//: Options always begin with '--' and are always the first arguments. An</span>
<span id="L14" class="LineNr"> 14 </span><span class="Comment">//: option will never follow a non-option.</span>
<span id="L15" class="LineNr"> 15 </span><span class="Normal">char</span>** <a href='001help.cc.html#L15'>arg</a> = &amp;argv[<span class="Constant">1</span>]<span class="Delimiter">;</span>
<span id="L16" class="LineNr"> 16 </span><span class="Normal">while</span> <span class="Delimiter">(</span>argc &gt; <span class="Constant">1</span> &amp;&amp; <a href='001help.cc.html#L102'>starts_with</a><span class="Delimiter">(</span>*arg<span class="Delimiter">,</span> <span class="Constant">&quot;--&quot;</span><span class="Delimiter">))</span> <span class="Delimiter">{</span>
<span id="L17" class="LineNr"> 17 </span>  <span class="Normal">if</span> <span class="Delimiter">(</span><span class="Constant">false</span><span class="Delimiter">)</span>
<span id="L18" class="LineNr"> 18 </span>    <span class="Delimiter">;</span>  <span class="Comment">// no-op branch just so any further additions can consistently always start with 'else'</span>
<span id="L19" class="LineNr"> 19 </span>  <span class="Comment">// End Commandline Options(*arg)</span>
<span id="L20" class="LineNr"> 20 </span>  <span class="Normal">else</span>
<span id="L21" class="LineNr"> 21 </span>    cerr &lt;&lt; <span class="Constant">&quot;skipping unknown option &quot;</span> &lt;&lt; *arg &lt;&lt; <span class="cSpecial">'\n'</span><span class="Delimiter">;</span>
<span id="L22" class="LineNr"> 22 </span>  --argc<span class="Delimiter">;</span>  ++argv<span class="Delimiter">;</span>  ++arg<span class="Delimiter">;</span>
<span id="L23" class="LineNr"> 23 </span><span class="Delimiter">}</span>
<span id="L24" class="LineNr"> 24 </span>
<span id="L25" class="LineNr"> 25 </span><span class="Normal">if</span> <span class="Delimiter">(</span><a href='001help.cc.html#L98'>is_equal</a><span class="Delimiter">(</span>argv[<span class="Constant">1</span>]<span class="Delimiter">,</span> <span class="Constant">&quot;help&quot;</span><span class="Delimiter">))</span> <span class="Delimiter">{</span>
<span id="L26" class="LineNr"> 26 </span>  <span class="Normal">if</span> <span class="Delimiter">(</span>argc == <span class="Constant">2</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L27" class="LineNr"> 27 </span>    cerr &lt;&lt; <span class="Constant">&quot;help on what?\n&quot;</span><span class="Delimiter">;</span>
<span id="L28" class="LineNr"> 28 </span>    <a href='001help.cc.html#L46'>help_contents</a><span class="Delimiter">();</span>
<span id="L29" class="LineNr"> 29 </span>    <span class="Identifier">return</span> <span class="Constant">0</span><span class="Delimiter">;</span>
<span id="L30" class="LineNr"> 30 </span>  <span class="Delimiter">}</span>
<span id="L31" class="LineNr"> 31 </span>  string key<span class="Delimiter">(</span>argv[<span class="Constant">2</span>]<span class="Delimiter">);</span>
<span id="L32" class="LineNr"> 32 </span>  <span class="Comment">// End Help Special-cases(key)</span>
<span id="L33" class="LineNr"> 33 </span>  <span class="Normal">if</span> <span class="Delimiter">(</span><a href='001help.cc.html#L256'>contains_key</a><span class="Delimiter">(</span><span class="Special"><a href='001help.cc.html#L53'>Help</a></span><span class="Delimiter">,</span> key<span class="Delimiter">))</span> <span class="Delimiter">{</span>
<span id="L34" class="LineNr"> 34 </span>    cerr &lt;&lt; get<span class="Delimiter">(</span><span class="Special"><a href='001help.cc.html#L53'>Help</a></span><span class="Delimiter">,</span> key<span class="Delimiter">);</span>
<span id="L35" class="LineNr"> 35 </span>    <span class="Identifier">return</span> <span class="Constant">0</span><span class="Delimiter">;</span>
<span id="L36" class="LineNr"> 36 </span>  <span class="Delimiter">}</span>
<span id="L37" class="LineNr"> 37 </span>  <span class="Normal">else</span> <span class="Delimiter">{</span>
<span id="L38" class="LineNr"> 38 </span>    cerr &lt;&lt; <span class="Constant">&quot;No help found for '&quot;</span> &lt;&lt; key &lt;&lt; <span class="Constant">&quot;'\n&quot;</span><span class="Delimiter">;</span>
<span id="L39" class="LineNr"> 39 </span>    <a href='001help.cc.html#L46'>help_contents</a><span class="Delimiter">();</span>
<span id="L40" class="LineNr"> 40 </span>    cerr &lt;&lt; <span class="Constant">&quot;Please check your command for typos.\n&quot;</span><span class="Delimiter">;</span>
<span id="L41" class="LineNr"> 41 </span>    <span class="Identifier">return</span> <span class="Constant">1</span><span class="Delimiter">;</span>
<span id="L42" class="LineNr"> 42 </span>  <span class="Delimiter">}</span>
<span id="L43" class="LineNr"> 43 </span><span class="Delimiter">}</span>
<span id="L44" class="LineNr"> 44 </span>
<span id="L45" class="LineNr"> 45 </span><span class="Delimiter">:(code)</span>
<span id="L46" class="LineNr"> 46 </span><span class="Normal">void</span> <a href='001help.cc.html#L46'>help_contents</a><span class="Delimiter">()</span> <span class="Delimiter">{</span>
<span id="L47" class="LineNr"> 47 </span>  cerr &lt;&lt; <span class="Constant">&quot;Available top-level topics:\n&quot;</span><span class="Delimiter">;</span>
<span id="L48" class="LineNr"> 48 </span>  cerr &lt;&lt; <span class="Constant">&quot;  usage\n&quot;</span><span class="Delimiter">;</span>
<span id="L49" class="LineNr"> 49 </span>  <span class="Comment">// End Help Contents</span>
<span id="L50" class="LineNr"> 50 </span><span class="Delimiter">}</span>
<span id="L51" class="LineNr"> 51 </span>
<span id="L52" class="LineNr"> 52 </span><span class="Delimiter">:(before &quot;End Globals&quot;)</span>
<span id="L53" class="LineNr"> 53 </span>map&lt;string<span class="Delimiter">,</span> string&gt; <span class="Special"><a href='001help.cc.html#L53'>Help</a></span><span class="Delimiter">;</span>
<span id="L54" class="LineNr"> 54 </span><span class="Delimiter">:(before &quot;End Includes&quot;)</span>
<span id="L55" class="LineNr"> 55 </span><span class="Comment">#include &lt;map&gt;</span>
<span id="L56" class="LineNr"> 56 </span><span class="Normal">using</span> std::map<span class="Delimiter">;</span>
<span id="L57" class="LineNr"> 57 </span><span class="Delimiter">:(before &quot;End One-time Setup&quot;)</span>
<span id="L58" class="LineNr"> 58 </span><a href='001help.cc.html#L60'>init_help</a><span class="Delimiter">();</span>
<span id="L59" class="LineNr"> 59 </span><span class="Delimiter">:(code)</span>
<span id="L60" class="LineNr"> 60 </span><span class="Normal">void</span> <a href='001help.cc.html#L60'>init_help</a><span class="Delimiter">()</span> <span class="Delimiter">{</span>
<span id="L61" class="LineNr"> 61 </span>  <a href='001help.cc.html#L252'>put</a><span class="Delimiter">(</span><span class="Special"><a href='001help.cc.html#L53'>Help</a></span><span class="Delimiter">,</span> <span class="Constant">&quot;usage&quot;</span><span class="Delimiter">,</span>
<span id="L62" class="LineNr"> 62 </span>    <span class="Constant">&quot;Welcome to SubX, a better way to <a href='011run.cc.html#L99'>program</a> in machine code.\n&quot;</span>
<span id="L63" class="LineNr"> 63 </span>    <span class="Constant">&quot;SubX uses a subset of the x86 instruction set. SubX programs will run\n&quot;</span>
<span id="L64" class="LineNr"> 64 </span>    <span class="Constant">&quot;without modification on Linux computers.\n&quot;</span>
<span id="L65" class="LineNr"> 65 </span>    <span class="Constant">&quot;It provides a better experience and better error messages than\n&quot;</span>
<span id="L66" class="LineNr"> 66 </span>    <span class="Constant">&quot;programming directly in machine <a href='031check_operands.cc.html#L11'>code</a>, but you have to stick to the\n&quot;</span>
<span id="L67" class="LineNr"> 67 </span>    <span class="Constant">&quot;instructions it supports.\n&quot;</span>
<span id="L68" class="LineNr"> 68 </span>    <span class="Constant">&quot;\n&quot;</span>
<span id="L69" class="LineNr"> 69 </span>    <span class="Constant">&quot;== Ways to invoke subx\n&quot;</span>
<span id="L70" class="LineNr"> 70 </span>    <span class="Constant">&quot;- Run tests:\n&quot;</span>
<span id="L71" class="LineNr"> 71 </span>    <span class="Constant">&quot;    subx test\n&quot;</span>
<span id="L72" class="LineNr"> 72 </span>    <span class="Constant">&quot;- See this message:\n&quot;</span>
<span id="L73" class="LineNr"> 73 </span>    <span class="Constant">&quot;    subx --help\n&quot;</span>
<span id="L74" class="LineNr"> 74 </span>    <span class="Constant">&quot;- Convert a textual SubX <a href='011run.cc.html#L99'>program</a> into a standard ELF binary that you can\n&quot;</span>
<span id="L75" class="LineNr"> 75 </span>    <span class="Constant">&quot;  <a href='011run.cc.html#L83'>run</a> on your computer:\n&quot;</span>
<span id="L76" class="LineNr"> 76 </span>    <span class="Constant">&quot;    subx translate input1.subx intput2.subx ... -o &lt;output ELF binary&gt;\n&quot;</span>
<span id="L77" class="LineNr"> 77 </span>    <span class="Constant">&quot;- Run a SubX binary using SubX itself (for better error messages):\n&quot;</span>
<span id="L78" class="LineNr"> 78 </span>    <span class="Constant">&quot;    subx <a href='011run.cc.html#L83'>run</a> &lt;ELF binary&gt;\n&quot;</span>
<span id="L79" class="LineNr"> 79 </span>    <span class="Constant">&quot;\n&quot;</span>
<span id="L80" class="LineNr"> 80 </span>    <span class="Constant">&quot;== Debugging aids\n&quot;</span>
<span id="L81" class="LineNr"> 81 </span>    <span class="Constant">&quot;- Add '--trace' to any of these commands to print a <a href='003trace.cc.html#L183'>trace</a> to stderr\n&quot;</span>
<span id="L82" class="LineNr"> 82 </span>    <span class="Constant">&quot;  for debugging purposes.\n&quot;</span>
<span id="L83" class="LineNr"> 83 </span>    <span class="Constant">&quot;- Add '--map' to add information to traces. 'subx --map translate' will save\n&quot;</span>
<span id="L84" class="LineNr"> 84 </span>    <span class="Constant">&quot;  (to a file called 'map') the mapping from labels to addresses that it computes\n&quot;</span>
<span id="L85" class="LineNr"> 85 </span>    <span class="Constant">&quot;  during translation. This file is then available to 'subx --map --trace run'\n&quot;</span>
<span id="L86" class="LineNr"> 86 </span>    <span class="Constant">&quot;  which prints out label names in the <a href='003trace.cc.html#L183'>trace</a> as it encounters them.\n&quot;</span>
<span id="L87" class="LineNr"> 87 </span>    <span class="Constant">&quot;\n&quot;</span>
<span id="L88" class="LineNr"> 88 </span>    <span class="Constant">&quot;Options starting with '--' must always come before any other arguments.\n&quot;</span>
<span id="L89" class="LineNr"> 89 </span>    <span class="Constant">&quot;\n&quot;</span>
<span id="L90" class="LineNr"> 90 </span>    <span class="Constant">&quot;To start learning how to write SubX programs, see Readme.md (particularly\n&quot;</span>
<span id="L91" class="LineNr"> 91 </span>    <span class="Constant">&quot;the section on the x86 instruction set) and then <a href='011run.cc.html#L83'>run</a>:\n&quot;</span>
<span id="L92" class="LineNr"> 92 </span>    <span class="Constant">&quot;  subx help\n&quot;</span>
<span id="L93" class="LineNr"> 93 </span>  <span class="Delimiter">);</span>
<span id="L94" class="LineNr"> 94 </span>  <span class="Comment">// End Help Texts</span>
<span id="L95" class="LineNr"> 95 </span><span class="Delimiter">}</span>
<span id="L96" class="LineNr"> 96 </span>
<span id="L97" class="LineNr"> 97 </span><span class="Delimiter">:(code)</span>
<span id="L98" class="LineNr"> 98 </span><span class="Normal">bool</span> <a href='001help.cc.html#L98'>is_equal</a><span class="Delimiter">(</span><span class="Normal">char</span>* s<span class="Delimiter">,</span> <span class="Normal">const</span> <span class="Normal">char</span>* lit<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L99" class="LineNr"> 99 </span>  <span class="Identifier">return</span> strncmp<span class="Delimiter">(</span>s<span class="Delimiter">,</span> lit<span class="Delimiter">,</span> strlen<span class="Delimiter">(</span>lit<span class="Delimiter">))</span> == <span class="Constant">0</span><span class="Delimiter">;</span>
<span id="L100" class="LineNr">100 </span><span class="Delimiter">}</span>
<span id="L101" class="LineNr">101 </span>
<span id="L102" class="LineNr">102 </span><span class="Normal">bool</span> <a href='001help.cc.html#L102'>starts_with</a><span class="Delimiter">(</span><span class="Normal">const</span> string&amp; s<span class="Delimiter">,</span> <span class="Normal">const</span> string&amp; pat<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L103" class="LineNr">103 </span>  string::const_iterator a=s<span class="Delimiter">.</span>begin<span class="Delimiter">(),</span> b=pat<span class="Delimiter">.</span>begin<span class="Delimiter">();</span>
<span id="L104" class="LineNr">104 </span>  <span class="Normal">for</span> <span class="Delimiter">(</span><span class="Comment">/*</span><span class="Comment">nada</span><span class="Comment">*/</span><span class="Delimiter">;</span>  a!=s<span class="Delimiter">.</span>end<span class="Delimiter">()</span> &amp;&amp; b!=pat<span class="Delimiter">.</span>end<span class="Delimiter">();</span>  ++a<span class="Delimiter">,</span> ++b<span class="Delimiter">)</span>
<span id="L105" class="LineNr">105 </span>    <span class="Normal">if</span> <span class="Delimiter">(</span>*a != *b<span class="Delimiter">)</span> <span class="Identifier">return</span><span class="Constant"> false</span><span class="Delimiter">;</span>
<span id="L106" class="LineNr">106 </span>  <span class="Identifier">return</span> b == pat<span class="Delimiter">.</span>end<span class="Delimiter">();</span>
<span id="L107" class="LineNr">107 </span><span class="Delimiter">}</span>
<span id="L108" class="LineNr">108 </span>
<span id="L109" class="LineNr">109 </span><span class="Comment">//: I'll throw some style conventions here for want of a better place for them.</span>
<span id="L110" class="LineNr">110 </span><span class="Comment">//: As a rule I hate style guides. Do what you want, that's my motto. But since</span>
<span id="L111" class="LineNr">111 </span><span class="Comment">//: we're dealing with C/C++, the one big thing we want to avoid is undefined</span>
<span id="L112" class="LineNr">112 </span><span class="Comment">//: behavior. If a compiler ever encounters undefined behavior it can make</span>
<span id="L113" class="LineNr">113 </span><span class="Comment">//: your program do anything it wants.</span>
<span id="L114" class="LineNr">114 </span><span class="Comment">//:</span>
<span id="L115" class="LineNr">115 </span><span class="Comment">//: For reference, my checklist of undefined behaviors to watch out for:</span>
<span id="L116" class="LineNr">116 </span><span class="Comment">//:   out-of-bounds access</span>
<span id="L117" class="LineNr">117 </span><span class="Comment">//:   uninitialized variables</span>
<span id="L118" class="LineNr">118 </span><span class="Comment">//:   use after free</span>
<span id="L119" class="LineNr">119 </span><span class="Comment">//:   dereferencing invalid pointers: null, a new of size 0, others</span>
<span id="L120" class="LineNr">120 </span><span class="Comment">//:</span>
<span id="L121" class="LineNr">121 </span><span class="Comment">//:   casting a large number to a type too small to hold it</span>
<span id="L122" class="LineNr">122 </span><span class="Comment">//:</span>
<span id="L123" class="LineNr">123 </span><span class="Comment">//:   integer overflow</span>
<span id="L124" class="LineNr">124 </span><span class="Comment">//:   division by zero and other undefined expressions</span>
<span id="L125" class="LineNr">125 </span><span class="Comment">//:   left-shift by negative count</span>
<span id="L126" class="LineNr">126 </span><span class="Comment">//:   shifting values by more than or equal to the number of bits they contain</span>
<span id="L127" class="LineNr">127 </span><span class="Comment">//:   bitwise operations on signed numbers</span>
<span id="L128" class="LineNr">128 </span><span class="Comment">//:</span>
<span id="L129" class="LineNr">129 </span><span class="Comment">//:   Converting pointers to types of different alignment requirements</span>
<span id="L130" class="LineNr">130 </span><span class="Comment">//:     T* -&gt; void* -&gt; T*: defined</span>
<span id="L131" class="LineNr">131 </span><span class="Comment">//:     T* -&gt; U* -&gt; T*: defined if non-function pointers and alignment requirements are same</span>
<span id="L132" class="LineNr">132 </span><span class="Comment">//:     function pointers may be cast to other function pointers</span>
<span id="L133" class="LineNr">133 </span><span class="Comment">//:</span>
<span id="L134" class="LineNr">134 </span><span class="Comment">//:       Casting a numeric value into a value that can't be represented by the target type (either directly or via static_cast)</span>
<span id="L135" class="LineNr">135 </span><span class="Comment">//:</span>
<span id="L136" class="LineNr">136 </span><span class="Comment">//: To guard against these, some conventions:</span>
<span id="L137" class="LineNr">137 </span><span class="Comment">//:</span>
<span id="L138" class="LineNr">138 </span><span class="Comment">//: 0. Initialize all primitive variables in functions and constructors.</span>
<span id="L139" class="LineNr">139 </span><span class="Comment">//:</span>
<span id="L140" class="LineNr">140 </span><span class="Comment">//: 1. Minimize use of pointers and pointer arithmetic. Avoid 'new' and</span>
<span id="L141" class="LineNr">141 </span><span class="Comment">//: 'delete' as far as possible. Rely on STL to perform memory management to</span>
<span id="L142" class="LineNr">142 </span><span class="Comment">//: avoid use-after-free issues (and memory leaks).</span>
<span id="L143" class="LineNr">143 </span><span class="Comment">//:</span>
<span id="L144" class="LineNr">144 </span><span class="Comment">//: 2. Avoid naked arrays to avoid out-of-bounds access. Never use operator[]</span>
<span id="L145" class="LineNr">145 </span><span class="Comment">//: except with map. Use at() with STL vectors and so on.</span>
<span id="L146" class="LineNr">146 </span><span class="Comment">//:</span>
<span id="L147" class="LineNr">147 </span><span class="Comment">//: 3. Valgrind all the things.</span>
<span id="L148" class="LineNr">148 </span><span class="Comment">//:</span>
<span id="L149" class="LineNr">149 </span><span class="Comment">//: 4. Avoid unsigned numbers. Not strictly an undefined-behavior issue, but</span>
<span id="L150" class="LineNr">150 </span><span class="Comment">//: the extra range doesn't matter, and it's one less confusing category of</span>
<span id="L151" class="LineNr">151 </span><span class="Comment">//: interaction gotchas to worry about.</span>
<span id="L152" class="LineNr">152 </span><span class="Comment">//:</span>
<span id="L153" class="LineNr">153 </span><span class="Comment">//: Corollary: don't use the size() method on containers, since it returns an</span>
<span id="L154" class="LineNr">154 </span><span class="Comment">//: unsigned and that'll cause warnings about mixing signed and unsigned,</span>
<span id="L155" class="LineNr">155 </span><span class="Comment">//: yadda-yadda. Instead use this macro below to perform an unsafe cast to</span>
<span id="L156" class="LineNr">156 </span><span class="Comment">//: signed. We'll just give up immediately if a container's ever too large.</span>
<span id="L157" class="LineNr">157 </span><span class="Comment">//: Basically, Mu is not concerned about this being a little slower than it</span>
<span id="L158" class="LineNr">158 </span><span class="Comment">//: could be. (<a href="https://gist.github.com/rygorous/e0f055bfb74e3d5f0af20690759de5a7">https://gist.github.com/rygorous/e0f055bfb74e3d5f0af20690759de5a7</a>)</span>
<span id="L159" class="LineNr">159 </span><span class="Comment">//:</span>
<span id="L160" class="LineNr">160 </span><span class="Comment">//: Addendum to corollary: We're going to uniformly use int everywhere, to</span>
<span id="L161" class="LineNr">161 </span><span class="Comment">//: indicate that we're oblivious to number size, and since Clang on 32-bit</span>
<span id="L162" class="LineNr">162 </span><span class="Comment">//: platforms doesn't yet support multiplication over 64-bit integers, and</span>
<span id="L163" class="LineNr">163 </span><span class="Comment">//: since multiplying two integers seems like a more common situation to end</span>
<span id="L164" class="LineNr">164 </span><span class="Comment">//: up in than integer overflow.</span>
<span id="L165" class="LineNr">165 </span><span class="Delimiter">:(before &quot;End Includes&quot;)</span>
<span id="L166" class="LineNr">166 </span><span class="Comment">#define SIZE(X) (assert((X).size() &lt; (1LL&lt;&lt;(sizeof(int)*8-2))), static_cast&lt;int&gt;((X).size()))</span>
<span id="L167" class="LineNr">167 </span>
<span id="L168" class="LineNr">168 </span><span class="Comment">//: 5. Integer overflow is guarded against at runtime using the -ftrapv flag</span>
<span id="L169" class="LineNr">169 </span><span class="Comment">//: to the compiler, supported by Clang (GCC version only works sometimes:</span>
<span id="L170" class="LineNr">170 </span><span class="Comment">//: <a href="http://stackoverflow.com/questions/20851061/how-to-make-gcc-ftrapv-work">http://stackoverflow.com/questions/20851061/how-to-make-gcc-ftrapv-work</a>).</span>
<span id="L171" class="LineNr">171 </span><span class="Delimiter">:(before &quot;atexit(reset)&quot;)</span>
<span id="L172" class="LineNr">172 </span><a href='001help.cc.html#L177'>initialize_signal_handlers</a><span class="Delimiter">();</span>  <span class="Comment">// not always necessary, but doesn't hurt</span>
<span id="L173" class="LineNr">173 </span><span class="CommentedCode">//? cerr &lt;&lt; INT_MAX+1 &lt;&lt; '\n';  // test overflow</span>
<span id="L174" class="LineNr">174 </span><span class="CommentedCode">//? assert(false);  // test SIGABRT</span>
<span id="L175" class="LineNr">175 </span><span class="Delimiter">:(code)</span>
<span id="L176" class="LineNr">176 </span><span class="Comment">// based on <a href="https://spin.atomicobject.com/2013/01/13/exceptions-stack-traces-c">https://spin.atomicobject.com/2013/01/13/exceptions-stack-traces-c</a></span>
<span id="L177" class="LineNr">177 </span><span class="Normal">void</span> <a href='001help.cc.html#L177'>initialize_signal_handlers</a><span class="Delimiter">()</span> <span class="Delimiter">{</span>
<span id="L178" class="LineNr">178 </span>  <span class="Normal">struct</span> sigaction action<span class="Delimiter">;</span>
<span id="L179" class="LineNr">179 </span>  bzero<span class="Delimiter">(</span>&amp;action<span class="Delimiter">,</span> <span class="Normal">sizeof</span><span class="Delimiter">(</span>action<span class="Delimiter">));</span>
<span id="L180" class="LineNr">180 </span>  action<span class="Delimiter">.</span>sa_sigaction = <a href='001help.cc.html#L185'>dump_and_exit</a><span class="Delimiter">;</span>
<span id="L181" class="LineNr">181 </span>  sigemptyset<span class="Delimiter">(</span>&amp;action<span class="Delimiter">.</span>sa_mask<span class="Delimiter">);</span>
<span id="L182" class="LineNr">182 </span>  sigaction<span class="Delimiter">(</span><span class="Constant">SIGABRT</span><span class="Delimiter">,</span> &amp;action<span class="Delimiter">,</span> <span class="Constant">NULL</span><span class="Delimiter">);</span>  <span class="Comment">// assert() failure or integer overflow on linux (with -ftrapv)</span>
<span id="L183" class="LineNr">183 </span>  sigaction<span class="Delimiter">(</span><span class="Constant">SIGILL</span><span class="Delimiter">,</span>  &amp;action<span class="Delimiter">,</span> <span class="Constant">NULL</span><span class="Delimiter">);</span>  <span class="Comment">// integer overflow on OS X (with -ftrapv)</span>
<span id="L184" class="LineNr">184 </span><span class="Delimiter">}</span>
<span id="L185" class="LineNr">185 </span><span class="Normal">void</span> <a href='001help.cc.html#L185'>dump_and_exit</a><span class="Delimiter">(</span><span class="Normal">int</span> sig<span class="Delimiter">,</span> siginfo_t* <span class="Comment">/*</span><span class="Comment">unused</span><span class="Comment">*/</span><span class="Delimiter">,</span> <span class="Normal">void</span>* <span class="Comment">/*</span><span class="Comment">unused</span><span class="Comment">*/</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L186" class="LineNr">186 </span>  <span class="Normal">switch</span> <span class="Delimiter">(</span>sig<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L187" class="LineNr">187 </span>    <span class="Normal">case</span> <span class="Constant">SIGABRT</span>:
<span id="L188" class="LineNr">188 </span>      <span class="Comment">#ifndef __APPLE__</span>
<span id="L189" class="LineNr">189 </span>        cerr &lt;&lt; <span class="Constant">&quot;SIGABRT: might be an integer overflow if it wasn't an assert() failure\n&quot;</span><span class="Delimiter">;</span>
<span id="L190" class="LineNr">190 </span>        _Exit<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">);</span>
<span id="L191" class="LineNr">191 </span>      <span class="Comment">#endif</span>
<span id="L192" class="LineNr">192 </span>      <span class="Identifier">break</span><span class="Delimiter">;</span>
<span id="L193" class="LineNr">193 </span>    <span class="Normal">case</span> <span class="Constant">SIGILL</span>:
<span id="L194" class="LineNr">194 </span>      <span class="Comment">#ifdef __APPLE__</span>
<span id="L195" class="LineNr">195 </span>        cerr &lt;&lt; <span class="Constant">&quot;SIGILL: most likely caused by integer overflow\n&quot;</span><span class="Delimiter">;</span>
<span id="L196" class="LineNr">196 </span>        _Exit<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">);</span>
<span id="L197" class="LineNr">197 </span>      <span class="Comment">#endif</span>
<span id="L198" class="LineNr">198 </span>      <span class="Identifier">break</span><span class="Delimiter">;</span>
<span id="L199" class="LineNr">199 </span>    <span class="Normal">default</span>:
<span id="L200" class="LineNr">200 </span>      <span class="Identifier">break</span><span class="Delimiter">;</span>
<span id="L201" class="LineNr">201 </span>  <span class="Delimiter">}</span>
<span id="L202" class="LineNr">202 </span><span class="Delimiter">}</span>
<span id="L203" class="LineNr">203 </span><span class="Delimiter">:(before &quot;End Includes&quot;)</span>
<span id="L204" class="LineNr">204 </span><span class="Comment">#include &lt;signal.h&gt;</span>
<span id="L205" class="LineNr">205 </span>
<span id="L206" class="LineNr">206 </span><span class="Comment">//: For good measure we'll also enable SIGFPE.</span>
<span id="L207" class="LineNr">207 </span><span class="Delimiter">:(before &quot;atexit(reset)&quot;)</span>
<span id="L208" class="LineNr">208 </span><a href='001help.cc.html#L221'>feenableexcept</a><span class="Delimiter">(</span>FE_OVERFLOW | FE_UNDERFLOW<span class="Delimiter">);</span>
<span id="L209" class="LineNr">209 </span><span class="CommentedCode">//? assert(sizeof(int) == 4 &amp;&amp; sizeof(float) == 4);</span>
<span id="L210" class="LineNr">210 </span><span class="CommentedCode">//? //                          | exp   |  mantissa</span>
<span id="L211" class="LineNr">211 </span><span class="CommentedCode">//? int smallest_subnormal = 0b00000000000000000000000000000001;</span>
<span id="L212" class="LineNr">212 </span><span class="CommentedCode">//? float smallest_subnormal_f = *reinterpret_cast&lt;float*&gt;(&amp;smallest_subnormal);</span>
<span id="L213" class="LineNr">213 </span><span class="CommentedCode">//? cerr &lt;&lt; &quot;ε: &quot; &lt;&lt; smallest_subnormal_f &lt;&lt; '\n';</span>
<span id="L214" class="LineNr">214 </span><span class="CommentedCode">//? cerr &lt;&lt; &quot;ε/2: &quot; &lt;&lt; smallest_subnormal_f/2 &lt;&lt; &quot; (underflow)\n&quot;;  // test SIGFPE</span>
<span id="L215" class="LineNr">215 </span><span class="Delimiter">:(before &quot;End Includes&quot;)</span>
<span id="L216" class="LineNr">216 </span><span class="Comment">#include &lt;fenv.h&gt;</span>
<span id="L217" class="LineNr">217 </span><span class="Delimiter">:(code)</span>
<span id="L218" class="LineNr">218 </span><span class="Comment">#ifdef __APPLE__</span>
<span id="L219" class="LineNr">219 </span><span class="Comment">// Public domain polyfill for feenableexcept on OS X</span>
<span id="L220" class="LineNr">220 </span><span class="Comment">// <a href="http://www-personal.umich.edu/~williams/archive/computation/fe-handling-example.c">http://www-personal.umich.edu/~williams/archive/computation/fe-handling-example.c</a></span>
<span id="L221" class="LineNr">221 </span><span class="Normal">int</span> <a href='001help.cc.html#L221'>feenableexcept</a><span class="Delimiter">(</span><span class="Normal">unsigned</span> <span class="Normal">int</span> excepts<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L222" class="LineNr">222 </span>  <span class="Normal">static</span> fenv_t fenv<span class="Delimiter">;</span>
<span id="L223" class="LineNr">223 </span>  <span class="Normal">unsigned</span> <span class="Normal">int</span> new_excepts = excepts &amp; FE_ALL_EXCEPT<span class="Delimiter">;</span>
<span id="L224" class="LineNr">224 </span>  <span class="Normal">unsigned</span> <span class="Normal">int</span> old_excepts<span class="Delimiter">;</span>
<span id="L225" class="LineNr">225 </span>  <span class="Normal">if</span> <span class="Delimiter">(</span>fegetenv<span class="Delimiter">(</span>&amp;fenv<span class="Delimiter">))</span> <span class="Identifier">return</span> -<span class="Constant">1</span><span class="Delimiter">;</span>
<span id="L226" class="LineNr">226 </span>  old_excepts = fenv<span class="Delimiter">.</span>__control &amp; FE_ALL_EXCEPT<span class="Delimiter">;</span>
<span id="L227" class="LineNr">227 </span>  fenv<span class="Delimiter">.</span>__control &amp;= ~new_excepts<span class="Delimiter">;</span>
<span id="L228" class="LineNr">228 </span>  fenv<span class="Delimiter">.</span>__mxcsr &amp;= ~<span class="Delimiter">(</span>new_excepts &lt;&lt; <span class="Constant">7</span><span class="Delimiter">);</span>
<span id="L229" class="LineNr">229 </span>  <span class="Identifier">return</span> fesetenv<span class="Delimiter">(</span>&amp;fenv<span class="Delimiter">)</span> ? -<span class="Constant">1</span> : old_excepts<span class="Delimiter">;</span>
<span id="L230" class="LineNr">230 </span><span class="Delimiter">}</span>
<span id="L231" class="LineNr">231 </span><span class="Comment">#endif</span>
<span id="L232" class="LineNr">232 </span>
<span id="L233" class="LineNr">233 </span><span class="Comment">//: 6. Map's operator[] being non-const is fucking evil.</span>
<span id="L234" class="LineNr">234 </span><span class="Delimiter">:(before &quot;Globals&quot;)</span>  <span class="Comment">// can't generate prototypes for these</span>
<span id="L235" class="LineNr">235 </span><span class="Comment">// from <a href="http://stackoverflow.com/questions/152643/idiomatic-c-for-reading-from-a-const-map">http://stackoverflow.com/questions/152643/idiomatic-c-for-reading-from-a-const-map</a></span>
<span id="L236" class="LineNr">236 </span><span class="Normal">template</span>&lt;<span class="Normal">typename</span> <span class="Special">T</span>&gt; <span class="Normal">typename</span> <span class="Special">T</span>::mapped_type&amp; get<span class="Delimiter">(</span><span class="Special">T</span>&amp; map<span class="Delimiter">,</span> <span class="Normal">typename</span> <span class="Special">T</span>::key_type <span class="Normal">const</span>&amp; key<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L237" class="LineNr">237 </span>  <span class="Normal">typename</span> <span class="Special">T</span>::iterator iter<span class="Delimiter">(</span>map<span class="Delimiter">.</span>find<span class="Delimiter">(</span>key<span class="Delimiter">));</span>
<span id="L238" class="LineNr">238 </span>  <span class="Normal">if</span> <span class="Delimiter">(</span>iter == map<span class="Delimiter">.</span>end<span class="Delimiter">())</span> <span class="Delimiter">{</span>
<span id="L239" class="LineNr">239 </span>    cerr &lt;&lt; <span class="Constant">&quot;get couldn't find key '&quot;</span> &lt;&lt; key &lt;&lt; <span class="Constant">&quot;'\n&quot;</span><span class="Delimiter">;</span>
<span id="L240" class="LineNr">240 </span>    assert<span class="Delimiter">(</span>iter != map<span class="Delimiter">.</span>end<span class="Delimiter">());</span>
<span id="L241" class="LineNr">241 </span>  <span class="Delimiter">}</span>
<span id="L242" class="LineNr">242 </span>  <span class="Identifier">return</span> iter<span class="Delimiter">-&gt;</span>second<span class="Delimiter">;</span>
<span id="L243" class="LineNr">243 </span><span class="Delimiter">}</span>
<span id="L244" class="LineNr">244 </span><span class="Normal">template</span>&lt;<span class="Normal">typename</span> <span class="Special">T</span>&gt; <span class="Normal">typename</span> <span class="Special">T</span>::mapped_type <span class="Normal">const</span>&amp; get<span class="Delimiter">(</span><span class="Normal">const</span> <span class="Special">T</span>&amp; map<span class="Delimiter">,</span> <span class="Normal">typename</span> <span class="Special">T</span>::key_type <span class="Normal">const</span>&amp; key<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L245" class="LineNr">245 </span>  <span class="Normal">typename</span> <span class="Special">T</span>::const_iterator iter<span class="Delimiter">(</span>map<span class="Delimiter">.</span>find<span class="Delimiter">(</span>key<span class="Delimiter">));</span>
<span id="L246" class="LineNr">246 </span>  <span class="Normal">if</span> <span class="Delimiter">(</span>iter == map<span class="Delimiter">.</span>end<span class="Delimiter">())</span> <span class="Delimiter">{</span>
<span id="L247" class="LineNr">247 </span>    cerr &lt;&lt; <span class="Constant">&quot;get couldn't find key '&quot;</span> &lt;&lt; key &lt;&lt; <span class="Constant">&quot;'\n&quot;</span><span class="Delimiter">;</span>
<span id="L248" class="LineNr">248 </span>    assert<span class="Delimiter">(</span>iter != map<span class="Delimiter">.</span>end<span class="Delimiter">());</span>
<span id="L249" class="LineNr">249 </span>  <span class="Delimiter">}</span>
<span id="L250" class="LineNr">250 </span>  <span class="Identifier">return</span> iter<span class="Delimiter">-&gt;</span>second<span class="Delimiter">;</span>
<span id="L251" class="LineNr">251 </span><span class="Delimiter">}</span>
<span id="L252" class="LineNr">252 </span><span class="Normal">template</span>&lt;<span class="Normal">typename</span> <span class="Special">T</span>&gt; <span class="Normal">typename</span> <span class="Special">T</span>::mapped_type <span class="Normal">const</span>&amp; <a href='001help.cc.html#L252'>put</a><span class="Delimiter">(</span><span class="Special">T</span>&amp; map<span class="Delimiter">,</span> <span class="Normal">typename</span> <span class="Special">T</span>::key_type <span class="Normal">const</span>&amp; key<span class="Delimiter">,</span> <span class="Normal">typename</span> <span class="Special">T</span>::mapped_type <span class="Normal">const</span>&amp; value<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L253" class="LineNr">253 </span>  map[key] = value<span class="Delimiter">;</span>
<span id="L254" class="LineNr">254 </span>  <span class="Identifier">return</span> map[key]<span class="Delimiter">;</span>
<span id="L255" class="LineNr">255 </span><span class="Delimiter">}</span>
<span id="L256" class="LineNr">256 </span><span class="Normal">template</span>&lt;<span class="Normal">typename</span> <span class="Special">T</span>&gt; <span class="Normal">bool</span> <a href='001help.cc.html#L256'>contains_key</a><span class="Delimiter">(</span><span class="Special">T</span>&amp; map<span class="Delimiter">,</span> <span class="Normal">typename</span> <span class="Special">T</span>::key_type <span class="Normal">const</span>&amp; key<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L257" class="LineNr">257 </span>  <span class="Identifier">return</span> map<span class="Delimiter">.</span>find<span class="Delimiter">(</span>key<span class="Delimiter">)</span> != map<span class="Delimiter">.</span>end<span class="Delimiter">();</span>
<span id="L258" class="LineNr">258 </span><span class="Delimiter">}</span>
<span id="L259" class="LineNr">259 </span><span class="Normal">template</span>&lt;<span class="Normal">typename</span> <span class="Special">T</span>&gt; <span class="Normal">typename</span> <span class="Special">T</span>::mapped_type&amp; <a href='001help.cc.html#L259'>get_or_insert</a><span class="Delimiter">(</span><span class="Special">T</span>&amp; map<span class="Delimiter">,</span> <span class="Normal">typename</span> <span class="Special">T</span>::key_type <span class="Normal">const</span>&amp; key<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L260" class="LineNr">260 </span>  <span class="Identifier">return</span> map[key]<span class="Delimiter">;</span>
<span id="L261" class="LineNr">261 </span><span class="Delimiter">}</span>
<span id="L262" class="LineNr">262 </span><span class="Normal">template</span>&lt;<span class="Normal">typename</span> <span class="Special">T</span>&gt; <span class="Normal">typename</span> <span class="Special">T</span>::mapped_type <span class="Normal">const</span>&amp; <a href='001help.cc.html#L262'>put_new</a><span class="Delimiter">(</span><span class="Special">T</span>&amp; map<span class="Delimiter">,</span> <span class="Normal">typename</span> <span class="Special">T</span>::key_type <span class="Normal">const</span>&amp; key<span class="Delimiter">,</span> <span class="Normal">typename</span> <span class="Special">T</span>::mapped_type <span class="Normal">const</span>&amp; value<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L263" class="LineNr">263 </span>  assert<span class="Delimiter">(</span>map<span class="Delimiter">.</span>find<span class="Delimiter">(</span>key<span class="Delimiter">)</span> == map<span class="Delimiter">.</span>end<span class="Delimiter">());</span>
<span id="L264" class="LineNr">264 </span>  map[key] = value<span class="Delimiter">;</span>
<span id="L265" class="LineNr">265 </span>  <span class="Identifier">return</span> map[key]<span class="Delimiter">;</span>
<span id="L266" class="LineNr">266 </span><span class="Delimiter">}</span>
<span id="L267" class="LineNr">267 </span><span class="Comment">//: The contract: any container that relies on get_or_insert should never call</span>
<span id="L268" class="LineNr">268 </span><span class="Comment">//: contains_key.</span>
<span id="L269" class="LineNr">269 </span>
<span id="L270" class="LineNr">270 </span><span class="Comment">//: 7. istreams are a royal pain in the arse. You have to be careful about</span>
<span id="L271" class="LineNr">271 </span><span class="Comment">//: what subclass you try to putback into. You have to watch out for the pesky</span>
<span id="L272" class="LineNr">272 </span><span class="Comment">//: failbit and badbit. Just avoid eof() and use this helper instead.</span>
<span id="L273" class="LineNr">273 </span><span class="Delimiter">:(code)</span>
<span id="L274" class="LineNr">274 </span><span class="Normal">bool</span> <a href='001help.cc.html#L274'>has_data</a><span class="Delimiter">(</span>istream&amp; in<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L275" class="LineNr">275 </span>  <span class="Identifier">return</span> in &amp;&amp; !in<span class="Delimiter">.</span>eof<span class="Delimiter">();</span>
<span id="L276" class="LineNr">276 </span><span class="Delimiter">}</span>
<span id="L277" class="LineNr">277 </span>
<span id="L278" class="LineNr">278 </span><span class="Delimiter">:(before &quot;End Includes&quot;)</span>
<span id="L279" class="LineNr">279 </span><span class="Comment">#include &lt;assert.h&gt;</span>
<span id="L280" class="LineNr">280 </span>
<span id="L281" class="LineNr">281 </span><span class="Comment">#include &lt;iostream&gt;</span>
<span id="L282" class="LineNr">282 </span><span class="Normal">using</span> std::istream<span class="Delimiter">;</span>
<span id="L283" class="LineNr">283 </span><span class="Normal">using</span> std::ostream<span class="Delimiter">;</span>
<span id="L284" class="LineNr">284 </span><span class="Normal">using</span> std::iostream<span class="Delimiter">;</span>
<span id="L285" class="LineNr">285 </span><span class="Normal">using</span> std::cin<span class="Delimiter">;</span>
<span id="L286" class="LineNr">286 </span><span class="Normal">using</span> std::cout<span class="Delimiter">;</span>
<span id="L287" class="LineNr">287 </span><span class="Normal">using</span> std::cerr<span class="Delimiter">;</span>
<span id="L288" class="LineNr">288 </span><span class="Comment">#include &lt;iomanip&gt;</span>
<span id="L289" class="LineNr">289 </span>
<span id="L290" class="LineNr">290 </span><span class="Comment">#include &lt;string.h&gt;</span>
<span id="L291" class="LineNr">291 </span><span class="Comment">#include &lt;string&gt;</span>
<span id="L292" class="LineNr">292 </span><span class="Normal">using</span> std::string<span class="Delimiter">;</span>
<span id="L293" class="LineNr">293 </span>
<span id="L294" class="LineNr">294 </span><span class="Comment">#include &lt;algorithm&gt;</span>
<span id="L295" class="LineNr">295 </span><span class="Normal">using</span> std::min<span class="Delimiter">;</span>
<span id="L296" class="LineNr">296 </span><span class="Normal">using</span> std::max<span class="Delimiter">;</span>
</pre>
</body>
</html>
<!-- vim: set foldmethod=manual : -->
*p) { CTrace((tfp, "LYProcessSetCookies: Using Set-Cookie header.\n")); } while (NumCookies <= max_cookies_domain && *p) { value_start = value_end = NULL; p = LYSkipCBlanks(p); /* * Get the attribute name. */ attr_start = p; while (*p != '\0' && !isspace(UCH(*p)) && *p != '=' && *p != ';' && *p != ',') p++; attr_end = p; p = LYSkipCBlanks(p); /* * Check for an '=' delimiter, or an 'expires' name followed by white, * since Netscape's bogus parser doesn't require an '=' delimiter, and * 'expires' attributes are being encountered without them. - FM */ if (*p == '=' || !strncasecomp(attr_start, "Expires", 7)) { /* * Get the value string. */ if (*p == '=') { p++; } p = LYSkipCBlanks(p); /* * Hack alert! We must handle Netscape-style cookies with * "Expires=Mon, 01-Jan-96 13:45:35 GMT" or * "Expires=Mon, 1 Jan 1996 13:45:35 GMT". * No quotes, but there are spaces. Argh... Anyway, we know it * will have at least 3 space separators within it, and two dashes * or two more spaces, so this code looks for a space after the 5th * space separator or dash to mark the end of the value. - FM */ if ((attr_end - attr_start) == 7 && !strncasecomp(attr_start, "Expires", 7)) { int spaces = 6; value_start = p; if (isdigit(UCH(*p))) { /* * No alphabetic day field. - FM */ spaces--; } else { /* * Skip the alphabetic day field. - FM */ while (*p != '\0' && isalpha(UCH(*p))) { p++; } while (*p == ',' || isspace(UCH(*p))) { p++; } spaces--; } while (*p != '\0' && *p != ';' && *p != ',' && spaces) { p++; if (isspace(UCH(*p))) { while (isspace(UCH(*(p + 1)))) p++; spaces--; } else if (*p == '-') { spaces--; } } value_end = p; /* * Hack Alert! The port attribute can take a comma separated * list of numbers as a value, and such values should be * quoted, but if not, make sure we don't treat a number in the * list as the start of a new cookie. - FM */ } else if ((attr_end - attr_start) == 4 && !strncasecomp(attr_start, "port", 4) && isdigit(UCH(*p))) { /* * The value starts as an unquoted number. */ const char *cp, *cp1; value_start = p; while (1) { while (isdigit(UCH(*p))) p++; value_end = p; p = LYSkipCBlanks(p); if (*p == '\0' || *p == ';') break; if (*p == ',') { cp = LYSkipCBlanks(p + 1); if (*cp != '\0' && isdigit(UCH(*cp))) { cp1 = cp; while (isdigit(UCH(*cp1))) cp1++; cp1 = LYSkipCBlanks(cp1); if (*cp1 == '\0' || *cp1 == ',' || *cp1 == ';') { p = cp; continue; } } } while (*p != '\0' && *p != ';' && *p != ',') p++; value_end = p; /* * Trim trailing spaces. */ if ((value_end > value_start) && isspace(UCH(*(value_end - 1)))) { value_end--; while ((value_end > (value_start + 1)) && isspace(UCH(*value_end)) && isspace(UCH(*(value_end - 1)))) { value_end--; } } break; } } else if (*p == '"') { BOOLEAN escaped = FALSE; /* * It looks like quoted string. */ p++; value_start = p; while (*p != '\0' && (*p != '"' || escaped)) { escaped = (BOOL) (!escaped && *p == '\\'); p++; } if (p != value_start && *p == '"' && !escaped) { value_end = p; p++; Quoted = TRUE; } else { value_start--; value_end = p; if (*p) p++; Quoted = FALSE; } } else { /* * Otherwise, it's an unquoted string. */ value_start = p; while (*p != '\0' && *p != ';' && *p != ',') p++; value_end = p; /* * Trim trailing spaces. */ if ((value_end > value_start) && isspace(UCH(*(value_end - 1)))) { value_end--; while ((value_end > (value_start + 1)) && isspace(UCH(*value_end)) && isspace(UCH(*(value_end - 1)))) { value_end--; } } } } /* * Check for a separator character, and skip it. */ if (*p == ';' || *p == ',') p++; /* * Now, we can handle this attribute/value pair. */ if (attr_end > attr_start) { char *value = alloc_attr_value(value_start, value_end); parse_flags = parse_attribute(parse_flags, cur_cookie, &cookie_len, attr_start, (int) (attr_end - attr_start), value, address, hostname, port); /* * Presence of value is needed (indicated normally by '='), * but it can be an empty string. - kw 1999-06-24 */ if (!(parse_flags & FLAGS_KNOWN_ATTR) && value && value_end >= value_start) { /* * If we've started a cookie, and it's not too big, save it in * the CombinedCookies list. - FM */ if (cookie_len <= max_cookies_buffer && cur_cookie != NULL) { /* * If we had a Set-Cookie2 header, make sure the version is * at least 1, and mark it for quoting. - FM */ if (SetCookie2 != NULL) { AssumeCookieVersion(cur_cookie); cur_cookie->quoted = TRUE; } HTList_appendObject(CombinedCookies, cur_cookie); } else if (cur_cookie != NULL) { CTrace((tfp, "LYProcessSetCookies: Rejecting Set-Cookie: %s=%s\n", (cur_cookie->name ? cur_cookie->name : "[no name]"), (cur_cookie->value ? cur_cookie->value : "[no value]"))); CTrace((tfp, " due to excessive length!\n")); freeCookie(cur_cookie); cur_cookie = NULL; } /* * Start a new cookie. - FM */ cur_cookie = newCookie(); NumCookies++; cookie_len = 0; MemAllocCopy(&(cur_cookie->name), attr_start, attr_end); cookie_len += (int) strlen(cur_cookie->name); MemAllocCopy(&(cur_cookie->value), value_start, value_end); cookie_len += (int) strlen(cur_cookie->value); StrAllocCopy(cur_cookie->domain, hostname); cookie_len += (int) strlen(hostname); StrAllocCopy(cur_cookie->path, path); cookie_len += (cur_cookie->pathlen = (int) strlen(cur_cookie->path)); cur_cookie->port = port; parse_flags = 0; cur_cookie->quoted = Quoted; Quoted = FALSE; SetCookieDomain(cur_cookie, hostname); } FREE(value); } } /* * Handle the final Set-Cookie cookie if within length limit. - FM */ if (NumCookies <= max_cookies_domain && cookie_len <= max_cookies_buffer && cur_cookie != NULL) { if (SetCookie2 != NULL) { AssumeCookieVersion(cur_cookie); cur_cookie->quoted = TRUE; } HTList_appendObject(CombinedCookies, cur_cookie); } else if (cur_cookie != NULL) { CTrace((tfp, "LYProcessSetCookies: Rejecting Set-Cookie: %s=%s\n", (cur_cookie->name ? cur_cookie->name : "[no name]"), (cur_cookie->value ? cur_cookie->value : "[no value]"))); CTrace((tfp, " due to excessive %s%s%s\n", (cookie_len > max_cookies_buffer ? "length" : ""), (cookie_len > max_cookies_buffer && NumCookies > max_cookies_domain ? " and " : ""), (NumCookies > max_cookies_domain ? "number!\n" : "!\n"))); freeCookie(cur_cookie); cur_cookie = NULL; } /* * OK, now we can actually store any cookies in the CombinedCookies list. * - FM */ cl = CombinedCookies; while (NULL != (co = (cookie *) HTList_nextObject(cl))) { CTrace((tfp, "LYProcessSetCookie: attr=value pair: '%s=%s'\n", (co->name ? co->name : "[no name]"), (co->value ? co->value : "[no value]"))); if (co->expires > 0) { CTrace((tfp, " expires: %" PRI_time_t ", %s\n", CAST_time_t (co->expires), ctime(&co->expires))); } if (isHTTPS_URL(address) && LYForceSSLCookiesSecure == TRUE && !(co->flags & COOKIE_FLAG_SECURE)) { co->flags |= COOKIE_FLAG_SECURE; CTrace((tfp, " Forced the 'secure' flag on.\n")); } store_cookie(co, hostname, path); } HTList_delete(CombinedCookies); CombinedCookies = NULL; return; } /* * Entry function for handling Set-Cookie: and/or Set-Cookie2: * reply headers. They may have been concatenated as comma * separated lists in HTTP.c or HTMIME.c. - FM */ void LYSetCookie(const char *SetCookie, const char *SetCookie2, const char *address) { BOOL BadHeaders = FALSE; char *hostname = NULL, *path = NULL, *ptr; int port = 80; /* * Get the hostname, port and path of the address, and report the * Set-Cookie and/or Set-Cookie2 header(s) if trace mode is on, but set the * cookie(s) only if LYSetCookies is TRUE. - FM */ if (((hostname = HTParse(address, "", PARSE_HOST)) != NULL) && (ptr = StrChr(hostname, ':')) != NULL) { /* * Replace default port number. */ *ptr = '\0'; ptr++; port = atoi(ptr); } else if (isHTTPS_URL(address)) { port = 443; } /* * Get the path from the request URI. */ if ((path = HTParse(address, "", PARSE_PATH | PARSE_PUNCTUATION)) != NULL) { /* * Trim off any parameters to provide something that we can compare * against the cookie's path for verifying if it has the proper prefix. */ if ((ptr = StrChr(path, '?')) != NULL) { CTrace((tfp, "discarding params \"%s\" in request URI\n", ptr)); *ptr = '\0'; } /* trim a trailing slash, unless we have only a "/" */ if ((ptr = strrchr(path, '/')) != NULL) { if (ptr == path) { ++ptr; } CTrace((tfp, "discarding \"%s\" from request URI\n", ptr)); *ptr = '\0'; } } if (isEmpty(SetCookie) && isEmpty(SetCookie2)) { /* * Yuk, something must have gone wrong in HTMIME.c or HTTP.c because * both SetCookie and SetCookie2 are NULL or zero-length. - FM */ BadHeaders = TRUE; } CTrace((tfp, "LYSetCookie called with host '%s', path '%s',\n", NonNull(hostname), NonNull(path))); if (SetCookie) { CTrace((tfp, " and Set-Cookie: '%s'\n", SetCookie)); } if (SetCookie2) { CTrace((tfp, " and Set-Cookie2: '%s'\n", SetCookie2)); } if (LYSetCookies == FALSE || BadHeaders == TRUE) { CTrace((tfp, " Ignoring this Set-Cookie/Set-Cookie2 request.\n")); } /* * We're done if LYSetCookies is off or we have bad headers. - FM */ if (LYSetCookies == FALSE || BadHeaders == TRUE) { FREE(hostname); FREE(path); return; } /* * Process the header(s). */ LYProcessSetCookies(SetCookie, SetCookie2, address, hostname, path, port); FREE(hostname); FREE(path); return; } /* * Entry function from creating a Cookie: request header * if needed. - AK & FM */ char *LYAddCookieHeader(char *hostname, char *path, int port, int secure) { char *header = NULL; HTList *hl = domain_list, *next = NULL; domain_entry *de; CTrace((tfp, "LYCookie: Searching for '%s:%d', '%s'.\n", NONNULL(hostname), port, NONNULL(path))); /* * Search the cookie_list elements in the domain_list for any cookies * associated with the //hostname:port/path */ while (hl) { de = (domain_entry *) hl->object; next = hl->next; if (de != NULL) { if (!HTList_isEmpty(de->cookie_list)) { /* * Scan the domain's cookie_list for any cookies we should * include in our request header. */ header = scan_cookie_sublist(hostname, path, port, de->cookie_list, header, secure); } else if (de->bv == QUERY_USER && de->invcheck_bv == DEFAULT_INVCHECK_BV) { /* * No cookies in this domain, and no default accept/reject * choice was set by the user, so delete the domain. - FM */ freeCookies(de); HTList_removeObject(domain_list, de); FREE(de); } } hl = next; } if (header) return (header); return (NULL); } #ifdef USE_PERSISTENT_COOKIES static int number_of_file_cookies = 0; /* rjp - cookie loading */ void LYLoadCookies(char *cookie_file) { FILE *cookie_handle; char *buf = NULL; static char domain[256], path[LY_MAXPATH], name[256], value[4100]; static char what[8], secure[8], expires_a[16]; /* *INDENT-OFF* */ static struct { char *s; size_t n; } tok_values[] = { { domain, sizeof(domain) }, { what, sizeof(what) }, { path, sizeof(path) }, { secure, sizeof(secure) }, { expires_a, sizeof(expires_a) }, { name, sizeof(name) }, { value, sizeof(value) }, { NULL, 0 } }; /* *INDENT-ON* */ time_t expires; cookie_handle = fopen(cookie_file, TXT_R); if (!cookie_handle) return; CTrace((tfp, "LYLoadCookies: reading cookies from %s\n", cookie_file)); number_of_file_cookies = 0; while (LYSafeGets(&buf, cookie_handle) != 0) { cookie *moo; int tok_loop; char *tok_out, *tok_ptr; LYTrimNewline(buf); if (buf[0] == '\0' || buf[0] == '#') { continue; } number_of_file_cookies++; strcat(buf, "\t"); /* add sep after line if enough space - kw */ /* * Tokenise the cookie line into its component parts - * this only works for Netscape style cookie files at the * moment. It may be worth investigating an alternative * format for Lynx because the Netscape format isn't all * that useful, or future-proof. - RP * * 'fixed' by using strsep instead of strtok. No idea * what kind of platform problems this might introduce. - RP */ /* * This fails when the path is blank * * sscanf(buf, "%s\t%s\t%s\t%s\t%d\t%s\t%[ -~]", * domain, what, path, secure, &expires, name, value); */ CTrace((tfp, "LYLoadCookies: tokenising %s\n", buf)); tok_ptr = buf; tok_out = LYstrsep(&tok_ptr, "\t"); for (tok_loop = 0; tok_out && tok_values[tok_loop].s; tok_loop++) { CTrace((tfp, "\t%d:[%03d]:[%s]\n", tok_loop, (int) (tok_out - buf), tok_out)); LYStrNCpy(tok_values[tok_loop].s, tok_out, (int) tok_values[tok_loop].n); /* * It looks like strtok ignores a leading delimiter, * which makes things a bit more interesting. Something * like "FALSE\t\tFALSE\t" translates to FALSE,FALSE * instead of FALSE,,FALSE. - RP */ tok_out = LYstrsep(&tok_ptr, "\t"); } if (tok_values[tok_loop].s) { /* tok_out in above loop must have been NULL prematurely - kw */ CTrace((tfp, "*** wrong format: not enough tokens, ignoring line!\n")); continue; } expires = atol(expires_a); CTrace((tfp, "expires:\t%s\n", ctime(&expires))); moo = newCookie(); StrAllocCopy(moo->domain, domain); SetCookieDomain(moo, domain); StrAllocCopy(moo->path, path); StrAllocCopy(moo->name, name); if (value[0] == '"' && value[1] && value[strlen(value) - 1] == '"' && value[strlen(value) - 2] != '\\') { value[strlen(value) - 1] = '\0'; StrAllocCopy(moo->value, value + 1); moo->quoted = TRUE; } else { StrAllocCopy(moo->value, value); } moo->pathlen = (int) strlen(moo->path); /* * Justification for following flags: * COOKIE_FLAG_FROM_FILE So we know were it comes from. * COOKIE_FLAG_EXPIRES_SET It must have had an explicit * expiration originally, otherwise * it would not be in the file. * COOKIE_FLAG_DOMAIN_SET, We don't know whether these were * COOKIE_FLAG_PATH_SET explicit or implicit, but this * only matters for sending version 1 * cookies; the cookies read from the * file are currently treated all like * version 0 (we don't set moo->version) * so $Domain= and $Path= will normally * not be sent to the server. But if * these cookies somehow get mixed with * new version 1 cookies we may end up * sending version 1 to the server, and * in that case we should send $Domain * and $Path. The state-man-mec drafts * and RFC 2109 say that $Domain and * $Path SHOULD be omitted if they were * not given explicitly, but not that * they MUST be omitted. * See 8.2 Cookie Spoofing in draft -10 * for a good reason to send them. * However, an explicit domain should be * now prefixed with a dot (unless it is * for a single host), so we check for * that. * COOKIE_FLAG_SECURE Should have "FALSE" for normal, * otherwise set it. */ moo->flags |= COOKIE_FLAG_FROM_FILE | COOKIE_FLAG_EXPIRES_SET | COOKIE_FLAG_PATH_SET; if (LeadingDot(domain)) moo->flags |= COOKIE_FLAG_DOMAIN_SET; if (secure[0] != 'F') moo->flags |= COOKIE_FLAG_SECURE; /* @@@ Should we set port to 443 if secure is set? @@@ */ moo->expires = expires; /* * I don't like using this to store the cookies because it's * designed to store cookies that have been received from an * HTTP request, not from a persistent cookie jar. Hence the * mucking about with the COOKIE_FLAG_FROM_FILE above. - RP */ store_cookie(moo, domain, path); } LYCloseInput(cookie_handle); } static FILE *NewCookieFile(char *cookie_file) { CTrace((tfp, "LYStoreCookies: save cookies to %s on exit\n", cookie_file)); return LYNewTxtFile(cookie_file); } /* rjp - persistent cookie support */ void LYStoreCookies(char *cookie_file) { HTList *dl, *cl; domain_entry *de; cookie *co; FILE *cookie_handle = NULL; time_t now = time(NULL); /* system specific? - RP */ if (isEmpty(cookie_file) || !strcmp(cookie_file, "/dev/null")) { /* We give /dev/null the Unix meaning, regardless of OS */ return; } /* * Check whether we have something to do. - FM */ if (HTList_isEmpty(domain_list) && number_of_file_cookies == 0) { /* No cookies now, and haven't read any, * so don't bother updating the file. */ return; } /* if we read cookies from the file, we'll update it even if now empty */ if (number_of_file_cookies != 0) { cookie_handle = NewCookieFile(cookie_file); if (cookie_handle == NULL) return; } for (dl = domain_list; dl != NULL; dl = dl->next) { de = (domain_entry *) (dl->object); if (de == NULL) /* * Fote says the first object is NULL. Go with that. */ continue; /* * Show the domain's cookies. - FM */ for (cl = de->cookie_list; cl != NULL; cl = cl->next) { /* * First object is always NULL. - FM */ if ((co = (cookie *) cl->object) == NULL) continue; CTrace((tfp, "LYStoreCookies: %" PRI_time_t " %s %" PRI_time_t " ", CAST_time_t (now), (now < co->expires) ? "<" : ">", CAST_time_t (co->expires))); if ((co->flags & COOKIE_FLAG_DISCARD)) { CTrace((tfp, "not stored - DISCARD\n")); continue; } else if (!(co->flags & COOKIE_FLAG_EXPIRES_SET)) { CTrace((tfp, "not stored - no expiration time\n")); continue; } else if (co->expires <= now) { CTrace((tfp, "not stored - EXPIRED\n")); continue; } /* when we're sure we'll write to the file - open it */ if (cookie_handle == NULL) { cookie_handle = NewCookieFile(cookie_file); if (cookie_handle == NULL) return; } fprintf(cookie_handle, "%s\t%s\t%s\t%s\t%" PRI_time_t "\t%s\t%s%s%s\n", de->ddomain, (co->flags & COOKIE_FLAG_DOMAIN_SET) ? "TRUE" : "FALSE", co->path, (co->flags & COOKIE_FLAG_SECURE) ? "TRUE" : "FALSE", CAST_time_t (co->expires), co->name, (co->quoted ? "\"" : ""), NonNull(co->value), (co->quoted ? "\"" : "")); CTrace((tfp, "STORED %s\n", de->ddomain)); } } if (cookie_handle != NULL) { LYCloseOutput(cookie_handle); HTSYS_purge(cookie_file); } } #endif /* * Check if the given string is completely US-ASCII. If so (and if the * original were hex-encoded), it is likely to be more useful in a decoded * form. */ static BOOLEAN valueNonAscii(const char *value) { BOOLEAN result = FALSE; while (*value != '\0') { int ch = UCH(*value++); if (ch < 32 || ch > 126) { result = TRUE; break; } } return result; } /* LYHandleCookies - F.Macrides (macrides@sci.wfeb.edu) * --------------- * * Lists all cookies by domain, and allows deletions of * individual cookies or entire domains, and changes of * 'allow' settings. The list is invoked via the COOKIE_JAR * command (Ctrl-K), and deletions or changes of 'allow' * settings are done by activating links in that list. * The procedure uses a LYNXCOOKIE: internal URL scheme. * * Semantics: * LYNXCOOKIE:/ Create and load the Cookie Jar Page. * LYNXCOOKIE://domain Manipulate the domain. * LYNXCOOKIE://domain/lynxID Delete cookie with lynxID in domain. * * New functions can be added as extensions to the path, and/or by * assigning meanings to ;parameters, a ?searchpart, and/or #fragments. */ static int LYHandleCookies(const char *arg, HTParentAnchor *anAnchor, HTFormat format_out, HTStream *sink) { HTFormat format_in = WWW_HTML; HTStream *target = NULL; char *buf = NULL; char *domain = NULL; char *lynxID = NULL; HTList *dl, *cl, *next; domain_entry *de; cookie *co; char *name = NULL, *value = NULL, *path = NULL; char *comment = NULL, *Address = NULL, *Title = NULL; int ch; /* * Check whether we have something to do. - FM */ if (HTList_isEmpty(domain_list)) { HTProgress(COOKIE_JAR_IS_EMPTY); LYSleepMsg(); HTNoDataOK = 1; return (HT_NO_DATA); } /* * If there's a domain string in the "host" field of the LYNXCOOKIE: URL, * this is a request to delete something or change and 'allow' setting. - * FM */ if ((domain = HTParse(arg, "", PARSE_HOST)) != NULL) { if (*domain == '\0') { FREE(domain); } else { /* * If there is a path string (not just a slash) in the LYNXCOOKIE: * URL, that's a cookie's lynxID and this is a request to delete it * from the Cookie Jar. - FM */ if ((lynxID = HTParse(arg, "", PARSE_PATH)) != NULL) { if (*lynxID == '\0') { FREE(lynxID); } } } } if (domain) { /* * Seek the domain in the domain_list structure. - FM */ if ((de = find_domain_entry(domain)) != NULL) { FREE(domain); /* * We found the domain. Check whether a lynxID is present. - FM */ if (lynxID) { /* * Seek and delete the cookie with this lynxID in the domain's * cookie list. - FM */ for (cl = de->cookie_list; cl != NULL; cl = cl->next) { if ((co = (cookie *) cl->object) == NULL) /* * First object is always empty. - FM */ continue; if (!strcmp(lynxID, co->lynxID)) { /* * We found the cookie. Delete it if confirmed. - FM */ if (HTConfirm(DELETE_COOKIE_CONFIRMATION) == FALSE) { FREE(lynxID); HTNoDataOK = 1; return (HT_NO_DATA); } HTList_removeObject(de->cookie_list, co); freeCookie(co); co = NULL; total_cookies--; if ((de->bv == QUERY_USER && HTList_isEmpty(de->cookie_list)) && HTConfirm(DELETE_EMPTY_DOMAIN_CONFIRMATION)) { /* * No more cookies in this domain, no default * accept/reject choice was set by the user, and * got confirmation on deleting the domain, so do * it. - FM */ freeCookies(de); HTList_removeObject(domain_list, de); FREE(de); HTProgress(DOMAIN_EATEN); } else { HTProgress(COOKIE_EATEN); } LYSleepMsg(); HTNoDataOK = 1; break; } } } else { /* * Prompt whether to delete all of the cookies in this domain, * or the domain if no cookies in it, or to change its 'allow' * setting, or to cancel, and then act on the user's response. * - FM */ if (HTList_isEmpty(de->cookie_list)) { _statusline(DELETE_DOMAIN_SET_ALLOW_OR_CANCEL); } else { _statusline(DELETE_COOKIES_SET_ALLOW_OR_CANCEL); } HTNoDataOK = 1; while (1) { ch = LYgetch_single(); #ifdef VMS if (HadVMSInterrupt) { HadVMSInterrupt = FALSE; ch = 'C'; } #endif /* VMS */ switch (ch) { case 'A': /* * Set to accept all cookies from this domain. - FM */ de->bv = ACCEPT_ALWAYS; HTUserMsg2(ALWAYS_ALLOWING_COOKIES, de->ddomain); return (HT_NO_DATA); case 'C': /* * Cancelled. - FM */ reject: HTUserMsg(CANCELLED); return (HT_NO_DATA); case 'D': if (HTList_isEmpty(de->cookie_list)) { /* * We had an empty domain, so we were asked to * delete it. - FM */ freeCookies(de); HTList_removeObject(domain_list, de); FREE(de); HTProgress(DOMAIN_EATEN); LYSleepMsg(); break; } Delete_all_cookies_in_domain: /* * Delete all cookies in this domain. - FM */ cl = de->cookie_list; while (cl) { next = cl->next; co = (cookie *) (cl->object); if (co) { HTList_removeObject(de->cookie_list, co); freeCookie(co); co = NULL; total_cookies--; } cl = next; } HTProgress(DOMAIN_COOKIES_EATEN); LYSleepMsg(); /* * If a default accept/reject choice is set, we're * done. - FM */ if (de->bv != QUERY_USER) return (HT_NO_DATA); /* * Check whether to delete the empty domain. - FM */ if (HTConfirm(DELETE_EMPTY_DOMAIN_CONFIRMATION)) { freeCookies(de); HTList_removeObject(domain_list, de); FREE(de); HTProgress(DOMAIN_EATEN); LYSleepMsg(); } break; case 'P': /* * Set to prompt for cookie acceptance from this * domain. - FM */ de->bv = QUERY_USER; HTUserMsg2(PROMPTING_TO_ALLOW_COOKIES, de->ddomain); return (HT_NO_DATA); case 'V': /* * Set to reject all cookies from this domain. - FM */ de->bv = REJECT_ALWAYS; HTUserMsg2(NEVER_ALLOWING_COOKIES, de->ddomain); if ((!HTList_isEmpty(de->cookie_list)) && HTConfirm(DELETE_ALL_COOKIES_IN_DOMAIN)) goto Delete_all_cookies_in_domain; return (HT_NO_DATA); default: if (LYCharIsINTERRUPT(ch)) goto reject; continue; } break; } } } if (HTList_isEmpty(domain_list)) { /* * There are no more domains left. Don't delete the domain_list, * otherwise atexit may be called multiple times. - kw */ HTProgress(ALL_COOKIES_EATEN); LYSleepMsg(); } FREE(domain); FREE(lynxID); return (HT_NO_DATA); } /* * If we get to here, it was a LYNXCOOKIE:/ URL for creating and displaying * the Cookie Jar Page, or we didn't find the domain or cookie in a * deletion request. Set up an HTML stream and return an updated Cookie * Jar Page. - FM */ target = HTStreamStack(format_in, format_out, sink, anAnchor); if (target == NULL) { HTSprintf0(&buf, CANNOT_CONVERT_I_TO_O, HTAtom_name(format_in), HTAtom_name(format_out)); HTAlert(buf); FREE(buf); return (HT_NOT_LOADED); } /* * Load HTML strings into buf and pass buf to the target for parsing and * rendering. - FM */ #define PUTS(buf) (*target->isa->put_block)(target, buf, (int) strlen(buf)) WriteStreamTitle(target, COOKIE_JAR_TITLE); HTSprintf0(&buf, "<h1>%s (%s)%s<a href=\"%s%s\">%s</a></h1>\n", LYNX_NAME, LYNX_VERSION, HELP_ON_SEGMENT, helpfilepath, COOKIE_JAR_HELP, COOKIE_JAR_TITLE); PUTS(buf); HTSprintf0(&buf, "<div><em>Note:</em> %s\n", ACTIVATE_TO_GOBBLE); PUTS(buf); HTSprintf0(&buf, "%s</div>\n", OR_CHANGE_ALLOW); PUTS(buf); HTSprintf0(&buf, "<dl compact>\n"); PUTS(buf); for (dl = domain_list; dl != NULL; dl = dl->next) { de = (domain_entry *) (dl->object); if (de == NULL) /* * First object always is NULL. - FM */ continue; /* * Show the domain link and 'allow' setting. - FM */ HTSprintf0(&buf, "<dt>%s<dd><a href=\"%s//%s/\"><em>Domain:</em> %s</a>\n", de->ddomain, STR_LYNXCOOKIE, de->ddomain, de->ddomain); PUTS(buf); switch (de->bv) { case (ACCEPT_ALWAYS): HTSprintf0(&buf, COOKIES_ALWAYS_ALLOWED); break; case (REJECT_ALWAYS): HTSprintf0(&buf, COOKIES_NEVER_ALLOWED); break; case (QUERY_USER): HTSprintf0(&buf, COOKIES_ALLOWED_VIA_PROMPT); break; } PUTS(buf); HTSprintf0(&buf, "\n"); PUTS(buf); /* * Show the domain's cookies. - FM */ for (cl = de->cookie_list; cl != NULL; cl = cl->next) { if ((co = (cookie *) cl->object) == NULL) /* * First object is always NULL. - FM */ continue; /* * Show the name=value pair. - FM */ if (co->name) { StrAllocCopy(name, co->name); LYEntify(&name, TRUE); } else { StrAllocCopy(name, NO_NAME); } if (co->value) { StrAllocCopy(value, co->value); HTUnEscape(value); if (valueNonAscii(value)) strcpy(value, co->value); LYEntify(&value, TRUE); } else { StrAllocCopy(value, NO_VALUE); } HTSprintf0(&buf, "<dd><a href=\"%s//%s/%s\"><em>%s</em>=%s</a>\n", STR_LYNXCOOKIE, de->ddomain, co->lynxID, name, value); FREE(name); FREE(value); PUTS(buf); if (co->flags & COOKIE_FLAG_FROM_FILE) { HTSprintf0(&buf, "%s\n", gettext("(from a previous session)")); PUTS(buf); } /* * Show the path, port, secure and discard setting. - FM */ if (co->path) { StrAllocCopy(path, co->path); LYEntify(&path, TRUE); } else { StrAllocCopy(path, "/"); } HTSprintf0(&buf, "<dd><em>Path:</em> %s\n<dd><em>Port:</em> %d <em>Secure:</em> %s <em>Discard:</em> %s\n", path, co->port, ((co->flags & COOKIE_FLAG_SECURE) ? "YES" : "NO"), ((co->flags & COOKIE_FLAG_DISCARD) ? "YES" : "NO")); FREE(path); PUTS(buf); /* * Show the list of acceptable ports, if present. - FM */ if (co->PortList) { HTSprintf0(&buf, "<dd><em>PortList:</em> \"%s\"\n", co->PortList); PUTS(buf); } /* * Show the commentURL, if we have one. - FM */ if (co->commentURL) { StrAllocCopy(Address, co->commentURL); LYEntify(&Address, FALSE); StrAllocCopy(Title, co->commentURL); LYEntify(&Title, TRUE); HTSprintf0(&buf, "<dd><em>CommentURL:</em> <a href=\"%s\">%s</a>\n", Address, Title); FREE(Address); FREE(Title); PUTS(buf); } /* * Show the comment, if we have one. - FM */ if (co->comment) { StrAllocCopy(comment, co->comment); LYEntify(&comment, TRUE); HTSprintf0(&buf, "<dd><em>Comment:</em> %s\n", comment); FREE(comment); PUTS(buf); } /* * Show the Maximum Gobble Date. - FM */ HTSprintf0(&buf, "<dd><em>%s</em> %s%s", gettext("Maximum Gobble Date:"), ((co->flags & COOKIE_FLAG_EXPIRES_SET) ? ctime(&co->expires) : END_OF_SESSION), ((co->flags & COOKIE_FLAG_EXPIRES_SET) ? "" : "\n")); PUTS(buf); } HTSprintf0(&buf, "\n"); PUTS(buf); } HTSprintf0(&buf, "</dl>\n</body>\n</html>\n"); PUTS(buf); /* * Free the target to complete loading of the Cookie Jar Page, and report a * successful load. - FM */ (*target->isa->_free) (target); FREE(buf); return (HT_LOADED); } /* cookie_domain_flag_set * ---------------------- * All purpose function to handle setting domain flags for a * comma-delimited list of domains. cookie_domain_flags handles * invcheck behavior, as well as accept/reject behavior. - BJP */ static void cookie_domain_flag_set(char *domainstr, int flag) { domain_entry *de = NULL; char **str = typecalloc(char *); char *dstr = NULL; char *strsmall = NULL; if (str == NULL) { HTAlwaysAlert(gettext("Internal"), gettext("cookie_domain_flag_set error, aborting program")); exit_immediately(EXIT_FAILURE); } /* * Is this the first domain we're handling? If so, initialize domain_list. */ if (domain_list == NULL) { #ifdef LY_FIND_LEAKS atexit(LYCookieJar_free); #endif domain_list = HTList_new(); total_cookies = 0; } StrAllocCopy(dstr, domainstr); *str = dstr; while ((strsmall = LYstrsep(str, ",")) != 0) { if (*strsmall == '\0') /* Never add a domain for empty string. It would actually * make more sense to use strtok here. - kw */ continue; /* * Check the list of existing domains to see if this is a * re-setting of an already existing domain -- if so, just * change the behavior, if not, create a new domain entry. */ if ((de = find_domain_entry(strsmall)) == NULL) { de = typecalloc(domain_entry); if (de == NULL) outofmem(__FILE__, "cookie_domain_flag_set"); de->bv = ACCEPT_ALWAYS; de->invcheck_bv = INVCHECK_QUERY; switch (flag) { case (FLAG_ACCEPT_ALWAYS): de->invcheck_bv = DEFAULT_INVCHECK_BV; break; case (FLAG_REJECT_ALWAYS): de->invcheck_bv = DEFAULT_INVCHECK_BV; break; case (FLAG_QUERY_USER): de->invcheck_bv = DEFAULT_INVCHECK_BV; break; case (FLAG_INVCHECK_QUERY): de->bv = QUERY_USER; break; case (FLAG_INVCHECK_STRICT): de->bv = QUERY_USER; break; case (FLAG_INVCHECK_LOOSE): de->bv = QUERY_USER; break; } StrAllocCopy(de->domain, strsmall); StrAllocCopy(de->ddomain, SkipLeadingDot(strsmall)); de->cookie_list = HTList_new(); HTList_appendObject(domain_list, de); } switch (flag) { case (FLAG_ACCEPT_ALWAYS): de->bv = ACCEPT_ALWAYS; break; case (FLAG_REJECT_ALWAYS): de->bv = REJECT_ALWAYS; break; case (FLAG_QUERY_USER): de->bv = QUERY_USER; break; case (FLAG_INVCHECK_QUERY): de->invcheck_bv = INVCHECK_QUERY; break; case (FLAG_INVCHECK_STRICT): de->invcheck_bv = INVCHECK_STRICT; break; case (FLAG_INVCHECK_LOOSE): de->invcheck_bv = INVCHECK_LOOSE; break; } CTrace((tfp, "cookie_domain_flag_set (%s, bv=%u, invcheck_bv=%u)\n", strsmall, de->bv, de->invcheck_bv)); } FREE(strsmall); FREE(str); FREE(dstr); } /* * If any COOKIE_{ACCEPT,REJECT}_DOMAINS have been defined, process them. * These are comma delimited lists of domains. - BJP * * And for query/strict/loose invalid cookie checking. - BJP */ void LYConfigCookies(void) { static const struct { char **domain; int flag; int once; } table[] = { /* *INDENT-OFF* */ { &LYCookieSAcceptDomains, FLAG_ACCEPT_ALWAYS, TRUE }, { &LYCookieSRejectDomains, FLAG_REJECT_ALWAYS, TRUE }, { &LYCookieSStrictCheckDomains, FLAG_INVCHECK_STRICT, TRUE }, { &LYCookieSLooseCheckDomains, FLAG_INVCHECK_LOOSE, TRUE }, { &LYCookieSQueryCheckDomains, FLAG_INVCHECK_QUERY, TRUE }, { &LYCookieAcceptDomains, FLAG_ACCEPT_ALWAYS, FALSE }, { &LYCookieRejectDomains, FLAG_REJECT_ALWAYS, FALSE }, { &LYCookieStrictCheckDomains, FLAG_INVCHECK_STRICT, FALSE }, { &LYCookieLooseCheckDomains, FLAG_INVCHECK_LOOSE, FALSE }, { &LYCookieQueryCheckDomains, FLAG_INVCHECK_QUERY, FALSE }, /* *INDENT-ON* */ }; unsigned n; CTrace((tfp, "LYConfigCookies\n")); for (n = 0; n < TABLESIZE(table); n++) { if (*(table[n].domain) != NULL) { cookie_domain_flag_set(*(table[n].domain), table[n].flag); /* * Discard the value for system settings after we've used them. * The local settings will be merged with the contents of .lynxrc */ if (table[n].once) { FREE(*(table[n].domain)); } } } } #ifdef GLOBALDEF_IS_MACRO #define _LYCOOKIE_C_GLOBALDEF_1_INIT { "LYNXCOOKIE",LYHandleCookies,0} GLOBALDEF(HTProtocol, LYLynxCookies, _LYCOOKIE_C_GLOBALDEF_1_INIT); #else GLOBALDEF HTProtocol LYLynxCookies = {"LYNXCOOKIE", LYHandleCookies, 0}; #endif /* GLOBALDEF_IS_MACRO */