#ifdef considered harmful, or portability experience with c news · 2019. 2. 25. · #ifdef...

13
#ifdef Considered Harmful, or Portability Experience With C News Henry Spencer -_Zo9l9gy-Computer Systems, University of Toronto Geoff Collyer - Software Tool & Die ABSTRACT We believethat a C programmer's impulseto use#ifdef in an attemptat portability is usuallya mistake. Portability is generally the result of advance planning ratheithan trench warfare. involving #ifdef. In the courseof developing C NewJ on difterent systems, we evolved various tactics . for dealing with differencès ãmong systems without producing a welter of #ifdefs at points of difference.We discuss the-altêmadves to, and occasio-nal proper useof, #ifdef. Introduction With uNrx running on many different comput- ers,vaguely t¡tlD(-likesystems runningon still more, and C running on practically everything, man! peo: ple are suddenlyfinding it necessary to pott C software from one machine to another. When differ- encesamong systems causetrouble, the usual first impulse is to write two different versions of the code-+ne per system-and use#ifdef to choose the appropriate one. This is usuallya mistake. Simple use of #ifdef works acceptably well when differences are localized and only two versions are present. Unfortunately, as software using this approach is ported to more and more systems, the #ifdefs proliferate, nest, and interlock. After a while, the result is usuallyan unreadable, unmain- tainable mess. Portability without tears requires better advance planning. When we wrote C News [Coll87a], we put a high priority on portability,sincewe ran several dif- ferent systems ourselves, and expected that the software would eventually be usedon many more. Planning for future adaptations saved us (and others) from trying to force changes into an uncooperative structure when we later encountered new svstems. Porting C News generally involveswriting a few small primitives. Therehavebeensurprises, but in the course of maintaining and improvingthe code and its portability, we insisted that the software remain readable and fixable. And we were not prepared to sacrifice performance, since one of C News's major virtuesis that it is far faster thanolder news software. We evolved several tactics that should be widely applicable. The Nature of the Problem Consider what happens when #ifdef is used carelessly. T\e first #ifdef probably doesn'tcause much trouble. Unfortunately,they breed. Worse, they nest, and tend to become more deeplynested with time. #ifdefs pile on top of *ifdets as portability problems are repeatedly worked around rather thansolved. The result is a tangled andoften impenetrable web. Here's- a noteworthy example from a popular newsreader.l SeeFigurei. Observe that, not contentwith merely nesting fifdefs, the author has #ifdef andordinary if statements þlus the mysteriousIF macros) interweaving. This makes the structure almost impossible to follow without going overit repeatedly, onecase at a time. Fufhermore, given worst caseelaboration and nesting (eachfifdef alwayshas a matching #else), the number of alternative code paths doub-ies wiitr each extra level of #ifdef. By the time the depth reaches 5 (not at all rare in the work of #ifdef enthusiasts), there are potentially 32 alternate code pathsto consider. How many of thosepathshave beentested? Probably two or three. How many of the possible combinations even make sense?Often not very many. Figure2 is another wonderful exam- ple, the LeaningTower Of Hostnames. It's most unlikely that anyone understands this codeany more. In suchsituations, maintenance is reduced to hit-or- miss patching. If you find and fix a bug, how many otherbranches doesit needto be fixed on? If vou discover a performance bottleneckand work out a way to fix it, will you have to apply the fix separately to each branch?Now envision what hap- pens when hurried or careless maintainers do¿,, apply their fixes in all the places where they are relevant. Philosophical Aspects The key step in avoidingsuch messes is to realize that portability requires planning. There is an abundance of bad examples to show that portabil- ity, cannot be added onto or patched into. unportable software. Many of the problemswe diÉcuis stem from the "never mind good, we want it nextweek" /To quote from the old tx¡x kernel: ..you are not expected to understand this", Summer '92 USENIX - June 8-June l:Z,1rgg? - San Antonio, TX 18s

Upload: others

Post on 29-Aug-2020

1 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: #ifdef Considered Harmful, or Portability Experience With C News · 2019. 2. 25. · #ifdef Considered Harmful, or Portability Experience With C News Henry Spencer -_Zo9l9gy-Computer

#ifdef Considered Harmful, orPortability Experience With C NewsHenry Spencer -_Zo9l9gy-Computer Systems, University of Toronto

Geoff Collyer - Software Tool & Die

ABSTRACT

We believe that a C programmer's impulse to use #ifdef in an attempt at portability isusually a mistake. Portability is generally the result of advance planning rathei than trenchwarfare. involving #ifdef. In the course of developing C NewJ on difterent systems, weevolved various tactics . for dealing with differencès ãmong systems without producing awelter of #ifdefs at points of difference. We discuss the-altêmadves to, and occasio-nalproper use of, #ifdef.

Introduction

With uNrx running on many different comput-ers, vaguely t¡tlD(-like systems running on still more,and C running on practically everything, man! peo:ple are suddenly finding it necessary to pott Csoftware from one machine to another. When differ-ences among systems cause trouble, the usual firstimpulse is to write two different versions of thecode-+ne per system-and use #ifdef to choose theappropriate one. This is usually a mistake.

Simple use of #ifdef works acceptably wellwhen differences are localized and only two versionsare present. Unfortunately, as software using thisapproach is ported to more and more systems, the#ifdefs proliferate, nest, and interlock. After awhile, the result is usually an unreadable, unmain-tainable mess. Portability without tears requiresbetter advance planning.

When we wrote C News [Coll87a], we put ahigh priority on portability, since we ran several dif-ferent systems ourselves, and expected that thesoftware would eventually be used on many more.Planning for future adaptations saved us (and others)from trying to force changes into an uncooperativestructure when we later encountered new svstems.Porting C News generally involves writing a fewsmall primitives. There have been surprises, but inthe course of maintaining and improving the codeand its portability, we insisted that the softwareremain readable and fixable. And we were notprepared to sacrifice performance, since one of CNews's major virtues is that it is far faster than oldernews software. We evolved several tactics thatshould be widely applicable.

The Nature of the Problem

Consider what happens when #ifdef is usedcarelessly. T\e first #ifdef probably doesn't causemuch trouble. Unfortunately, they breed. Worse,they nest, and tend to become more deeply nestedwith time. #ifdefs pile on top of *ifdets as

portability problems are repeatedly worked aroundrather than solved. The result is a tangled and oftenimpenetrable web. Here's- a noteworthy examplefrom a popular newsreader.l See Figure i. Observethat, not content with merely nesting fifdefs, theauthor has #ifdef and ordinary if statements þlus themysterious IF macros) interweaving. This makesthe structure almost impossible to follow withoutgoing over it repeatedly, one case at a time.

Fufhermore, given worst case elaboration andnesting (each fifdef always has a matching #else),the number of alternative code paths doub-ies wiitreach extra level of #ifdef. By the time the depthreaches 5 (not at all rare in the work of #ifdefenthusiasts), there are potentially 32 alternate codepaths to consider. How many of those paths havebeen tested? Probably two or three. How many ofthe possible combinations even make sense? Oftennot very many. Figure 2 is another wonderful exam-ple, the Leaning Tower Of Hostnames. It's mostunlikely that anyone understands this code any more.In such situations, maintenance is reduced to hit-or-miss patching. If you find and fix a bug, how manyother branches does it need to be fixed on? If voudiscover a performance bottleneck and work out away to fix it, will you have to apply the fixseparately to each branch? Now envision what hap-pens when hurried or careless maintainers do¿,,apply their fixes in all the places where they arerelevant.

Philosophical Aspects

The key step in avoiding such messes is torealize that portability requires planning. There isan abundance of bad examples to show that portabil-ity, cannot be added onto or patched into. unportablesoftware. Many of the problems we diÉcuis stemfrom the "never mind good, we want it next week"

/To quote from the old tx¡x kernel: ..you are notexpected to understand this",

Summer '92 USENIX - June 8-June l:Z,1rgg? - San Antonio, TX 18s

Page 2: #ifdef Considered Harmful, or Portability Experience With C News · 2019. 2. 25. · #ifdef Considered Harmful, or Portability Experience With C News Henry Spencer -_Zo9l9gy-Computer

fifdef Considered Harmful ...

approach to software.Even the best planning canriot anticipate all

problems, but it is important to retain the emphasison planning even into ongoing maintenance. Whena new portability problem surfaces, it is important tostep back and think about the problem and its solu-tion. Is this a unique problem, or the harbinger of awhole new class of them? Usuallv it's the latter.which makes planning all the more õrucial: how canthe solution deal with all of them, not just thecurrent one? Failure to think leads to the patch-upon-patch approach to portability, rapidly producingunreadable and unmaintainable code.

Once the problem (class) and the solution areunderstood, then and only then it is time to startwork on the code. Typically this will mean re-implementing parts of it, not just hacking up the oldcode to work somehow. This highlights anotherissue: to revise the code, you must understand it...and that means not making an incomprehensiblemess this time to interfere with maintenance nexttime.

All of this is typically more work than justhacking in a quick fix. Sometimes a quick fix maybe necessary, or later thought may show that an

Spencer,& Collyer

earlier "solution" was really a quick fix and needsgeneralizing. In such cases, it is important to goback and fix the kludges, The time is not wasted; itis an investment in the future.

More generally, portability requires time andthought. Nobody gets everything right the frrst time;getting the code right means taking the time to thinkabout what went wrong, decide what the mistakeswere, and go back and frx them.

The alert reader may notice that almost all theremarks in this section could also be applied toachieving high performance, high reliability, etc.,and that no specific boundary between developmentand maintenance was mentioned. 'We've

really dis-cussed how to achieve high-quality software. In ourexperience, this approach works; we can't imagineany other that would.

Portable Interfaces

Systems do, unfortunately, differ. It's oftenpossible to avoid system-dependent areas wellenough that the same code will run on all systems;we'll discuss that later. But sometimes multiplevariants are inevitable. Even within the uNIx family,there are significant variations between systems.

wldcl.¡nu9_!cl ){

ragl.t.E tc_nut trgtt!.gt.t.t re_f,Uü bgþ.Lty - 0 t

tl,!d.! vln¡O8l¡l( v.rËor.)

!Þuta( 'Chackl¡g out ! rou! ,ù&r!c--h69 oû À raco¡d,, , \D., . !dout)llSSHl

E8Etodl!,L!d.! îER6E

lDuÈr l 'Ch.ckl ,Dg .nfr .rc--hùg on.. . \n. , rÈdouÈ) rLUOH rt6dl!

tor ¡ng* . 0, ô9a < nqÈlcl¡¡ . t nga++l {i l ( tor.âdfng¡t >. lR_w8uB) (

. aat_Þ!a¡d(n9x)t / r thl ' ey raaaÈ ndrglouÞ r/

/ . o r d . c l â r a l t b o g u a ' /,1! (Èo!.Àdfn9r¡ . . ÎR-EOCU8)

ÞtoaltY++t

)lot ¡ngr - ¡qucl l ¡ . - l r ngx >¡ o ¡¡ ro! .¡dfngnl . . tR_rocust n9¡--)

bo9o. l ty-- , /. dfacounÈ alla¡dy evad ona¡ i/

l ! . (s.r tscl l ¡ . > 5 ¡ t bogo. l ty > n.xr lc l ln. / 2) {It¡uE.l

'It læka llk. gha actlva !11a la r.a.d upt 6nt¡ct !,our Dasa adñtDlaÈs¡Èor,\D\r. .Èdout) t

!¡ruÈa ('I.Âva th. \'bogu¡\. gþuD¡ aloÞ., ¡Dd th.y My c6. back b noml. ¡t¡yb.,\¡\' , . tdoue) l lU8H,

)ll!d.! ¡lDocÀtr

.1. . l t (bogorlÊy) {,I!d.l vlRAO88

l l(v.rbo¡. )!Þuta(,Eovl¡g bogua Dd.gÞuDa b tha and o! your ,eÍarc.\n, ,

rtCout) turs[rt!81

a6d1!,l!d.! ÎlRgl

!Þur.( ' lbvl¡g bogu.. . b t¡r . .nd.\ t r i , ¡ rdour) ! !u8l l ,t6dt l

to! ( t ¡gi l >. 0, ngr--) {tl lÞr.rdfng¡l .. Zn-Eocus)

ralæata_ndaglou¡r(n9¡rrsÈrct l ¡a-1 ) tI

lr,ld.! DEllocug!aâak_bogua¡

l¡_ch¡a( 'D. l . t . bolu. ¡d.grouÞ¡? I¡y¡ . , ,D')¡

. .crt . ! ( bul , . ¡ . ) ttltC.! vrRr¡l

Drl¡rqd( ) t,6d1!

¡rurch.r( , \B' t l lu88'1 ! l r b { ! . . ' h ' ) {

llfC.l V!R¡O88It(v.rbo.. l

lDuÈ. ( ' \

Îyt . y b d.ta¡. bogu. nNrl !ouÞ.. \n\1y¡1. n ot 8t þ l.Âva th4 rÈ !h. 6d 1r CAr. thry r.Èuh.\n\' , . tdour) ¡ tg8¡ l¡

tlSl,6d1!, i ld. t î !R8!

l9ut. l 'y b d. l .È., n !o k. .¡ , \n. ,rèdour) l lugHt16di!

goto !a.ak_bcgu.t

). ¡ . . l l ( . b u ! . . , n ' I I ' b u f . . , q ' )

.1. . l ' ! l 'bu! . - 'Y' l {Yàl l . ' (Èa.¡dftr .xÈlcl la.-¡ l . ¡ Aî_loco8 ¡¡ ¡ . r lscl ln. > O)

--nrtacl l ¡at /r a.¡1 tough, huh? r/

).1. . {

lDut.(hforh. lD,.ÈCouÈ) ! !U6H,r.sll._ddír( ),ggco aaaak_boqua,

lt6dl !

), .1r.tltd.! WRAOSI

¡ l I v. !hoa.)!Þutr l ' lou ahould adlt bogua ndagrouDa ouË o! !þur .¡ú¡rc. \D.,

lÈdcuÈ) t lu88,ttSE

t6dl ltf!d.! îlR8E

!9uÊ. l ' ld lr bogur. . lson,nd.!c. \ t r . , r tdouc) rr ,ug¡t ,t6dl!,6d1,!

¡f,amold . lESlt

Figure 1: Example of overuse of #ifdef

186 Summer '92 USENIX - June 8-June 12, Lgg2 - San Antonio, TX

Page 3: #ifdef Considered Harmful, or Portability Experience With C News · 2019. 2. 25. · #ifdef Considered Harmful, or Portability Experience With C News Henry Spencer -_Zo9l9gy-Computer

Spencer,& Collyer

#lfdef, or something similar, ultimately is unavoid-able. It can be managed, however, to minimizeproblems.

Among the basic principles of good softwareengineering are clean interfaces and information hid-ing: when faced with a decision that might change,hide it in one module, with a simple outside-worldinterface defined independently of exactly how thedecision is made inside. One would think that well-educated modern programmers would not need to betaught the virtues of this technique. Unfortunately,fifdef doesn't hide anything, and the interface itcreates is arbitrarily complex and almost never docu-mented.

The best method of managing system-specificvariants is to follow those same basic principles:define a portable interface to suitably-chosen primi-tives, and then implement different variants of theprimitives for different systems. The well.definedinterface is the important part: the bulk of thesoftware, including most of the complexity, can bewritten as a single version using that interface, andcan be read and understood in portable terms. It iscommon wisdom2 that localizing system dependen-cies in this way eases porting in cases where thecode must actually be rewritten. Our point is that it

ffiething that is widely knownbut usually ignored. (uxx programmer,s definition.)

fifdef Considered Harmful ...

makes the code simpler, cleaner, and more manage-able even when no rewrite is expected.

As a small case in point, when part of C Newswishes to anange that a file descriptor associatedwith a sldio stream be closed at exec time, to avoidpassing it to unprepared children, this is done by

f c l s e x e c ( f p ) ;(where þ is the sfdio structure pointer) rather thanby some _complex invocation of. ioctl or somethingsimilar. Only the implementation of. fclsexec needsto be cluttered with the details. (As others havenoted in the past [ODel87a, SpenSSa] in other con-texts, one paradoxical problem of UMx's not-too-complex system interfaces is that that they havediscouraged the development of librarieð withcleaner, higher-level interfaces.)

This confines #ifdef, but at first glance doesn'tseem to eliminate it. Sometimes several svstem-specific primitives will be compiled from the samesource, with portions selected by fifdef. Note thateven limiting the damage can be very important.However, in our experience, it's much more usualfor the different variants to be completely differentcode, compiled from different source files-inessence, the parts oußide the #ifdef disappear, Theindividual source files are generally imall andcomprehensible, since they implement only the prim-itives and are uncluttered with the complexities ofthe main-line logic. Out of 50 such source files in C

/* name of this si te */#ifdef GETHOSTNA¡',IE

char *hostnamei# undef SITENA¡,IE# define SITENA¡'{E hostname#el-se /', IGETHoSTNA¡,1E */# ifdef DOUNAME# incl-ude <sys/utsname.h>

struct utsname utsni# undef SITENA¡,IE

def ine SITENAME utsn.nodename# else /* IDoUNAME */# ifdef pHOSTNAtvtE

char *hogtnamei

undef SITENA¡,IEdefíne SITENAIIE hostnane

# else /* tpHosTNAME */# ifdef !{HOAtvtr

undef SITENAI|E# define SITENA¡,IE sysname# endi f / * VüHOAMT */# endif /* PHOSTNA¡',IE */# endif /* DOUNAME */

#endif /* C¡IHOSTNAÌ|E */

Figure 2: The Iæaning Tower of Hostnames

Summer '92 USENIX - June 8-June tZ,lgg}- San Antonio, TX

##

187

Page 4: #ifdef Considered Harmful, or Portability Experience With C News · 2019. 2. 25. · #ifdef Considered Harmful, or Portability Experience With C News Henry Spencer -_Zo9l9gy-Computer

fifdef Considered Harmful ...

News, half are less than 25 lines, most are under 50,and only a few are over 100. As an example, Figure3 and Figure 4 are two implementations of. fclsexec.There is hardly anything to be gained by trying tocombine these two files into one file with fifdefsevery second line.

There are, of course, things that cannot con-veniently be encapsulated as functions, for reasonsof either interface or efficiency. But a "primitive"is not necessarily a function. Types and macrosdefined in a header file are also useful ways of hid-ing system-specific detail. Programmers often usesuch facilities on a small scale; e.g. the use of olltas the system-supplied type for a size of a file or anoffset within it, but they don't write such header

Spencer,& Collyer

files nearly as often as they should.Although C's limited macro facilities hamper

large-scale use of header-file encapsulation, moreambitious applications can be useful despite occa-sional clumsiness. As an example, consider ourSTRCHR primitive, which generates in-line codeexcept on machines with compilers clever enough todo so automatically (see Figure 5). This is a bitawkward: what is being defined here is not exactlya function, but C preprocessor macros neverthelessforce it to look like one. In the absence of a stan-dard way to force inline expansion of normal func-tions, it remains a powerful technique for portableperformance engineering despite its flaws: this andsimilar portable optimizations sped up major

/ ** set c lose on exec (on UNIX)* /

#include <stdio.h>#include <sgtty.h>

voidfclsexec ( fp )F ILE * fP i

{(vo id ) ioc t l ( f i l eno( fp ) , F IOCLEX, (s t ruc t sg t tyb * )NULL) ;

)

Figure 3: One implementation of. fclsexec

/ ** set c lose on exec (on System V)t t /

#include <stdio.h>#include <fcnt l .h>

voidfclsexec ( fp )FTLE * fP ;

{1 v o Í d ) f c n t l ( f i l e n o ( f p ) , F _ S E T F D , t ) ;

)

Figure 4: Another implementation of. fclsexec

#ifdef#define#eIse#def ine

fort

i f

#endif

FÀSTSTRCHRSTRCHR(src , chr , des t ) (des t ) = s t rchr (s rc , chr )

STRCHR(src , chr , des t ¡ \( ( d e s t ) = ( s r c ) i * ( d e s t , ) l = , \ 0 , & & * ( d e s t ¡ t = ( c h r ) ; + + ( d e s t ) ) \\

( t ( d e s t ¡ = = ' \ 0 ' ) \(dest) = NULL /* t ¡ .8 . : ¡n iss ing sen i -co lon * /

Flgure 5: To inline or not to inline

Summer '92 USENIX - June 8,June 12,lgg2- San Antonio, TXlEE

Page 5: #ifdef Considered Harmful, or Portability Experience With C News · 2019. 2. 25. · #ifdef Considered Harmful, or Portability Experience With C News Henry Spencer -_Zo9l9gy-Computer

Spencer,& Collyer

components of C News by 4OVo without serious lossof clarity.

If one must use fifdef, and it cannot beconfined to header files and the like, one good ruleof thumb is ¿se #ifdef only in declaratians (where"declarations" is understood to include macrodefinitions). This at least encourages some thoughtabout defining an interface, rather than just hackingin something that somehow seems to work.

Finally, when defining interfaces, it is impor-tant to document them. The biggest reason for doingthis is that it is important discipline that forces youto think about the issues and fill in fuzzy spots. Theresulting documentation is also very ïaiuable formaintenance. Perhaps somewhat surprisingly, it'salso valuable for development, even if the project isnot an army-of-ants operation using buildings full ofpeople. We found it very important to documentcrucial interfaces like our configuration primitives,even though only two people were involved, to makesure things were being done consistently and weunderstood each other.S

Standard Interfaces

Of course, good interface design is not simple,especially given the limitations of existing program-ming languages. Often the best way to solvè thisproblem is to avoid it instead. If an interface isneeded, there is much to be said for choosing onethat is already standard.

#ifdef Considered Harmful ...

There are several sources of reasonably decentstandard interfaces, notably ANSI C [Inst89a] andPOSIX 1003.1[Engi90a]. Since these srandards arequite recent, many of the systems of interest do notimplemenl them firlly. This doesn't preclude usingthe interfaces, however: you can supply your ownimplementation(s) for use on outdated systêms. Anexample is the ANSI function strerror (shown inFigure 6).

This approach does impose a few constraints,since the standard interfaces sometimes are a bitugly, and often aren't ideal for every program. It'stempting to come up with customized ones instead.But the standard ones have major advantages. Forone thing, people understand (or will understand)them without having to decipher your code. Foranother, on systems which do implement the stan-dard interfaces, the system-provided ones can beused. (This is particularly significant for primitiveslike memcpy, where system-specific tuning can pro-duce major improvements in efficiency [Spen88a]. Ifyou define your own customized interface, you mustdo your own customized implementation, whichdenies you the opportunity to benefit from the workof others.) For a third, while the standard interfacesmay not be ideal, by and large they contain nogrievous mistakes, and avoiding disasters is usuallymore important than achieving a precisely optimalsolution. Finally, a standard interface saves endlesspuzzling, not to mention uncomplimentary specula-tion, by later maintainers: "did he have some deepsubtle reason for using a non-standard interface, orwas he just stupid?".

Reimplementing a standard interface can be auseful tactic when the standard interface does theright thing but the usual implementations perform

3lndeed, places where intemal interfaces weren,tcompletely documented were fruitful sourc€s ofmisun<lerstandings, bugs, and a certain amount of snarlingat each other.

/ t .

* strerror - map error number to descriptive string** This version is obviously somewhat UNlx-specif ic.) . /

char *strerror( errnum)int errnum;{

extern int sys_nerr;ex te rn char *sys_er r l i s t [ ] ;

i f (errnum > 0 && errnum < sys_nerr)return ( sl¡s_errlist I errnum] ) t

e lse i f (e r rnum t= 0)returnl "unknown error" ¡ ;

e lsereturn( "no detai ls given' , ¡ ;

Figure 6: strerror

Summer '92 USENIX - Jirne E-June lZ, tgg2- San Antonio, TX 189

Page 6: #ifdef Considered Harmful, or Portability Experience With C News · 2019. 2. 25. · #ifdef Considered Harmful, or Portability Experience With C News Henry Spencer -_Zo9l9gy-Computer

fifdef Considered Harmful ...

poorly. A version which is faster but compatiblecan solve performance problems while leaving thedoor open to the possibility that the system imple-mentations will improve someday. The stdio libraryis a particular case in point: old implementations offunctions like lgets and fread are extremelyinefñcient, and even modern ones often can beimproved on. This particular case gets tricky,because doing better means relying on ill-documented and somewhat variable internal inter-faces,4 but the performance wins for C News are somassive that we nevertheless did it.

Pitfalls that need careful attention when usingstandard interfaces are error checking and boundaryconditions. It is important not to make assumptionsthat aren't in the standard. For example, a depress-ing amount of umx software assumes that closenever returns any interesting status. Unfortunately,as networked file systems get more common andother complications are introduced, it is not at allunthinkable for an I/O error to be discovered onlv atclose time. Meticulous eror checking

- is

important [Darw85a]. For example, see Figure 7.Finally, note that standard interfaces exist on

more than just the C level. By including an "over-ride" directory early in the shell's search path, itbecomes trivial to substitute reimplemented pro-grams for standard ones that are missing or

4The standard build procedu¡e for C News runs a testp¡ogram to check compatibility with the locåI stdíoimplementation.

Spencer,& Collyer

defective. We have a remarkably large-andsteadily growing-list of known portability problemsthat arise from defective implementations of standardlrxx programs.S

Inside-Out Interfaces

Sometimes there simply isn't any way to pro-vide a nec€ssary primitive on some systems. Forexample, most modern UNIXes permit setting the realuserlD to equal the effective userlD, but some oldsystems allow only root to change the real IDs...and it is necessary to change the real IDs to createdirectories with proper ownerships. Given that manypeople will beo reluctant to let a large and complexprogram written by a stranger run as root, theredoesn't seem to be any easy way out.

In this case, there is: turn the interface insideout, and have the dirty work done by caller ratherthan callee. Specifically, have the cÒmplex programinvoked by a simple setuid-roof program which setsthings up properly on uncooperative systems.

@re disparaging porers and¡esellers only, we should comment that AT&T is as guiltyas anyone else. For example, several releases of SystemY makc have violated the System V Interface Definition intheir handling of command lines like test -s f ile inmakcfiles. (Makefile command lines are specified to beexecuted just as if by the shell, but if resr is a shell built.in and there is no actual program by that name, malæoften chokes and dies on this line.)

60r shouldbelll

/ t

* n fc lose(s t rean) - f lush the s t ream, fsync* fc lose the stream, checking for errors at* is needed to work around the lack of UNIX* in Sun 's NFS. Returns EOF on er ro r .* /

i ts f i le descr ipt ,or anda l l s tages . Th Ís dancef i le system semantics

#include <stdio.h>

intnfclose ( stream)register FILE *stream;

{register j -nt ret = 0 i

i f ( f f l u s h ( s t r e a m ) = = E O F )ret = EOF'

i f ( f sync( f i leno(s t ream)) < 0 l / * may ge t de layed er ro r here * /ret = EOF'

i f ( f c l o s e ( s t r e a m ) = = E O F )ret = EOF'

return ret i)

Figure 7: Necessary enor checking

Summer '92 USENIX - June E.June 12,1992 - San Antonio, TX190

Page 7: #ifdef Considered Harmful, or Portability Experience With C News · 2019. 2. 25. · #ifdef Considered Harmful, or Portability Experience With C News Henry Spencer -_Zo9l9gy-Computer

Spencer,& Collyer

A more mundane example is the problem ofreading directories. Thanks to the lack of a librarypackage for directory-reading in the oldest UNrxes,there isn't any standard way to do it. Raw readsdon't work on 4.2+BSD systems (and increasingly-many others), the Berkeley directory library workswell but has stupid name clashes with many old sys-tems, and the POSIX library isn't widespread yet.Worse, because the insides of a directory-readinglibrary are system-specific, it's difficult to provide ãportable reimplementation of the POSIX functions.

The simplest way around this one is to movethe problem out to a higher level of abstraction. Thels utility portably does the job, so wrap the invoca-tion of the program in a shell file, with the list ofnames generated by ls and fed into the application asarguments or on standard input. The performanceimpact is rarely significant, and the alternativecurrently involves at least s¡x different variants ofthe code, with more surfacing daily.

A less happy example of this technique is CNews's spaceþr program, used to check disk spaceso activity can be curtailed when it runs short. Itsinterface is simple and clean, and it is used every-where in C News. Making it a shell programoffered the possibility of exploiting the d/ commlnd,whicl encapsulates the ugly complications of findingout hov/ much space is available (and, sometimes,the root privileges needed to do so). Unfortunately,d/ is often relatively costly to invoke; worse, theonly portable \¡/ay to do 32-bit arithmetic from shellscripts is to use awk, which likewise tends to haveconsiderable startup overhead. With some care, theperformance impact was tolerable, although notentirely pleasant.

What we had not anticipated was that every lit-tle uNIx variant has its own different, incompãtibled/ oulpur format. Even "consider it standardt, Sys-tem V has at least three. The importance of pro- ¡gram-output being useful as program inputÍRitclïalhas been disregarded completely. In the

-end, wè

found that while the d/ version remains useful-people with really odd systems can customize iteasily-it was best to also provide C variants thatuse the three or four commonest space-determiningsystem calls, improving (!) portability within a fairlylarge subset ofu¡nx variants.

Levels of Abstraction

In general, avoiding problems is better thansolving them. The best ',¡iay to solve portabilityproblems is not to get involved with them. Some-times they can't be avoided, but often a bit ofingenuity suffices to find a way around them.

_The most powerful way of avoiding problems isto choose a level of abstraction where thev don'tshow up, The /s example earlier was a case inpoint. The standard ur.rrx shell is a very powerful

#ifdef Considered Harmful ...

programming language, sufficiently removed fromthe lower levels of the system that shell programsare often highly portable. (Gratuitous diffeiences inutility programs do get in the way, as do attempts to"improve" the shell that result in subtle or not-so-subtle incompatibilities, but this is usually a manage-able problem.)

. -_The usual objection to shell programming is theinefficiency of the result, but a careful division oflabor between the shell and the programs it invokesis all that is needed. Most of the C News batchingsubsystem is written in shell, but it remains highlyefficient, because most of its time is spent in-thê"batcher I compress I uux" pþeline, andthose are all C programs.

Intermediate levels of abstraction, althoughharder to find, do exist. Substantial pieces of CNews are coded in aw,t[Spen9l.a] where efficiency isnot crucial and requirements permit.

One situation where high-level abstractions areparticularly beneficial is when one must step outside"common base uMx". Common base

-uNIx is

essentially Version 7lLaboS2al, rhough the later V7innovations have taken a while to find their wav intoSystem V (and some have never done so). p'OSIX1003.1 [Engi90a] is mosrly an artempt ro codify com-mon base UMX. Unfortunately, common base UNIXdid not address some issues at all, notably dealingwith real-time networks like the Internet. Attemptsto define interfaces for real-time networks l,Divig3a.ATT86a] have generally resulted in complex anáugly messes./ Worse, there is no consensus on which9n9 to use, and the quality of the designs can bejudged by the rate at which they are beingredesigned to deal with unexpected problems-.Although higher-level abstractioas for networkingare not as common or as well-designed as theyshould be, networked file systems and shell invoca-tion of programs like rså can provide limited net-working functionality without having to deal withthe underlying mess.

A side benefit of high-level abstractions is thatthe resulting programs are generally far easier tomodify and customize. This is a particularly impor-tant consideration for software intended to be run onm¿ny systems with varying administrative policies.Many system administrators who are not up to deci-phering a 5000-line C program can cope quite wellwith modifying a 50-line shell script. We havemade a conscious effort to put policy decisions inshell scripts, not in C code, wherever possible, andhave had extensive and loud positive feedback onthis.

ffiepdons like v1o Resea¡chtnx [Cent90a] that are useful sources of interface ideas.

Summer '92 USENIX - June 8-June LZ,lgg2 - San Antonio, TX 191

Page 8: #ifdef Considered Harmful, or Portability Experience With C News · 2019. 2. 25. · #ifdef Considered Harmful, or Portability Experience With C News Henry Spencer -_Zo9l9gy-Computer

#ifdef Considered Harmfr¡l ... Spencer,& Collyer

- - There _is one negative aspect to moving to a underlying abstractions. Porting C News to a radi-higher_level of abstraction:- the resulting programs cally non-uNx-like operating syitem reportedly typi-depend on a larger and perhaps more fragile set of cally involves little change to itre C code, since-the

#ffdef SYSLOG#ifdef BSD_¿2

openloE( "nntpxf€r", f ,Oc_pID) i#eIee

openlogl "nntpxfer", LOc_pfD, SySLOc) ;#endif#endÍf

#ifdef DBMIf (dbmfnlt(HrSToRy_FrLE) < 0)

t#ifdef SYSLOG

syslog(LOG_ERR, "couldn,t open hietory f l le: trn,, ) ;#eIee

perror( "nntpxferr couldn,t open history f1le"¡ ¡#endff

e x i t ( 1 ) t)

#endif#ifdef NDBM

If ((db - dbn_openlHISTORY_FILE, O_RDONI.Y, O)) -- NULL)t

#lfdef SySLOcsyalog(LOG_ERR,"couldn,t open hlstory f i le: trn' ,¡ ;

#elseperror( "nntpxferr couldn,t open history f i le, ' ) ;

#endlfe x l t ( 1 ) ì)

#endlfI f ( (server - ge t_ tcp_conn(argv l1 ¡ , .nn tp , , ) ) < O)

{#lfdef SYSLOG

syslog(LOG_ERR, "could not open socket: tm,,) i#eIse

perrorf"nntpxfer: could .not open socket, ' ) ;#endif

e x l t ( 1 ) ì)

i f ( ( rd_ fp - fdopenqaerver , ' , r , , ¡ ¡ - - (F I ÌE * ) O) {#ifdef sYsLOc

syslog(LOG_ERRr "could not fdopen aocket: trn,') ;#else

perror( "nntpxfer: could not fdopen socket, ' ) ;#endlf

e x l t ( 1 ) t)

#lfdef SYSLOGsys log(Loc_DEBuc, "connected to nn tp server a t t s , , , a rgv l l ¡ ) ;

#endlf#Ifdef DEBUG

pr ln t f ( "connêct€d to nn tp aerv€r a t $B\n , ' , a rgv l1 ] ) i#endlf

/ *r ok, at thfe point n€,re connected to the nntp daenon* at the dlstant ho8t.* /

Figure E: A truly awful style

Summer '92 USENIX - June 8.June 12, L992 - San Antonio, TXt92

Page 9: #ifdef Considered Harmful, or Portability Experience With C News · 2019. 2. 25. · #ifdef Considered Harmful, or Portability Experience With C News Henry Spencer -_Zo9l9gy-Computer

Spencer,& Collyer

u¡¡lx and C programming interfaces are widespreadeven on non-UNIX systems, but substantial shell filesrelying on dozens of major UMX utilities are more ofa challenge. There is also the problem, mentionedearlier, of u¡ux suppliers breaking formerly-workingutilities.

Low-Level Portability

We assume that everyone reading this has hadexposure to elementary notions of portability likeusing typedef names, avoiding stupid assumptionsabout the sizes of integers and/or pointers, beingcareful about byte order in interchange formats, etc.There are nevertheless a good many fine points thatdeserve some illumination, particularly in the area ofhow to use #ifdef safely.

As rnentioned earlier, if #ifdef is needed at all,it is best confined to declarations, to try to preservesome explicit notion of interfaces. Such declara-tions, in turn, preferably should be confined toheader (.h) files, to minimize the temptation to intro-duce #ifdef into main-line code.

An optional feature such as debugging assis-tance or logging can be defined as a macro or func-tion that does nothing when not needed, else thefull-blown function can be defined (perhaps in one ofseveral system-specific ways, e.g. using a sys/og dae-mon or not). At worst, this requires one #ifdef persuch feature rather than the now-notorious style,seen in various bits of popular software, of clustering#ifdefs at the site of èaõh call of. said function(sisee Figure 8.

One awkward areaS is functions with variablenumbers of arguments. There is no way to write a Cmacro that can take a variable number of arguments,which makes it awkward to provide such an inter-face while still being able to hide the innards. Vari-ous tricks are in use, none of them entirely satisfac-tory; perhaps the least objectionable is an extra levelof parentheses:

D E B U G ( ( " o o p s : t s t d \ n " , b , c ) ) iwhich lets a header file decide to either pass or dis-card the whole argument list:

#ifdef NDEBUG# def ine DEBUG( l i s t ) / * no th ing *7#eIse# de f ine DEBUG( l i s t ) p r in t f t i s t#endif

A related problem is that definition of avariable-arguments function pretty well invariablyinvolves some #ifdefing to cope with the unfortunatedifferences between ANSI C stdarg.h and the tradi-tional (although less portable) varargs.h.

SActually, it's awkward in a great many ways, this beingonly one.

#ifdef Considered Harmful ...

Although macros cannot take variable numbersof arguments, it ¿,s still possible to have them pickand choose among a fixed number of arguments.For example, the VERBOSE-TERSE business in oneof our first exhibits, an attempt to avoid compiling inunneeded strings, can be handled with a macro:

MSG(short_forn, long_forn, iostream)A shorþform-only definition of the macro simplydoesn't use the long_form argument. The choice caneven be made at run time using f or the '?' opera-tor, all by changing only the definitíon of the macro.

One valid use of #ifdef, particularly in headerfiles, is the idiom

#ifndef COPYSIzE#define COPYSIzE 8192

/* uni t of copying */#endif

to supply a default value that can be overridden atcompilation time (with cc -DCOPYSIZE=4096).One could wish for a shorter form (e.g., #ifndefdef),or even a compiler option allowing one to specify avalue that overrides the first one defined in the pro-gram, since this idiom is common and very usefui.

However, the first question to ask about suchnumeric parameters is whether they should be thereat all. consider:

#i fdef pdpl1#def ine LBUFLEN 512

/* l ine bu f fe r leng th * /#e lse#def ine LBUFLEN 1024

/* I ine buffer length ' t l#endif

This code presumes that people on small machines(or at least PDP-11s) prefer their programs to crashearlier than people on large machines. Any codeusing such (unchecked) fixed-sized buffers is proneto falling over and dying (or at best mysteriouslytruncating or wrapping long lines) anyway; the#ifdefs tip us óff that rhese limits should be abol-ished and replaced with code that deals withdynamically-sized strings.

Another legitimate use of #ifde[ in factrequired by the ANSI C standard in standard headerfiles, is in protecting header files against multipleinclusion. In complex programs it can be quitedifficult to ensure that a needed header file isincluded once and only once, and including it morethan once typically causes problems with duplicatetypedefs, structure tags, etc. Ignoring some issues ofname-space control, the usual idiom for defendingheader files against multiple inclusion goes some-thing like this:

#ifndef FOO_H#define Foo_H 1/* interface to the foo module */

Summer '92 USENIX - June 8-June 12,lgg2 - San Antonio, TX 193

Page 10: #ifdef Considered Harmful, or Portability Experience With C News · 2019. 2. 25. · #ifdef Considered Harmful, or Portability Experience With C News Henry Spencer -_Zo9l9gy-Computer

fifdef Considered Harmful ...

typedef struct {char *foo_aichar *foo_b;

) foo ;extern foo *mkfoo( ) ;extern int rmfoo( ) ;#endif

(Some compiler implementors have invented buanespecial-purpose constructs, typically using ANSI C's#pragma, to avoid having the compiler re-scan the

#ifdef vaxf ( * p t r ) t

#endif#i fdef pyr

/ ** darned Pyramid is so picky* about nul l pointers* /

i f (p t r t= NULL)f ( * p t r ) ,

#endif#i fdef sparc

/ * the Sun 4 is jus t as badt * /i f (p t r t= NULL)

f ( *p t r ) ;#endif/ * t /

Figure 9: Protecting broken code

header file on later inclusions. That is not neces-sary. It suffices to have the compiler remember thatthe entire text of the file is inside the #ifndef, andhence need not be rescanned if FOO H is stilldefined.)

#ifdef is often used to protect broken code inthe style sho\¡/n in Figure 9. The solution here is toface realities and write the code in a conect andportable manner:

/* avoid dereferencing nuII * /i f (p r r l= NULL)

f ( * p t r ) ;

A related point, also illustrated by that exam-ple, is that if. one must use fifdef, one should test forspecific features or characteristics (typically indi-cated to the compiler by symbols defined in a headerfile or on a command line), not for specificmøchines. There will almost always be anothermachine with the same problem. Consider the

Spencer,& Collyer

interesting bit of code shown in Figure 10. Rathermysterious, isn't it? What is so odd about Crays,and is it only Crays that are affected?

If testing for particular machines is unavoid-able, perhaps because of some highly machine-specific operation, consider what happens if nomachine is specified (or if the machine is one you'venever heard of and hence didn't bother to list).Don't assume there is a default machine. It is muchkinder to produce a syntax enor than silently inap-propriate code.

Occurrences of finclude inside #ifdef shouldalways be viewed with suspicion. There are betterways. Consider:

#ifdef NDIR#ifdef M_XENIX#include <sys/ndir .h>#else#include <ndir .h>#endif#eIse#include <sys/dir .h>#endif#i fdef USG#include <t ime.h>#e lse#include <sys/t ime.h>#endif

This clutter could be avoided via judicious use of cc-Ilusrlincludelsys and consistent use of dirent.h, pto-viding a fake one if necessary:

#include <direct.h>#def ine dirent direct

Arranging, typically via a makefile, to put an "over-ride" directory in the search path for header files isa tremendously powerful way of fixing botches in asite's header frles without #ifdef.

When one uses #ifdef, one should base the testson individual features:

#include <signal.h>/* nay def ine srcrsrP */

#i fdef SIGTSTP(vo id ) s igna l (S IGTSTP, S IG_IGN) ;

/* no suspension, thanke */#endif

and not on (inaccurate) generalisations:

#ifdef cray) w h i l e ' 1 * g

#eIse) whi le 1 *g

#endif

l t t i l l a

/ * t i l l a

Figure 10: Mysterious code

newline (not

newline (not

* /

* /

t = ' \ r ' ) i

' \ n ' ) i

echoed)

echoed )

194

! =

Summer '92 USENIX - June 8.June 12,1992 - San Antonio, TX

Page 11: #ifdef Considered Harmful, or Portability Experience With C News · 2019. 2. 25. · #ifdef Considered Harmful, or Portability Experience With C News Henry Spencer -_Zo9l9gy-Computer

Spencer,& Collyer

#i fndef SYSV(vo id ) s igna l (S IGTSTP¡ SIG- IGN) ;

/* no suspension, thanks */#endif

or this example of the reverse problem (generalisingfrom the specific) from a newsreader

/* Things vre can f igure out */

#i fdef sIcTsTP# define BERKELEy

/* include job control s ignals? */#endif

This particular point is worth emphasizing: the UNrxworld is not cleanly split into System V and 4BSDcamps, particularly with the advent of System vRelease 4. Hybrid uNlxes are the rule, not theexception, nowadays.

Pragmatic Aspects of Portability

In practice, one encounters all manner of break-age in vendor-supplied system software: compilers,utilities (notably the shell and awk), libraries, ker-nels. Optiqrizers may need to be turned off if theyare broken.v Installers may have to pick up workingcommands f¡om other sources (e.g. the Usenetgroup comp.sources.unix or the GNU [Founa] pro-ject). Sometimes it is worth supplying simple butcorrect versions of small things (e.g. library func-tions) when a large class of machines is known tohave broken ones. We ultimately decided that wecould not provide complete replacements, or evenworkarounds, for all potentially-broken systemsoftware. Sometimes the problems are horrificenough that the right response is not to contort one'scode but to get the customers to complain about thebreakage until it is fixed.

Given all these potential problems, it is impor-tant to deteu breakage as well as avoiding it or cop-ing with it. IVe think very highly of regressiontests, prepackaged tests that exercise the basic func-tionality of the software and check that the resultsare correct. They are very useful during develop-ment, both for bûg-hunting in new codeI0 and forconfidence testing before release.f/ Of equal impor-tance, though, is that they give the installer reason-able confrdence that the software is actually workingon his system, and that no glaring portability prob-lems have escaped his notice.

. tlhe single most frequently reported ,,bug,' in C News

is,actually a bug in a popular 386 C compilei's optimizer.ruOne of us (HS) observes: "When I set up aregression test for software that has never had one before,I always find bugs. Always. Every tíme,"rrOne very useful t¡ick is to add a regression-test itemlooking for each bug that is found. This avoids the classicsyndrome of having "fixed" bugs reappear in a laterrelease,

#ifdef Considered Harmful ...

Similarly, intemal consistency checks, such asvalidated magic numbers in structures passedbetween user code and libraries, can save one's san-ity by detecting breakage in system software early,before corruption spreads everywhere. Trying todebug a core dump by mail on an unfamiliarmachine is not fun.

To a greater extent than we had anticipated,one leains about portability by porting. The systemcall variations among uNIx systems are fairly welldocumented and understood. The variations in com-mands were less well understood, at least by us, andthe variations in programming environments werestill more surprising. There is no substitute for try-ing your software on several seriously-different/2machines before release. It's also worth making aneffort to pick your beta-testers for maximum diver-sity of envi¡onments: we found a lot of unexpectedproblems that way.

Finally, a plea: if you find portability prob-lems, document them. You can't expect everyone toactually read the documentation-we frequentlyrespond to queries with "please read section so-and-so in document such-and-such, it'll tell you allabout it"-but the more careful and conscientiousinstallers benefit greatly from an advance look atknown problems, especially when a truly weird sys-tem is involved.

Configuration

Given the senseless diversity in existing sys-tems, some way to configure software for a new sys-tem is needed. Given that #ifdef can't do the wholejob, how should we proceed? C News currently hasan interactive build script that interrogates theinstaller about his system and then constructs a fewshell scripts, which when run will use make to buildthe software. We intend to push most of the shellscripts nto the makefiles, so that casual use of makeworks as people expect,l3 but the general approachseems to be a good one: ask which emulation rou-tines and header files are necess:rry. rather than rv-ing to guess. This strategy e.rln aflows cross-configuration and some degree of cross-compilation,which autoconfiguration schemes generally don't. Itis also more trustworthy than autoconfigurationschemes, which can be fooled by some new innova-tion.

Almost all of. build's configuration questionsl4turn into choices of files rather than values for #ifdef

ffi our university environmenr,it was quite difficult to find System V machines. Whenwe actually tried one, not long before our first real release,there were some unpleasant surprises.

rJThe main reason for not doing this from the start wasthe lack of a standard #include mechanism in make.

Summer '92 USENIX - June 8-June lZ,1992 - San Antohio, TX 195

Page 12: #ifdef Considered Harmful, or Portability Experience With C News · 2019. 2. 25. · #ifdef Considered Harmful, or Portability Experience With C News Henry Spencer -_Zo9l9gy-Computer

comment 4config. 6protect .h 5_STDC- 3pdp11 2lint 1sccsid 0STATS Oother 0

#ifdef Considered Harmful ...

to examine. The few exceptions are mostly histori-cal relics, and will be revised or deleted as time per-mits.

Statistics

A snapshot of cunent C News working sourcesshows 955 lines of header files and 19,762lines of Cflles, split between 5,640 lines from libraries (includ-ing alternate versions of primitives), and 14,122 linesof mainline C code. Here is a breakdown of thefifdef usage in that code:

reason .h main .c dbz rna totalifndefdef 13 40 6 0

Spencer,& Collyer

In our experience, #ifdef is usually a bad idea(although we do use it in places). Its legitimate usesare fairly narow, and it gets abused almost as badlyas the notorious goto statement. Like the goto, the#ifdef often degrades modularity and readability(intentionally or not). Given some advance plan-ning, there are better ways to be portable.

Acknowledgements

Thanks to Rob Kolstad for helpful commentson a draft of this paper. Thanks to James Clark forgrefer (and the rest of. groff). And thanks ro rheauthors of our bad examples-you know who youare.

References

ATT86a. AT&T, System V Interface Definition, 2,1986.

Cent90a. Computing Science Research Center,AT&T Bell Laboratories, Murray Hill, NewJersey, UNIX Research System Programmer'sManuøL, Tenth Editio4 Saunders College Pub-lishing, 1990.

Coll87a. Geoff Collyer and Henry Spencer, "NewsNeed Not Be Slow," Proc. Winter UsenixConf. Washington 1987, pp. 181-190, January7987.

Darw85a. Ian Darwin and Geoff Collyer, 'lCan'tHappen or /* NOTREACHED */ or Real Pro-grams Dump Core," Proc. Winter Usenix Conf,Dallas 1985, pp. 136-157, January 1985.

Divi83a. Computer Science Division, Dept. of E.E.and C.S., UCB, U¡üã Programmer's Manual4.2 Berkelq Softare Dßtribution, August,1983.

Engi90a. Institute of Electrical and ElechonicsEngineers, Portable Operating System Interface(POSU), Part l: System Application ProgramInterface (API) rc Languagel (IEEE Std1003.1-1990) = ISOIIEC 9945-1:1990, IEEE,New York, 1990.

Founa. Free Software Foundation, GNU software,anonymous ftp from prep.ai.mit.edu:/pub/gnu.

InstS9a.American National Standards Institute,X3J11 committee, Amerícan Natìonal Stan-dards Institute X3.159-1989 - programmingLanguage C, = ISOIIEC 9899:1990, ANSI,New York, 1989.

Labo82a.Bell Laboratories, UNIX Progrømmer'sManual, Holt, Rinehart and Winston, 1982.

ODel87a. Mike O'Dell, "UNIX: The World View,"Proc. Winter Usenix Conf. Washington 1987,pp. 35-45, January 1987.

Ritc78a. D. M. Ritchie, "UND( Time-Sharing Sys-tem: A Retrospective," Bell Sys, Tech. J., vol.57, no. 6, pp; L947-1969, L978. Also in Proc.Hawaii International Conference on SystemsScience, Honolulu, Hawaii, Jan, 1977.

592557

7241

1

2 7 0 02 5 1 9 70 0 03 1 00 0 01 , 2 01 0 05 0 01 0 0

total 34 97 28 7 t66

The ,h column represents header files. The main .ccolumn represents all .c files other than those in thedbz and nra (Australian readnews) directories. Theifndefdef row represeäts the 'if not defined, define'idiom. The comment row represents uses of #ifdefto comment out obsolete, futuristic or otherwiseunwanted code. The config. row represents uses of#ifdef to configure the software.

rnø is presented separately because we inher-ited it rather than writing it. dbz is presentedseparately because it usés #ifdef heavíly forconfiguration, for backwæd compatibility and toattempt to stand independently of C News. Themain C files' use of #ifdef for "configuration', ismisleading; in fact this is mostly vestigial code,superseded but not yet deleted from our currentworking copies.

Conclusions

Despite problems along the way, C News isoutstandingly portable. It comes up easily on anamazing variety of UND( systems. Other people havereported porting C News relatively easily to environ-ments that we had considered too hostile, or at leasttoo different from umx, to even consider as possibletarget systems: notably VMS, MS-DOS and AmigaDOS. The only major operating system known topresent serious obstacles is VM/CMS.

@iladon at all; some questionsare decisions affecting setup of control files for thecompiled software to usc.

L96 Summer '92 USENIX - June t:June 12,lgg? - San Antonlo, TX

Page 13: #ifdef Considered Harmful, or Portability Experience With C News · 2019. 2. 25. · #ifdef Considered Harmful, or Portability Experience With C News Henry Spencer -_Zo9l9gy-Computer

Spencer,& Collyer

Spen88a. Henry Spencer, "How To Steal Code,"Proc. Winter Usenix Conf. Dallas 1988, pp.335-345, January 1988.

Spen91a. Henry Spencer, "Awk As A Major Sys-tems Programming Language," Proc, WhterUsenix Conf, Dallas 1991, pp. t37-t43, January199r.

Author Information

Henry Spencer is head of Zoology ComputerSystems at the University of Toronto. He is knownfor his regular expression and string libraries, and asa co-author of the C News netnews software. Reachhim via Canada Post at Zoology Computer Systems,25 Harbord St., University of Toronto, Toronto, Ont.M5S 141 Canada. His electronic mail address isutzoo I henry or henry€zoo. toronto.edu.

Geoff Collyer leads C News development atSoftware Tool & Die. He is senior author of the CNews netnews software. His interests include sim-ple, small, fast, elegant and powerful systemsoftware. Reach him via U.S. Mail at Softrvare Tool& Die, 1330 Beacon St. #215, Brookline, MA02146. His electronic mail address isworldl geoff or geoff€world. std.com.

#ifdef Consldered Harmful ...

Summer '92 USENIX - June 8-June 12, LggZ - San Antonio, TX L97