home *** CD-ROM | disk | FTP | other *** search
/ ftp.cse.unsw.edu.au / 2014.06.ftp.cse.unsw.edu.au.tar / ftp.cse.unsw.edu.au / pub / doc / papers / misc / cs.toronto.edu:programming / steal.ms < prev    next >
Encoding:
Text File  |  1992-10-18  |  31.2 KB  |  976 lines

  1. .\" beware:  not all versions of -ms approve of .UX inside the abstract
  2. .de P \" point in a list
  3. .IP \(bu 2
  4. ..
  5. .de R \" reference
  6. .IP [\\$1] 4
  7. ..
  8. .de A \" appendix formatting changes (smaller size type etc.)
  9. .br
  10. .nr PS 8
  11. .ps 8
  12. .nr VS 9
  13. .vs 9
  14. ..
  15. .de T \" template (in appendix)
  16. .sp 3
  17. .SH L
  18. .\" I wanted to make this ".BX \\$1", but that doesn't work quite right here
  19. \\$1
  20. .LP
  21. .nf
  22. .\" be nice to switch to a small monospace font, but I don't have one handy
  23. .\" backslashes in templates have all been edited into \e
  24. .\" minuses in templates should be turned into \- if monospace font not used
  25. .\" non-default tab stops may be useful/needed in appendix
  26. ..
  27. .TL
  28. How To Steal Code
  29. .br
  30. or
  31. .br
  32. Inventing The Wheel Only Once
  33. .AU
  34. Henry Spencer
  35. .AI
  36. Zoology Computer Systems
  37. 25 Harbord St.
  38. University of Toronto
  39. Toronto, Ont. M5S\|1A1  Canada
  40. {allegra,ihnp4,decvax,utai}!utzoo!henry
  41. .AB
  42. Much is said about ``standing on other people's shoulders, not their toes'',
  43. but in fact the wheel is re-invented every day in the
  44. Unix/C
  45. community.
  46. Worse, often it is re-invented badly, with bumps, corners, and cracks.
  47. There are ways of avoiding this:
  48. some of them bad, some of them good,
  49. most of them under-appreciated and under-used.
  50. .AE
  51. .SH L
  52. Introduction
  53. .LP
  54. ``Everyone knows'' that that the
  55. .UX /C
  56. community and its programmers are the very paragons of re-use of software.
  57. In some ways this is true.
  58. Brian Kernighan [1] and others have waxed eloquent about how outstanding
  59. .UX
  60. is as an environment for software re-use.
  61. Pipes, the shell, and the design of programs as `filters' do much to
  62. encourage programmers to build on others' work rather than starting from
  63. scratch.
  64. Major applications can be, and often are, written without a line of C.
  65. Of course, there are always people who insist on doing everything themselves,
  66. often citing `efficiency' as the compelling reason why they
  67. can't possibly build on the work of others
  68. (see [2] for some commentary on this).
  69. But surely these are the lamentable exceptions, rather than the rule?
  70. .LP
  71. Well, in a word, no.
  72. .LP
  73. At the level of shell programming, yes, software re-use is widespread in the
  74. .UX /C
  75. community.
  76. Not quite as widespread or as effective as it might be, but definitely common.
  77. When the time comes to write programs in C, however, the situation changes.
  78. It took a radical change in directory format to make people use
  79. a library to read directories.
  80. Many new programs still contain hand-crafted code to analyze their arguments,
  81. even though prefabricated help for this has been available for years.
  82. C programmers tend to think that ``re-using software''
  83. means being able to take
  84. the source for an existing program and edit it to produce the source for
  85. a new one.
  86. While that \fIis\fR a useful technique, there are better ways.
  87. .LP
  88. Why does it matter that re-invention is rampant?
  89. Apart from the obvious, that programmers have more work to do, I mean?
  90. Well, extra work for the programmers is not exactly an unmixed blessing,
  91. even from the programmers' viewpoint!
  92. Time spent re-inventing facilities that are already available is time that
  93. is \fInot\fR available to improve user interfaces, or to make the program
  94. run faster, or to chase down the proverbial Last Bug.
  95. Or, to get really picky, to make the code readable and clear so that our
  96. successors can \fIunderstand\fR it.
  97. .LP
  98. Even more seriously, re-invented wheels are often square.
  99. Every time that a line of code is re-typed is a new chance for bugs to
  100. be introduced.
  101. There will always be the temptation to take shortcuts based on how
  102. the code will be used\(emshortcuts that may turn around and bite the
  103. programmer when
  104. the program is modified or used for something unexpected.
  105. An inferior algorithm may be used because it's ``good enough'' and
  106. the better algorithms are too difficult to reproduce on the spur of
  107. the moment... but the definition of ``good enough'' may change later.
  108. And unless the program is well-commented [here we pause for laughter],
  109. the next person who works on it will have to study the code at length
  110. to dispel the suspicion that there is some subtle reason for the
  111. seeming re-invention.
  112. Finally, to quote [2],
  113. \fIif you re-invent the square wheel, you will not benefit when
  114. somebody else rounds off the corners\fR.
  115. .LP
  116. In short, re-inventing the wheel ought to be a rare event,
  117. occurring only for the most compelling reasons.
  118. Using an existing wheel, or improving an existing one, is usually superior
  119. in a variety of ways.
  120. There is nothing dishonorable about stealing code*
  121. .FS
  122. * Assuming no software licences, copyrights, patents, etc. are violated!
  123. .FE
  124. to make life easier and better.
  125. .SH L
  126. Theft via the Editor
  127. .LP
  128. .UX
  129. historically has flourished in environments in which full
  130. sources for the system are available.
  131. This led to the most obvious and crudest way of stealing code:
  132. copy the source of an existing program and edit it to do something new.
  133. .LP
  134. This approach does have its advantages.
  135. By its nature, it is the most flexible method of stealing code.
  136. It may be the only viable approach when what is desired is some variant
  137. of a complex algorithm that exists only within an existing program;
  138. a good example was V7 \fIdumpdir\fR (which printed a table of contents
  139. of a backup tape), visibly a modified copy of V7 \fIrestor\fR (the only
  140. other program that understood the obscure format of backup tapes).
  141. And it certainly is easy.
  142. .LP
  143. On the other hand, this approach also has its problems.
  144. It creates two subtly-different copies of the same code, which have to
  145. be maintained separately.
  146. Worse, they often have to be maintained ``separately but simultaneously'',
  147. because the new program inherits all the mistakes of the original.
  148. Fixing the same bug repeatedly is so mind-deadening that there is great
  149. temptation to fix it in only
  150. the program that is actually giving trouble... which means that when the
  151. other gives trouble, re-doing the cure must be preceded by re-doing the
  152. investigation and diagnosis.
  153. Still worse, such non-simultaneous bug fixes cause the variants of the
  154. code to diverge steadily.
  155. This is also true of improvements and cleanup work.
  156. .LP
  157. A program created in this way may also be inferior, in some ways, to one
  158. created from scratch.
  159. Often there will be vestigial code left over from the program's
  160. evolutionary ancestors.
  161. Apart from consuming resources (and possibly harboring bugs) without a useful
  162. purpose, such vestigial code greatly complicates understanding the new
  163. program in isolation.
  164. .LP
  165. There is also the possibility that the new program has inherited a poor
  166. algorithm from the old one.
  167. This is actually a universal problem with stealing code,
  168. but it is especially troublesome with this technique because the original
  169. program probably was not built with such re-use in mind.
  170. Even if its algorithms were good for \fIits\fR intended purpose,
  171. they may not be versatile enough to do a good job in their new role.
  172. .LP
  173. One relatively clean form of theft via editing is to alter the original
  174. program's source to generate either desired program by conditional
  175. compilation.
  176. This eliminates most of the problems.
  177. Unfortunately, it does so only if the two programs are sufficiently similar
  178. that they can share most of the source.
  179. When they diverge significantly, the result can be a maintenance nightmare,
  180. actually worse than two separate sources.
  181. Given a close similarity, though, this method can work well.
  182. .SH L
  183. Theft via Libraries
  184. .LP
  185. The obvious way of using somebody else's code is to call a library function.
  186. Here,
  187. .UX
  188. has had some success stories.
  189. Almost everybody uses the \fIstdio\fR library rather than inventing their own
  190. buffered-I/O package.
  191. (That may sound trivial to those who never programmed on a V6 or earlier
  192. .UX ,
  193. but in fact it's a great improvement on the earlier state of affairs.)
  194. The simpler sorts of string manipulations are usually done with the
  195. \fIstrxxx\fR functions rather than by hand-coding them,
  196. although efficiency issues and the wide diversity of requirements have
  197. limited these functions to less complete success.
  198. Nobody who knows about \fIqsort\fR bothers to write his own sorting
  199. function.
  200. .LP
  201. However, these success stories are pleasant islands in an ocean of mud.
  202. The fact is that
  203. .UX 's
  204. libraries are a disgrace.
  205. They are well enough implemented, and their design flaws are seldom more
  206. than nuisances, but there aren't \fIenough\fR of them!
  207. Ironically,
  208. .UX 's
  209. ``poor cousin'',
  210. the Software Tools community [3,4],
  211. has done much better at this.
  212. Faced with a wild diversity of different operating systems,
  213. they were forced to put much more emphasis on identifying clean abstractions
  214. for system services.
  215. .LP
  216. For example,
  217. the Software Tools version of \fIls\fR runs unchanged, \fIwithout\fR
  218. conditional compilation, on dozens of different operating systems [4].
  219. By contrast,
  220. .UX
  221. programs that read directories invariably dealt with the raw system
  222. data structures, until Berkeley turned this cozy little world upside-down
  223. with a change to those data structures.
  224. The Berkeley implementors were wise enough to provide a library for
  225. directory access, rather than just documenting the new underlying structure.
  226. However,
  227. true to the
  228. .UX
  229. pattern,
  230. they designed a library which quietly assumed (in some of its naming
  231. conventions) that the underlying system used \fItheir\fR structures!
  232. This particular nettle has finally been grasped firmly by the IEEE POSIX
  233. project [5], at the cost of yet another slightly-incompatible interface.
  234. .LP
  235. The adoption of the new directory libraries is not just
  236. a matter of convenience and portability:
  237. in general the libraries are faster than the hand-cooked code
  238. they replace.
  239. Nevertheless, Berkeley's original announcement of the change was greeted
  240. with a storm of outraged protest.
  241. .LP
  242. Directories, alas, are not an isolated example.
  243. The
  244. .UX /C
  245. community simply hasn't made
  246. much of an effort to identify common code
  247. and package it for re-use.
  248. One of the two major variants of
  249. .UX
  250. still lacks a library function for binary search,
  251. an algorithm which is notorious for both the performance boost it can
  252. produce
  253. and the difficulty of coding a fully-correct version from scratch.
  254. No major variant of
  255. .UX
  256. has a library function for either one of the following code fragments,
  257. both omnipresent (or at least, they \fIshould\fR be omnipresent [6]) in
  258. simple* programs that use the relevant facilities:
  259. .FS
  260. * I include the qualification ``simple'' because complex programs often
  261. want to do more intelligent error recovery than these code fragments suggest.
  262. However, \fImost\fR of the programs that use these functions \fIdon't\fR need
  263. fancy error recovery, and the error responses indicated are \fIbetter\fR than
  264. the ones those programs usually have now!
  265. .FE
  266. .DS
  267. if ((f = fopen(filename, mode)) == NULL)
  268.     \fIprint error message with filename, mode, and specific
  269.     reason for failure, and then exit\fR
  270. .DE
  271. .DS
  272. if ((p = malloc(amount)) == NULL)
  273.     \fIprint error message and exit\fR
  274. .DE
  275. These may sound utterly trivial, but in fact programmers almost never
  276. produce as good an error message for \fIfopen\fR as ten lines
  277. of library code can, and half the time the return value from \fImalloc\fR
  278. isn't checked at all!
  279. .LP
  280. These examples
  281. illustrate a general principle, a side benefit of stealing code:
  282. the way to encourage standardization\(dg and
  283. .FS
  284. \(dg Speaking of encouraging standardization:
  285. we use the names \fIefopen\fR and \fIemalloc\fR for the checked versions
  286. of \fIfopen\fR and \fImalloc\fR,
  287. and
  288. arguments and returned values are the same as the unchecked versions
  289. except that the returned value is guaranteed non-NULL if the function
  290. returns at all.
  291. .FE
  292. quality is to make it easier to be
  293. careful and standard than to be sloppy and non-standard.
  294. On systems with library functions for error-checked
  295. \fIfopen\fR and \fImalloc\fR, it is easier to use the system
  296. functions\(emwhich take some care to do ``the right thing''\(emthan to
  297. kludge it yourself.
  298. This makes converts very quickly.
  299. .LP
  300. These are not isolated examples.
  301. Studying the libraries of most any
  302. .UX "" non-
  303. system will yield other ideas for useful library functions
  304. (as well as a lot of silly nonsense that
  305. .UX
  306. doesn't need, usually!).
  307. A few years of
  308. .UX
  309. systems programming also leads to recognition of repeated needs.
  310. Does \fIyour\fR*
  311. .UX
  312. have library functions to:
  313. .FS
  314. * As you might guess, my system has all of these.
  315. Most of them are trivial to write,
  316. or are available in public-domain forms.
  317. .FE
  318. .RS
  319. .P
  320. decide whether a filename is well-formed (contains no control characters,
  321. shell metacharacters, or white space, and is within
  322. any name-length limits your system sets)?
  323. .P
  324. close all file descriptors except the standard ones?
  325. .P
  326. compute a standard CRC (Cyclic Redundancy Check ``checksum'')?
  327. .P
  328. operate on \fImalloc\fRed unlimited-length strings?
  329. .P
  330. do what \fIaccess\fR(2) does but using the effective userid?
  331. .P
  332. expand metacharacters in a filename the same way the shell does?
  333. (the simplest way to make sure that the two agree is to use \fIpopen\fR
  334. and \fIecho\fR for anything complicated)
  335. .P
  336. convert integer baud rates to and from the speed codes used by
  337. your system's serial-line \fIioctl\fRs?
  338. .P
  339. convert integer file modes to and from the \fIrwx\fR strings
  340. used\(dg to present such modes to humans?
  341. .FS
  342. \(dg If you think only \fIls\fR uses these, consider that \fIrm\fR and
  343. some similar programs \fIought\fR to use \fIrwx\fR strings, not octal
  344. modes, when requesting confirmation!
  345. .FE
  346. .P
  347. do a binary search through a file the way \fIlook\fR(1) does?
  348. .RE
  349. .LP
  350. The above are fairly trivial examples of the sort of things that \fIought\fR
  351. to be in
  352. .UX
  353. libraries.
  354. More sophisticated libraries can also be useful,
  355. especially if the language provides better support for them
  356. than C does; C++ is an example [7].
  357. Even in C, though, there is much room for improvement.
  358. .LP
  359. Adding library functions does have its disadvantages.
  360. The interface to a library function is important,
  361. and getting it right is hard.
  362. Worse, once users have started using one version of an interface,
  363. changing it is very difficult even when hindsight clearly shows mistakes;
  364. the near-useless return values of some of the common
  365. .UX
  366. library functions are obvious examples.
  367. Satisfactory handling of error conditions can be difficult.
  368. (For example, the error-checking \fImalloc\fR mentioned earlier is very
  369. handy for programmers, but invoking it from a library function would be
  370. a serious mistake, removing any possibility of more intelligent response
  371. to that error.)
  372. And there is the perennial headache of trying to get others to adopt your
  373. pet function, so that programs using it can be portable without having to
  374. drag the source of the function around too.
  375. For all this, though, libraries are in many ways the most satisfactory
  376. way of encouraging code theft.
  377. .LP
  378. Alas, encouraging code theft does not guarantee it.
  379. Even widely-available library functions often are not used nearly
  380. as much as they should be.
  381. A conspicuous example is \fIgetopt\fR, for command-line argument parsing.
  382. \fIGetopt\fR
  383. supplies only quite modest help in parsing the command line,
  384. but the standardization and consistency that its use produces is still
  385. quite valuable; there are far too many pointless variations in command
  386. syntax in the hand-cooked argument parsers in most
  387. .UX
  388. programs.
  389. Public-domain implementations of \fIgetopt\fR have been available for years,
  390. and AT&T has published (!) the source for the System V implementation.
  391. Yet people continue to write their own argument parsers.
  392. There is one valid reason for this, to be discussed in the next section.
  393. There are also a number of excuses, mostly the standard ones for not
  394. using library functions:
  395. .RS
  396. .P
  397. ``It doesn't do quite what I want.''
  398. \fIBut often it is close enough to serve, and the combined
  399. benefits of code theft and standardization outweigh the minor mismatches.\fR
  400. .P
  401. ``Calling a library function is too inefficient.''
  402. \fIThis is mostly heard from people who have never profiled their programs
  403. and hence have no \fRreliable\fI information about what their code's
  404. efficiency problems are [2].\fR
  405. .P
  406. ``I didn't know about it.''
  407. \fICompetent programmers know the contents of their toolboxes.\fR
  408. .P
  409. ``That whole concept is ugly, and should be redesigned.''
  410. (Often said of \fIgetopt\fR, since the usual
  411. .UX
  412. single-letter-option syntax
  413. that \fIgetopt\fR implements
  414. is widely criticized as user-hostile.)
  415. \fIHow likely is it that the rest of the world will go along with your
  416. redesign (assuming you ever finish it)?
  417. Consistency and a high-quality implementation are valuable even if the
  418. standard being implemented is suboptimal.\fR
  419. .P
  420. ``I would have done it differently.''
  421. \fIThe triumph of personal taste over professional programming.\fR
  422. .RE
  423. .SH L
  424. Theft via Templates
  425. .LP
  426. \fITemplates\fR are a major and much-neglected approach to code sharing:
  427. ``boilerplate'' programs which contain a carefully-written skeleton
  428. for some moderately stereotyped task,
  429. which can then be adapted and filled in as needed.
  430. This method has some of the vices of modifying existing programs,
  431. but the template can be designed for the purpose,
  432. with attention to quality and versatility.
  433. .LP
  434. Templates can be particularly useful when library functions are used in
  435. a stereotyped way that is a little complicated to write from scratch;
  436. \fIgetopt\fR is an excellent example.
  437. The one really valid objection to \fIgetopt\fR is that
  438. its invocation is not trivial,
  439. and typing in the correct sequence from scratch is a real test of memory.
  440. The usual \fIgetopt\fR manual page contains a lengthy example
  441. which is essentially a template for
  442. a \fIgetopt\fR-using program.
  443. .LP
  444. When the first public-domain \fIgetopt\fR appeared,
  445. it quickly became clear that it would be convenient to have a template
  446. for its use handy.
  447. This template eventually grew to incorporate a number of other things:
  448. a useful macro or two,
  449. definition of \fImain\fR,
  450. opening of files in the standard
  451. .UX
  452. filter fashion,
  453. checking for mistakes like opening a directory,
  454. filename and line-number tracking for error messages,
  455. and some odds and ends.
  456. The full current version can be found in the Appendix;
  457. actually it diverged into two distinct versions when it became clear that
  458. some filters wanted the illusion of a single input stream,
  459. while others wanted to handle each input file individually (or didn't care).
  460. .LP
  461. The obvious objection to this line of development is ``it's more complicated
  462. than I need''.
  463. In fact, it turns out to be surprisingly convenient to have all this
  464. machinery presupplied.
  465. \fIIt is much easier to alter or delete lines of code than to add them.\fR
  466. If directories are legitimate input, just delete the code that catches them.
  467. If no filenames are allowed as input, or exactly one must be present,
  468. change one line of code to enforce the restriction and a few more to
  469. deal with the arguments correctly.
  470. If the arguments are not filenames at all, just delete the bits of code
  471. that assume they are.
  472. And so forth.
  473. .LP
  474. The job of
  475. writing an ordinary filter-like program is reduced to filling in two or
  476. three blanks* in the template, and then writing the code that actually
  477. .FS
  478. * All marked with the string `xxx' to make them easy for
  479. a text editor to find.
  480. .FE
  481. processes the data.
  482. Even quick improvisations become good-quality programs,
  483. doing things the standard way with all the proper amenities,
  484. because even a quick improvisation is easier
  485. to do by starting from the template.
  486. \fITemplates are an unmixed blessing;
  487. anyone who types a non-trivial program in from scratch is
  488. wasting his time and his employer's money.\fR
  489. .LP
  490. Templates are also useful for other stereotyped files, even ones that
  491. are not usually thought of as programs.
  492. Most versions of
  493. .UX
  494. have a simple template for manual pages hiding somewhere
  495. (in V7 it was \fI/usr/man/man0/xx\fR).
  496. Shell files that want to analyze complex argument lists have the same
  497. \fIgetopt\fR problem as C programs, with the same solution.
  498. There is enough machinery in a ``production-grade'' \fImake\fR file to
  499. make a template worthwhile, although this one tends to get altered
  500. fairly heavily; our current one is in the Appendix.
  501. .SH L
  502. Theft via Inclusion
  503. .LP
  504. Source inclusion (\fB#include\fR) provides a way of sharing both
  505. data structures and executable code.
  506. Header files (e.g. \fIstdio.h\fR) in particular tend to be taken for granted.
  507. Again, those who haven't been around long enough to remember V6
  508. .UX
  509. may
  510. have trouble grasping what a revolution it was when V7 introduced systematic
  511. use of header files!
  512. .LP
  513. However, even mundane header files could be rather more useful than they
  514. normally are now.
  515. Data structures in header files are widely accepted, but there is somewhat
  516. less use of them to declare the return types of functions.
  517. One or two common header files like \fIstdio.h\fR and \fImath.h\fR do this,
  518. but programmers are still used to the idea that the type of (e.g.)
  519. \fIatol\fR has to be typed in by hand.
  520. Actually, all too often the programmer says ``oh well, on my machine it
  521. works out all right if I don't bother declaring \fIatol\fR'',
  522. and the result is dirty and unportable code.
  523. The X3J11 draft ANSI standard for C addresses this by defining some more
  524. header files and requiring their use for portable programs,
  525. so that the header files can do all the work and do it \fIright\fR.
  526. .LP
  527. In principle, source inclusion can be used for more than just header files.
  528. In practice, almost anything that can be done with source inclusion can be
  529. done, and usually done more cleanly, with header files and libraries.
  530. There are occasional specialized exceptions, such as using macro definitions
  531. and source inclusion to fake parameterized data types.
  532. .SH L
  533. Theft via Invocation
  534. .LP
  535. Finally, it is often possible to steal another program's code simply by
  536. invoking that program.
  537. Invoking other programs via
  538. \fIsystem\fR or
  539. \fIpopen\fR for things that are easily done in
  540. C is a common beginner's error.
  541. More experienced programmers can go too far the other way, however, insisting
  542. on doing everything in C, even when a leavening of other methods would
  543. give better results.
  544. The best way to sort a large file is probably to invoke \fIsort\fR(1),
  545. not to do it yourself.
  546. Even invoking a shell file can be useful,
  547. although a bit odd-seeming
  548. to most C programmers,
  549. when elaborate file manipulation is needed and efficiency is not critical.
  550. .LP
  551. Aside from invoking other programs at run time,
  552. it can also be useful to invoke them at compile time.
  553. Particularly when dealing with large tables,
  554. it is often better to dynamically generate the C code from some
  555. more compact and readable notation.
  556. \fIYacc\fR and \fIlex\fR are familiar examples of this on a large scale,
  557. but simple \fIsed\fR and \fIawk\fR programs can build tables in more
  558. specialized, application-specific ways.
  559. Whether this is really theft is debatable, but it's a valuable technique
  560. all the same.
  561. It can neatly bypass a lot of objections that start with
  562. ``but C won't let me write...''.
  563. .SH L
  564. An Excess of Invention
  565. .LP
  566. With all these varied methods, why is code theft not more widespread?
  567. Why are so many programs unnecessarily invented from scratch?
  568. .LP
  569. The most obvious answer is the hardest to counter:
  570. theft requires that there be something to steal.
  571. Use of library functions is impossible unless somebody sets up a library.
  572. Designing the interfaces for library functions is not easy.
  573. Worse, doing it \fIwell\fR requires insight, which generally isn't
  574. available on demand.
  575. The same is true, to varying degrees, for the other forms of theft.
  576. .LP
  577. Despite its reputation as a hotbed of software re-use,
  578. .UX
  579. is actually hostile to some of these activities.
  580. If
  581. .UX
  582. directories had been complex and obscure,
  583. directory-reading libraries would have been present from the beginning.
  584. As it is, it was simply \fItoo easy\fR to do things ``the hard way''.
  585. There \fIstill\fR is no portable set of functions to perform the
  586. dozen or so useful manipulations of terminal modes that a user program
  587. might want to do, a major nuisance
  588. because changing those modes ``in the raw'' is simple but highly unportable.
  589. .LP
  590. Finally,
  591. there is the Not Invented Here syndrome, and its relatives,
  592. Not Good Enough and Not Understood Here.
  593. How else to explain AT&T
  594. .UX 's
  595. persistent lack of the \fIdbm\fR library for hashed databases
  596. (even though it was developed at Bell Labs and hence is available to AT&T),
  597. and Berkeley
  598. .UX 's
  599. persistent lack of the full set of \fIstrxxx\fR functions
  600. (even though a public-domain implementation has existed for years)?
  601. The X3J11 and
  602. POSIX efforts are making some progress at developing a common nucleus of
  603. functionality, but they are aiming at a common subset of current
  604. systems, when what is really wanted is a common superset.
  605. .SH L
  606. Conclusion
  607. .LP
  608. In short, never build what you can (legally) steal!
  609. Done right, it yields better programs for less work.
  610. .SH L
  611. References
  612. .LP
  613. .R 1
  614. Brian W. Kernighan, \fIThe Unix System and Software Reusability\fR,
  615. IEEE Transactions on Software Engineering, Vol SE-10, No. 5, Sept. 1984,
  616. pp. 513-8.
  617. .R 2
  618. Geoff Collyer and Henry Spencer, \fINews Need Not Be Slow\fR,
  619. Usenix Winter 1987 Technical Conference, pp. 181-190.
  620. .R 3
  621. Brian W. Kernighan and P.J. Plauger, \fISoftware Tools\fR, Addison-Wesley,
  622. Reading, Mass. 1976.
  623. .R 4
  624. Mike O'Dell, \fIUNIX: The World View\fR, Usenix Winter 1987 Technical
  625. Conference, pp. 35-45.
  626. .R 5
  627. IEEE, \fIIEEE Trial-Use Standard 1003.1 (April 1986):
  628. Portable Operating System for
  629. Computer Environments\fR, IEEE and Wiley-Interscience, New York, 1986.
  630. .R 6
  631. Ian Darwin and Geoff Collyer, \fICan't Happen or /* NOTREACHED */ or
  632. Real Programs Dump Core\fR, Usenix Winter 1985 Technical Conference,
  633. pp. 136-151.
  634. .R 7
  635. Bjarne Stroustrup, \fIThe C++ Programming Language\fR, Addison-Wesley,
  636. Reading, Mass. 1986.
  637. .SH L
  638. Appendix
  639. .LP
  640. Warning:  these templates have been in use for varying lengths of time,
  641. and are not necessarily all entirely bug-free.
  642. .A
  643. .T "C program, single stream of input"
  644. /*
  645.  * name - purpose xxx
  646.  *
  647.  * $Log$
  648.  */
  649.  
  650. #include <stdio.h>
  651. #include <sys/types.h>
  652. #include <sys/stat.h>
  653. #include <string.h>
  654.  
  655. #define    MAXSTR    500        /* For sizing strings -- DON'T use BUFSIZ! */
  656. #define    STREQ(a, b)    (*(a) == *(b) && strcmp((a), (b)) == 0)
  657.  
  658. #ifndef lint
  659. static char RCSid[] = "$Header$";
  660. #endif
  661.  
  662. int debug = 0;
  663. char *progname;
  664.  
  665. char **argvp;                /* scan pointer for nextfile() */
  666. char *nullargv[] = { "-", NULL };    /* dummy argv for case of no args */
  667. char *inname;                /* filename for messages etc. */
  668. long lineno;                /* line number for messages etc. */
  669. FILE *in = NULL;            /* current input file */
  670.  
  671. extern void error(), exit();
  672. #ifdef UTZOOERR
  673. extern char *mkprogname();
  674. #else
  675. #define    mkprogname(a)    (a)
  676. #endif
  677.  
  678. char *nextfile();
  679. void fail();
  680.  
  681. /*
  682.  - main - parse arguments and handle options
  683.  */
  684. main(argc, argv)
  685. int argc;
  686. char *argv[];
  687. {
  688.     int c;
  689.     int errflg = 0;
  690.     extern int optind;
  691.     extern char *optarg;
  692.     void process();
  693.  
  694.     progname = mkprogname(argv[0]);
  695.  
  696.     while ((c = getopt(argc, argv, "xxxd")) != EOF)
  697.         switch (c) {
  698.         case 'xxx':    /* xxx meaning of option */
  699.             xxx
  700.             break;
  701.         case 'd':    /* Debugging. */
  702.             debug++;
  703.             break;
  704.         case '?':
  705.         default:
  706.             errflg++;
  707.             break;
  708.         }
  709.     if (errflg) {
  710.         fprintf(stderr, "usage: %s ", progname);
  711.         fprintf(stderr, "xxx [file] ...\en");
  712.         exit(2);
  713.     }
  714.  
  715.     if (optind >= argc)
  716.         argvp = nullargv;
  717.     else
  718.         argvp = &argv[optind];
  719.     inname = nextfile();
  720.     if (inname != NULL)
  721.         process();
  722.     exit(0);
  723. }
  724.  
  725. /*
  726.  - getline - get next line (internal version of fgets)
  727.  */
  728. char *
  729. getline(ptr, size)
  730. char *ptr;
  731. int size;
  732. {
  733.     register char *namep;
  734.  
  735.     while (fgets(ptr, size, in) == NULL) {
  736.         namep = nextfile();
  737.         if (namep == NULL)
  738.             return(NULL);
  739.         inname = namep;        /* only after we know it's good */
  740.     }
  741.     lineno++;
  742.     return(ptr);
  743. }
  744.  
  745. /*
  746.  - nextfile - switch files
  747.  */
  748. char *                /* filename */
  749. nextfile()
  750. {
  751.     register char *namep;
  752.     struct stat statbuf;
  753.     extern FILE *efopen();
  754.  
  755.     if (in != NULL)
  756.         (void) fclose(in);
  757.  
  758.     namep = *argvp;
  759.     if (namep == NULL)    /* no more files */
  760.         return(NULL);
  761.     argvp++;
  762.  
  763.     if (STREQ(namep, "-")) {
  764.         in = stdin;
  765.         namep = "stdin";
  766.     } else {
  767.         in = efopen(namep, "r");
  768.         if (fstat(fileno(in), &statbuf) < 0)
  769.             error("can't fstat `%s'", namep);
  770.         if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
  771.             error("`%s' is directory!", namep);
  772.     }
  773.  
  774.     lineno = 0;
  775.     return(namep);
  776. }
  777.  
  778. /*
  779.  - fail - complain and die
  780.  */
  781. void
  782. fail(s1, s2)
  783. char *s1;
  784. char *s2;
  785. {
  786.     fprintf(stderr, "%s: (file `%s', line %ld) ", progname, inname, lineno);
  787.     fprintf(stderr, s1, s2);
  788.     fprintf(stderr, "\en");
  789.     exit(1);
  790. }
  791.  
  792. /*
  793.  - process - process input data
  794.  */
  795. void
  796. process()
  797. {
  798.     char line[MAXSTR];
  799.  
  800.     while (getline(line, (int)sizeof(line)) != NULL) {
  801.         xxx
  802.     }
  803. }
  804. .T "C program, separate input files"
  805. /*
  806.  * name - purpose xxx
  807.  *
  808.  * $Log$
  809.  */
  810.  
  811. #include <stdio.h>
  812. #include <sys/types.h>
  813. #include <sys/stat.h>
  814. #include <string.h>
  815.  
  816. #define    MAXSTR    500        /* For sizing strings -- DON'T use BUFSIZ! */
  817. #define    STREQ(a, b)    (*(a) == *(b) && strcmp((a), (b)) == 0)
  818.  
  819. #ifndef lint
  820. static char RCSid[] = "$Header$";
  821. #endif
  822.  
  823. int debug = 0;
  824. char *progname;
  825.  
  826. char *inname;                /* filename for messages etc. */
  827. long lineno;                /* line number for messages etc. */
  828.  
  829. extern void error(), exit();
  830. #ifdef UTZOOERR
  831. extern char *mkprogname();
  832. #else
  833. #define    mkprogname(a)    (a)
  834. #endif
  835. void fail();
  836.  
  837. /*
  838.  - main - parse arguments and handle options
  839.  */
  840. main(argc, argv)
  841. int argc;
  842. char *argv[];
  843. {
  844.     int c;
  845.     int errflg = 0;
  846.     FILE *in;
  847.     struct stat statbuf;
  848.     extern int optind;
  849.     extern char *optarg;
  850.     extern FILE *efopen();
  851.     void process();
  852.  
  853.     progname = mkprogname(argv[0]);
  854.  
  855.     while ((c = getopt(argc, argv, "xxxd")) != EOF)
  856.         switch (c) {
  857.         case 'xxx':    /* xxx meaning of option */
  858.             xxx
  859.             break;
  860.         case 'd':    /* Debugging. */
  861.             debug++;
  862.             break;
  863.         case '?':
  864.         default:
  865.             errflg++;
  866.             break;
  867.         }
  868.     if (errflg) {
  869.         fprintf(stderr, "usage: %s ", progname);
  870.         fprintf(stderr, "xxx [file] ...\en");
  871.         exit(2);
  872.     }
  873.  
  874.     if (optind >= argc)
  875.         process(stdin, "stdin");
  876.     else
  877.         for (; optind < argc; optind++)
  878.             if (STREQ(argv[optind], "-"))
  879.                 process(stdin, "-");
  880.             else {
  881.                 in = efopen(argv[optind], "r");
  882.                 if (fstat(fileno(in), &statbuf) < 0)
  883.                     error("can't fstat `%s'", argv[optind]);
  884.                 if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
  885.                     error("`%s' is directory!", argv[optind]);
  886.                 process(in, argv[optind]);
  887.                 (void) fclose(in);
  888.             }
  889.     exit(0);
  890. }
  891.  
  892. /*
  893.  - process - process input file
  894.  */
  895. void
  896. process(in, name)
  897. FILE *in;
  898. char *name;
  899. {
  900.     char line[MAXSTR];
  901.  
  902.     inname = name;
  903.     lineno = 0;
  904.  
  905.     while (fgets(line, sizeof(line), in) != NULL) {
  906.         lineno++;
  907.         xxx
  908.     }
  909. }
  910.  
  911. /*
  912.  - fail - complain and die
  913.  */
  914. void
  915. char *s1;
  916. char *s2;
  917. {
  918.     fprintf(stderr, "%s: (file `%s', line %ld) ", progname, inname, lineno);
  919.     fprintf(stderr, s1, s2);
  920.     fprintf(stderr, "\en");
  921.     exit(1);
  922. }
  923. .T "Make file"
  924. # Things you might want to put in ENV and LENV:
  925. # -Dvoid=int        compiler lacks void
  926. # -DCHARBITS=0377    compiler lacks unsigned char
  927. # -DSTATIC=extern    compiler dislikes "static foo();" as forward decl.
  928. # -DREGISTER=        machines with few registers for register variables
  929. # -DUTZOOERR        have utzoo-compatible error() function and friends
  930. ENV = -DSTATIC=extern -DREGISTER= -DUTZOOERR
  931. LENV = -Dvoid=int -DCHARBITS=0377 -DREGISTER= -DUTZOOERR
  932.  
  933. # Things you might want to put in TEST:
  934. # -DDEBUG        debugging hooks
  935. # -I.            header files in current directory
  936. TEST = -DDEBUG
  937.  
  938. # Things you might want to put in PROF:
  939. # -Dstatic='/* */'    make everything global so profiler can see it.
  940. # -p            profiler
  941. PROF =
  942.  
  943. CFLAGS = -O $(ENV) $(TEST) $(PROF)
  944. LINTFLAGS = $(LENV) $(TEST) -ha
  945. LDFLAGS = -i
  946.  
  947. OBJ = xxx
  948. LSRC = xxx
  949. DTR = README dMakefile tests tests.good xxx.c
  950.  
  951. xxx:    xxx.o
  952.     $(CC) $(CFLAGS) $(LDFLAGS) xxx.o -o xxx
  953.  
  954. xxx.o:    xxx.h
  955.  
  956. lint:    $(LSRC)
  957.     lint $(LINTFLAGS) $(LSRC) | tee lint
  958.  
  959. r:    xxx tests tests.good    # Regression test.
  960.     xxx <tests >tests.new
  961.     diff -h tests.new tests.good && rm tests.new
  962.  
  963. # Prepare good output for regression test -- name isn't "tests.good"
  964. # because human judgement is needed to decide when output is good.
  965. good:    xxx tests
  966.     xxx <tests >tests.good
  967.  
  968. dtr:    r $(DTR)
  969.     makedtr $(DTR) >dtr
  970.  
  971. dMakefile:    Makefile
  972.     sed '/^L*ENV=/s/ *-DUTZOOERR//' Makefile >dMakefile
  973.  
  974. clean:
  975.     rm -f *.o lint tests.new dMakefile dtr core mon.out xxx
  976.