TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

Fixing a 37-year-old bug by merging a 22-year-old fix

268 pointsby tschernoover 10 years ago

10 comments

kazinatorover 10 years ago
This fix leaves a variable uninitialized, and depends on funny logic to later give it a value.<p>The general structure should be<p><pre><code> FILE *fp = stdin; &#x2F;* sane default *&#x2F; </code></pre> then override with a new pointer from fopen if necessary.<p>In fact, looking at this more closely, note how fp is scoped to the body of the for loop. That&#x27;s where it should be defined:<p><pre><code> for (firsttime = 1; ; firsttime = 0) { FILE *fp = stdin; &#x2F;* logic flips it to fp = fopen(...) as necessary *&#x2F; } </code></pre> [Note: the C compiler Joy was working with probably didn&#x27;t support block scope declarations except at the top of the function. This comment applies to merging the fix using the environment of today.]<p>Note how the fclose at the bottom of the loop ends up closing stdin in the case that fp == stdin! It ensures that this only happens once, but you have to analyze it to see that this is true. I would write this as:<p><pre><code> if (fp != stdin) &#x2F;* defensive! *&#x2F; fclose(fp); </code></pre> Making it an infinite loop terminated by calling exit is unnecessary. There is already a continue in the loop, and a break in an inner loop; another break instead of the exit, and a &quot;return status;&quot; at the bottom wouldn&#x27;t hurt. Better yet:<p><pre><code> &#x2F;* Enter loop whenever there are args left to process, or even if there are no args if it is the first time through (to process stdin instead). *&#x2F; for (firsttime = 1; firsttime || *argv; firsttime = 0) { FILE *fp = stdin; if (*argv) { &#x2F;* have args; do the fopen to replace stdin *&#x2F; &#x2F;* set status = 1 and continue if cannot fopen *&#x2F; } &#x2F;* do head logic *&#x2F; if (fp != stdin) fclose(fp); } return status; </code></pre> C itself was only a couple of years old when that code was written originally, and the systems were small. Whereas I&#x27;ve been using it since 1989. Not criticizing the original!
评论 #8432041 未加载
评论 #8429424 未加载
评论 #8429781 未加载
userbinatorover 10 years ago
The extremely odd for-loop structure feels to me like someone was trying very hard to avoid a goto, and in the process obfuscated the flow quite a bit more. The special case of head&#x27;ing stdin is similar to the classic &quot;loop and a half problem&quot; (which is really only a problem if you religiously abstain from goto use.) I&#x27;d consider this a more straightforward way to do it:<p><pre><code> FILE *f; int i = 1; ... if(argc &lt; 2) { f = stdin; goto do_head; } ... while(i&lt;argc) { f = fopen(argv[i], &quot;r&quot;); &#x2F;* check for failure to open *&#x2F; ... do_head: &#x2F;* head&#x27;ing code goes here *&#x2F; ... fclose(f); i++; } </code></pre> That being said, OpenBSD code is still far more concise and readable than GNU; compare coreutils&#x27; head.c here:<p><a href="http://code.metager.de/source/xref/gnu/coreutils/src/head.c" rel="nofollow">http:&#x2F;&#x2F;code.metager.de&#x2F;source&#x2F;xref&#x2F;gnu&#x2F;coreutils&#x2F;src&#x2F;head.c</a><p>GNU head has a bit more functionality, but I don&#x27;t think the increase in complexity - nearly 10x more lines - is proportional to that.
评论 #8431529 未加载
ceejayozover 10 years ago
In a 24-year-old version control system.<p><i>edit:</i> Wow, not sure what&#x27;s offensive here...
评论 #8430466 未加载
评论 #8429345 未加载
评论 #8429418 未加载
评论 #8429362 未加载
kazinatorover 10 years ago
This link to the commit log is slightly better:<p><a href="http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/head/head.c" rel="nofollow">http:&#x2F;&#x2F;cvsweb.openbsd.org&#x2F;cgi-bin&#x2F;cvsweb&#x2F;src&#x2F;usr.bin&#x2F;head&#x2F;he...</a><p>You&#x27;re one click away from the diff.
评论 #8429353 未加载
评论 #8429262 未加载
corditeover 10 years ago
One of my coworkers found a data integrity causing bug that has been around for 11 years.<p>He and his team lead had no idea how such a bug missed 11 years of peer-QA through all edits on that module &#x2F; activity.<p>It was as simple as converting to string and back and hitting an edge case on internationalization with commas vs periods.
评论 #8429851 未加载
评论 #8429234 未加载
评论 #8430974 未加载
ape4over 10 years ago
I like that the -number option is &quot;obsolete&quot;. I still use it.
评论 #8429551 未加载
ahmettover 10 years ago
Here is the diff:<p><a href="http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/head/head.c.diff?r1=1.17&amp;r2=1.18&amp;f=h" rel="nofollow">http:&#x2F;&#x2F;cvsweb.openbsd.org&#x2F;cgi-bin&#x2F;cvsweb&#x2F;src&#x2F;usr.bin&#x2F;head&#x2F;he...</a>
gojomoover 10 years ago
If OpenBSD has anyone young enough, would&#x27;ve been a fun twist to have someone who wasn&#x27;t even born at the time of the fix make the commit.
Scudsover 10 years ago
&gt; for (firsttime = 1; ; firsttime = 0) {<p>does this line predate the while loop?<p>or boolean values in C?
评论 #8429285 未加载
评论 #8429157 未加载
评论 #8429149 未加载
评论 #8429146 未加载
评论 #8429131 未加载
评论 #8429576 未加载
aalbertsonover 10 years ago
Looking through all these comments, my only response is &quot;effing nerds...&quot; &lt;3