<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: arclight</title>
    <description>The latest articles on DEV Community by arclight (@arclight).</description>
    <link>https://dev.to/arclight</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F229216%2F3cf1c7d5-50f0-4f27-936a-e0e473585f43.jpg</url>
      <title>DEV Community: arclight</title>
      <link>https://dev.to/arclight</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/arclight"/>
    <language>en</language>
    <item>
      <title>Revitalizing Castlequest, Part 4: Squarified Cushioned Treemaps</title>
      <dc:creator>arclight</dc:creator>
      <pubDate>Tue, 19 Oct 2021 21:57:11 +0000</pubDate>
      <link>https://dev.to/arclight/revitalizing-castlequest-part-4-squarified-cushioned-treemaps-1261</link>
      <guid>https://dev.to/arclight/revitalizing-castlequest-part-4-squarified-cushioned-treemaps-1261</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/arclight/revitalizing-castlequest-part-3-the-shape-of-code-19fi"&gt;previous installment&lt;/a&gt;, we looked at the source code's literal text shape to quickly estimate code "legacyness". FORTRAN source code prior to Fortran 90 has a very distinct shape due to the historical use of punched cards for code input. Knowing the language evolved toward an ALGOL-like block structure, we can even differentiate F66 from F77 just by looking at code shape. While that allows us to make a quick qualitative assessment of individual files, we often want to quickly assess the complexity of a complete project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Listings and Trees
&lt;/h2&gt;

&lt;p&gt;A very quick way to get information about a project is simply to look at the project's directory listing. For Castlequest, we see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ dir /s /O-s
 Volume in drive C is OS
 Volume Serial Number is 569C-2024

 Directory of C:\Users\apthorpe\Documents\git_projects\appraiser\tests\data\Castlequest_F66

04/12/2021  07:57         6,847,398 castlequest.pdf
04/12/2021  07:57           341,671 castlequest.ocr.txt
09/17/2021  10:40             3,384 WizTree_20210917103835.csv
08/07/2021  10:56             2,179 README.md
04/12/2021  07:57               188 LICENSE
08/20/2021  07:39    &amp;lt;DIR&amp;gt;          src
09/17/2021  10:38    &amp;lt;DIR&amp;gt;          ..
09/17/2021  10:38    &amp;lt;DIR&amp;gt;          .
               5 File(s)      7,194,820 bytes

 Directory of C:\Users\apthorpe\Documents\git_projects\appraiser\tests\data\Castlequest_F66\src

08/07/2021  10:56            64,190 main.f
08/07/2021  10:56            20,196 long.dat
08/07/2021  10:56             6,663 move.f
08/07/2021  10:56             6,499 init.f
08/07/2021  10:56             4,347 des.f
08/07/2021  10:56             4,330 help.f
08/07/2021  10:56             3,971 input.f
08/07/2021  10:56             3,950 short.dat
08/07/2021  10:56             2,918 invent.f
08/07/2021  10:56             2,889 object.dat
08/07/2021  10:56             1,648 fread.f
08/07/2021  10:56             1,598 hint.dat
08/07/2021  10:56             1,465 inst.dat
08/07/2021  10:56             1,369 wwolf.f
08/07/2021  10:56             1,238 savres.f
08/07/2021  10:56             1,026 ggnome.f
08/07/2021  10:56               913 adscor.f
08/07/2021  10:56               653 obj.f
08/07/2021  10:56               423 Makefile
08/07/2021  10:56               413 yorn.f
08/07/2021  10:56               243 rstart.f
08/07/2021  10:56               178 isig.f
08/07/2021  10:56               126 asa.py
08/20/2021  07:39    &amp;lt;DIR&amp;gt;          .
08/20/2021  07:39    &amp;lt;DIR&amp;gt;          ..
              23 File(s)        131,246 bytes

     Total Files Listed:
              28 File(s)      7,326,066 bytes
               5 Dir(s)  617,442,611,200 bytes free
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is from using the Windows command shell; Linux and MacOS shell users can get about the same information with &lt;code&gt;ls -R -S -s -h -1&lt;/code&gt;. The selected options are recursive, ordered by decreasing size in human-readable units, single column.&lt;/p&gt;

&lt;p&gt;It's not the greatest display but it's simple. Source files are under &lt;code&gt;./src&lt;/code&gt;, the largest one is &lt;code&gt;main.f&lt;/code&gt; followed by &lt;code&gt;move.f&lt;/code&gt; down to &lt;code&gt;isig.f&lt;/code&gt;. This may be adequate for small projects but starts showing its limits after about half a screen of output.&lt;/p&gt;

&lt;p&gt;A slightly better command line tool is &lt;code&gt;tree&lt;/code&gt; which as the name suggests displays the directory structure as a tree. Windows' &lt;code&gt;tree.com&lt;/code&gt; is not nearly as useful as the Unix version. Here we see the files arranged in a tree structure ordered by decreasing file size:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ c:\tools\msys64\usr\bin\tree.exe -h --sort=size
.
├── [6.5M]  castlequest.pdf
├── [334K]  castlequest.ocr.txt
├── [3.3K]  WizTree_20210917103835.csv
├── [2.1K]  README.md
├── [ 188]  LICENSE
└── [   0]  src
    ├── [ 63K]  main.f
    ├── [ 20K]  long.dat
    ├── [6.5K]  move.f
    ├── [6.3K]  init.f
    ├── [4.2K]  des.f
    ├── [4.2K]  help.f
    ├── [3.9K]  input.f
    ├── [3.9K]  short.dat
    ├── [2.8K]  invent.f
    ├── [2.8K]  object.dat
    ├── [1.6K]  fread.f
    ├── [1.6K]  hint.dat
    ├── [1.4K]  inst.dat
    ├── [1.3K]  wwolf.f
    ├── [1.2K]  savres.f
    ├── [1.0K]  ggnome.f
    ├── [ 913]  adscor.f
    ├── [ 653]  obj.f
    ├── [ 423]  Makefile
    ├── [ 413]  yorn.f
    ├── [ 243]  rstart.f
    ├── [ 178]  isig.f
    └── [ 126]  asa.py

1 directory, 28 files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, only slightly better than the flat display of &lt;code&gt;dir&lt;/code&gt; or &lt;code&gt;ls&lt;/code&gt; and really only suitable for small projects. Beyond a screenful of output, it's difficult to grasp the entirety of a project, its structure, size, and composition. The terminal interface is limiting; let's look for a graphical tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Treemaps
&lt;/h2&gt;

&lt;p&gt;We'd like to have a single image which displays the fraction of project space occupied by each file and directory over the entire project, ideally with some means of differentiating code from data from infrastructure, &lt;em&gt;etc.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Introducing the &lt;em&gt;treemap&lt;/em&gt;, a way of displaying hierarchical information as a two-dimensional map. Elements and groups are represented as rectangles, hierarchy is displayed using arrangement / layout, and rectangle size and color communicate properties about each element. Here is the full Castlequest project viewed as a treemap:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bzXUSFB---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hldhbv56thxfnjdj0rt2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bzXUSFB---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hldhbv56thxfnjdj0rt2.png" alt="Castlequest project treemap"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The area of a rectangle corresponds to file or directory size. Files are represented as individual rectangles which may be grouped into larger rectangles to show the directory hierarchy. Color indicates file type (extension).&lt;/p&gt;

&lt;p&gt;We immediately see that the bulk of the project's size is taken up by the PDF scans of the source code. This is not terribly useful but it does indicate that the interesting parts of the project take up only a small amount of space in the project.&lt;/p&gt;

&lt;p&gt;Ideally there would be a way to emphasize only the types of files we're interested in, but common treemap visualizers have very limited filtering capabilities. The primary use case of this class of utility is to quickly identify large files to help with filesystem maintenance. The example here is taken from &lt;a href="https://diskanalyzer.com/"&gt;WizTree&lt;/a&gt; on Windows but there are a number of free and commercial tools available for each platform. &lt;a href="https://www.win.tue.nl/sequoiaview/"&gt;SequoiaView&lt;/a&gt;, &lt;a href="https://github.com/shundhammer/qdirstat"&gt;KDirStat/QDirStat&lt;/a&gt;, &lt;a href="https://windirstat.net/"&gt;WinDirStat&lt;/a&gt;, and &lt;a href="http://www.derlien.com/"&gt;Disk Inventory X&lt;/a&gt; all provide roughly the same functionality.&lt;/p&gt;

&lt;p&gt;This is a good starting point; let's move on to looking specifically at Castlequest's &lt;code&gt;src&lt;/code&gt; directory:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HAslrqbp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9832qhn6e160dbcp0bgz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HAslrqbp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9832qhn6e160dbcp0bgz.png" alt="Castlequest source directory treemap"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One file (&lt;code&gt;main.f&lt;/code&gt;) is obviously much larger than all the other source files. Knowing nothing else about the project, we can guess that most of our modernization effort will be focused on that single file. We also see a number of data files associated with the project which implies there are file operations and data structures within the source code to manage this data. The size of the data files may imply the amount of static storage (RAM) or file activity the code requires. Remember that we're looking at the whole project, not just source code.&lt;/p&gt;

&lt;p&gt;This abstraction comes with a cost. While we gain an overall impression of project size and composition, some useful detail is obscured such as file and directory names. Using WizTree interactively we can see full path name and both allocated and actual file size. That's not apparent just from screenshots of the application. Still, we might like visual indication of the dialect of Fortran used in a file, the number and size of program units (e.g. functions, subroutines) in each file, and other metrics like number and severity of uncomfortable legacy constructs.&lt;/p&gt;

&lt;p&gt;It's unlikely an existing tool does everything we might want but depending on how useful we find this analysis, we might put some effort toward extending these utilities or writing a custom visualizer as was done for displaying code shape.&lt;/p&gt;

&lt;p&gt;Conveniently, treemap generation is not terribly complex; the original papers describing the algorithms for &lt;a href="http://www.win.tue.nl/~vanwijk/stm.pdf"&gt;squarifying&lt;/a&gt; and &lt;a href="http://www.win.tue.nl/~vanwijk/ctm.pdf"&gt;cushioning&lt;/a&gt; treemaps are readily available. Further, treemaps have been used to &lt;a href="http://www.cs.umd.edu/hcil/treemap-history/"&gt;visualize a wide variety of hierarchical data&lt;/a&gt; beyond filesystem contents. There are libraries in a number of languages which simplify treemap creation, for example the Python library &lt;a href="https://github.com/laserson/squarify"&gt;squarify&lt;/a&gt; generates treemap images using the Matplotlib plotting library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Visualizing Other Projects
&lt;/h2&gt;

&lt;p&gt;Castlequest is a relatively small and simple project. A slightly more complex project is NASA's &lt;a href="https://www1.grc.nasa.gov/research-and-engineering/ceaweb/"&gt;CEA2&lt;/a&gt; (Chemical Equilibrium with Applications) which performs literal rocket science.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lqs2xUpT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ztdq835d02bznimy4uk2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lqs2xUpT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ztdq835d02bznimy4uk2.png" alt="CEA2 treemap"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Source code is shown in yellow; we see it's a small fraction of the chemical transport and thermodynamic datafiles and example data. We also see source code spread over a few directories. CEA2 consists of an analytical package and several utilities for managing its physical property databases.&lt;/p&gt;

&lt;p&gt;A larger legacy FORTRAN project is &lt;a href="https://github.com/nasa/NASTRAN-95"&gt;NASTRAN-95&lt;/a&gt;, a finite element structural analysis code (again from NASA) that consists of almost 2000 individual source files as well as substantial example case input and output files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Di28m5QI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1yptjokcpsr8p793hxzb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Di28m5QI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1yptjokcpsr8p793hxzb.png" alt="NASTRAN-95 treemap"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we see a very clear organization of source and data. Source files are rather uniform in size compared to Castlequest, suggesting that NASTRAN is more evenly decomposed. Individual routines may be more focused and clearer than what we expect of Castlequest's large main program.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This sort of visualization is not necessary for refactoring but it helps with project estimation especially when a quick estimate is needed. Treemaps are an interesting way of visualizing data. Most standalone treemapping tools are aimed at filesystem maintenance but the general technique is useful for visualizing any form of hierarchical data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Image Credits:
&lt;/h2&gt;

&lt;p&gt;Cushion photo courtesy of &lt;a href="https://commons.wikimedia.org/wiki/File:Bali_cushions.JPG"&gt;~riley&lt;/a&gt;, &lt;a href="https://creativecommons.org/licenses/by-sa/3.0"&gt;CC BY-SA 3.0&lt;/a&gt;, via Wikimedia Commons&lt;/p&gt;

</description>
      <category>fortran</category>
      <category>legacy</category>
      <category>refactoring</category>
      <category>adventure</category>
    </item>
    <item>
      <title>Revitalizing Castlequest, Part 3: The Shape of Code</title>
      <dc:creator>arclight</dc:creator>
      <pubDate>Thu, 30 Sep 2021 14:59:31 +0000</pubDate>
      <link>https://dev.to/arclight/revitalizing-castlequest-part-3-the-shape-of-code-19fi</link>
      <guid>https://dev.to/arclight/revitalizing-castlequest-part-3-the-shape-of-code-19fi</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/arclight/revitalizing-castlequest-part-2-now-without-shouting-27hn"&gt;previous installment&lt;/a&gt;, I covered some of the inconvenient aspects of legacy FORTRAN composition and output, explained the origin of what we now consider misfeatures, and presented a case for changing Castlequest to play more comfortably on modern systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Apologies
&lt;/h2&gt;

&lt;p&gt;Before I get into today's topic, I have to apologize for the delay in writing. The Castlequest story has been stagnant but not for lack of interest or motivation. Originally I hoped to discuss what we might mean by legacy code and show qualitative views of a project that may help us estimate "legacyness" of a project.&lt;/p&gt;

&lt;p&gt;This rapidly devolved into an extended tool-building effort which has gone on far longer than I had intended. As tool construction dragged on, it has been difficult to show even interim results because showing a single indirect representation of the code or project seems to overemphasize the significance of the results or the importance of a single tool.&lt;/p&gt;

&lt;p&gt;It's ironic that this article has taken so long to produce because the whole intent of using qualitative graphical views is to make initial assessment faster and easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's Talk About Code Shape
&lt;/h2&gt;

&lt;p&gt;One method of assessing code "legacyness" I have been exploring is visualizing code "shape". You can often tell the vintage of old Fortran source code just by looking at the arrangement of text on a page without looking directly at the source code.&lt;/p&gt;

&lt;p&gt;As an example, FORTRAN 66 is very "flat", monolithic, and linear because the language lacks the block constructs introduced in FORTRAN 77. "Structured" programming didn't arrive until the late 60s or early 70s. Also, F66 dates to the era of punchcards and batch computing on relatively limited hardware. The cost of whitespace, structure, routine calls, &lt;em&gt;etc.&lt;/em&gt; was much higher in that era. The shape of F66 code reflects limitations of the language, hardware, and development paradigms available at the time.&lt;/p&gt;

&lt;p&gt;In contrast, FORTRAN 77 inherited much of ALGOL's block structure (&lt;code&gt;IF-THEN-ELSE-ENDIF&lt;/code&gt;, &lt;code&gt;DO-ENDDO&lt;/code&gt;). It was released well into the era of interactive computing where developers had immediate access to a command shell, editor, and compiler. The cost of whitespace, routine calls, structure, &lt;em&gt;etc.&lt;/em&gt; had dropped and resources for large programs were more widely available. The problems with monolithic and unstructured code were well known by this point. Developers knew decomposition was necessary to build and maintain large systems. The shape of F77 code again reflects the state of the language, hardware, and development paradigms. F77 source code is much more likely to be indented, decomposed into subroutines and functions, and generally be much more readable than F66. &lt;/p&gt;

&lt;p&gt;Fortran 90 introduced free-form source format, doing away with the fixed columnar format dating back to the language's introduction in 1956. The form of comments and line continuations were substantially changed and new block structures, modules, user-defined data types, and classes were added to the language. The six character limit on variable and routine names was lifted and lower case names were supported as standard. These advances meant that modern Fortran can have a much less distinct shape than its predecessors; from a distance, modern Fortran can be indistinguishable from C or C++.&lt;/p&gt;

&lt;p&gt;For better or worse, Fortran has valued backward compatibility from its earliest standards so it's possible to write F77 in F66 format or F90 in F77 format or just ignore modern constructs entirely and write F66 in F90 format. Due to backwards compatibility, the presence or absence of specific statements or constructs may not be enough to assess a code's "legacyness". Code shape may be a better indicator of "legacyness" than file extension. &lt;/p&gt;

&lt;p&gt;While it's an imperfect measure, the shape of source can tell us much about the condition of a Fortran codebase. Compare that to Python where the shape of modern and legacy source code is virtually identical making "legacyness" difficult to detect without detailed analysis, either by automatic parsing or manual inspection.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem With Parsing
&lt;/h2&gt;

&lt;p&gt;Even though parsing can be automated, it is still computationally intensive compared to generating a bitmap image from a text file. Legacy code parsers also need to cope with a variety of proprietary and obsolete constructs whereas an image generator just needs the ability to read text and produce a bitmap. This makes a code shape visualizer much easier to write than a special-purpose legacy code parser or a utility to automate compilation and sift through errors and diagnostics. Special-purpose parsing is valuable, but as I have found out it's also complex and time-consuming. Identifying and visualizing characteristics of legacy code is not exactly straightforward and it's not a simple matter to repurpose a development tool for custom static analysis.&lt;/p&gt;

&lt;p&gt;Another problem with repurposing a compiler for legacy code detection is that the source code may be so obtuse or non-standard that it does not compile with modern tools. A code may be of interest specifically because it does not easily compile and the owner wants to estimate the effort needed to return the code to a buildable, maintainable state.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Benefits of Shape Analysis
&lt;/h2&gt;

&lt;p&gt;Intentionally reducing the fidelity of the source code view keeps the eye focused on the code shape without the risk of staring too deeply into details. There's a limit to what code shape will tell you about a project but that's a good thing.&lt;/p&gt;

&lt;p&gt;A quick assessment should be quick. It's too easy to get bogged down looking at specific code constructs and thinking through how they might be improved or eliminated instead of doing a quick high-level inspection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fire!
&lt;/h2&gt;

&lt;p&gt;It's time for another tortured analogy to justify rapid low fidelity assessment.&lt;/p&gt;

&lt;p&gt;Imagine there's a fire reported at an apartment complex. Firefighters arrive and the first thing they do is survey the fireground. They look very generally at what's on fire (a dumpster, a single unit, one building, multiple buildings, &lt;em&gt;etc.&lt;/em&gt;), how intense or widespread the fire appears, and sort out where residents and employees are likely to be. They do this quickly.&lt;/p&gt;

&lt;p&gt;Once they estimate the totality of the fire and the characteristics of the fireground, they'll formulate a plan of attack and get to work. Eventually they will check unit by unit, floor by floor, and building by building to evacuate people. Initially however they need a rough idea of what they're up against and will spend the time to gather enough information to make a plan.&lt;/p&gt;

&lt;p&gt;Refactoring software does not have the same urgency and risk as firefighting. On the other hand, firefighters don't have to negotiate with the apartment complex owner, mayor, and city council over how many firefighters, trucks, hoses, gallons of water, and time they will be allowed to use to put out the fire.&lt;/p&gt;

&lt;p&gt;In our case, gathering enough information to make a plan takes effort, effort translates to time which translates to money, and clients and managers want to see a plan and cost estimate before they risk any money.&lt;/p&gt;

&lt;p&gt;Firefighters trade personal safety for freedom of action and occasionally a sweet mustache. They are very judicious about what risks they'll accept but they also have some very direct priorities: preserve life (theirs, the public's) and property in that order. If they need to break down a door or cut a hole in the roof, they just do it. No all-day meetings with managers and stakeholders. &lt;em&gt;Door, meet &lt;a href="https://en.wikipedia.org/wiki/Halligan_bar" rel="noopener noreferrer"&gt;halligan&lt;/a&gt;...&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In both cases, you need enough information to make a plan and you need to gather that information quickly. That plan will be created from incomplete information but that's okay. It only needs to be good enough to address big picture risks and estimate the resources required to finish the job. You won't know what you're actually up against until you're in the middle of it. You always risk facing a situation substantially worse than your initial assessment. With practice and experience, you'll learn to "read" the fireground faster and more accurately, you'll make better plans and reduce avoidable risks.&lt;/p&gt;

&lt;p&gt;Nice analogy, but what does this have to do with Castlequest? Not much. Let's look at some pretty pictures.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Shape of ADSCOR
&lt;/h2&gt;

&lt;p&gt;This is &lt;code&gt;ADSCOR.F&lt;/code&gt;, the subroutine that totals the player's score and returns it via the argument &lt;code&gt;II&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fortran"&gt;&lt;code&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;----------------------------------------------------&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="k"&gt;SUBROUTINE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ADSCOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="kt"&gt;INTEGER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SAVAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="kt"&gt;LOGICAL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DEBUG&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="k"&gt;COMMON&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;DEBUG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ISEED&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="k"&gt;COMMON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;BLOCK2&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SAVAR&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SAVAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;.EQ.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="mi"&gt;+9&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SAVAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;.EQ.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="mi"&gt;+10&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SAVAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;.EQ.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="mi"&gt;+10&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SAVAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;.EQ.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="mi"&gt;+10&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SAVAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;.EQ.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;81&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="mi"&gt;+1&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SAVAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;.EQ.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="mi"&gt;+10&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SAVAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;.EQ.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="mi"&gt;+10&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SAVAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;.EQ.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="mi"&gt;+10&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SAVAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;.EQ.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="mi"&gt;+10&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SAVAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;.EQ.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="mi"&gt;+10&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SAVAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;.EQ.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="mi"&gt;+10&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Score&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dependent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;moves&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;ITEMP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SAVAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ITEMP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;.GE.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ITEMP&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DEBUG&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WRITE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;8001&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;II&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="mi"&gt;8001&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FORMAT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'0  RESULT OF "ADSCOR" IS:'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;I3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="k"&gt;RETURN&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try to ignore the literal text and just focus on its shape. It's difficult. We are familiar with text, we expect it to have meaning, and we are naturally predisposed to look for patterns. We can't really help it. If we want to look at the shape, we need to obscure the text:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkkktva9afx74p1rx37x1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkkktva9afx74p1rx37x1.png" alt="src_adscor_16x"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A note on color: black is source code, red is a comment, light green is a line label (number), spaces are very light gray, and the page background is a very light cream color to very slightly differentiate space characters from empty space (a lack of characters).&lt;/p&gt;

&lt;p&gt;What stands out?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There are very few comments and no blank lines&lt;/li&gt;
&lt;li&gt;The first few columns contain either blanks, a label, or a comment&lt;/li&gt;
&lt;li&gt;Every comment starts in the first column&lt;/li&gt;
&lt;li&gt;Every line of code is indented by a (mostly) consistent number of spaces with only one further level of indenting&lt;/li&gt;
&lt;li&gt;There is unique content at the top and bottom of the file but there are a number of very similar lines in the middle&lt;/li&gt;
&lt;li&gt;This block of similar lines is the only section of the code which is indented&lt;/li&gt;
&lt;li&gt;Only one line near the end of the file contains a statement label&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can immediately tell this source code is fixed-format, probably F77 or F66. It's rather short, probably a single routine. There's some indenting but nothing that would indicate more than one level of nesting.&lt;/p&gt;

&lt;p&gt;In Fortran, line labels specify a &lt;code&gt;GOTO&lt;/code&gt; target, the terminating statement of a loop, or the reference number of a non-executable &lt;code&gt;FORMAT&lt;/code&gt; statement. If this is F66, it probably contains only one loop if it has any; multiple loops may use a single labeled statement as their termination. What we know is the label is not on a simple &lt;code&gt;CONTINUE&lt;/code&gt; statement which is the typical loop termination. It has the right shape for a &lt;code&gt;FORMAT&lt;/code&gt; statement but we can't infer much more than that. We don't know what the labeled statement is, but it's definitely not a &lt;code&gt;CONTINUE&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The uniform section in the middle of the file is possibly a table of data or a block of very similar I/O statements. Looking closer, it's probably not I/O: each line starts with a two-character word, much too short to contain &lt;code&gt;READ&lt;/code&gt; or &lt;code&gt;WRITE&lt;/code&gt;. It's long enough to contain a variable name or the statements &lt;code&gt;IF&lt;/code&gt;, or &lt;code&gt;DO&lt;/code&gt;. We can rule out &lt;code&gt;DO&lt;/code&gt; since there's only one labeled statement to act as a loop terminator and we can't identify a corresponding number of lines that could contain &lt;code&gt;END DO&lt;/code&gt; or &lt;code&gt;ENDDO&lt;/code&gt; statements. I believe the only statement which can begin with a variable name is an assignment such as &lt;code&gt;A = B + C&lt;/code&gt; so this block is most likely a stretch of conditional statements or assignments.&lt;/p&gt;

&lt;p&gt;This seems like a simple linear routine with no obvious signs of convoluted &lt;code&gt;GOTO&lt;/code&gt; logic or deeply-nested loops. This really restricts the type of painful constructs that might appear. The routine is also short which limits how many painful constructs might appear.&lt;/p&gt;

&lt;h2&gt;
  
  
  Elementary?
&lt;/h2&gt;

&lt;p&gt;There's something &lt;em&gt;just so&lt;/em&gt; about this analysis. It feels like a Sherlock Holmes tale with too many "obvious" inferences and details. We looked at the source code first so how many of those brilliant inferences are just &lt;em&gt;post hoc&lt;/em&gt; rationalizations?&lt;/p&gt;

&lt;p&gt;Further, is this sort of detailed inference any faster or better than just looking at the source code? Did the shape tell us anything we couldn't discover just by skimming the source text or using simple line count and pattern matching utilities? Skepticism is definitely warranted. Keeping this criticism in mind, we can move on to a more complex example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Differential Analysis
&lt;/h2&gt;

&lt;p&gt;Let's look at the shape of several longer routines. &lt;code&gt;DES.F&lt;/code&gt;, &lt;code&gt;HELP.F&lt;/code&gt;, &lt;code&gt;INIT.F&lt;/code&gt;, and &lt;code&gt;MOVE.F&lt;/code&gt; are all about the same length (120-130 lines), about five times the length of &lt;code&gt;ADSCOR.F&lt;/code&gt;. We'll reduce the magnification so the images fit better on the screen. This has the added benefit of making it more difficult to cheat by counting pixels.&lt;/p&gt;

&lt;p&gt;Here's the bitmap of &lt;code&gt;DES.F&lt;/code&gt;:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkkw6t8zc8abmt47ly9fi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkkw6t8zc8abmt47ly9fi.png" alt="DES.F bitmap"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;HELP.F&lt;/code&gt;:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fufs5cdjjr41w7a4i5ga5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fufs5cdjjr41w7a4i5ga5.png" alt="HELP.F bitmap"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;INIT.F&lt;/code&gt;:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcjspsd5l3iidj0dlm2qm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcjspsd5l3iidj0dlm2qm.png" alt="INIT.F bitmap"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and &lt;code&gt;MOVE.F&lt;/code&gt;:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F98l3ou4t83ghoh37fzjl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F98l3ou4t83ghoh37fzjl.png" alt="MOVE.F bitmap"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A new color has appeared: cornflower blue indicates continuation characters.&lt;/p&gt;

&lt;p&gt;Instead of analyzing each image individually, identifying and explaining interesting features, let's take a more open-ended approach. Compare the set of images and think through the following questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How many routines are there in each file?&lt;/li&gt;
&lt;li&gt;Which routines are most likely to have complex logic, for example loops, conditionals, and &lt;code&gt;GOTO&lt;/code&gt;s?&lt;/li&gt;
&lt;li&gt;Which produce the most diverse output? Hint: Look at how the &lt;code&gt;FORMAT&lt;/code&gt; statement is represented in &lt;code&gt;ADSCOR.F&lt;/code&gt;, both as source code and as a bitmap.&lt;/li&gt;
&lt;li&gt;Which routines contain the most static data?&lt;/li&gt;
&lt;li&gt;What is the balance between data and code in each routine?&lt;/li&gt;
&lt;li&gt;Can you tell the difference between comments that provide documentation versus those that disable source code?&lt;/li&gt;
&lt;li&gt;Can you see groups of similar statements? For example, can you identify large blocks of assignment or &lt;code&gt;FORMAT&lt;/code&gt; statements?&lt;/li&gt;
&lt;li&gt;Do the routines look as if they were written by the same person, many different people, or a team using a consistent code style?&lt;/li&gt;
&lt;li&gt;Which routines seem most interesting? If you could look at only one routine in detail, which would you choose?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now review the list of questions and ask how the answers color your view of how "legacy" a project is and what sort of refactorings may be needed for each routine. This is clearly procedural code; if you were converting it to object-oriented code, which routines suggest attributes and which suggest methods?&lt;/p&gt;

&lt;p&gt;Finally, consider how long it took to answer these questions for each file. The Castlequest source code is distributed among 16 files; how long would it take to assess the whole codebase by viewing bitmaps versus skimming source files in an IDE or text editor? By comparison, the &lt;a href="https://github.com/nasa/NASTRAN-95" rel="noopener noreferrer"&gt;NASTRAN-95 project&lt;/a&gt; consists of almost 1900 separate source files distributed among 10 directories - would the graphical or the textual approach be more effective on a project of that size? Would either approach be effective at that scale?&lt;/p&gt;

&lt;h3&gt;
  
  
  Aside: Copypasta
&lt;/h3&gt;

&lt;p&gt;Compare the bitmaps of &lt;code&gt;HELP.F&lt;/code&gt; and &lt;code&gt;INIT.F&lt;/code&gt;: did you notice the large block of code that appears to be duplicated in both files?&lt;/p&gt;

&lt;h2&gt;
  
  
  Your Own Half-Acre of Misery
&lt;/h2&gt;

&lt;p&gt;Part of my motivation for revitalizing Castlequest was to discover new and useful questions to ask about legacy code and find creative ways of getting answers. You probably aren't refactoring legacy FORTRAN or complex text adventure games, so what good is this in general?&lt;/p&gt;

&lt;p&gt;I believe there's value in stepping back from your code and asking these questions. It took a few hours over a day or two to write a tool to create very simple colorized bitmaps from Fortran source code. What do you think you'd learn about your own projects with this sort of analysis? Does your code or language have an identifiable shape? For as well as you know your language, what details can you identify just by looking at the shape of your source code?&lt;/p&gt;

&lt;h2&gt;
  
  
  Tomb of Horrors
&lt;/h2&gt;

&lt;p&gt;Let's look at one last bitmap: &lt;code&gt;MAIN.F&lt;/code&gt;, the heart and brains of Castlequest. Overall, the project contains roughly 3000 lines of combined source code and comments in total, and of this, two-thirds is concentrated in &lt;code&gt;MAIN.F&lt;/code&gt;. The shape of the code mostly speaks for itself:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8cqz26377cyz425luf5f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8cqz26377cyz425luf5f.png" alt="MAIN.F bitmap"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Due to the color scheme and image scale it's difficult to see but there are a whole lot of statement labels scattered throughout this routine. There are many loops and at least 650 &lt;code&gt;GOTO&lt;/code&gt; statements in this single routine.&lt;/p&gt;

&lt;p&gt;Every one of those &lt;code&gt;GOTO&lt;/code&gt; statements was refactored away. Eliminating the last 10 required a great deal of effort and resulted in some architectural compromises that I'm still not comfortable with. Rather than being an exercise in diminishing returns, it highlighted where deeper object oriented analysis and reorganization is needed. But that's a story for another day. &lt;/p&gt;




&lt;p&gt;In the next installment in the Castlequest series I'll map the flow of control in &lt;code&gt;MAIN.F&lt;/code&gt;. There's much we can learn by qualitatively visualizing our code's logical complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  References and Credits
&lt;/h2&gt;

&lt;p&gt;My limited understanding of practical firefighting comes from &lt;a href="https://fireengineeringbooks.com/collapse-of-burning-buildings-a-guide-to-fireground-safety-second-edition/" rel="noopener noreferrer"&gt;"Collapse of Burning Buildings: A Guide to Fireground Safety"&lt;/a&gt;, 2nd edition by Deputy Chief Vincent Dunn, NYFD (ret.). It's a fascinating book with interesting criticisms of fire science as well as modern building construction techniques. For as often as software developers use the analogy of firefighting, the reality is that a fire chief that regularly orders crews into hopeless conflagrations with inadequate planning and support and no regard for their well-being is unlikely to keep their job. In the software industry, that level of pathological behavior often gets one promoted.&lt;/p&gt;

&lt;p&gt;Halligan bar photo by &lt;a href="https://commons.wikimedia.org/w/index.php?curid=29205023" rel="noopener noreferrer"&gt;Marcel Rogge - Own work, CC BY-SA 3.0&lt;/a&gt;&lt;/p&gt;

</description>
      <category>fortran</category>
      <category>legacy</category>
      <category>refactoring</category>
      <category>adventure</category>
    </item>
    <item>
      <title>Revitalizing Castlequest, Part 2: NOW WITHOUT SHOUTING</title>
      <dc:creator>arclight</dc:creator>
      <pubDate>Fri, 03 Sep 2021 21:56:20 +0000</pubDate>
      <link>https://dev.to/arclight/revitalizing-castlequest-part-2-now-without-shouting-27hn</link>
      <guid>https://dev.to/arclight/revitalizing-castlequest-part-2-now-without-shouting-27hn</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/arclight/revitalizing-castlequest-part-1-museums-and-garages-4mhc"&gt;previous installment&lt;/a&gt;, I discussed several motivations for embarking on this modernization effort. It's important to be honest about why you are changing a code, whether you're modernizing, rewriting, or even retiring software.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Shall we play a game?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I've been writing about Castlequest but I haven't really shown it in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ .\cquest.exe
- Welcome to CASTLEQUEST!! Would you like instructions?

Y
0  You are in a remote castle somewhere in Eastern Europe.
   I will be your eyes and hands.  Direct me with words such
   as "LOOK", "TAKE", or "DROP".  To move, enter compass points
   (N,NE,E,SE,S,SW,W,NW), UP, or DOWN.  To get a list of what
   you are carrying, say "INVENTORY".  To save the current game
   so it can be finished later say "SAVE".  Say "RESTORE" as
   your first command to finish a game that had been saved.
0  The object of the game is to find the master of the castle
   and kill him, while accumulating as many treasures as possible.
   You get maximum points for depositing the treasures in the
   vault.  Notice that the descriptions of treasures have an
   exclamation point.  Be wary, as many dangers await you in
   in the castle.
0  Would you like more detailed instructions?

N
0  You are in a large, tarnished brass bed in an old, musty bedroom.
   cobwebs hang from the ceiling.  A few rays of light filter through
   the shutters.  There is a nightstand nearby with a single wooden
   drawer.  The door west creaks in the breeze.  A macabre portrait
   hangs to the left of an empty fireplace.
0  The shutters are closed.
0  There is a silver bullet here.

OPEN SHUTTERS
0  The shutter is open, but there are bars over the window.
0  Something on the ground outside is glistening brightly.

TAKE BULLET
0  OK

INVENTORY
0  You are carrying the following object:
      Silver bullet   

QUIT
0  You scored    0 out of  300 points.
0  You are a GREENHORN at this game!!

$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's a basic text adventure with a verb-noun command form. It recognizes only a few words (80 verbs and 76 nouns) and not every combination makes sense. Even fewer combinations do anything useful. Still, Castlequest's vocabulary is reasonable for the game's size and era.&lt;/p&gt;

&lt;p&gt;Two aspects of the game transcript are worth further discussion: why all my commands are in UPPER CASE and why there are all those zeroes at the beginning of a line.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Case of the Upper Case
&lt;/h2&gt;

&lt;p&gt;The upper case user input is maybe easiest to explain, and no, it's not that the cat stepped on caps-lock while I was distracted. F66 has almost no support for character data. The language has no string types and there are no functions for changing case or otherwise manipulating text. About all you can do is print text.&lt;/p&gt;

&lt;p&gt;Here's a fragment of the input routine (creatively named &lt;code&gt;INPUT&lt;/code&gt;, located in the file &lt;code&gt;input.f&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fortran"&gt;&lt;code&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;-----------------------------------------------------&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="k"&gt;SUBROUTINE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;INPUT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ACTION&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="k"&gt;IMPLICIT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;INTEGER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Z&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="kt"&gt;INTEGER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ACTION&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;VERBS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NOUNS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;76&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;BLANK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NVERBS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;/,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;NNOUNS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;76&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="kt"&gt;INTEGER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NOUN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;VERB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="k"&gt;EQUIVALENCE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VERB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NOUN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="k"&gt;DATA&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;VERBS&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hATTA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hBACK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hBREA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hBRIE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;61&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hCHOP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hCLIM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hCLOS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hCROS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;43&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hD&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hDEBU&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hDOWN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hDRIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hDROP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hE&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hEAST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hEAT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hENTE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hEXIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hEXTI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hFEED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hFILL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hFIRE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hFUCK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hGOTO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;51&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hHELP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hHINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hHONK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hIN&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hINVE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hJUMP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hKILL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hL&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;52&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hLEAV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hLEFT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;52&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hLIGH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hLOAD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hLOCK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hLONG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hLOOK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hMELT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hN&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hNE&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hNORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hNW&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hOFF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hON&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hOPEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hOUT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hPOOF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hPOUR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hQUIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hR&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;53&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hREAD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hREST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;58&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hRIGH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;53&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hS&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hSAVE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;57&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hSCOR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hSE&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hSHOO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hSHOW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hSOUT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hSTAB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hSUSP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;57&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hSW&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hSWIM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hT&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hTAKE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hTHRO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hTIE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hU&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hUNLO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hUNTI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hUP&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hVERB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hW&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hWAKE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hWATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hWAVE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;hWEST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code takes advantage of the fact that integer variables are stored as 4 consecutive bytes, characters can be represented with one byte, and most F66 compilers did not check type. So all the variables that look like they're holding nouns and verbs are actually integers. Here the big list of verbs is represented in Hollerith format (&lt;a href="https://www.ibm.com/ibm/history/ibm100/us/en/icons/tabulator/"&gt;named after Herman Hollerith&lt;/a&gt;, famed inventor of &lt;a href="http://www.quadibloc.com/comp/cardint.htm"&gt;punchcard&lt;/a&gt; tabulating machines in the 1890s). &lt;a href="https://en.wikipedia.org/wiki/Hollerith_constant"&gt;Hollerith format&lt;/a&gt; consists of the number of characters in the string followed by the letter &lt;code&gt;'H'&lt;/code&gt; followed by the actual characters in the string. The first verb in the list, &lt;code&gt;4hATTA&lt;/code&gt; is just the four characters &lt;code&gt;ATTA&lt;/code&gt;. Why not just put text between quotes like a normal person?&lt;/p&gt;

&lt;p&gt;There aren't any quotes in standard F66.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Seriously.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is the entire F66 character set:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 =+-*/(),.$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compiler vendors could support other characters as unofficial extensions to the language. For portability however, you're limited to those 47 characters. So per the language standard the text &lt;code&gt;ATTA&lt;/code&gt; should be written as &lt;code&gt;4HATTA&lt;/code&gt;. The upshot is all the game commands are upper case essentially because the language isn't guaranteed to support lower case and there's no good way to normalize text case in F66.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zeroes for Heroes
&lt;/h2&gt;

&lt;p&gt;Remember the gameplay from before? With all the leading zeroes sprinkled through the text? This?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Welcome to CASTLEQUEST!! Would you like instructions?

Y
0  You are in a remote castle somewhere in Eastern Europe.
   I will be your eyes and hands.  Direct me with words such
   as "LOOK", "TAKE", or "DROP".  To move, enter compass points
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first character on each line is an &lt;a href="https://en.wikipedia.org/wiki/ASA_carriage_control_characters"&gt;ASA carriage control code&lt;/a&gt; used to format text sent to a &lt;a href="https://en.wikipedia.org/wiki/Carriage_control_tape"&gt;(weird, ancient) printer&lt;/a&gt;. &lt;code&gt;' '&lt;/code&gt; (a space) is single-space, &lt;code&gt;'0'&lt;/code&gt; is double-space, &lt;code&gt;'-'&lt;/code&gt; is triple-space, &lt;code&gt;'+'&lt;/code&gt; is overstrike (zero-space?), and &lt;code&gt;'1'&lt;/code&gt; is form-feed. This is how the original gameplay looks when run through an ASA format converter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ .\cquest.exe | .\asa.ex
 Welcome to CASTLEQUEST!! Would you like instructions?
Y


  You are in a remote castle somewhere in Eastern Europe.
  I will be your eyes and hands.  Direct me with words such
  as "LOOK", "TAKE", or "DROP".  To move, enter compass points
  (N,NE,E,SE,S,SW,W,NW), UP, or DOWN.  To get a list of what
  you are carrying, say "INVENTORY".  To save the current game
  so it can be finished later say "SAVE".  Say "RESTORE" as
  your first command to finish a game that had been saved.

  The object of the game is to find the master of the castle
  and kill him, while accumulating as many treasures as possible.
  You get maximum points for depositing the treasures in the
  vault.  Notice that the descriptions of treasures have an
  exclamation point.  Be wary, as many dangers await you in
  in the castle.

  Would you like more detailed instructions?
N


  You are in a large, tarnished brass bed in an old, musty bedroom.
  cobwebs hang from the ceiling.  A few rays of light filter through
  the shutters.  There is a nightstand nearby with a single wooden
  drawer.  The door west creaks in the breeze.  A macabre portrait
  hangs to the left of an empty fireplace.

  The shutters are closed.

  There is a silver bullet here.
OPEN SHUTTERS


  The shutter is open, but there are bars over the window.

  Something on the ground outside is glistening brightly.
TAKE BULLET


  OK
INVENTORY


  You are carrying the following object:
     Silver bullet
QUIT


  You scored    0 out of  300 points.

  You are a GREENHORN at this game!!

$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Much better but still not great. But that's what you get with a language that dates to the early 60s. Escaped notation such as &lt;code&gt;"\n"&lt;/code&gt; wouldn't be invented until 1972 with C and wouldn't be a &lt;em&gt;de facto&lt;/em&gt; standard until much later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Do We Care?
&lt;/h2&gt;

&lt;p&gt;So that's the explanation for SHOUTED COMMANDS and the Mystery Zeroes in the original game output.&lt;/p&gt;

&lt;p&gt;Why does this matter? Two reasons: user experience and testing. The game is a lot less fun to play IF YOU HAVE TO SHOUT ALL YOUR COMMANDS. Recognizing commands in upper case only is authentic and there's an argument to be made for preserving that behavior. My counter-argument is that it doesn't add anything to game play, it strains the eyes, and the caps-lock key is stupid. When modernizing Castlequest, I made it a top priority to change the game to accept lower case.&lt;/p&gt;

&lt;p&gt;This also leads to issues when testing. In its original form, Castlequest can only be tested interactively. The game logic is tangled up in an immense monolithic main program. By definition, unit testing only works on discrete program units, not the whole code at once -- that's integral testing. I did all the coding and testing so I wound up playing a lot of Castlequest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So. Much. Castlequest.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To preserve my eyesight and my remaining sanity, I rewrote the input routines to ignore case. Replacing the long-deprecated Hollerith format with actual quotes was next, mainly because it was an easy change. Conversely, one of my very last changes was making ASA carriage control optional. That change was complex and miserable even after cleaning up the rest of the code.&lt;/p&gt;

&lt;p&gt;Beyond that, I made very few changes to the user experience. All the original typos are preserved; the game just crashes less and is easier on the eyes. But the innards of the code? Very little was left untouched.&lt;/p&gt;

&lt;p&gt;If you really want that authentic Castlequest experience, it's still in there. If you just want to play the game and find out what's in the drawer of the nightstand without fiddling about with &lt;code&gt;asa&lt;/code&gt;, you can do that too. NOW WITHOUT SHOUTING.&lt;/p&gt;

</description>
      <category>fortran</category>
      <category>legacy</category>
      <category>refactoring</category>
      <category>adventure</category>
    </item>
    <item>
      <title>Revitalizing Castlequest, Part 1: Museums and Garages</title>
      <dc:creator>arclight</dc:creator>
      <pubDate>Fri, 03 Sep 2021 20:07:14 +0000</pubDate>
      <link>https://dev.to/arclight/revitalizing-castlequest-part-1-museums-and-garages-4mhc</link>
      <guid>https://dev.to/arclight/revitalizing-castlequest-part-1-museums-and-garages-4mhc</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/arclight/revitalizing-castlequest-part-0-a-rumor-of-treasure-4b77"&gt;previous installment&lt;/a&gt;, I gave a brief overview of my eight week effort to convert the 1979 text adventure Castlequest from FORTRAN 66 to Fortran 2018.&lt;/p&gt;

&lt;p&gt;A big question: &lt;em&gt;"Why?"&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s My Motivation?
&lt;/h2&gt;

&lt;p&gt;My teen years were spent playing the text adventures that came in the wake of Castlequest, both Infocom classics such as &lt;em&gt;Zork&lt;/em&gt; to BASIC programs you had to type in yourself from a magazine or library book. I won't lie - nostalgia is part of my motivation but there's more to it than that. If I just wanted to play the game, it is &lt;a href="https://quuxplusone.github.io/blog/2021/03/11/castlequest-update/"&gt;already in a working playable state&lt;/a&gt;. Similarly, if I wanted to study the code as if it was a museum piece, the original 1981 code is &lt;a href="https://github.com/Quuxplusone/Castlequest/blob/main/castlequest.pdf"&gt;easily available in PDF form&lt;/a&gt;. Neither is a reason to modernize the code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Aside: "FORTRAN" was spelled in all caps until the 1990 revision&lt;br&gt;
when the name was officially changed to "Fortran". Typically&lt;br&gt;
"FORTRAN" refers to the revisions I, II, IV, 66, and 77 and&lt;br&gt;
"Fortran" refers to revisions made in 1990, 1995, 2003, 2008,&lt;br&gt;
and 2018. The former set is currently considered&lt;br&gt;
'legacy  FORTRAN' and the latter 'Modern Fortran'.&lt;/p&gt;

&lt;p&gt;To avoid eye strain and/or hearing loss, the 1966 and 1977 &lt;br&gt;
revisions of the language will be abbreviated "F66" and "F77"&lt;br&gt;
respectively.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Arthur O'Dwyer (introduced last episode) went through &lt;a href="https://quuxplusone.github.io/blog/2021/03/11/castlequest-update/"&gt;no small amount of difficulty&lt;/a&gt; trying to build and run the game after forty years without maintenance. I want Castlequest to be playable forty years from now, preferably with less pain.&lt;/p&gt;

&lt;p&gt;There is a problem, however. Well, &lt;em&gt;problems&lt;/em&gt;. I will cover interesting problems in detail later, but the major problem is that the code contains a number of F66 features which have been deprecated and deleted in Fortran 2018. The next time the code fails to compile it will be even more difficult to recover and there will be fewer people who understand F66 and its idioms well enough to recover it.&lt;/p&gt;

&lt;p&gt;It's not just that the implementation language is long obsolete. The design of the code is alien to software developers who have spent their careers using object-oriented languages such as C++, Java, Python, &lt;em&gt;etc&lt;/em&gt;. Castlequest's design and implementation don't affect gameplay so, from the player's perspective, they don't matter. What the program does is far more important than how it does it.&lt;/p&gt;

&lt;p&gt;The game is not the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Museums and Garages
&lt;/h2&gt;

&lt;p&gt;Arthur and I have very different philosophies regarding Castlequest's source code. He's acting as a museum - acquiring artifacts, preserving them, and presenting them for display and study. With Castlequest, his goal is to keep the source code and gameplay as close as possible to the 1981 version while making the game playable on modern systems.&lt;/p&gt;

&lt;p&gt;In contrast, I am acting as a garage. I bring a car into the bay, open the hood and assess its condition, then make it as safe, reliable, efficient, and maintainable as I can within the owner's constraints. At bare minimum it will be clean, road-legal, and will start, drive, and stop reliably. It may or may not have a pine tree air freshener hanging from the mirror but it will definitely have &lt;em&gt;New Car Smell&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8lnjohKW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xz9a70zsjjakttvd0r0u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8lnjohKW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xz9a70zsjjakttvd0r0u.png" alt="A pine tree air freshener"&gt;&lt;/a&gt;&lt;/p&gt;
New Car Smell



&lt;p&gt;My goals with Castlequest are to modernize the code to meet current language standards, restructure it to simplify maintenance, and clarify its internal operation should someone wish to implement the game in a new language. I want to preserve the original story and mechanics while improving the user experience on modern systems. I'm willing to sacrifice authenticity for playability and long-term maintainability.&lt;/p&gt;

&lt;p&gt;Arthur and I have very different approaches but they're both completely valid. We don't have to make an &lt;em&gt;either-or&lt;/em&gt; choice between &lt;a href="https://www.austincarmuseum.org/"&gt;museums and garages&lt;/a&gt;. There's space enough for archivists and mechanics and plenty of others.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/2Mxs6A1byIo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;
You find one in every car -- you'll see.






&lt;p&gt;Pine tree air freshener photo courtesy of Pål Berge via Wikimedia Commons&lt;/p&gt;

</description>
      <category>fortran</category>
      <category>legacy</category>
      <category>refactoring</category>
      <category>adventure</category>
    </item>
    <item>
      <title>Revitalizing Castlequest, Part 0: A Rumor of Treasure</title>
      <dc:creator>arclight</dc:creator>
      <pubDate>Fri, 03 Sep 2021 16:53:07 +0000</pubDate>
      <link>https://dev.to/arclight/revitalizing-castlequest-part-0-a-rumor-of-treasure-4b77</link>
      <guid>https://dev.to/arclight/revitalizing-castlequest-part-0-a-rumor-of-treasure-4b77</guid>
      <description>&lt;p&gt;Back in the Days of Old when college students hung out in dank computer labs wasting time and avoiding the sun, there once was a game called Castlequest. Although widely believed to be forever lost, a determined adventurer by the name of Arthur O'Dwyer sought out its authors &lt;a href="https://quuxplusone.github.io/blog/2021/03/09/castlequest/"&gt;which led to the game's rediscovery&lt;/a&gt; deep within the &lt;a href="https://cocatalog.loc.gov/cgi-bin/Pwebrecon.cgi?Search_Arg=TXu000091366&amp;amp;Search_Code=REGS&amp;amp;CNT=10&amp;amp;HIST=1"&gt;catacombs of the US Copyright Office&lt;/a&gt;. With no small effort, Arthur manually transcribed the scanned source code to its original machine-readable format and through various magicks brought it back to life.&lt;/p&gt;

&lt;p&gt;Ok, so he used a Fortran compiler.&lt;/p&gt;

&lt;p&gt;Castlequest was originally written in FORTRAN 66, a language usually associated with numerical analysis, punchcards, and overwhelming malaise. F66 is not a particularly good language for writing games, but Castlequest was written in 1979. FORTRAN 77 had only been formalized the year before, so - all things considered - the decision to use FORTRAN 66 was completely reasonable at that time.&lt;/p&gt;

&lt;p&gt;Arthur published the recovered Castlequest source code in March of 2021 and I learned of it in April. By mid-June I had converted the original rat's nest of FORTRAN 66 to Fortran 2018 - object-oriented, fully documented, with unit tests, build automation, and installation packages for MacOS, Windows, and Linux. From a user's perspective, Castlequest still plays the same but the internals are wildly different.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DWM6RefU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6eo62k3ptb62qqqsp4dm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DWM6RefU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6eo62k3ptb62qqqsp4dm.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Castlequest is an interesting program with some history behind it. It's also a worst-case scenario of tangled logic and unstructured, unmaintained code, the sort that Dijkstra warned us about. But beyond that, it's a vehicle for applying modern software development techniques and tools such as build automation, continuous integration, test-driven-development (TDD), and more. Finally, it demonstrates the resilience of 40-year-old software written in a 55-year-old dialect of a 65-year-old language, one that's still widely used for large-scale high performance numerical computing.&lt;/p&gt;

&lt;p&gt;I have been meaning to write about the Castlequest modernization project while the experience is still fresh: motivation, goals, processes and technologies applied, lessons learned, &lt;em&gt;etc.&lt;/em&gt; Rarely does one have the time or motivation to draft an extended &lt;em&gt;post mortem&lt;/em&gt; analysis of a software recovery project; my question is whether there's an audience for reading it. Drop me a comment if this tale sounds like it's worth continuing and what interests you the most about it.&lt;/p&gt;

&lt;p&gt;In closing, much credit goes to Arthur O'Dwyer for his persistent detective work and initial code recovery. That was a &lt;em&gt;lot&lt;/em&gt; of retyping...&lt;/p&gt;




&lt;p&gt;Note: This article was originally published (by me) on LinkedIn&lt;/p&gt;

</description>
      <category>fortran</category>
      <category>legacy</category>
      <category>refactoring</category>
      <category>adventure</category>
    </item>
    <item>
      <title>Balancing Clarity, Elegance, and Risk in Regular Expressions </title>
      <dc:creator>arclight</dc:creator>
      <pubDate>Mon, 16 Aug 2021 20:57:53 +0000</pubDate>
      <link>https://dev.to/arclight/balancing-clarity-elegance-and-risk-in-regular-expressions-44gc</link>
      <guid>https://dev.to/arclight/balancing-clarity-elegance-and-risk-in-regular-expressions-44gc</guid>
      <description>&lt;p&gt;Recently a colleague posted a security incident report about a single-character error in a regular expression. It got me thinking about the techniques I have used to reduce errors in my regular expressions and how I could use this incident as a guide to applying these techniques. In the process, I found this is a case where elegance works against clarity; sometimes clarity needs to win especially in security or safety applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;Before I start, I want to make one thing clear: regular expressions are complex and brittle. Everyone makes this sort of mistake so do not interpret this as a personal criticism of any individual, any group, any language, or any project. I am not second-guessing design decisions. Nobody cares about how I would have written the original code. Even I don't.&lt;/p&gt;

&lt;p&gt;Very simply, &lt;strong&gt;the code is what it is&lt;/strong&gt;; my goal is to see what we can learn from it and what a team can do to reduce the risk posed by regular expressions. This is not an exhaustive tutorial. The examples below are meant to illustrate techniques for writing safer, more reviewable code. That's all.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Incident
&lt;/h2&gt;

&lt;p&gt;Daniel Cuthbert (@dcuthbert) writes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nice little bug this showing how not properly escaping a full stop&lt;br&gt;
(.) can lead to code execution The bug writeup is brilliant too&lt;br&gt;
&lt;a href="https://github.com/pi-hole/AdminLTE/security/advisories/GHSA-5cm9-6p3m-v259"&gt;https://github.com/pi-hole/AdminLTE/security/advisories/GHSA-5cm9-6p3m-v259&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;tl;dr&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The one line technical summary from @SchneiderSec gets right to the point:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The issue lies in the fact that one of the periods is not escaped&lt;br&gt;
allowing any character to be used in it's place.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  A Few Words About Regular Expressions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Regular expressions are extremely powerful tools&lt;/li&gt;
&lt;li&gt;Regular expression notation is notoriously difficult to read&lt;/li&gt;
&lt;li&gt;Regular expressions are remarkably brittle and can fail in unexpected and catastrophic ways&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When we use them (or any construct, really) we are balancing &lt;em&gt;clarity&lt;/em&gt;, &lt;em&gt;elegance&lt;/em&gt;, and &lt;em&gt;risk&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Regular expressions are not an &lt;em&gt;always/never&lt;/em&gt; tool. There are as many good reasons to use them as to avoid them. Here we are analyzing short strings which is the dominant use case for regular expressions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;

&lt;p&gt;Here is code we are worried about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;validDomainWildcard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$domain_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// There has to be either no or at most one "*" at the beginning of a line&lt;/span&gt;
    &lt;span class="nv"&gt;$validChars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/^((\*.)?[_a-z\d](-*[_a-z\d])*)(\.([_a-z\d](-*[a-z\d])*))*(\.([_a-z\d])*)*$/i"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$domain_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$lengthCheck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/^.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;1,253&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;$/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$domain_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$labelLengthCheck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/^[^\.]&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;1,63}(\.[^\.]{1,63&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)*$/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$domain_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$validChars&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$lengthCheck&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$labelLengthCheck&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//length of each label&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function checks if a string matches three search patterns. The function returns &lt;code&gt;True&lt;/code&gt; if all three patterns match; if any pattern doesn't match, it returns &lt;code&gt;False&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Impressions and Setting Focus
&lt;/h2&gt;

&lt;p&gt;The problem is that there is a simple typo which drastically changes the function's behavior. It's not easy to see either as an author or reviewer. Otherwise there's nothing fundamentally wrong with this code.&lt;/p&gt;

&lt;p&gt;Parts of this routine are extremely simple and parts are very complex. The simple parts are &lt;strong&gt;really simple&lt;/strong&gt; - three assignments and a logical expression. The complex parts are the regexes so that is where we will focus our analytical efforts.&lt;/p&gt;

&lt;p&gt;So what can we do to limit the risks that regexes pose?&lt;/p&gt;

&lt;h2&gt;
  
  
  Spacing and Documentation
&lt;/h2&gt;

&lt;p&gt;Let's start by addressing clarity - how can we make regexes more readable?&lt;/p&gt;

&lt;p&gt;We can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add whitespace&lt;/li&gt;
&lt;li&gt;decompose complex expressions into meaningful subexpressions&lt;/li&gt;
&lt;li&gt;add comments to explain the intent of subexpressions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In some regex languages (for example PCRE and those embedded in Perl, Python, and PHP) you can disable the significance of carriage returns and other whitespace and allow embedded comments. Perl, Python, and PHP all support the '&lt;code&gt;x&lt;/code&gt;' option. In PHP, the '&lt;code&gt;x&lt;/code&gt;' option can also be enabled using the &lt;code&gt;PCRE_EXTENDED&lt;/code&gt; modifier. Check your languagefor details.&lt;/p&gt;

&lt;p&gt;Note that comments embedded in a regex are subject to additional escaping rules; we'll keep our documentation simple. There is already enough punctuation; let's try not to add any more.&lt;/p&gt;

&lt;p&gt;But say our regex language doesn't support embedded comments. Can we still use this method?&lt;/p&gt;

&lt;p&gt;Sure! Even without strange regex option flags, we can&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;decompose the pattern string into smaller substrings,&lt;/li&gt;
&lt;li&gt;document them, and&lt;/li&gt;
&lt;li&gt;compose the full pattern string from parts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is straightforward and should work in any language so you may want to try this tactic first.&lt;/p&gt;

&lt;h3&gt;
  
  
  When Should We Apply This?
&lt;/h3&gt;

&lt;p&gt;There is no hard and fast rule but if a regex has more than 2-3 paren'd groups or is more than half a line, consider breaking it up and commenting it.&lt;/p&gt;

&lt;h3&gt;
  
  
  PHP Example
&lt;/h3&gt;

&lt;p&gt;This is an example of adding whitespace and comments to the &lt;code&gt;$validChars&lt;/code&gt; regex. The result is still rather long and confusing, but the structure and intent are much more apparent. Repetitive groups and character classes stand out and we can see inconsistencies in patterns. As a reviewer, I should at least mark these down and ask the author for clarification of any suspicious constructs.&lt;/p&gt;

&lt;p&gt;Side note: My job as a reviewer is to ask for clarification, not to rewrite the code or redesign the application. These examples are more intended for code authors than reviewers.&lt;/p&gt;

&lt;p&gt;We might want to break out groups 1, 4, and 6 and explain what we are looking for but this is a start.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$validChars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;"/^                         # Start of string,
      (                         # Open group 1
        (\*.)?           # ERROR: Optional '*.' literal (domain wildcard) as group 2
        [_a-z\d]                # One letter, digit, or underscore
        (                       # Start group 3
          -*                    # Any number of hyphens
          [_a-z\d]              # One letter, digit, or underscore
        )*                      # Any number of group 3 matches
      )                         # Close group 1; group 1 should match once
      (                         # Open group 4
        \.                      # Literal '.'
        (                       # Open group 5
          [_a-z\d]              # One letter, digit, or underscore
          (                     # Open group 6
            -*                  # Any number of hyphens
            [a-z\d]             # One letter or digit -- Q: Should this have an underscore?
          )*                    # Any number of group 6 matches
        )                       # Close group 5
      )*                        # Close group 4; any number of group 4 matches
      (                         # Open group 6
        \.                      # Literal '.'
        (                       # Open group 7
          [_a-z\d]              # One letter, digit, or underscore
        )*                      # Close group 7; any number of group 7 matches
                                # -- Q: Group 7 can be empty; is that intended?
      )*                        # Close group 6; any number of group 6
      $/ix"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$domain_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;# End of string; case-insensitive,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have ever spent time programming LISP or Scheme, you might think this looks very similar to those languages. That is no accident. Regular expression notation is a low-level functional language. It just isn't as obvious when it's all crammed onto a single line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Backslashitis, Delimiters, and Character Classes
&lt;/h2&gt;

&lt;p&gt;Another clarity-killer is excessive escaping or as it is more commonly known, &lt;em&gt;Backslashitis&lt;/em&gt;. Some common entities and patterns require backslashes such as tab (&lt;code&gt;\t&lt;/code&gt;), digit (&lt;code&gt;\d&lt;/code&gt;), and word boundary (&lt;code&gt;\b&lt;/code&gt;). Some characters require escaping because they match regex operators.&lt;/p&gt;

&lt;p&gt;Basic examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.&lt;/code&gt; matches any character,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;\.&lt;/code&gt; matches a literal &lt;code&gt;.&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;*&lt;/code&gt; is the Kleene closure which modifies the pattern before it (pattern matches zero or more times),&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;\*&lt;/code&gt; matches a literal &lt;code&gt;*&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Parens are often used for embedding regex options or for capturing (grouping) parts of matches. This isn't consistent among regex languages so sometimes &lt;code&gt;\(&lt;/code&gt; and &lt;code&gt;\)&lt;/code&gt; are used for grouping, other times &lt;code&gt;()&lt;/code&gt; defines a group.&lt;/p&gt;

&lt;p&gt;Still other characters need to be escaped because they match pattern delimiters, historically &lt;code&gt;/&lt;/code&gt;. For this reason &lt;em&gt;Backslashitis&lt;/em&gt; is especially painful in web and filesystem code. The use of backslashes as path separators on Windows adds another layer of badness but it is only slightly less bad everywhere else.&lt;/p&gt;

&lt;h3&gt;
  
  
  Alternate Delimiters
&lt;/h3&gt;

&lt;p&gt;Perl worked around delimiter collision by allowing letting users set alternate pattern delimiters; for example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="sr"&gt;/my\/path/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can be replaced with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="sr"&gt;m#my/path#&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The leading &lt;code&gt;m&lt;/code&gt; stands for &lt;code&gt;match&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Python and PHP use normal strings to hold patterns so there is some overlap between escaped characters in both strings and regexes, &lt;em&gt;e.g.&lt;/em&gt; &lt;code&gt;\t&lt;/code&gt; for horizontal tab. You may not remove all escaping backslashes by changing delimiters but it is an easy way to clarify slash-heavy regexes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Character Classes
&lt;/h3&gt;

&lt;p&gt;If you can’t change delimiters, you can use character classes to reduce escaping. &lt;code&gt;[.]&lt;/code&gt; can often be used for as &lt;code&gt;\.&lt;/code&gt; and  &lt;code&gt;[*]&lt;/code&gt; for &lt;code&gt;\*&lt;/code&gt; so the original example pattern&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;could be rephrased as&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s still not great; it looks like ASCII art of a confused robot. It is definitely clearer and it makes some problems more obvious.&lt;/p&gt;

&lt;p&gt;Note that typos in character class notation have a very different failure mode than typos in backslash-escaped characters.&lt;/p&gt;

&lt;p&gt;Accidentally omitting &lt;code&gt;[&lt;/code&gt; or &lt;code&gt;]&lt;/code&gt; will probably cause the regex to fail to compile. It won’t silently change the meaning as drastically as dropping &lt;code&gt;\&lt;/code&gt; from &lt;code&gt;\.&lt;/code&gt; or &lt;code&gt;\*&lt;/code&gt; will.&lt;/p&gt;

&lt;h3&gt;
  
  
  Named Entities
&lt;/h3&gt;

&lt;p&gt;But what about entities like &lt;code&gt;\d&lt;/code&gt; or &lt;code&gt;\b&lt;/code&gt; which don't correspond to a single character? Often regex languages give these patterns explicit names.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\d&lt;/code&gt; is shorthand for &lt;code&gt;[0-9]&lt;/code&gt;; in POSIX bracket expression notation it can be written as &lt;code&gt;[:digit:]&lt;/code&gt;. &lt;code&gt;[A-Za-z]&lt;/code&gt; can be written as &lt;code&gt;[:alpha:]&lt;/code&gt;. &lt;code&gt;[:alnum:]&lt;/code&gt; is equivalent to &lt;code&gt;[A-Za-z0-9]&lt;/code&gt;, and &lt;code&gt;[A-Za-z0-9_]&lt;/code&gt; can be written as &lt;code&gt;\w&lt;/code&gt; or &lt;code&gt;[:word:]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In some regex dialects, &lt;code&gt;\b&lt;/code&gt; matches a word boundary, a zero-length position &lt;em&gt;before or after&lt;/em&gt; a pattern if it is outside a character class, but a &lt;em&gt;backspace&lt;/em&gt; if inside a character class.&lt;/p&gt;

&lt;p&gt;The point is that sometimes you are stuck using an escaped entity for which there is no good backslash-free substitute. That's okay; just rephrasing common patterns can improve clarity.&lt;/p&gt;

&lt;h3&gt;
  
  
  When Should We Apply This?
&lt;/h3&gt;

&lt;p&gt;If there are many escaped slashes in your pattern, see if you can change the pattern delimiters to something other than &lt;code&gt;/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you use the same character class more than 2-3 times in a pattern, try to substitute a POSIX bracketed expression or define it as a string, document it, and compose the main regex from these subpatterns.&lt;/p&gt;

&lt;p&gt;Keep a cheat sheet on your language's regex syntax handy; the meaning of entities like &lt;code&gt;\s&lt;/code&gt; (whitespace) varies among languages. You don't have to memorize all this weirdness; be safe and look it up. Life's too short to spend it debugging regexes.&lt;/p&gt;

&lt;h3&gt;
  
  
  PHP Example
&lt;/h3&gt;

&lt;p&gt;Let's revisit our &lt;code&gt;$validChars&lt;/code&gt; regex. PHP supports POSIX expressions so we can clarify our pattern by using named entities. Recall the named patterns we found earlier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;single digits can be matched with &lt;code&gt;[0-9]&lt;/code&gt;, &lt;code&gt;\d&lt;/code&gt;, or &lt;code&gt;[:digit:]&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;single letters can be matched with &lt;code&gt;[A-Za-z]&lt;/code&gt; or &lt;code&gt;[:alpha:]&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;a single digit or letter is called an &lt;em&gt;alphanumeric&lt;/em&gt; character and can be matched with &lt;code&gt;[A-Za-z0-9]&lt;/code&gt;, and&lt;/li&gt;
&lt;li&gt;'word' characters are defined as &lt;code&gt;[A-Za-z0-9_]&lt;/code&gt; and can be matched using &lt;code&gt;\w&lt;/code&gt; or &lt;code&gt;[:word:]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that &lt;code&gt;[[:alnum:]_]&lt;/code&gt; also matches word characters; please do not do this.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;$validChars&lt;/code&gt; regex made heavy use of &lt;code&gt;[_a-z\d]&lt;/code&gt; and &lt;code&gt;[a-z\d]&lt;/code&gt;. We can replace these with &lt;code&gt;[:word:]&lt;/code&gt; and &lt;code&gt;[:alnum:]&lt;/code&gt; respectively. This is possible because the &lt;code&gt;/i&lt;/code&gt; flag at the end of the pattern makes it case-insensitive so &lt;code&gt;[A-Z]&lt;/code&gt; is treated just like &lt;code&gt;[a-z]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Also, let's replace the problematic &lt;code&gt;(\*.)?&lt;/code&gt; construct with our confused robot regex. Our &lt;code&gt;/&lt;/code&gt; delimiter is fine as-is; we would probably want to change it if we were matching paths or URLs.&lt;/p&gt;

&lt;p&gt;We could also replace &lt;code&gt;\.&lt;/code&gt; with &lt;code&gt;[.]&lt;/code&gt;. It is not a big improvement in readability but it is cheap risk reduction so let's swap that in too. We can always change it back if it looks stupid.&lt;/p&gt;

&lt;p&gt;Substitution gives us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$validChars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;"/^                 # Start of string
    (                   # Open group 1
      ([*][.])?         # FIXED: Optional '*.' literal (domain wildcard) as group 2
      [:word:]          # One letter, digit, or underscore
      (                 # Start group 3
        -*              # Any number of hyphens
        [:word:]        # One letter, digit, or underscore
      )*                # Any number of group 3 matches
    )                   # Close group 1; group 1 should match once
    (                   # Open group 4
      [.]               # Literal '.'
      (                 # Open group 5
        [:word:]        # One letter, digit, or underscore
        (               # Open group 6
          -*            # Any number of hyphens
          [:alnum:]     # One letter or digit -- Q: Should this be [:word:]?
        )*              # Any number of group 6 matches
      )                 # Close group 5
    )*                  # Close group 4; any number of group 4 matches
    (                   # Open group 6
      [.]               # Literal '.'
      (                 # Open group 7
        [:word:]        # One letter, digit, or underscore
      )*                # Close group 7; any number of group 7 matches
                        # -- Q: Group 7 can be empty; is that intended?
    )*                  # Close group 6; any number of group 6
    $/ix"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;              &lt;span class="nv"&gt;$domain_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;# End of string; case-insensitive,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We completely eliminated backslashes and made the distinction between &lt;code&gt;[_a-z\d]&lt;/code&gt; and &lt;code&gt;[a-z\d]&lt;/code&gt; much more obvious. Consistent use of &lt;code&gt;[.]&lt;/code&gt; makes domain name separators look a little strange if we are used to &lt;code&gt;\.&lt;/code&gt; but the domain wildcard pattern is safer. It is much easier to see the difference between&lt;/p&gt;

&lt;p&gt;&lt;code&gt;([*][.])?&lt;/code&gt; and &lt;code&gt;([*].)?&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;versus&lt;/p&gt;

&lt;p&gt;&lt;code&gt;(\*\.)?&lt;/code&gt; and &lt;code&gt;(\*.)?&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You have to drop both '&lt;code&gt;[&lt;/code&gt;' and '&lt;code&gt;]&lt;/code&gt;' from around '&lt;code&gt;.&lt;/code&gt;' to cause the bug in the original code. Dropping just one would cause the regex to fail to compile and give us a chance to find the original error.&lt;/p&gt;

&lt;p&gt;At this point we are mostly left with grouping characters (parens) and have possibly identified a larger subpattern we may want to extract, document, and test separately, &lt;em&gt;i.e.&lt;/em&gt; &lt;code&gt;[:word:](-*[:word:])*&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We also see some breaks in symmetry; domain parts are variously matched by&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;[:word:](-*[:word:])*&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[:word:](-*[:alnum:])*&lt;/code&gt;, and&lt;/li&gt;
&lt;li&gt;&lt;code&gt;([:word:])*&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The reviewer should ask if these differences are intended; it isn't clear from the context or documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;This is a deep and complex topic so I will keep it brief.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Build test cases that are expected to fail.&lt;/em&gt; This may be difficult to remember because unit testing is often a constructive affirmation process used to demonstrate expected behavior with expected input. Compare this against adversarial security testing which actively seeks to exploit a function by throwing mountains of bad input at it.&lt;/p&gt;

&lt;p&gt;If you can test subpatterns, do so. It is easier to compose test cases for simple patterns than complex ones and you will need fewer tests overall.&lt;/p&gt;

&lt;p&gt;Remember that unit tests are a developer convenience, not a substitute for verification and validation (V &amp;amp; V), acceptance tests, integral tests, regression tests, &lt;em&gt;etc.&lt;/em&gt; In legacy code, the cost of backfitting unit tests is often prohibitive but you can add unit tests to new code and remediate the rest as time permits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is There a Simpler or Clearer Way to Do This?
&lt;/h2&gt;

&lt;p&gt;I programmed Perl for over a decade; I think regexes are awesome. Also, I avoid them if possible because as we see here, regex errors are subtle, easy to make, and difficult to find.&lt;/p&gt;

&lt;p&gt;What are some simpler replacements?&lt;/p&gt;

&lt;h3&gt;
  
  
  Direct Checks
&lt;/h3&gt;

&lt;p&gt;One common regex is &lt;code&gt;/^X/&lt;/code&gt; (that is, does this string start with the specific character &lt;code&gt;X&lt;/code&gt;?). It is simple, clear, and probably optimized in your language’s regex engine.&lt;/p&gt;

&lt;p&gt;But why not directly compare the first character of the string to &lt;code&gt;X&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Which is easier to explain, this?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Xena'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/^X/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$target&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;# Do some warrior princess things&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or this?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Xena'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$target&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'X'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;# Do some warrior princess things&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The direct check has several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it does not rely on cryptic regular expression notation,&lt;/li&gt;
&lt;li&gt;direct checks often use fewer resources (memory, time) than regexes, and&lt;/li&gt;
&lt;li&gt;direct checks fail less spectacularly than regexes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The regex has two advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it is more flexible, and&lt;/li&gt;
&lt;li&gt;it does not rely on the correct number of '&lt;code&gt;=&lt;/code&gt;' signs to work correctly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Question: is this limited to PHP or are there other languages where this is an issue?&lt;/p&gt;

&lt;p&gt;If we want to make this a case-insensitive test or search for multiple characters, the direct comparison method gets complex and ugly fast. It is easy enough to convert the direct check to a regex later if we need to.&lt;/p&gt;

&lt;p&gt;Note that in PHP there is a risk of the conditional changing meaning if we leave out one or more &lt;code&gt;=&lt;/code&gt; signs in the direct check. The consequences of using &lt;code&gt;==&lt;/code&gt; instead of &lt;code&gt;===&lt;/code&gt; may be small but the consequences of using &lt;code&gt;=&lt;/code&gt; instead of &lt;code&gt;==&lt;/code&gt; can be massive.&lt;/p&gt;

&lt;p&gt;A simple way to guard against this is to swap the positions of the constant and the variable terms in the test, such as&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Xena'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'X'&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$target&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;# Do some warrior princess things&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks strange but it has the same meaning. The only difference is that if there is a typo in the conditional operator, the code will crash rather than silently making an assignment and continuing execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simple Length Checks
&lt;/h3&gt;

&lt;p&gt;The original example code uses two regexes to check the length of the domain name and its parts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$lengthCheck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/^.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;1,253&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;$/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$domain_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$labelLengthCheck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/^[^\.]&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;1,63}(\.[^\.]{1,63&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)*$/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$domain_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's focus on the first one. Most languages have a string length function. For example, string length is found in PHP using &lt;code&gt;strlen()&lt;/code&gt;, Python uses &lt;code&gt;len()&lt;/code&gt;, &lt;em&gt;etc.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The regex here seems like seems like overkill. As a reviewer I have to ask if there is a reason string length checks are done with regexes instead of &lt;code&gt;strlen()&lt;/code&gt;? There might be; I don't know. That's why I'm asking.&lt;/p&gt;

&lt;p&gt;We can easily replace&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$lengthCheck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/^.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;1,253&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;$/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$domain_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$domainLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$domain_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$lengthCheck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;$domainLength&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$domainLength&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;253&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can be clarified further if we define a utility function such as&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="o"&gt;**&lt;/span&gt;
 &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nc"&gt;Determines&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;between&lt;/span&gt; &lt;span class="nv"&gt;$min&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="nv"&gt;$max&lt;/span&gt;
 &lt;span class="o"&gt;*&lt;/span&gt;
 &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;  &lt;span class="n"&gt;integer&lt;/span&gt;  &lt;span class="nv"&gt;$number&lt;/span&gt;     &lt;span class="nc"&gt;The&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;
 &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;  &lt;span class="n"&gt;integer&lt;/span&gt;  &lt;span class="nv"&gt;$min&lt;/span&gt;        &lt;span class="nc"&gt;The&lt;/span&gt; &lt;span class="n"&gt;minimum&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;range&lt;/span&gt;
 &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;  &lt;span class="n"&gt;integer&lt;/span&gt;  &lt;span class="nv"&gt;$max&lt;/span&gt;        &lt;span class="nc"&gt;The&lt;/span&gt; &lt;span class="n"&gt;maximum&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;range&lt;/span&gt;
 &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;  &lt;span class="n"&gt;boolean&lt;/span&gt;  &lt;span class="nv"&gt;$inclusive&lt;/span&gt;  &lt;span class="nc"&gt;Whether&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;range&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="n"&gt;inclusive&lt;/span&gt; &lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="n"&gt;not&lt;/span&gt;
 &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;boolean&lt;/span&gt;              &lt;span class="nc"&gt;Whether&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="n"&gt;was&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;range&lt;/span&gt;
 &lt;span class="o"&gt;*/&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;in_range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$max&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$inclusive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;is_int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;is_int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$min&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;is_int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$max&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$inclusive&lt;/span&gt;
            &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nv"&gt;$min&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nv"&gt;$max&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$min&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;$max&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(credit: Luke at &lt;em&gt;Stack Overflow&lt;/em&gt; &lt;a href="https://stackoverflow.com/a/35272658"&gt;https://stackoverflow.com/a/35272658&lt;/a&gt; )&lt;/p&gt;

&lt;p&gt;We can reduce the check down to a single line with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$lengthCheck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;in_range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$domain_name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;253&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This change is not critical but it is an option.&lt;/p&gt;

&lt;h3&gt;
  
  
  Complex Length Checks
&lt;/h3&gt;

&lt;p&gt;The second length check is more complex, checking if all the domain name components have a length between 1 and 63 inclusive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$labelLengthCheck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/^[^\.]&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;1,63}(\.[^\.]{1,63&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)*$/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$domain_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It would take some effort to replace this regex with elemental string functions and probably will not make it any clearer. What we can do is break up the regex and document it.&lt;/p&gt;

&lt;p&gt;Maybe do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$labelLengthCheck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;"/^              # Starts with
    [^\.]&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;1,63&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;      # 1-63 non-dot chars
    (                # any number of groups
        \.           # containing a dot
        [^\.]&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;1,63&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;  # followed by 1-63 non-dot chars
    )*               # until the end of the string
    $/x"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$domain_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$domain_part&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'[^\.]{1,63}'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$labelLengthPattern&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/^${domain_part}(\.${domain_part})*$/"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$domain_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'reasonable.example.com'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$labelLengthCheck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$labelLengthPattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$domain_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or alternately, since we are already detecting and grouping domain parts in the &lt;code&gt;$validChars&lt;/code&gt; regex, we could apply our length checks to selected groups. We might want to use &lt;em&gt;non-capturing subpatterns&lt;/em&gt; to restrict the captured matches to just the full domain parts. Non-capturing subpatterns are inside parens just like groups but the opening paren is followed by '&lt;code&gt;?:&lt;/code&gt;'. If we wanted our confused robot regex to be non-capturing, we&lt;br&gt;
could change it from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;?:&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our Confused Robot Regex looks even more confused. The Very Confused Robot Regex.&lt;/p&gt;

&lt;p&gt;The optional third argument to &lt;code&gt;preg_match&lt;/code&gt; is a list of captured groups. We would really like to capture everything that matches groups 1, 4, and 6 and check if all those matches have 1-63 characters. Note that groups 4 and 6 may match multiple times and we need to check each one. This also leads to weird numbering in the capture array; here it doesn't matter since we have to check all the matched domain parts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$validChars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;"/^                 # Start of string
    (                   # Open group 1
                        # Next line is non-capturing pattern AAA
      (?:[*][.])?       # FIXED: Optional '*.' literal (domain wildcard)
      [:word:]          # One letter, digit, or underscore
      (?:               # Open non-capturing pattern BBB
        -*              # Any number of hyphens
        [:word:]        # One letter, digit, or underscore
      )*                # Any number of group BBB matches
    )                   # Close group 1; group 1 should match once
    (?:                 # Open non-capturing group CCC
      [.]               # Literal '.'
      (                 # Open capturing group 2
        (?:             # Open non-capturing pattern DDD
          [:word:]      # One letter, digit, or underscore
          (?:           # Open non-capturing pattern EEE
            -*          # Any number of hyphens
            [:alnum:]   # One letter or digit -- Q: Should this be [:word:]?
          )*            # Close group EEE. Any number of group EEE matches
        )               # Close group DDD
      )                 # Close group 2; any number of group 2 matches captured
    )*                  # Close group CCC
    (?:                 # Open non-capturing pattern FFF
      [.]               # Literal '.'
      (                 # Open capturing group 3
        (?:             # Non-capturing group GGG
          [:word:]      # One letter, digit, or underscore
        )*              # Close non-capturing group GGG;
      )                 # Close group 3; There may be any number of group 3 matches
                        # -- Q: Group GGG can be empty; is that intended?
                        # -- Q: Can domain part end in a dot
    )*                  # Close non-capturing group FFF
    $/ix"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;              &lt;span class="nv"&gt;$domain_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$domain_parts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;# End of string; case-insensitive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's edit out unnecessary non-capturing groups and compress some of the vertical space:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$validChars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;"/^                   # Start of string
    (                     # Open group 1
      (?:[*][.])?         # FIXED: Optional '*.' literal (domain wildcard)
      [:word:]            # One letter, digit, or underscore
      (?:-*[:word:])*     # Optional hyphen then a word character (0 or more)
    )                     # Close group 1; group 1 should match once. Captured.
    (?:                   # Open non-capturing group AAA
      [.]                 # Literal '.'
      (                   # Open capturing group 2
        [:word:]          # One letter, digit, or underscore
        (?:-*[:alnum:])*  # Optional hyphen then an alphanumeric character (0 or more)
      )                   # Close group 2; any number of group 2 matches captured
    )*                    # Close group AAA
    (?:                   # Open non-capturing pattern BBB
      [.]                 # Literal '.'
      ([:word:]*)         # Group 3 captures any number of word characters
                          # -- Q: Group 3 can be empty; is that intended?
                          # -- Q: Can domain part end in a dot
    )*                    # Close non-capturing group BBB
    $/ix"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                &lt;span class="nv"&gt;$domain_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$domain_parts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;# End of string; case-insensitive&lt;/span&gt;

&lt;span class="c1"&gt;// Initially set $labelLengthCheck = 1 (TRUE)&lt;/span&gt;
&lt;span class="c1"&gt;// Iterate over domain parts; if one is too short&lt;/span&gt;
&lt;span class="c1"&gt;// or too long, set $labelLengthCheck = 0 (FALSE) and exit loop&lt;/span&gt;
&lt;span class="nv"&gt;$labelLengthCheck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$domain_parts&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$part&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$partLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$part&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nf"&gt;in_range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$partLength&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;63&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$labelLengthCheck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm not sure this is any clearer than the original &lt;code&gt;$labelLengthCheck&lt;/code&gt; regex; someone with more recent PHP experience could clean this further. Again, as a reviewer my job is to point out that the &lt;code&gt;$ValidChars&lt;/code&gt; regex already returns enough information &lt;em&gt;for free&lt;/em&gt; that we can set &lt;code&gt;$labelLengthCheck&lt;/code&gt; without using an additional regex. It is not my goal to rewrite the code, just to ask if errors are more obvious or more catastrophic in the regex version or the &lt;code&gt;strlen()&lt;/code&gt; version.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;$validChars&lt;/code&gt; regex has been simplified in some ways, made more complex in others. There is a legibility cost incurred by using non-capturing patterns.&lt;/p&gt;

&lt;h3&gt;
  
  
  When Do We Apply This?
&lt;/h3&gt;

&lt;p&gt;When you see a short regex, ask if a regex is really necessary.&lt;/p&gt;

&lt;p&gt;Look for clearer alternatives with safer and more obvious failure modes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Elegance Does Not Guarantee Clarity
&lt;/h2&gt;

&lt;p&gt;Let’s look at the original function again (complete with error), but rearrange it so regexes are stored in strings&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;validDomainWildcard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$domain_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// There has to be either no or at most one "*" at the beginning of a line&lt;/span&gt;
    &lt;span class="nv"&gt;$validCharsPattern&lt;/span&gt;       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/^((\*.)?[_a-z\d](-*[_a-z\d])*)"&lt;/span&gt;
                             &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"(\.([_a-z\d](-*[a-z\d])*))*"&lt;/span&gt;
                             &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"(\.([_a-z\d])*)*$/i"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$lengthCheckPattern&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/^.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;1,253&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;$/"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$labelLengthCheckPattern&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/^[^\.]&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;1,63}(\.[^\.]{1,63&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)*$/"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nv"&gt;$validChars&lt;/span&gt;       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$validCharsPattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="nv"&gt;$domain_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$lengthCheck&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$lengthCheckPattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="nv"&gt;$domain_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$labelLengthCheck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$labelLengthCheckPattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$domain_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$validChars&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$lengthCheck&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$labelLengthCheck&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//length of each label&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using three regex checks in a row like this has a nice symmetry; a column each for results, regex patterns and targets. At a high level, it is very clear what is intended. At a low level it’s difficult to see if it is implemented correctly.&lt;/p&gt;

&lt;p&gt;We can rephrase it as&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;in_range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$max&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$inclusive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;is_int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;is_int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$min&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;is_int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$max&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$inclusive&lt;/span&gt;
            &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nv"&gt;$min&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nv"&gt;$max&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$min&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;$max&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;validDomainWildcard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$domain_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// There has to be either no or at most one "*" at the beginning of a line&lt;/span&gt;
    &lt;span class="nv"&gt;$validChars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;"/^                   # Start of string
        (                     # Open group 1
          (?:[*][.])?         # FIXED: Optional '*.' literal (domain wildcard)
          [:word:]            # One letter, digit, or underscore
          (?:-*[:word:])*     # Optional hyphen then a word character (0 or more)
        )                     # Close group 1; group 1 should match once. Captured.
        (?:                   # Open non-capturing group AAA
          [.]                 # Literal '.'
          (                   # Open capturing group 2
            [:word:]          # One letter, digit, or underscore
            (?:-*[:alnum:])*  # Optional hyphen then an alphanumeric character (0 or more)
          )                   # Close group 2; any number of group 2 matches captured
        )*                    # Close group AAA
        (?:                   # Open non-capturing pattern BBB
          [.]                 # Literal '.'
          ([:word:]*)         # Group 3 captures any number of word characters
                              # -- Q: Group 3 can be empty; is that intended?
                              # -- Q: Can domain part end in a dot
        )*                    # Close non-capturing group BBB
        $/ix"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                &lt;span class="nv"&gt;$domain_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$domain_parts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;# End of string; case-insensitive&lt;/span&gt;

    &lt;span class="c1"&gt;// Check overall domain length is from 1 to 253 characters&lt;/span&gt;
    &lt;span class="nv"&gt;$lengthCheck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;in_range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$domain_name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;253&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;// Iterate over domain parts; if one is too short&lt;/span&gt;
    &lt;span class="c1"&gt;// or too long, set $labelLengthCheck = 0 (FALSE) and exit loop&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$domain_parts&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$part&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$labelLengthCheck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;in_range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$part&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;63&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$labelLengthCheck&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// If any check fails, return false (0)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$validChars&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$lengthCheck&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$labelLengthCheck&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is longer and uglier; we lost the clean symmetry and brevity of the original. We also added another function which needs testing, documentation, &lt;em&gt;etc.&lt;/em&gt; It's less elegant but the complex regex is documented so overall it's easier to review.&lt;/p&gt;

&lt;p&gt;Elegance isn’t bad, it just may not always serve us.&lt;/p&gt;

&lt;p&gt;Important code like a security check requires extra clarity; failure modes are serious. Regexes are difficult to read and they are key to maintaining security at this point in the code. And here, clarity at low level is more important than at high level because errors are more likely to occur at the low level of regexes.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;del&gt;You Are Probably Misusing&lt;/del&gt; &lt;em&gt;Use&lt;/em&gt; Peer Review
&lt;/h2&gt;

&lt;p&gt;Find a colleague not on the project who can behave like a professional, have them read over your code and write down any technical questions they have. Read through their questions and resolve each one by changing the code or adding documentation to make it clearer. That's it.&lt;/p&gt;

&lt;p&gt;Any mentoring that happens in this process is incidental. A technical review is for finding errors or weaknesses and making sure the code is clear enough that any member of the team can read and debug it.&lt;/p&gt;

&lt;p&gt;Whatever you do, &lt;strong&gt;DO NOT&lt;/strong&gt; sit around a table with a half-dozen team members going line-by-line through someone's code. This wastes a lot of time and can easily turn into a developer-beating party. This is neither productive nor effective and it can turn ugly and unprofessional before anyone realizes it's happening.&lt;/p&gt;

&lt;p&gt;Aside: I worked in the nuclear industry under a formal independent technical review process. Every engineer was assigned as a technical reviewer on their peers' work; it was a normal, integral part of our work process whether we were documenting engineering calculations or writing code. I found errors in senior engineers' work. People found errors in mine. Errors happen; it is a team effort to sniff them out before a report is issued or code is released. Peer technical review is one aspect of nuclear work I miss when in other environments. If I could, I wouldn't release important code without it.&lt;/p&gt;

&lt;p&gt;You don't have to be working on safety-critical systems to get the benefit of peer technical review. Peer review does not have to be a nitpicky ritual beating; it shouldn't be. If it is, something is very, very wrong with your process or work culture.&lt;/p&gt;

&lt;p&gt;The three rules for technical review: be professional, be thorough, and be fast -- in that order.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Regular expressions are very powerful tools but they can be difficult to read. They fail in subtle, unexpected, and spectacular ways.&lt;/p&gt;

&lt;p&gt;It is impossible to keep errors out of your regular expressions but there are techniques to make errors more obvious in review and less catastrophic in production. Some are easier to apply than others but none of them require you to become some regex wizard overnight. You may not need every one on every project but it helps to know what techniques are available to give you and your team the best chance of success.&lt;/p&gt;

</description>
      <category>regex</category>
      <category>security</category>
      <category>risk</category>
    </item>
    <item>
      <title>How much do you trust your toolchain and why?</title>
      <dc:creator>arclight</dc:creator>
      <pubDate>Mon, 30 Sep 2019 03:12:31 +0000</pubDate>
      <link>https://dev.to/arclight/how-much-do-you-trust-your-toolchain-and-why-19hp</link>
      <guid>https://dev.to/arclight/how-much-do-you-trust-your-toolchain-and-why-19hp</guid>
      <description>&lt;p&gt;Whether you use a compiled or interpreted language or something in-between, how do you know your compiler/interpreter faithfully and correctly implements the language?&lt;/p&gt;

&lt;p&gt;How much undefined or vendor/processor-dependent behavior is in your language specification? Meaning, how complete is your language's specification?&lt;/p&gt;

&lt;p&gt;Do you evaluate your toolchain for completeness and correctness? Moreso than just having a list of open issues, how much can you trust that the language you use is consistently implementable?&lt;/p&gt;

&lt;p&gt;Is this the first time you've ever thought about how much you can trust your toolchain?&lt;/p&gt;

</description>
      <category>codequality</category>
      <category>safety</category>
    </item>
  </channel>
</rss>
