12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304 |
- /*++
- Copyright (c) 2013 Minoca Corp.
- This file is licensed under the terms of the GNU General Public License
- version 3. Alternative licensing terms are available. Contact
- info@minocacorp.com for details. See the LICENSE file at the root of this
- project for complete licensing information.
- Module Name:
- tzcomp.c
- Abstract:
- This module implements the time zone compiler program, which translates
- time zone data into a binary format.
- Author:
- Evan Green 2-Aug-2013
- Environment:
- Build
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/lib/types.h>
- #include <minoca/lib/status.h>
- #include <minoca/lib/rtl.h>
- #include <minoca/lib/tzfmt.h>
- #include <assert.h>
- #include <ctype.h>
- #include <errno.h>
- #include <getopt.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- //
- // --------------------------------------------------------------------- Macros
- //
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define TIME_ZONE_COMPILER_VERSION_MAJOR 1
- #define TIME_ZONE_COMPILER_VERSION_MINOR 1
- #define TIME_ZONE_COMPILER_USAGE \
- "Usage: tzcomp [-p] [-f <zone>] [-o <outputfile>] [files...]\n" \
- "The tzcomp utility compiles standard time zone data files into a binary " \
- "format. Options are:\n\n" \
- " -o, --output=<file> -- Write the output to the given file rather \n" \
- " than the default file name \"" TIME_ZONE_DEFAULT_OUTPUT_FILE \
- "\".\n\n" \
- " -v, --verbose -- Print the parsed results coming from the input files." \
- "\n" \
- " -y, --year=<year> -- Write only zone information newer than the \n" \
- "given year.\n" \
- " -z, --zone=<zone> -- Produce output only for the time zone of the \n" \
- " given name.\n" \
- #define INITIAL_MALLOC_SIZE 32
- #define TIME_ZONE_DEFAULT_OUTPUT_FILE "tzdata"
- #define TZCOMP_OPTIONS_STRING "ho:vy:z:V"
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- typedef enum _TZC_RULE_FIELD {
- RuleFieldMagic,
- RuleFieldName,
- RuleFieldFrom,
- RuleFieldTo,
- RuleFieldType,
- RuleFieldIn,
- RuleFieldOn,
- RuleFieldAt,
- RuleFieldSave,
- RuleFieldLetters,
- RuleFieldCount
- } TZC_RULE_FIELD, *PTZC_RULE_FIELD;
- typedef enum _TZC_ZONE_FIELD {
- ZoneFieldMagic,
- ZoneFieldName,
- ZoneFieldGmtOffset,
- ZoneFieldRules,
- ZoneFieldFormat,
- ZoneFieldUntilYear,
- ZoneFieldUntilMonth,
- ZoneFieldUntilDay,
- ZoneFieldUntilTime,
- ZoneFieldCount
- } TZC_ZONE_FIELD, *PTZC_ZONE_FIELD;
- typedef enum _TZC_LINK_FIELD {
- LinkFieldMagic,
- LinkFieldFrom,
- LinkFieldTo,
- LinkFieldCount
- } TZC_LINK_FIELD, *PTZC_LINK_FIELD;
- typedef enum _TZC_LEAP_FIELD {
- LeapFieldMagic,
- LeapFieldYear,
- LeapFieldMonth,
- LeapFieldDay,
- LeapFieldTime,
- LeapFieldCorrection,
- LeapFieldRollingOrStationary,
- LeapFieldCount
- } TZC_LEAP_FIELD, *PTZC_LEAP_FIELD;
- typedef struct _TZC_RULE {
- LIST_ENTRY ListEntry;
- ULONG NameIndex;
- SHORT From;
- SHORT To;
- TIME_ZONE_MONTH Month;
- TIME_ZONE_OCCASION On;
- LONG At;
- TIME_ZONE_LENS AtLens;
- LONG Save;
- ULONG LettersOffset;
- } TZC_RULE, *PTZC_RULE;
- typedef struct _TZC_ZONE {
- LIST_ENTRY ListEntry;
- ULONG NameOffset;
- ULONG ZoneEntryIndex;
- ULONG ZoneEntryCount;
- } TZC_ZONE, *PTZC_ZONE;
- typedef struct _TZC_LINK {
- LIST_ENTRY ListEntry;
- PSTR From;
- PSTR To;
- } TZC_LINK, *PTZC_LINK;
- typedef struct _TZC_ZONE_ENTRY {
- LIST_ENTRY ListEntry;
- ULONG Index;
- LONG GmtOffset;
- ULONG RulesNameIndex;
- LONG Save;
- ULONG FormatOffset;
- LONGLONG Until;
- } TZC_ZONE_ENTRY, *PTZC_ZONE_ENTRY;
- typedef struct _TZC_LEAP {
- LIST_ENTRY ListEntry;
- LONGLONG Date;
- CHAR Positive;
- CHAR LocalTime;
- } TZC_LEAP, *PTZC_LEAP;
- typedef struct _TZC_STRING {
- LIST_ENTRY ListEntry;
- ULONG Offset;
- PSTR String;
- } TZC_STRING, *PTZC_STRING;
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- INT
- ReadTimeZoneFile (
- PSTR FilePath
- );
- INT
- ProcessTimeZoneRule (
- PSTR *Fields,
- ULONG FieldCount
- );
- INT
- ProcessTimeZone (
- PSTR *Fields,
- ULONG FieldCount,
- PBOOL Continuation
- );
- INT
- ProcessTimeZoneLink (
- PSTR *Fields,
- ULONG FieldCount
- );
- INT
- ProcessTimeZoneLeap (
- PSTR *Fields,
- ULONG FieldCount
- );
- INT
- ReadTimeZoneFields (
- FILE *File,
- PVOID *LineBuffer,
- PULONG LineBufferSize,
- PSTR **Fields,
- PULONG FieldsSize,
- PULONG FieldCount,
- PBOOL EndOfFile
- );
- INT
- ReadTimeZoneLine (
- FILE *File,
- PVOID *LineBuffer,
- PULONG LineBufferSize,
- PBOOL EndOfFile
- );
- INT
- TranslateLinksToZones (
- );
- INT
- TimeZoneFilter (
- PSTR Name,
- INT Year
- );
- INT
- WriteTimeZoneData (
- PSTR FileName
- );
- VOID
- DestroyTimeZoneRule (
- PTZC_RULE Rule
- );
- VOID
- DestroyTimeZone (
- PTZC_ZONE Zone
- );
- VOID
- DestroyTimeZoneEntry (
- PTZC_ZONE_ENTRY ZoneEntry
- );
- VOID
- DestroyTimeZoneLink (
- PTZC_LINK Link
- );
- VOID
- DestroyTimeZoneLeap (
- PTZC_LEAP Leap
- );
- VOID
- DestroyTimeZoneStringList (
- PLIST_ENTRY ListHead
- );
- INT
- ParseTimeZoneRuleLimit (
- PSTR Field,
- SHORT OnlyValue,
- PSHORT Value
- );
- INT
- ParseTimeZoneMonth (
- PSTR Field,
- PTIME_ZONE_MONTH Value
- );
- INT
- ParseTimeZoneRuleWeekday (
- PSTR Field,
- PTIME_ZONE_WEEKDAY Value
- );
- INT
- ParseTimeZoneOccasion (
- PSTR Field,
- PTIME_ZONE_OCCASION Occasion
- );
- INT
- ParseTimeZoneTime (
- PSTR Field,
- PLONG Time,
- PTIME_ZONE_LENS Lens
- );
- VOID
- PrintTimeZoneRule (
- PTZC_RULE Rule
- );
- VOID
- PrintTimeZone (
- PTZC_ZONE Zone
- );
- VOID
- PrintTimeZoneLink (
- PTZC_LINK Link
- );
- VOID
- PrintTimeZoneLeap (
- PTZC_LEAP Leap
- );
- VOID
- PrintTimeZoneEntry (
- PTZC_ZONE_ENTRY ZoneEntry
- );
- VOID
- PrintTimeZoneTime (
- LONG Time,
- TIME_ZONE_LENS Lens
- );
- VOID
- PrintTimeZoneDate (
- LONGLONG Date
- );
- INT
- CalculateOccasionForDate (
- PTIME_ZONE_OCCASION Occasion,
- INT Year,
- TIME_ZONE_MONTH Month,
- PINT Date
- );
- INT
- CalculateWeekdayForMonth (
- INT Year,
- TIME_ZONE_MONTH Month,
- PTIME_ZONE_WEEKDAY Weekday
- );
- LONG
- ComputeDaysForYear (
- INT Year
- );
- INT
- ComputeYearForDays (
- PLONG Days
- );
- PSTR
- TimeZoneGetString (
- PLIST_ENTRY ListHead,
- ULONG Offset
- );
- INT
- TimeZoneAddString (
- PSTR String,
- PULONG Offset
- );
- INT
- TimeZoneAddRuleString (
- PSTR String,
- PULONG Index
- );
- INT
- TimeZoneAddStringToList (
- PSTR String,
- PLIST_ENTRY ListHead,
- PULONG ListSize,
- BOOL TrackSize,
- PULONG Offset
- );
- VOID
- TimeZoneCompressEntries (
- PLIST_ENTRY ZoneEntryList,
- PULONG ZoneEntryCount,
- PTZC_ZONE Zone
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- struct option TzcompLongOptions[] = {
- {"output", required_argument, 0, 'o'},
- {"verbose", no_argument, 0, 'v'},
- {"year", required_argument, 0, 'y'},
- {"zone", required_argument, 0, 'z'},
- {"help", no_argument, 0, 'h'},
- {"version", no_argument, 0, 'V'},
- {NULL, 0, 0, 0},
- };
- LIST_ENTRY TimeZoneRuleList;
- LIST_ENTRY TimeZoneList;
- LIST_ENTRY TimeZoneEntryList;
- LIST_ENTRY TimeZoneLinkList;
- LIST_ENTRY TimeZoneLeapList;
- //
- // Store the string table and next free offset into it.
- //
- LIST_ENTRY TimeZoneStringList;
- ULONG TimeZoneNextStringOffset;
- //
- // Store the string table for the rule list (which will eventually be
- // discarded) and the next valid rule number.
- //
- LIST_ENTRY TimeZoneRuleStringList;
- ULONG TimeZoneNextRuleNumber;
- ULONG TimeZoneNextZoneEntryIndex;
- ULONG TimeZoneRuleCount;
- ULONG TimeZoneCount;
- ULONG TimeZoneLeapCount;
- PSTR TimeZoneMonthStrings[TimeZoneMonthCount] = {
- "January",
- "February",
- "March",
- "April",
- "May",
- "June",
- "July",
- "August",
- "September",
- "October",
- "November",
- "December"
- };
- PSTR TimeZoneAbbreviatedMonthStrings[TimeZoneMonthCount] = {
- "Jan",
- "Feb",
- "Mar",
- "Apr",
- "May",
- "Jun",
- "Jul",
- "Aug",
- "Sep",
- "Oct",
- "Nov",
- "Dec"
- };
- PSTR TimeZoneWeekdayStrings[TimeZoneWeekdayCount] = {
- "Sunday",
- "Monday",
- "Tuesday",
- "Wednesday",
- "Thursday",
- "Friday",
- "Saturday"
- };
- PSTR TimeZoneAbbreviatedWeekdayStrings[TimeZoneWeekdayCount] = {
- "Sun",
- "Mon",
- "Tue",
- "Wed",
- "Thu",
- "Fri",
- "Sat"
- };
- CHAR TimeZoneDaysPerMonth[2][TimeZoneMonthCount] = {
- {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
- {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
- };
- SHORT TimeZoneMonthDays[2][TimeZoneMonthCount] = {
- {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
- {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335},
- };
- //
- // ------------------------------------------------------------------ Functions
- //
- INT
- main (
- INT ArgumentCount,
- CHAR **Arguments
- )
- /*++
- Routine Description:
- This routine is the main entry point for the time zone compiler program.
- Arguments:
- ArgumentCount - Supplies the number of command line arguments the program
- was invoked with.
- Arguments - Supplies a tokenized array of command line arguments.
- Return Value:
- Returns an integer exit code. 0 for success, nonzero otherwise.
- --*/
- {
- PSTR Argument;
- INT ArgumentIndex;
- PLIST_ENTRY CurrentEntry;
- PSTR FilterZone;
- PTZC_LEAP Leap;
- PTZC_LINK Link;
- INT Option;
- PSTR OutputName;
- BOOL PrintParsedEntries;
- INT Result;
- PTZC_RULE Rule;
- ULONG UnusedIndex;
- INT YearFilter;
- PTZC_ZONE Zone;
- PTZC_ZONE_ENTRY ZoneEntry;
- INITIALIZE_LIST_HEAD(&TimeZoneRuleList);
- INITIALIZE_LIST_HEAD(&TimeZoneList);
- INITIALIZE_LIST_HEAD(&TimeZoneEntryList);
- INITIALIZE_LIST_HEAD(&TimeZoneLinkList);
- INITIALIZE_LIST_HEAD(&TimeZoneLeapList);
- INITIALIZE_LIST_HEAD(&TimeZoneStringList);
- INITIALIZE_LIST_HEAD(&TimeZoneRuleStringList);
- FilterZone = NULL;
- OutputName = TIME_ZONE_DEFAULT_OUTPUT_FILE;
- PrintParsedEntries = FALSE;
- Result = 0;
- YearFilter = 0;
- if (ArgumentCount <= 1) {
- fprintf(stderr, TIME_ZONE_COMPILER_USAGE);
- return 1;
- }
- //
- // Add the null strings to the first positions.
- //
- TimeZoneAddString("", &UnusedIndex);
- TimeZoneAddRuleString("", &UnusedIndex);
- //
- // Loop through the arguments.
- //
- while (TRUE) {
- Option = getopt_long(ArgumentCount,
- Arguments,
- TZCOMP_OPTIONS_STRING,
- TzcompLongOptions,
- NULL);
- if (Option == -1) {
- break;
- }
- if ((Option == '?') || (Option == ':')) {
- Result = 1;
- goto MainEnd;
- }
- switch (Option) {
- case 'o':
- OutputName = optarg;
- break;
- case 'v':
- PrintParsedEntries = TRUE;
- break;
- case 'y':
- YearFilter = strtoul(optarg, NULL, 10);
- if ((YearFilter <= 0) || (YearFilter > 9999)) {
- fprintf(stderr, "Invalid year %s\n", optarg);
- Result = 1;
- goto MainEnd;
- }
- break;
- case 'z':
- FilterZone = optarg;
- break;
- case 'h':
- printf(TIME_ZONE_COMPILER_USAGE);
- return 1;
- case 'V':
- printf("Tzcomp version %d.%d\n",
- TIME_ZONE_COMPILER_VERSION_MAJOR,
- TIME_ZONE_COMPILER_VERSION_MINOR);
- return 1;
- default:
- assert(FALSE);
- Result = 1;
- goto MainEnd;
- }
- }
- ArgumentIndex = optind;
- while (ArgumentIndex < ArgumentCount) {
- Argument = Arguments[ArgumentIndex];
- Result = ReadTimeZoneFile(Argument);
- if (Result != 0) {
- fprintf(stderr,
- "tzcomp: Failed to process time zone data file %s.\n",
- Argument);
- goto MainEnd;
- }
- ArgumentIndex += 1;
- }
- Result = TranslateLinksToZones();
- if (Result != 0) {
- goto MainEnd;
- }
- //
- // Filter for the requested name and/or years if requested.
- //
- if ((FilterZone != NULL) || (YearFilter != 0)) {
- Result = TimeZoneFilter(FilterZone, YearFilter);
- if (Result != 0) {
- fprintf(stderr,
- "tzcomp: Error: Failed to filter time zone: %s.\n",
- strerror(errno));
- goto MainEnd;
- }
- }
- //
- // If requested, print all the parsed entries.
- //
- if (PrintParsedEntries != FALSE) {
- CurrentEntry = TimeZoneRuleList.Next;
- while (CurrentEntry != &TimeZoneRuleList) {
- Rule = LIST_VALUE(CurrentEntry, TZC_RULE, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- PrintTimeZoneRule(Rule);
- }
- CurrentEntry = TimeZoneList.Next;
- while (CurrentEntry != &TimeZoneList) {
- Zone = LIST_VALUE(CurrentEntry, TZC_ZONE, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- PrintTimeZone(Zone);
- }
- CurrentEntry = TimeZoneLinkList.Next;
- while (CurrentEntry != &TimeZoneLinkList) {
- Link = LIST_VALUE(CurrentEntry, TZC_LINK, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- PrintTimeZoneLink(Link);
- }
- CurrentEntry = TimeZoneLeapList.Next;
- while (CurrentEntry != &TimeZoneLeapList) {
- Leap = LIST_VALUE(CurrentEntry, TZC_LEAP, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- PrintTimeZoneLeap(Leap);
- }
- }
- Result = WriteTimeZoneData(OutputName);
- if (Result != 0) {
- fprintf(stderr,
- "tzcomp: Error: Failed to write time zone data: %s.\n",
- strerror(errno));
- goto MainEnd;
- }
- MainEnd:
- while (LIST_EMPTY(&TimeZoneRuleList) == FALSE) {
- Rule = LIST_VALUE(TimeZoneRuleList.Next, TZC_RULE, ListEntry);
- LIST_REMOVE(&(Rule->ListEntry));
- DestroyTimeZoneRule(Rule);
- }
- while (LIST_EMPTY(&TimeZoneList) == FALSE) {
- Zone = LIST_VALUE(TimeZoneList.Next, TZC_ZONE, ListEntry);
- LIST_REMOVE(&(Zone->ListEntry));
- DestroyTimeZone(Zone);
- }
- while (LIST_EMPTY(&TimeZoneLinkList) == FALSE) {
- Link = LIST_VALUE(TimeZoneLinkList.Next, TZC_LINK, ListEntry);
- LIST_REMOVE(&(Link->ListEntry));
- DestroyTimeZoneLink(Link);
- }
- while (LIST_EMPTY(&TimeZoneLeapList) == FALSE) {
- Leap = LIST_VALUE(TimeZoneLeapList.Next, TZC_LEAP, ListEntry);
- LIST_REMOVE(&(Leap->ListEntry));
- DestroyTimeZoneLeap(Leap);
- }
- while (LIST_EMPTY(&TimeZoneEntryList) == FALSE) {
- ZoneEntry = LIST_VALUE(TimeZoneEntryList.Next,
- TZC_ZONE_ENTRY,
- ListEntry);
- LIST_REMOVE(&(ZoneEntry->ListEntry));
- DestroyTimeZoneEntry(ZoneEntry);
- }
- DestroyTimeZoneStringList(&TimeZoneStringList);
- DestroyTimeZoneStringList(&TimeZoneRuleStringList);
- return Result;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- INT
- ReadTimeZoneFile (
- PSTR FilePath
- )
- /*++
- Routine Description:
- This routine reads in time zone data from a file.
- Arguments:
- FilePath - Supplies a pointer to the file path of the data to read in.
- Return Value:
- 0 on success.
- Non-zero on failure.
- --*/
- {
- BOOL EndOfFile;
- ULONG FieldCount;
- PSTR *Fields;
- ULONG FieldsSize;
- FILE *File;
- PVOID LineBuffer;
- ULONG LineBufferSize;
- ULONG LineNumber;
- INT Result;
- BOOL ZoneContinuation;
- EndOfFile = FALSE;
- FieldsSize = 0;
- Fields = NULL;
- LineBuffer = NULL;
- LineBufferSize = 0;
- ZoneContinuation = FALSE;
- File = fopen(FilePath, "rb");
- if (File == NULL) {
- fprintf(stderr,
- "tzcomp: Failed to open %s: %s\n",
- FilePath,
- strerror(errno));
- Result = errno;
- goto ReadTimeZoneFileEnd;
- }
- //
- // Loop reading and processing lines.
- //
- LineNumber = 1;
- while (TRUE) {
- Result = ReadTimeZoneFields(File,
- &LineBuffer,
- &LineBufferSize,
- &Fields,
- &FieldsSize,
- &FieldCount,
- &EndOfFile);
- if (Result != 0) {
- fprintf(stderr,
- "tzcomp: Failed to read line %s:%d: %s\n",
- FilePath,
- LineNumber,
- strerror(Result));
- goto ReadTimeZoneFileEnd;
- }
- //
- // Process the line according to its type.
- //
- if (FieldCount != 0) {
- Result = 0;
- if ((ZoneContinuation != FALSE) ||
- (strcasecmp(Fields[0], "Zone") == 0)) {
- Result = ProcessTimeZone(Fields, FieldCount, &ZoneContinuation);
- } else if (strcasecmp(Fields[0], "Rule") == 0) {
- Result = ProcessTimeZoneRule(Fields, FieldCount);
- } else if (strcasecmp(Fields[0], "Link") == 0) {
- Result = ProcessTimeZoneLink(Fields, FieldCount);
- } else if (strcasecmp(Fields[0], "Leap") == 0) {
- Result = ProcessTimeZoneLeap(Fields, FieldCount);
- }
- if (Result != 0) {
- fprintf(stderr,
- "tzcomp: Failed to process line %s:%d: %s.\n",
- FilePath,
- LineNumber,
- strerror(Result));
- goto ReadTimeZoneFileEnd;
- }
- }
- if (EndOfFile != FALSE) {
- break;
- }
- LineNumber += 1;
- }
- Result = 0;
- ReadTimeZoneFileEnd:
- if (File != NULL) {
- fclose(File);
- }
- if (LineBuffer != NULL) {
- free(LineBuffer);
- }
- if (Fields != NULL) {
- free(Fields);
- }
- return Result;
- }
- INT
- ProcessTimeZoneRule (
- PSTR *Fields,
- ULONG FieldCount
- )
- /*++
- Routine Description:
- This routine processes a time zone rule line.
- Arguments:
- Fields - Supplies a pointer to the fields in the line.
- FieldCount - Supplies the number of elements in the fields array.
- Return Value:
- 0 on success.
- Non-zero on failure.
- --*/
- {
- INT Result;
- PTZC_RULE Rule;
- //
- // Validate the number of fields and the first field.
- //
- assert(strcasecmp(Fields[RuleFieldMagic], "Rule") == 0);
- Rule = NULL;
- if (FieldCount != RuleFieldCount) {
- fprintf(stderr,
- "tzcomp: Expected %d fields in a Rule, got %d.\n",
- RuleFieldCount,
- FieldCount);
- Result = EILSEQ;
- goto ProcessTimeZoneRuleEnd;
- }
- //
- // Allocate the new rule structure.
- //
- Rule = malloc(sizeof(TZC_RULE));
- if (Rule == NULL) {
- Result = ENOMEM;
- goto ProcessTimeZoneRuleEnd;
- }
- memset(Rule, 0, sizeof(TZC_RULE));
- //
- // Copy the name.
- //
- Result = TimeZoneAddRuleString(Fields[RuleFieldName], &(Rule->NameIndex));
- if (Result != 0) {
- goto ProcessTimeZoneRuleEnd;
- }
- //
- // Parse the FROM and TO years.
- //
- Result = ParseTimeZoneRuleLimit(Fields[RuleFieldFrom],
- MIN_TIME_ZONE_YEAR,
- &(Rule->From));
- if (Result != 0) {
- fprintf(stderr,
- "Failed to process Rule FROM: %s\n",
- Fields[RuleFieldFrom]);
- goto ProcessTimeZoneRuleEnd;
- }
- Result = ParseTimeZoneRuleLimit(Fields[RuleFieldTo],
- Rule->From,
- &(Rule->To));
- if (Result != 0) {
- fprintf(stderr,
- "Failed to process Rule TO: %s\n",
- Fields[RuleFieldTo]);
- goto ProcessTimeZoneRuleEnd;
- }
- //
- // Skip over the type field.
- //
- if (strcmp(Fields[RuleFieldType], "-") != 0) {
- fprintf(stderr,
- "Warning: Ignoring rule type %s.\n",
- Fields[RuleFieldType]);
- }
- //
- // Parse the IN month.
- //
- Result = ParseTimeZoneMonth(Fields[RuleFieldIn], &(Rule->Month));
- if (Result != 0) {
- fprintf(stderr,
- "Failed to process Rule IN: %s\n",
- Fields[RuleFieldIn]);
- goto ProcessTimeZoneRuleEnd;
- }
- //
- // Parse the ON occasion.
- //
- Result = ParseTimeZoneOccasion(Fields[RuleFieldOn], &(Rule->On));
- if (Result != 0) {
- fprintf(stderr,
- "Failed to process Rule ON: %s\n",
- Fields[RuleFieldOn]);
- goto ProcessTimeZoneRuleEnd;
- }
- //
- // Parse the AT time.
- //
- Result = ParseTimeZoneTime(Fields[RuleFieldAt],
- &(Rule->At),
- &(Rule->AtLens));
- if (Result != 0) {
- fprintf(stderr,
- "Failed to process Rule AT: %s\n",
- Fields[RuleFieldAt]);
- goto ProcessTimeZoneRuleEnd;
- }
- //
- // Parse the SAVE time.
- //
- Result = ParseTimeZoneTime(Fields[RuleFieldSave], &(Rule->Save), NULL);
- if (Result != 0) {
- fprintf(stderr,
- "Failed to process Rule SAVE: %s\n",
- Fields[RuleFieldSave]);
- goto ProcessTimeZoneRuleEnd;
- }
- //
- // Copy the letters.
- //
- Result = TimeZoneAddString(Fields[RuleFieldLetters],
- &(Rule->LettersOffset));
- if (Result != 0) {
- goto ProcessTimeZoneRuleEnd;
- }
- INSERT_BEFORE(&(Rule->ListEntry), &TimeZoneRuleList);
- TimeZoneRuleCount += 1;
- Result = 0;
- ProcessTimeZoneRuleEnd:
- if (Result != 0) {
- if (Rule != NULL) {
- DestroyTimeZoneRule(Rule);
- }
- }
- return Result;
- }
- INT
- ProcessTimeZone (
- PSTR *Fields,
- ULONG FieldCount,
- PBOOL Continuation
- )
- /*++
- Routine Description:
- This routine processes a time zone line.
- Arguments:
- Fields - Supplies a pointer to the fields in the line.
- FieldCount - Supplies the number of elements in the fields array.
- Continuation - Supplies a pointer that on input contains whether or not
- this zone line is a continuation line. On output, this variable will
- be set to indicate whether another continuation line is expected.
- Return Value:
- 0 on success.
- Non-zero on failure.
- --*/
- {
- PSTR AfterScan;
- INT Day;
- PSTR Field;
- ULONG FieldOffset;
- INT Leap;
- TIME_ZONE_MONTH Month;
- TIME_ZONE_OCCASION Occasion;
- INT Result;
- LONG ScannedValue;
- TIME_ZONE_LENS UntilLens;
- LONG UntilTime;
- BOOL UntilValid;
- INT Year;
- PTZC_ZONE Zone;
- PTZC_ZONE_ENTRY ZoneEntry;
- UntilValid = FALSE;
- Zone = NULL;
- ZoneEntry = NULL;
- //
- // If this is a continuation, then get the most recent zone and set the
- // field offset since the "Zone" and Name fields will be missing.
- //
- if (*Continuation != FALSE) {
- FieldOffset = ZoneFieldGmtOffset;
- if (FieldCount < ZoneFieldFormat - FieldOffset) {
- fprintf(stderr,
- "Error: Not enough fields for zone continuation.\n");
- Result = EILSEQ;
- goto ProcessTimeZoneEnd;
- }
- Zone = LIST_VALUE(TimeZoneList.Previous, TZC_ZONE, ListEntry);
- //
- // This is not a continuation.
- //
- } else {
- assert(strcasecmp(Fields[ZoneFieldMagic], "Zone") == 0);
- FieldOffset = 0;
- if (FieldCount < ZoneFieldRules) {
- fprintf(stderr,
- "Error: Not enough fields for zone line.\n");
- Result = EILSEQ;
- goto ProcessTimeZoneEnd;
- }
- Zone = malloc(sizeof(TZC_ZONE));
- if (Zone == NULL) {
- Result = ENOMEM;
- goto ProcessTimeZoneEnd;
- }
- memset(Zone, 0, sizeof(TZC_ZONE));
- //
- // Copy the name.
- //
- Result = TimeZoneAddString(Fields[ZoneFieldName], &(Zone->NameOffset));
- if (Result != 0) {
- free(Zone);
- goto ProcessTimeZoneEnd;
- }
- Zone->ZoneEntryIndex = TimeZoneNextZoneEntryIndex;
- INSERT_BEFORE(&(Zone->ListEntry), &TimeZoneList);
- TimeZoneCount += 1;
- }
- //
- // Create the zone entry.
- //
- ZoneEntry = malloc(sizeof(TZC_ZONE_ENTRY));
- if (ZoneEntry == NULL) {
- Result = ENOMEM;
- goto ProcessTimeZoneEnd;
- }
- memset(ZoneEntry, 0, sizeof(TZC_ZONE_ENTRY));
- ZoneEntry->Until = MAX_TIME_ZONE_DATE;
- ZoneEntry->Index = TimeZoneNextZoneEntryIndex;
- TimeZoneNextZoneEntryIndex += 1;
- //
- // Get the GMT offset time.
- //
- Field = Fields[ZoneFieldGmtOffset - FieldOffset];
- Result = ParseTimeZoneTime(Field, &(ZoneEntry->GmtOffset), NULL);
- if (Result != 0) {
- fprintf(stderr,
- "Error: Failed to parse Zone GMTOFFSET %s.\n",
- Field);
- goto ProcessTimeZoneEnd;
- }
- //
- // Get the rules string. If it's a -, then this zone is always in
- // standard time. Otherwise, it could be a save value (like 1:00) or
- // the name of a set of rules.
- //
- Field = Fields[ZoneFieldRules - FieldOffset];
- ZoneEntry->RulesNameIndex = -1;
- if (strcmp(Field, "-") != 0) {
- if ((*Field == '-') || (isdigit(*Field))) {
- Result = ParseTimeZoneTime(Field, &(ZoneEntry->Save), NULL);
- if (Result != 0) {
- fprintf(stderr,
- "Error: Failed to parse Zone SAVE %s.\n",
- Field);
- goto ProcessTimeZoneEnd;
- }
- } else {
- Result = TimeZoneAddRuleString(Field, &(ZoneEntry->RulesNameIndex));
- if (Result != 0) {
- goto ProcessTimeZoneEnd;
- }
- }
- }
- //
- // Copy the format string.
- //
- Field = Fields[ZoneFieldFormat - FieldOffset];
- Result = TimeZoneAddString(Field, &(ZoneEntry->FormatOffset));
- if (Result != 0) {
- goto ProcessTimeZoneEnd;
- }
- //
- // If there's no until year, then this is done.
- //
- if (FieldCount <= ZoneFieldUntilYear - FieldOffset) {
- Result = 0;
- goto ProcessTimeZoneEnd;
- }
- Field = Fields[ZoneFieldUntilYear - FieldOffset];
- ScannedValue = strtol(Field, &AfterScan, 10);
- if ((ScannedValue <= MIN_TIME_ZONE_YEAR) ||
- (ScannedValue >= MAX_TIME_ZONE_YEAR) ||
- (AfterScan == Field)) {
- fprintf(stderr,
- "Error: Failed to parse Zone UNTIL YEAR %s.\n",
- Field);
- Result = EILSEQ;
- goto ProcessTimeZoneEnd;
- }
- Year = ScannedValue;
- UntilValid = TRUE;
- ZoneEntry->Until = (LONGLONG)ComputeDaysForYear(Year) * SECONDS_PER_DAY;
- //
- // If there's no until month, this is done.
- //
- if (FieldCount <= ZoneFieldUntilMonth - FieldOffset) {
- Result = 0;
- goto ProcessTimeZoneEnd;
- }
- Leap = 0;
- if (IS_LEAP_YEAR(ScannedValue)) {
- Leap = 1;
- }
- Field = Fields[ZoneFieldUntilMonth - FieldOffset];
- Result = ParseTimeZoneMonth(Field, &Month);
- if (Result != 0) {
- fprintf(stderr,
- "Error: Failed to parse Zone UNTIL MONTH %s.\n",
- Field);
- goto ProcessTimeZoneEnd;
- }
- ZoneEntry->Until += TimeZoneMonthDays[Leap][Month] * SECONDS_PER_DAY;
- //
- // If there is no until day, this is done.
- //
- if (FieldCount <= ZoneFieldUntilDay - FieldOffset) {
- Result = 0;
- goto ProcessTimeZoneEnd;
- }
- Field = Fields[ZoneFieldUntilDay - FieldOffset];
- //
- // The day portion of the until field can apparently either be a number or
- // an occasion.
- //
- if (isdigit(*Field)) {
- ScannedValue = strtol(Field, &AfterScan, 10);
- if ((ScannedValue <= 0) || (ScannedValue > 31) ||
- (AfterScan == Field)) {
- fprintf(stderr,
- "Error: Failed to parse Zone UNTIL DAY %s.\n",
- Field);
- Result = EILSEQ;
- goto ProcessTimeZoneEnd;
- }
- Day = ScannedValue;
- } else {
- memset(&Occasion, 0, sizeof(TIME_ZONE_OCCASION));
- Result = ParseTimeZoneOccasion(Field, &Occasion);
- if (Result != 0) {
- fprintf(stderr,
- "Error: Failed to parse Zone UNTIL DAY (occasion) %s.\n",
- Field);
- goto ProcessTimeZoneEnd;
- }
- Result = CalculateOccasionForDate(&Occasion, Year, Month, &Day);
- if (Result != 0) {
- fprintf(stderr, "Error: Zone UNTIL DAY occasion does not exist.\n");
- goto ProcessTimeZoneEnd;
- }
- }
- ZoneEntry->Until += (Day - 1) * SECONDS_PER_DAY;
- //
- // Finally, if there is no time, this is done.
- //
- if (FieldCount <= ZoneFieldUntilTime - FieldOffset) {
- Result = 0;
- goto ProcessTimeZoneEnd;
- }
- Field = Fields[ZoneFieldUntilTime - FieldOffset];
- Result = ParseTimeZoneTime(Field, &UntilTime, &UntilLens);
- if (Result != 0) {
- fprintf(stderr,
- "Error: Failed to parse Zone UNTIL TIME %s.\n",
- Field);
- goto ProcessTimeZoneEnd;
- }
- ZoneEntry->Until += UntilTime;
- if ((UntilLens == TimeZoneLensLocalTime) ||
- (UntilLens == TimeZoneLensLocalStandardTime)) {
- ZoneEntry->Until += ZoneEntry->GmtOffset;
- if (UntilLens == TimeZoneLensLocalTime) {
- ZoneEntry->Until += ZoneEntry->Save;
- }
- }
- ProcessTimeZoneEnd:
- if (Result != 0) {
- if (ZoneEntry != NULL) {
- DestroyTimeZoneEntry(ZoneEntry);
- }
- } else {
- assert((ZoneEntry != NULL) && (Zone != NULL));
- Zone->ZoneEntryCount += 1;
- INSERT_BEFORE(&(ZoneEntry->ListEntry), &TimeZoneEntryList);
- //
- // If this is the last zone entry, attempt to compress by finding the
- // same zone entries elsewhere.
- //
- if (UntilValid == FALSE) {
- TimeZoneCompressEntries(&TimeZoneEntryList,
- &TimeZoneNextZoneEntryIndex,
- Zone);
- }
- }
- *Continuation = UntilValid;
- return Result;
- }
- INT
- ProcessTimeZoneLink (
- PSTR *Fields,
- ULONG FieldCount
- )
- /*++
- Routine Description:
- This routine processes a time zone link line.
- Arguments:
- Fields - Supplies a pointer to the fields in the line.
- FieldCount - Supplies the number of elements in the fields array.
- Return Value:
- 0 on success.
- Non-zero on failure.
- --*/
- {
- PTZC_LINK Link;
- INT Result;
- Link = NULL;
- Result = ENOMEM;
- if (FieldCount != LinkFieldCount) {
- fprintf(stderr,
- "Error: Link should have had %d fields, had %d.\n",
- LinkFieldCount,
- FieldCount);
- return EILSEQ;
- }
- assert(strcasecmp(Fields[LinkFieldMagic], "Link") == 0);
- Link = malloc(sizeof(TZC_LINK));
- if (Link == NULL) {
- goto ProcessTimeZoneLinkEnd;
- }
- memset(Link, 0, sizeof(TZC_LINK));
- Link->From = strdup(Fields[LinkFieldFrom]);
- if (Link->From == NULL) {
- goto ProcessTimeZoneLinkEnd;
- }
- Link->To = strdup(Fields[LinkFieldTo]);
- if (Link->To == NULL) {
- goto ProcessTimeZoneLinkEnd;
- }
- INSERT_BEFORE(&(Link->ListEntry), &TimeZoneLinkList);
- Result = 0;
- ProcessTimeZoneLinkEnd:
- if (Result != 0) {
- if (Link != NULL) {
- DestroyTimeZoneLink(Link);
- }
- }
- return Result;
- }
- INT
- ProcessTimeZoneLeap (
- PSTR *Fields,
- ULONG FieldCount
- )
- /*++
- Routine Description:
- This routine processes a time zone leap second line.
- Arguments:
- Fields - Supplies a pointer to the fields in the line.
- FieldCount - Supplies the number of elements in the fields array.
- Return Value:
- 0 on success.
- Non-zero on failure.
- --*/
- {
- PSTR AfterScan;
- INT Day;
- PSTR Field;
- PTZC_LEAP Leap;
- INT LeapYear;
- TIME_ZONE_MONTH Month;
- INT Result;
- LONG ScannedValue;
- LONG Time;
- INT Year;
- Leap = NULL;
- Result = ENOMEM;
- if (FieldCount != LeapFieldCount) {
- fprintf(stderr,
- "Error: Link should have had %d fields, had %d.\n",
- LinkFieldCount,
- FieldCount);
- return EILSEQ;
- }
- assert(strcasecmp(Fields[LeapFieldMagic], "Leap") == 0);
- Leap = malloc(sizeof(TZC_LEAP));
- if (Leap == NULL) {
- goto ProcessTimeZoneLeapEnd;
- }
- memset(Leap, 0, sizeof(TZC_LEAP));
- //
- // Process the year.
- //
- Field = Fields[LeapFieldYear];
- ScannedValue = strtol(Field, &AfterScan, 10);
- if ((ScannedValue <= MIN_TIME_ZONE_YEAR) ||
- (ScannedValue >= MAX_TIME_ZONE_YEAR) ||
- (AfterScan == Field)) {
- fprintf(stderr,
- "Error: Failed to parse Leap YEAR %s.\n",
- Field);
- Result = EILSEQ;
- goto ProcessTimeZoneLeapEnd;
- }
- Year = ScannedValue;
- Leap->Date = (LONGLONG)ComputeDaysForYear(Year) * SECONDS_PER_DAY;
- //
- // Process the month.
- //
- LeapYear = 0;
- if (IS_LEAP_YEAR(Year)) {
- LeapYear = 1;
- }
- Field = Fields[LeapFieldMonth];
- Result = ParseTimeZoneMonth(Field, &Month);
- if (Result != 0) {
- fprintf(stderr,
- "Error: Failed to parse Leap MONTH %s.\n",
- Field);
- goto ProcessTimeZoneLeapEnd;
- }
- Leap->Date += TimeZoneMonthDays[LeapYear][Month] * SECONDS_PER_DAY;
- //
- // Process the month day.
- //
- Field = Fields[LeapFieldDay];
- ScannedValue = strtol(Field, &AfterScan, 10);
- if ((ScannedValue <= 0) || (ScannedValue > 31) ||
- (AfterScan == Field)) {
- fprintf(stderr,
- "Error: Failed to parse Leap DAY %s.\n",
- Field);
- Result = EILSEQ;
- goto ProcessTimeZoneLeapEnd;
- }
- Day = ScannedValue;
- Leap->Date += (Day - 1) * SECONDS_PER_DAY;
- //
- // Process the time.
- //
- Field = Fields[LeapFieldTime];
- Result = ParseTimeZoneTime(Field, &Time, NULL);
- if (Result != 0) {
- fprintf(stderr,
- "Error: Failed to parse Leap TIME %s.\n",
- Field);
- goto ProcessTimeZoneLeapEnd;
- }
- Leap->Date += Time;
- //
- // Process the correction, which should be + or -.
- //
- Field = Fields[LeapFieldCorrection];
- if (strcmp(Field, "+") == 0) {
- Leap->Positive = TRUE;
- } else if (strcmp(Field, "-") == 0) {
- Leap->Positive = FALSE;
- } else {
- fprintf(stderr,
- "Error: Failed to parse Leap CORRECTION %s.\n",
- Field);
- Result = EILSEQ;
- goto ProcessTimeZoneLeapEnd;
- }
- //
- // Process the Rolling/Stationary bit, which should be R or S.
- //
- Field = Fields[LeapFieldRollingOrStationary];
- if (strcasecmp(Field, "R") == 0) {
- Leap->LocalTime = TRUE;
- } else if (strcasecmp(Field, "S") == 0) {
- Leap->LocalTime = FALSE;
- } else {
- fprintf(stderr,
- "Error: Failed to parse Leap R/S %s.\n",
- Field);
- Result = EILSEQ;
- goto ProcessTimeZoneLeapEnd;
- }
- INSERT_BEFORE(&(Leap->ListEntry), &TimeZoneLeapList);
- TimeZoneLeapCount += 1;
- Result = 0;
- ProcessTimeZoneLeapEnd:
- if (Result != 0) {
- if (Leap != NULL) {
- DestroyTimeZoneLeap(Leap);
- }
- }
- return Result;
- }
- INT
- ReadTimeZoneFields (
- FILE *File,
- PVOID *LineBuffer,
- PULONG LineBufferSize,
- PSTR **Fields,
- PULONG FieldsSize,
- PULONG FieldCount,
- PBOOL EndOfFile
- )
- /*++
- Routine Description:
- This routine reads a line in from the time zone file.
- Arguments:
- File - Supplies the file stream to read from.
- LineBuffer - Supplies a pointer that on input points to an allocated buffer
- (which can be null). On output, this buffer will be used for the
- field values, and potentially realloced.
- LineBufferSize - Supplies a pointer that on input contains the size of the
- line buffer in bytes. On output this value will be updated to reflect
- the new buffer allocation size.
- Fields - Supplies a pointer that on input contains a pointer to an array
- of pointers to strings. On output, the pointers to the various fields
- of the line will be returned here. The entire buffer may be realloced
- for large field counts.
- FieldsSize - Supplies a pointer that on input contains the size of the
- fields buffer in bytes. This value will be updated on output.
- FieldCount - Supplies a pointer where the number of fields in this line
- will be returned.
- EndOfFile - Supplies a pointer where a boolean will be returned indicating
- if the end of the input file was reached.
- Return Value:
- 0 on success.
- Non-zero on failure.
- --*/
- {
- ULONG ElementCount;
- ULONG FieldsCapacity;
- BOOL InQuote;
- PSTR Line;
- PSTR *LineFields;
- PVOID NewBuffer;
- ULONG NewCapacity;
- INT Result;
- ElementCount = 0;
- FieldsCapacity = *FieldsSize;
- LineFields = *Fields;
- Result = ReadTimeZoneLine(File, LineBuffer, LineBufferSize, EndOfFile);
- if (Result != 0) {
- goto ReadTimeZoneFieldsEnd;
- }
- //
- // Loop delimiting fields.
- //
- Line = *LineBuffer;
- while (*Line != '\0') {
- //
- // Swoop past leading spaces.
- //
- while (isspace(*Line)) {
- Line += 1;
- }
- //
- // If the next character is a terminator or # (comment), then this line
- // is toast.
- //
- if ((*Line == '\0') || (*Line == '#')) {
- break;
- }
- //
- // Reallocate the fields buffer if needed.
- //
- if ((ElementCount * sizeof(PSTR)) >= FieldsCapacity) {
- NewCapacity = FieldsCapacity;
- if (NewCapacity == 0) {
- NewCapacity = INITIAL_MALLOC_SIZE;
- } else {
- NewCapacity *= 2;
- }
- assert(NewCapacity > (ElementCount * sizeof(PSTR)));
- NewBuffer = realloc(LineFields, NewCapacity);
- if (NewBuffer == NULL) {
- Result = ENOMEM;
- goto ReadTimeZoneFieldsEnd;
- }
- FieldsCapacity = NewCapacity;
- LineFields = NewBuffer;
- }
- //
- // Set the entry in the fields array.
- //
- LineFields[ElementCount] = Line;
- ElementCount += 1;
- //
- // Find the end of the field.
- //
- InQuote = FALSE;
- while ((*Line != '\0') && ((!isspace(*Line)) || (InQuote != FALSE))) {
- if (InQuote != FALSE) {
- if (*Line == '"') {
- InQuote = FALSE;
- }
- } else {
- if (*Line == '"') {
- InQuote = TRUE;
- }
- }
- Line += 1;
- }
- //
- // Terminate the field.
- //
- if (*Line == '\0') {
- break;
- }
- *Line = '\0';
- Line += 1;
- }
- Result = 0;
- ReadTimeZoneFieldsEnd:
- *FieldCount = ElementCount;
- *FieldsSize = FieldsCapacity;
- *Fields = LineFields;
- return Result;
- }
- INT
- ReadTimeZoneLine (
- FILE *File,
- PVOID *LineBuffer,
- PULONG LineBufferSize,
- PBOOL EndOfFile
- )
- /*++
- Routine Description:
- This routine reads a line in from the time zone file.
- Arguments:
- File - Supplies the file stream to read from.
- LineBuffer - Supplies a pointer that on input points to an allocated buffer
- (which can be null). On output, this buffer will contain the null
- terminated line.
- LineBufferSize - Supplies a pointer that on input contains the size of the
- line buffer in bytes. On output this value will be updated to reflect
- the new buffer allocation size.
- EndOfFile - Supplies a pointer where a boolean will be returned indicating
- if the end of file was hit.
- Return Value:
- 0 on success.
- Non-zero on failure.
- --*/
- {
- INT Character;
- BOOL EndOfLine;
- ULONG Length;
- PSTR Line;
- ULONG LineCapacity;
- PVOID NewBuffer;
- ULONG NewLineCapacity;
- INT Result;
- *EndOfFile = FALSE;
- EndOfLine = FALSE;
- Line = *LineBuffer;
- LineCapacity = *LineBufferSize;
- Length = 0;
- while (TRUE) {
- //
- // Read a character from the file.
- //
- Character = fgetc(File);
- if (Character == EOF) {
- if (feof(File) != 0) {
- *EndOfFile = TRUE;
- EndOfLine = TRUE;
- Character = '\0';
- } else {
- fprintf(stderr, "Error reading file: %s.\n", strerror(errno));
- Result = errno;
- goto ReadTimeZoneLineEnd;
- }
- }
- //
- // Reallocate the buffer if it's too small to hold this character.
- //
- if (Length >= LineCapacity) {
- NewLineCapacity = LineCapacity;
- if (NewLineCapacity == 0) {
- NewLineCapacity = INITIAL_MALLOC_SIZE;
- } else {
- NewLineCapacity *= 2;
- }
- assert(NewLineCapacity > Length);
- NewBuffer = realloc(Line, NewLineCapacity);
- if (NewBuffer == NULL) {
- Result = ENOMEM;
- goto ReadTimeZoneLineEnd;
- }
- LineCapacity = NewLineCapacity;
- Line = NewBuffer;
- }
- //
- // Terminate if this is a newline.
- //
- if (Character == '\n') {
- Character = '\0';
- EndOfLine = TRUE;
- }
- //
- // Add the line to the buffer.
- //
- Line[Length] = Character;
- Length += 1;
- if (EndOfLine != FALSE) {
- break;
- }
- }
- Result = 0;
- ReadTimeZoneLineEnd:
- *LineBuffer = Line;
- *LineBufferSize = LineCapacity;
- return Result;
- }
- INT
- TranslateLinksToZones (
- )
- /*++
- Routine Description:
- This routine converts time zone links into time zone structures.
- Arguments:
- None.
- Return Value:
- 0 on success.
- ENOMEM on allocation failure.
- --*/
- {
- PLIST_ENTRY CurrentEntry;
- PLIST_ENTRY CurrentZoneEntry;
- PTZC_ZONE DestinationZone;
- PTZC_LINK Link;
- INT Result;
- PTZC_ZONE Zone;
- PSTR ZoneName;
- CurrentEntry = TimeZoneLinkList.Next;
- while (CurrentEntry != &TimeZoneLinkList) {
- Link = LIST_VALUE(CurrentEntry, TZC_LINK, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- //
- // Loop through all the zones looking for the destination.
- //
- DestinationZone = NULL;
- CurrentZoneEntry = TimeZoneList.Next;
- while (CurrentZoneEntry != &TimeZoneList) {
- DestinationZone = LIST_VALUE(CurrentZoneEntry, TZC_ZONE, ListEntry);
- CurrentZoneEntry = CurrentZoneEntry->Next;
- ZoneName = TimeZoneGetString(&TimeZoneStringList,
- DestinationZone->NameOffset);
- if (strcmp(ZoneName, Link->From) == 0) {
- break;
- }
- DestinationZone = NULL;
- }
- if (DestinationZone == NULL) {
- fprintf(stderr,
- "tzcomp: Warning: Link destination time zone %s not "
- "found. Source (%s).\n",
- Link->From,
- Link->To);
- continue;
- }
- //
- // Create a time zone structure and initialize it based on the
- // destination.
- //
- Zone = malloc(sizeof(TZC_ZONE));
- if (Zone == NULL) {
- return ENOMEM;
- }
- memset(Zone, 0, sizeof(TZC_ZONE));
- Result = TimeZoneAddString(Link->To, &(Zone->NameOffset));
- if (Result != 0) {
- free(Zone);
- return Result;
- }
- Zone->ZoneEntryIndex = DestinationZone->ZoneEntryIndex;
- Zone->ZoneEntryCount = DestinationZone->ZoneEntryCount;
- INSERT_BEFORE(&(Zone->ListEntry), &TimeZoneList);
- TimeZoneCount += 1;
- }
- return 0;
- }
- INT
- TimeZoneFilter (
- PSTR Name,
- INT Year
- )
- /*++
- Routine Description:
- This routine removes all time zone data from the global list except for
- that that matches the given time zone name and/or starts after the given
- year.
- Arguments:
- Name - Supplies an optional pointer to a string containing the name of the
- time zone to keep.
- Year - Supplies an optional year before which to exclude any time zone
- entries.
- Return Value:
- 0 on success.
- Returns an error number on failure.
- --*/
- {
- PLIST_ENTRY CurrentEntry;
- PLIST_ENTRY CurrentZoneEntry;
- ULONG CurrentZoneEntryCount;
- ULONG EntryIndex;
- PSTR FormatString;
- PSTR LettersString;
- LIST_ENTRY NewEntryList;
- LIST_ENTRY NewRuleList;
- LIST_ENTRY NewStringList;
- ULONG NewStringListSize;
- PTZC_ZONE_ENTRY NewZoneEntry;
- LIST_ENTRY NewZoneList;
- PTZC_ZONE RemoveZone;
- INT Result;
- PTZC_RULE Rule;
- ULONG RuleCount;
- PLIST_ENTRY RuleEntry;
- LONGLONG Until;
- ULONG UnusedOffset;
- PTZC_ZONE Zone;
- PTZC_ZONE_ENTRY ZoneEntry;
- ULONG ZoneEntryCount;
- PSTR ZoneName;
- ULONG ZonesAdded;
- INITIALIZE_LIST_HEAD(&NewEntryList);
- INITIALIZE_LIST_HEAD(&NewRuleList);
- INITIALIZE_LIST_HEAD(&NewStringList);
- INITIALIZE_LIST_HEAD(&NewZoneList);
- NewStringListSize = 0;
- RuleCount = 0;
- Zone = NULL;
- ZoneEntryCount = 0;
- ZoneName = NULL;
- Until = (LONGLONG)ComputeDaysForYear(Year) * SECONDS_PER_DAY;
- TimeZoneAddStringToList("",
- &NewStringList,
- &NewStringListSize,
- TRUE,
- &UnusedOffset);
- //
- // Loop looking for time zones that match the name and the year.
- //
- ZonesAdded = 0;
- CurrentZoneEntry = TimeZoneList.Next;
- while (CurrentZoneEntry != &TimeZoneList) {
- Zone = LIST_VALUE(CurrentZoneEntry, TZC_ZONE, ListEntry);
- CurrentZoneEntry = CurrentZoneEntry->Next;
- ZoneName = TimeZoneGetString(&TimeZoneStringList, Zone->NameOffset);
- if (Name != NULL) {
- if (strcasecmp(Name, ZoneName) != 0) {
- continue;
- }
- }
- //
- // Find the starting zone entry.
- //
- CurrentEntry = TimeZoneEntryList.Next;
- for (EntryIndex = 0;
- EntryIndex < Zone->ZoneEntryIndex;
- EntryIndex += 1) {
- assert(CurrentEntry != &TimeZoneEntryList);
- CurrentEntry = CurrentEntry->Next;
- }
- //
- // Pull the zone entries onto the new list.
- //
- CurrentZoneEntryCount = 0;
- Zone->ZoneEntryIndex = ZoneEntryCount;
- for (EntryIndex = 0;
- EntryIndex < Zone->ZoneEntryCount;
- EntryIndex += 1) {
- ZoneEntry = LIST_VALUE(CurrentEntry, TZC_ZONE_ENTRY, ListEntry);
- assert(CurrentEntry != &TimeZoneEntryList);
- CurrentEntry = CurrentEntry->Next;
- //
- // Skip an entry that is too old.
- //
- if (ZoneEntry->Until <= Until) {
- continue;
- }
- NewZoneEntry = malloc(sizeof(TZC_ZONE_ENTRY));
- if (NewZoneEntry == NULL) {
- Result = ENOMEM;
- goto TimeZoneFilterByNameEnd;
- }
- memcpy(NewZoneEntry, ZoneEntry, sizeof(TZC_ZONE_ENTRY));
- INSERT_BEFORE(&(NewZoneEntry->ListEntry), &NewEntryList);
- NewZoneEntry->Index = ZoneEntryCount;
- ZoneEntryCount += 1;
- CurrentZoneEntryCount += 1;
- FormatString = TimeZoneGetString(&TimeZoneStringList,
- ZoneEntry->FormatOffset);
- Result = TimeZoneAddStringToList(FormatString,
- &NewStringList,
- &NewStringListSize,
- TRUE,
- &(NewZoneEntry->FormatOffset));
- if (Result != 0) {
- goto TimeZoneFilterByNameEnd;
- }
- //
- // Loop through and pull off any rules that apply to this zone
- // entry.
- //
- if (NewZoneEntry->RulesNameIndex != -1) {
- RuleEntry = TimeZoneRuleList.Next;
- while (RuleEntry != &TimeZoneRuleList) {
- Rule = LIST_VALUE(RuleEntry, TZC_RULE, ListEntry);
- RuleEntry = RuleEntry->Next;
- if (Rule->NameIndex == NewZoneEntry->RulesNameIndex) {
- if (Rule->To <= Year) {
- continue;
- }
- LettersString = TimeZoneGetString(&TimeZoneStringList,
- Rule->LettersOffset);
- Result = TimeZoneAddStringToList(
- LettersString,
- &NewStringList,
- &NewStringListSize,
- TRUE,
- &(Rule->LettersOffset));
- if (Result != 0) {
- goto TimeZoneFilterByNameEnd;
- }
- LIST_REMOVE(&(Rule->ListEntry));
- INSERT_BEFORE(&(Rule->ListEntry), &NewRuleList);
- RuleCount += 1;
- }
- }
- }
- }
- if (CurrentZoneEntryCount != 0) {
- Zone->ZoneEntryCount = CurrentZoneEntryCount;
- LIST_REMOVE(&(Zone->ListEntry));
- INSERT_BEFORE(&(Zone->ListEntry), &NewZoneList);
- ZonesAdded += 1;
- //
- // Add the zone name to the string table.
- //
- Result = TimeZoneAddStringToList(ZoneName,
- &NewStringList,
- &NewStringListSize,
- TRUE,
- &(Zone->NameOffset));
- if (Result != 0) {
- goto TimeZoneFilterByNameEnd;
- }
- TimeZoneCompressEntries(&NewEntryList,
- &ZoneEntryCount,
- Zone);
- }
- }
- //
- // Fail if the time zone was not found.
- //
- if (ZonesAdded == 0) {
- Result = EINVAL;
- if (Name != NULL) {
- fprintf(stderr,
- "Error: Could not find time zone \"%s\" after year %d.\n",
- Name,
- Year);
- } else {
- fprintf(stderr,
- "Error: No time zones after year %d.\n",
- Year);
- }
- goto TimeZoneFilterByNameEnd;
- }
- //
- // Destroy all the other zones, rules, and zone entries.
- //
- while (LIST_EMPTY(&TimeZoneRuleList) == FALSE) {
- Rule = LIST_VALUE(TimeZoneRuleList.Next, TZC_RULE, ListEntry);
- LIST_REMOVE(&(Rule->ListEntry));
- DestroyTimeZoneRule(Rule);
- }
- while (LIST_EMPTY(&TimeZoneList) == FALSE) {
- RemoveZone = LIST_VALUE(TimeZoneList.Next, TZC_ZONE, ListEntry);
- LIST_REMOVE(&(RemoveZone->ListEntry));
- DestroyTimeZone(RemoveZone);
- }
- while (LIST_EMPTY(&TimeZoneEntryList) == FALSE) {
- ZoneEntry = LIST_VALUE(TimeZoneEntryList.Next,
- TZC_ZONE_ENTRY,
- ListEntry);
- LIST_REMOVE(&(ZoneEntry->ListEntry));
- DestroyTimeZoneEntry(ZoneEntry);
- }
- DestroyTimeZoneStringList(&TimeZoneStringList);
- //
- // Now insert all entries from the local lists onto the global lists.
- //
- if (!LIST_EMPTY(&NewRuleList)) {
- MOVE_LIST(&NewRuleList, &TimeZoneRuleList);
- INITIALIZE_LIST_HEAD(&NewRuleList);
- }
- TimeZoneRuleCount = RuleCount;
- if (!LIST_EMPTY(&NewEntryList)) {
- MOVE_LIST(&NewEntryList, &TimeZoneEntryList);
- INITIALIZE_LIST_HEAD(&NewEntryList);
- }
- TimeZoneNextZoneEntryIndex = ZoneEntryCount;
- if (!LIST_EMPTY(&NewStringList)) {
- MOVE_LIST(&NewStringList, &TimeZoneStringList);
- INITIALIZE_LIST_HEAD(&NewStringList);
- }
- TimeZoneNextStringOffset = NewStringListSize;
- if (!LIST_EMPTY(&NewZoneList)) {
- MOVE_LIST(&NewZoneList, &TimeZoneList);
- INITIALIZE_LIST_HEAD(&NewZoneList);
- }
- TimeZoneCount = ZonesAdded;
- Result = 0;
- TimeZoneFilterByNameEnd:
- while (LIST_EMPTY(&NewRuleList) == FALSE) {
- Rule = LIST_VALUE(NewRuleList.Next, TZC_RULE, ListEntry);
- LIST_REMOVE(&(Rule->ListEntry));
- DestroyTimeZoneRule(Rule);
- }
- while (LIST_EMPTY(&NewEntryList) == FALSE) {
- ZoneEntry = LIST_VALUE(NewEntryList.Next,
- TZC_ZONE_ENTRY,
- ListEntry);
- LIST_REMOVE(&(ZoneEntry->ListEntry));
- DestroyTimeZoneEntry(ZoneEntry);
- }
- while (LIST_EMPTY(&NewZoneList) == FALSE) {
- Zone = LIST_VALUE(NewZoneList.Next, TZC_ZONE, ListEntry);
- LIST_REMOVE(&(Zone->ListEntry));
- DestroyTimeZone(Zone);
- }
- DestroyTimeZoneStringList(&NewStringList);
- return Result;
- }
- INT
- WriteTimeZoneData (
- PSTR FileName
- )
- /*++
- Routine Description:
- This routine writes the time zone data out to a file in binary format.
- Arguments:
- FileName - Supplies a pointer to a string containing the name of the file
- to write.
- Return Value:
- 0 on success.
- Returns an error number on failure.
- --*/
- {
- size_t BytesWritten;
- PLIST_ENTRY CurrentEntry;
- ULONG ElementsWritten;
- FILE *File;
- TIME_ZONE_LEAP_SECOND FileLeap;
- TIME_ZONE_RULE FileRule;
- TIME_ZONE FileZone;
- TIME_ZONE_ENTRY FileZoneEntry;
- TIME_ZONE_HEADER Header;
- PTZC_LEAP Leap;
- INT Result;
- PTZC_RULE Rule;
- PTZC_STRING String;
- size_t StringLength;
- PTZC_ZONE Zone;
- PTZC_ZONE_ENTRY ZoneEntry;
- memset(&Header, 0, sizeof(TIME_ZONE_HEADER));
- File = fopen(FileName, "wb");
- if (File == NULL) {
- Result = errno;
- fprintf(stderr,
- "tzcomp: Failed to open output file \"%s\": %s.\n",
- FileName,
- strerror(Result));
- goto WriteTimeZoneDataEnd;
- }
- //
- // Write out the header.
- //
- Header.Magic = TIME_ZONE_HEADER_MAGIC;
- Header.RuleOffset = sizeof(TIME_ZONE_HEADER);
- Header.RuleCount = TimeZoneRuleCount;
- Header.ZoneOffset = Header.RuleOffset +
- (Header.RuleCount * sizeof(TIME_ZONE_RULE));
- Header.ZoneCount = TimeZoneCount;
- Header.ZoneEntryOffset = Header.ZoneOffset +
- (Header.ZoneCount * sizeof(TIME_ZONE));
- Header.ZoneEntryCount = TimeZoneNextZoneEntryIndex;
- Header.LeapOffset = Header.ZoneEntryOffset +
- (Header.ZoneEntryCount * sizeof(TIME_ZONE_ENTRY));
- Header.LeapCount = TimeZoneLeapCount;
- Header.StringsOffset = Header.LeapOffset +
- (Header.LeapCount * sizeof(TIME_ZONE_LEAP_SECOND));
- Header.StringsSize = TimeZoneNextStringOffset;
- BytesWritten = fwrite(&Header, 1, sizeof(TIME_ZONE_HEADER), File);
- if (BytesWritten <= 0) {
- Result = errno;
- fprintf(stderr, "tzcomp: Write error: %s.\n", strerror(Result));
- goto WriteTimeZoneDataEnd;
- }
- assert(ftell(File) == Header.RuleOffset);
- //
- // Write out the rules.
- //
- memset(&FileRule, 0, sizeof(TIME_ZONE_RULE));
- ElementsWritten = 0;
- CurrentEntry = TimeZoneRuleList.Next;
- while (CurrentEntry != &TimeZoneRuleList) {
- Rule = LIST_VALUE(CurrentEntry, TZC_RULE, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- FileRule.Number = Rule->NameIndex;
- FileRule.From = Rule->From;
- FileRule.To = Rule->To;
- FileRule.Month = Rule->Month;
- FileRule.On = Rule->On;
- FileRule.At = Rule->At;
- FileRule.AtLens = Rule->AtLens;
- FileRule.Save = Rule->Save;
- FileRule.Letters = Rule->LettersOffset;
- BytesWritten = fwrite(&FileRule, 1, sizeof(TIME_ZONE_RULE), File);
- if (BytesWritten <= 0) {
- Result = errno;
- fprintf(stderr, "tzcomp: Write error: %s.\n", strerror(Result));
- goto WriteTimeZoneDataEnd;
- }
- ElementsWritten += 1;
- }
- assert(ElementsWritten == Header.RuleCount);
- assert(ftell(File) == Header.ZoneOffset);
- //
- // Write out the zones.
- //
- memset(&FileZone, 0, sizeof(TIME_ZONE));
- ElementsWritten = 0;
- CurrentEntry = TimeZoneList.Next;
- while (CurrentEntry != &TimeZoneList) {
- Zone = LIST_VALUE(CurrentEntry, TZC_ZONE, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- FileZone.Name = Zone->NameOffset;
- FileZone.EntryIndex = Zone->ZoneEntryIndex;
- FileZone.EntryCount = Zone->ZoneEntryCount;
- BytesWritten = fwrite(&FileZone, 1, sizeof(TIME_ZONE), File);
- if (BytesWritten <= 0) {
- Result = errno;
- fprintf(stderr, "tzcomp: Write error: %s.\n", strerror(Result));
- goto WriteTimeZoneDataEnd;
- }
- ElementsWritten += 1;
- }
- assert(ElementsWritten == Header.ZoneCount);
- assert(ftell(File) == Header.ZoneEntryOffset);
- //
- // Write out the zone entries.
- //
- memset(&FileZoneEntry, 0, sizeof(TIME_ZONE_ENTRY));
- ElementsWritten = 0;
- CurrentEntry = TimeZoneEntryList.Next;
- while (CurrentEntry != &TimeZoneEntryList) {
- ZoneEntry = LIST_VALUE(CurrentEntry, TZC_ZONE_ENTRY, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- FileZoneEntry.GmtOffset = ZoneEntry->GmtOffset;
- FileZoneEntry.Rules = ZoneEntry->RulesNameIndex;
- FileZoneEntry.Save = ZoneEntry->Save;
- FileZoneEntry.Format = ZoneEntry->FormatOffset;
- FileZoneEntry.Until = ZoneEntry->Until;
- BytesWritten = fwrite(&FileZoneEntry, 1, sizeof(TIME_ZONE_ENTRY), File);
- if (BytesWritten <= 0) {
- Result = errno;
- fprintf(stderr, "tzcomp: Write error: %s.\n", strerror(Result));
- goto WriteTimeZoneDataEnd;
- }
- ElementsWritten += 1;
- }
- assert(ElementsWritten == Header.ZoneEntryCount);
- assert(ftell(File) == Header.LeapOffset);
- //
- // Write out the leap seconds.
- //
- memset(&FileLeap, 0, sizeof(TIME_ZONE_LEAP_SECOND));
- ElementsWritten = 0;
- CurrentEntry = TimeZoneLeapList.Next;
- while (CurrentEntry != &TimeZoneLeapList) {
- Leap = LIST_VALUE(CurrentEntry, TZC_LEAP, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- FileLeap.Date = Leap->Date;
- FileLeap.Positive = Leap->Positive;
- FileLeap.LocalTime = Leap->LocalTime;
- BytesWritten = fwrite(&FileLeap,
- 1,
- sizeof(TIME_ZONE_LEAP_SECOND),
- File);
- if (BytesWritten <= 0) {
- Result = errno;
- fprintf(stderr, "tzcomp: Write error: %s.\n", strerror(Result));
- goto WriteTimeZoneDataEnd;
- }
- ElementsWritten += 1;
- }
- assert(ElementsWritten == Header.LeapCount);
- assert(ftell(File) == Header.StringsOffset);
- //
- // Write out the string table.
- //
- ElementsWritten = 0;
- CurrentEntry = TimeZoneStringList.Next;
- while (CurrentEntry != &TimeZoneStringList) {
- String = LIST_VALUE(CurrentEntry, TZC_STRING, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- StringLength = strlen(String->String) + 1;
- BytesWritten = fwrite(String->String, 1, StringLength, File);
- if (BytesWritten <= 0) {
- Result = errno;
- fprintf(stderr, "tzcomp: Write error: %s.\n", strerror(Result));
- goto WriteTimeZoneDataEnd;
- }
- ElementsWritten += StringLength;
- }
- assert(ElementsWritten == Header.StringsSize);
- Result = 0;
- WriteTimeZoneDataEnd:
- if (File != NULL) {
- fclose(File);
- }
- return Result;
- }
- VOID
- DestroyTimeZoneRule (
- PTZC_RULE Rule
- )
- /*++
- Routine Description:
- This routine frees all memory associated with a time zone rule. This
- routine assumes the rule has already been pulled off of any list it was on.
- Arguments:
- Rule - Supplies a pointer to the rule to destroy.
- Return Value:
- None.
- --*/
- {
- free(Rule);
- return;
- }
- VOID
- DestroyTimeZone (
- PTZC_ZONE Zone
- )
- /*++
- Routine Description:
- This routine destroys a time zone (the structure, of course). This routine
- assumes the zone has already been pulled off of any list it was on.
- Arguments:
- Zone - Supplies a pointer to the zone to destroy.
- Return Value:
- None.
- --*/
- {
- free(Zone);
- return;
- }
- VOID
- DestroyTimeZoneEntry (
- PTZC_ZONE_ENTRY ZoneEntry
- )
- /*++
- Routine Description:
- This routine destroys a time zone entry. This routine assumes the entry has
- already been pulled off of any list it was on.
- Arguments:
- ZoneEntry - Supplies a pointer to the zone entry to destroy.
- Return Value:
- None.
- --*/
- {
- free(ZoneEntry);
- return;
- }
- VOID
- DestroyTimeZoneLink (
- PTZC_LINK Link
- )
- /*++
- Routine Description:
- This routine destroys a time zone link. This routine assumes the structure
- has already been pulled off of any list it was on.
- Arguments:
- Link - Supplies a pointer to the zone link to destroy.
- Return Value:
- None.
- --*/
- {
- if (Link->From != NULL) {
- free(Link->From);
- }
- if (Link->To != NULL) {
- free(Link->To);
- }
- free(Link);
- return;
- }
- VOID
- DestroyTimeZoneLeap (
- PTZC_LEAP Leap
- )
- /*++
- Routine Description:
- This routine destroys a time zone leap second structure. This routine
- assumes the structure has already been pulled off of any list it was on.
- Arguments:
- Leap - Supplies a pointer to the leap second to destroy.
- Return Value:
- None.
- --*/
- {
- free(Leap);
- return;
- }
- VOID
- DestroyTimeZoneStringList (
- PLIST_ENTRY ListHead
- )
- /*++
- Routine Description:
- This routine destroys a time zone string list.
- Arguments:
- ListHead - Supplies a pointer to the head of the list.
- Return Value:
- None.
- --*/
- {
- PTZC_STRING StringEntry;
- //
- // First search for the string.
- //
- while (LIST_EMPTY(ListHead) == FALSE) {
- StringEntry = LIST_VALUE(ListHead->Next, TZC_STRING, ListEntry);
- LIST_REMOVE(&(StringEntry->ListEntry));
- free(StringEntry->String);
- free(StringEntry);
- }
- return;
- }
- INT
- ParseTimeZoneRuleLimit (
- PSTR Field,
- SHORT OnlyValue,
- PSHORT Value
- )
- /*++
- Routine Description:
- This routine parses a rule limit (the FROM and TO fields of the rule).
- Valid values are a year, Minimum, Maximum, and Only (with abbreviations).
- Arguments:
- Field - Supplies a pointer to the field.
- OnlyValue - Supplies the value to return if the field has "only" in it.
- Value - Supplies a pointer where the value will be returned on success.
- Return Value:
- 0 on success.
- Non-zero on failure.
- --*/
- {
- PSTR AfterScan;
- INT Result;
- LONG ScannedValue;
- Result = 0;
- if ((strcasecmp(Field, "Minimum") == 0) ||
- (strcasecmp(Field, "Min") == 0)) {
- *Value = MIN_TIME_ZONE_YEAR;
- } else if ((strcasecmp(Field, "Maximum") == 0) ||
- (strcasecmp(Field, "Max") == 0)) {
- *Value = MAX_TIME_ZONE_YEAR;
- } else if (strcasecmp(Field, "Only") == 0) {
- *Value = OnlyValue;
- } else {
- ScannedValue = strtol(Field, &AfterScan, 10);
- if ((ScannedValue < MIN_TIME_ZONE_YEAR) ||
- (ScannedValue > MAX_TIME_ZONE_YEAR) ||
- (AfterScan == Field)) {
- fprintf(stderr, "Error: Cannot parse rule limit %s.\n", Field);
- Result = EILSEQ;
- }
- *Value = ScannedValue;
- }
- return Result;
- }
- INT
- ParseTimeZoneMonth (
- PSTR Field,
- PTIME_ZONE_MONTH Value
- )
- /*++
- Routine Description:
- This routine parses a month (such as in the Rule IN column).
- Arguments:
- Field - Supplies a pointer to the field.
- Value - Supplies a pointer where the value will be returned on success.
- Return Value:
- 0 on success.
- Non-zero on failure.
- --*/
- {
- TIME_ZONE_MONTH Month;
- for (Month = TimeZoneMonthJanuary;
- Month <= TimeZoneMonthDecember;
- Month += 1) {
- if ((strcasecmp(Field, TimeZoneMonthStrings[Month]) == 0) ||
- (strcasecmp(Field, TimeZoneAbbreviatedMonthStrings[Month]) == 0)) {
- *Value = Month;
- return 0;
- }
- }
- *Value = TimeZoneMonthCount;
- return EILSEQ;
- }
- INT
- ParseTimeZoneRuleWeekday (
- PSTR Field,
- PTIME_ZONE_WEEKDAY Value
- )
- /*++
- Routine Description:
- This routine parses a rule weekday (buried in the ON column).
- Arguments:
- Field - Supplies a pointer to the field.
- Value - Supplies a pointer where the value will be returned on success.
- Return Value:
- 0 on success.
- Non-zero on failure.
- --*/
- {
- TIME_ZONE_WEEKDAY Weekday;
- for (Weekday = TimeZoneWeekdaySunday;
- Weekday <= TimeZoneWeekdaySaturday;
- Weekday += 1) {
- if ((strcasecmp(Field, TimeZoneWeekdayStrings[Weekday]) == 0) ||
- (strcasecmp(Field, TimeZoneAbbreviatedWeekdayStrings[Weekday]) ==
- 0)) {
- *Value = Weekday;
- return 0;
- }
- }
- *Value = TimeZoneWeekdayCount;
- return EILSEQ;
- }
- INT
- ParseTimeZoneOccasion (
- PSTR Field,
- PTIME_ZONE_OCCASION Occasion
- )
- /*++
- Routine Description:
- This routine parses an occasion (such as in the Rule ON column).
- Arguments:
- Field - Supplies a pointer to the field.
- Occasion - Supplies a pointer where the result will be returned on success.
- Return Value:
- 0 on success.
- Non-zero on failure.
- --*/
- {
- PSTR AfterScan;
- CHAR Comparator;
- PSTR Equals;
- CHAR LastString[5];
- INT Result;
- LONG ScanResult;
- TIME_ZONE_WEEKDAY Weekday;
- PSTR WeekdayString;
- Result = EILSEQ;
- memset(Occasion, 0, sizeof(TIME_ZONE_OCCASION));
- memcpy(LastString, Field, sizeof(LastString) - 1);
- LastString[sizeof(LastString) - 1] = '\0';
- //
- // If the field starts with a digit, it's just a straight up month date.
- //
- if ((*Field >= '0') && (*Field <= '9')) {
- ScanResult = strtol(Field, &AfterScan, 10);
- if ((AfterScan == Field) || (ScanResult < 0) || (ScanResult > 31)) {
- fprintf(stderr,
- "Error: Unable to scan occasion month date %s.\n",
- Field);
- return Result;
- }
- Occasion->Type = TimeZoneOccasionMonthDate;
- Occasion->MonthDay = (CHAR)ScanResult;
- //
- // If the field starts with "last", then it's the last weekday in a given
- // month.
- //
- } else if (strcasecmp(LastString, "Last") == 0) {
- WeekdayString = Field + 4;
- if (*WeekdayString == '-') {
- WeekdayString += 1;
- }
- Result = ParseTimeZoneRuleWeekday(WeekdayString, &Weekday);
- if (Result != 0) {
- return Result;
- }
- Occasion->Type = TimeZoneOccasionLastWeekday;
- Occasion->Weekday = Weekday;
- //
- // If the field has a = in it, then it's the last weekday >= a month date or
- // the first weekday <= a month date.
- //
- } else if (strchr(Field, '=') != NULL) {
- Equals = strchr(Field, '=');
- if (Equals == Field) {
- fprintf(stderr, "Error: Unable to scan occasion %s.\n", Field);
- return Result;
- }
- Comparator = *(Equals - 1);
- if (Comparator == '>') {
- Occasion->Type = TimeZoneOccasionGreaterOrEqualWeekday;
- } else if (Comparator == '<') {
- Occasion->Type = TimeZoneOccasionLessOrEqualWeekday;
- } else {
- fprintf(stderr, "Error: Unable to scan occasion %s.\n", Field);
- return Result;
- }
- //
- // Scan the month date.
- //
- ScanResult = strtol(Equals + 1, &AfterScan, 10);
- if ((AfterScan == Field) || (ScanResult < 0) || (ScanResult > 31)) {
- fprintf(stderr,
- "Error: Unable to scan occasion month date %s.\n",
- Field);
- return Result;
- }
- Occasion->MonthDay = (CHAR)ScanResult;
- //
- // Terminate and scan the weekday.
- //
- *(Equals - 1) = '\0';
- Result = ParseTimeZoneRuleWeekday(Field, &Weekday);
- if (Result != 0) {
- return Result;
- }
- Occasion->Weekday = Weekday;
- //
- // This is unrecognized.
- //
- } else {
- fprintf(stderr, "Error: Unable to scan occasion %s.\n", Field);
- return Result;
- }
- Result = 0;
- return Result;
- }
- INT
- ParseTimeZoneTime (
- PSTR Field,
- PLONG Time,
- PTIME_ZONE_LENS Lens
- )
- /*++
- Routine Description:
- This routine parses a rule time (in the Rule ON column).
- Arguments:
- Field - Supplies a pointer to the field.
- Time - Supplies a pointer where the time in seconds will be returned.
- Lens - Supplies an optional pointer where the lens under which to view this
- time.
- Return Value:
- 0 on success.
- Non-zero on failure.
- --*/
- {
- PSTR AfterScan;
- BOOL Negative;
- PSTR OriginalField;
- INT Result;
- LONG ScannedValue;
- TIME_ZONE_LENS TimeLens;
- TimeLens = TimeZoneLensLocalTime;
- *Time = 0;
- OriginalField = Field;
- Result = EILSEQ;
- Negative = FALSE;
- if (*Field == '-') {
- Negative = TRUE;
- Field += 1;
- }
- //
- // Parse some hours.
- //
- ScannedValue = strtol(Field, &AfterScan, 10);
- if ((ScannedValue < 0) || ((AfterScan == Field) && (Negative == FALSE))) {
- goto ParseTimeZoneTimeEnd;
- }
- *Time = ScannedValue * SECONDS_PER_HOUR;
- Field = AfterScan;
- //
- // Parse some optional minutes.
- //
- if (*Field == ':') {
- Field += 1;
- ScannedValue = strtol(Field, &AfterScan, 10);
- if ((ScannedValue < 0) || (AfterScan == Field)) {
- goto ParseTimeZoneTimeEnd;
- }
- *Time += ScannedValue * SECONDS_PER_MINUTE;
- Field = AfterScan;
- //
- // Parse some optonal seconds.
- //
- if (*Field == ':') {
- Field += 1;
- ScannedValue = strtol(Field, &AfterScan, 10);
- if ((ScannedValue < 0) || (AfterScan == Field)) {
- goto ParseTimeZoneTimeEnd;
- }
- *Time += ScannedValue;
- Field = AfterScan;
- }
- }
- //
- // Parse an optional lens with which to understand this time.
- //
- if (*Field == 'w') {
- TimeLens = TimeZoneLensLocalTime;
- } else if (*Field == 's') {
- TimeLens = TimeZoneLensLocalStandardTime;
- } else if ((*Field == 'u') || (*Field == 'g') || (*Field == 'z')) {
- TimeLens = TimeZoneLensUtc;
- } else if (*Field != '\0') {
- goto ParseTimeZoneTimeEnd;
- }
- if (Negative != FALSE) {
- *Time = -*Time;
- }
- Result = 0;
- ParseTimeZoneTimeEnd:
- if (Lens != NULL) {
- *Lens = TimeLens;
- }
- if (Result != 0) {
- fprintf(stderr,
- "Error: Failed to scan time field %s.\n",
- OriginalField);
- }
- return Result;
- }
- VOID
- PrintTimeZoneRule (
- PTZC_RULE Rule
- )
- /*++
- Routine Description:
- This routine prints a time zone rule.
- Arguments:
- Rule - Supplies a pointer to the rule to print.
- Return Value:
- None.
- --*/
- {
- INT Weekday;
- printf("Rule %3d: %-13s %04d-%04d %-9s ",
- Rule->NameIndex,
- TimeZoneGetString(&TimeZoneRuleStringList, Rule->NameIndex),
- Rule->From,
- Rule->To,
- TimeZoneMonthStrings[Rule->Month]);
- Weekday = Rule->On.Weekday;
- switch (Rule->On.Type) {
- case TimeZoneOccasionMonthDate:
- printf("%-7d ", Rule->On.MonthDay);
- break;
- case TimeZoneOccasionLastWeekday:
- printf("Last%s ", TimeZoneAbbreviatedWeekdayStrings[Weekday]);
- break;
- case TimeZoneOccasionGreaterOrEqualWeekday:
- printf("%s>=%-2d ",
- TimeZoneAbbreviatedWeekdayStrings[Weekday],
- Rule->On.MonthDay);
- break;
- case TimeZoneOccasionLessOrEqualWeekday:
- printf("%s<=%-2d ",
- TimeZoneAbbreviatedWeekdayStrings[Weekday],
- Rule->On.MonthDay);
- break;
- default:
- assert(FALSE);
- break;
- }
- PrintTimeZoneTime(Rule->At, Rule->AtLens);
- printf(" ");
- PrintTimeZoneTime(Rule->Save, TimeZoneLensLocalTime);
- printf(" %s\n",
- TimeZoneGetString(&TimeZoneStringList, Rule->LettersOffset));
- return;
- }
- VOID
- PrintTimeZone (
- PTZC_ZONE Zone
- )
- /*++
- Routine Description:
- This routine prints a time zone.
- Arguments:
- Zone - Supplies a pointer to the zone to print.
- Return Value:
- None.
- --*/
- {
- PLIST_ENTRY CurrentEntry;
- ULONG EntryIndex;
- PTZC_ZONE_ENTRY ZoneEntry;
- printf("Zone: %s (Entry index %d, count %d)\n",
- TimeZoneGetString(&TimeZoneStringList, Zone->NameOffset),
- Zone->ZoneEntryIndex,
- Zone->ZoneEntryCount);
- //
- // Skip to the proper index in the list.
- //
- CurrentEntry = TimeZoneEntryList.Next;
- for (EntryIndex = 0; EntryIndex < Zone->ZoneEntryIndex; EntryIndex += 1) {
- assert(CurrentEntry != &TimeZoneEntryList);
- CurrentEntry = CurrentEntry->Next;
- }
- for (EntryIndex = 0; EntryIndex < Zone->ZoneEntryCount; EntryIndex += 1) {
- assert(CurrentEntry != &TimeZoneEntryList);
- ZoneEntry = LIST_VALUE(CurrentEntry, TZC_ZONE_ENTRY, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- printf(" ");
- PrintTimeZoneEntry(ZoneEntry);
- }
- printf("\n");
- return;
- }
- VOID
- PrintTimeZoneEntry (
- PTZC_ZONE_ENTRY ZoneEntry
- )
- /*++
- Routine Description:
- This routine prints a time zone entry.
- Arguments:
- ZoneEntry - Supplies a pointer to the zone entry to print.
- Return Value:
- None.
- --*/
- {
- PSTR RuleName;
- PrintTimeZoneTime(ZoneEntry->GmtOffset, TimeZoneLensLocalTime);
- printf(" ");
- if (ZoneEntry->RulesNameIndex != -1) {
- RuleName = TimeZoneGetString(&TimeZoneRuleStringList,
- ZoneEntry->RulesNameIndex);
- printf("%-12s ", RuleName);
- } else {
- PrintTimeZoneTime(ZoneEntry->Save, TimeZoneLensLocalTime);
- printf(" ");
- }
- printf("%-7s ",
- TimeZoneGetString(&TimeZoneStringList, ZoneEntry->FormatOffset));
- if (ZoneEntry->Until < MAX_TIME_ZONE_DATE) {
- PrintTimeZoneDate(ZoneEntry->Until);
- }
- printf("\n");
- return;
- }
- VOID
- PrintTimeZoneLink (
- PTZC_LINK Link
- )
- /*++
- Routine Description:
- This routine prints a time zone link.
- Arguments:
- Link - Supplies a pointer to the link to print.
- Return Value:
- None.
- --*/
- {
- printf("Link: %s TO %s\n", Link->From, Link->To);
- return;
- }
- VOID
- PrintTimeZoneLeap (
- PTZC_LEAP Leap
- )
- /*++
- Routine Description:
- This routine prints a time zone leap second.
- Arguments:
- Leap - Supplies a pointer to the leap second to print.
- Return Value:
- None.
- --*/
- {
- CHAR Correction;
- CHAR RollingOrStationary;
- printf("Leap: ");
- PrintTimeZoneDate(Leap->Date);
- Correction = '-';
- if (Leap->Positive != FALSE) {
- Correction = '+';
- }
- RollingOrStationary = 'S';
- if (Leap->LocalTime != FALSE) {
- RollingOrStationary = 'R';
- }
- printf(" %c %c\n", Correction, RollingOrStationary);
- return;
- }
- VOID
- PrintTimeZoneTime (
- LONG Time,
- TIME_ZONE_LENS Lens
- )
- /*++
- Routine Description:
- This routine prints a time zone time.
- Arguments:
- Time - Supplies the time to print (in seconds).
- Lens - Supplies a lens to print as well.
- Return Value:
- None.
- --*/
- {
- LONG Hours;
- INT Length;
- CHAR LensCharacter;
- LONG Minutes;
- BOOL Negative;
- LONG Seconds;
- Length = 0;
- Negative = FALSE;
- if (Time < 0) {
- Negative = TRUE;
- Time = -Time;
- }
- Hours = Time / SECONDS_PER_HOUR;
- Time -= Hours * SECONDS_PER_HOUR;
- Minutes = Time / SECONDS_PER_MINUTE;
- Time -= Minutes * SECONDS_PER_MINUTE;
- Seconds = Time;
- if (Negative != FALSE) {
- printf("-");
- Length += 1;
- }
- printf("%d:%02d", Hours, Minutes);
- Length += 4;
- if (Hours >= 10) {
- Length += 1;
- }
- if (Seconds != 0) {
- printf(":%02d", Seconds);
- Length += 3;
- }
- switch (Lens) {
- case TimeZoneLensLocalTime:
- LensCharacter = ' ';
- break;
- case TimeZoneLensLocalStandardTime:
- LensCharacter = 's';
- break;
- case TimeZoneLensUtc:
- LensCharacter = 'u';
- break;
- default:
- assert(FALSE);
- LensCharacter = 'X';
- break;
- }
- printf("%-*c", 10 - Length, LensCharacter);
- return;
- }
- VOID
- PrintTimeZoneDate (
- LONGLONG Date
- )
- /*++
- Routine Description:
- This routine prints a time zone date.
- Arguments:
- Date - Supplies the date in seconds since the epoch.
- Return Value:
- None.
- --*/
- {
- INT Day;
- LONG Days;
- INT Leap;
- INT Month;
- INT Year;
- //
- // Figure out and subtract off the year. Make the remainder positive (so
- // that something like -1 becomes December 31, 2000.
- //
- Days = Date / SECONDS_PER_DAY;
- Date -= (LONGLONG)Days * SECONDS_PER_DAY;
- if (Date < 0) {
- Date += SECONDS_PER_DAY;
- Days -= 1;
- }
- Year = ComputeYearForDays(&Days);
- Leap = 0;
- if (IS_LEAP_YEAR(Year)) {
- Leap = 1;
- }
- //
- // Subtract off the months.
- //
- Month = 0;
- Day = Days;
- while (Day >= TimeZoneDaysPerMonth[Leap][Month]) {
- Day -= TimeZoneDaysPerMonth[Leap][Month];
- Month += 1;
- assert(Month < TimeZoneMonthCount);
- }
- //
- // Days of the month start with 1.
- //
- Day += 1;
- assert(Date < SECONDS_PER_DAY);
- printf("%04d", Year);
- if ((Month != TimeZoneMonthJanuary) || (Day != 1) || (Date != 0)) {
- printf(" %s %2d ",
- TimeZoneAbbreviatedMonthStrings[Month],
- Day);
- PrintTimeZoneTime((LONG)Date, TimeZoneLensLocalTime);
- } else {
- printf("%8s", "");
- }
- return;
- }
- INT
- CalculateOccasionForDate (
- PTIME_ZONE_OCCASION Occasion,
- INT Year,
- TIME_ZONE_MONTH Month,
- PINT Date
- )
- /*++
- Routine Description:
- This routine determines the day of the month for the given occasion.
- Arguments:
- Occasion - Supplies a pointer to the occasion.
- Year - Supplies the year to calculate the occasion for.
- Month - Supplies the month to calculate the occasion for.
- Date - Supplies a pointer where the date (of the month) when the occasion
- occurs will be returned.
- Return Value:
- 0 on success.
- 1 if the occasion does not occur in the given year and month.
- --*/
- {
- INT DaysInMonth;
- INT Leap;
- INT MonthDate;
- INT Result;
- TIME_ZONE_WEEKDAY Weekday;
- Leap = 0;
- if (IS_LEAP_YEAR(Year)) {
- Leap = 1;
- }
- DaysInMonth = TimeZoneDaysPerMonth[Leap][Month];
- if (Occasion->Type == TimeZoneOccasionMonthDate) {
- if (Occasion->MonthDay < DaysInMonth) {
- *Date = Occasion->MonthDay;
- return 0;
- }
- return EINVAL;
- }
- //
- // Calculate the weekday for the first of the month.
- //
- Result = CalculateWeekdayForMonth(Year, Month, &Weekday);
- if (Result != 0) {
- return Result;
- }
- MonthDate = 1;
- //
- // Calculate the first instance of the desired weekday.
- //
- if (Occasion->Weekday >= Weekday) {
- MonthDate += Occasion->Weekday - Weekday;
- } else {
- MonthDate += DAYS_PER_WEEK - (Weekday - Occasion->Weekday);
- }
- switch (Occasion->Type) {
- //
- // Add a week as many times as possible.
- //
- case TimeZoneOccasionLastWeekday:
- while (MonthDate + DAYS_PER_WEEK <= DaysInMonth) {
- MonthDate += DAYS_PER_WEEK;
- }
- break;
- //
- // Add a week as long as it's less than the required minimum month day. If
- // that pushes it over the month, then the occasion doesn't exist.
- //
- case TimeZoneOccasionGreaterOrEqualWeekday:
- while (MonthDate < Occasion->MonthDay) {
- MonthDate += DAYS_PER_WEEK;
- }
- if (MonthDate > DaysInMonth) {
- return EINVAL;
- }
- break;
- //
- // If the first instance of that weekday is already too far, then the
- // occasion doesn't exist. Otherwise, keep adding weeks as long as it's
- // still under the limit.
- //
- case TimeZoneOccasionLessOrEqualWeekday:
- if (MonthDate > Occasion->MonthDay) {
- return EINVAL;
- }
- while (MonthDate + DAYS_PER_WEEK < Occasion->MonthDay) {
- MonthDate += DAYS_PER_WEEK;
- }
- break;
- default:
- assert(FALSE);
- return 1;
- }
- *Date = MonthDate;
- return 0;
- }
- INT
- CalculateWeekdayForMonth (
- INT Year,
- TIME_ZONE_MONTH Month,
- PTIME_ZONE_WEEKDAY Weekday
- )
- /*++
- Routine Description:
- This routine calculates the weekday for the first of the month on the
- given month and year.
- Arguments:
- Year - Supplies the year to calculate the weekday for.
- Month - Supplies the month to calculate the weekday for.
- Weekday - Supplies a pointer where the weekday will be returned on success.
- Return Value:
- 0 on success.
- ERANGE if the result was out of range.
- --*/
- {
- LONG Days;
- INT Leap;
- INT Modulo;
- if ((Year > MAX_TIME_ZONE_YEAR) || (Year < MIN_TIME_ZONE_YEAR)) {
- return ERANGE;
- }
- Days = ComputeDaysForYear(Year);
- Leap = 0;
- if (IS_LEAP_YEAR(Year)) {
- Leap = 1;
- }
- Days += TimeZoneMonthDays[Leap][Month];
- Modulo = ((TIME_ZONE_EPOCH_WEEKDAY + Days) % DAYS_PER_WEEK);
- if (Modulo < 0) {
- Modulo = DAYS_PER_WEEK + Modulo;
- }
- *Weekday = Modulo;
- return 0;
- }
- LONG
- ComputeDaysForYear (
- INT Year
- )
- /*++
- Routine Description:
- This routine calculates the number of days for the given year, relative to
- the epoch.
- Arguments:
- Year - Supplies the target year.
- Return Value:
- Returns the number of days since the epoch that January 1st of the given
- year occurred.
- --*/
- {
- LONG Days;
- Days = 0;
- if (Year >= TIME_ZONE_EPOCH_YEAR) {
- while (Year > TIME_ZONE_EPOCH_YEAR) {
- if (IS_LEAP_YEAR(Year)) {
- Days += DAYS_PER_LEAP_YEAR;
- } else {
- Days += DAYS_PER_YEAR;
- }
- Year -= 1;
- }
- } else {
- while (Year < TIME_ZONE_EPOCH_YEAR) {
- if (IS_LEAP_YEAR(Year)) {
- Days -= DAYS_PER_LEAP_YEAR;
- } else {
- Days -= DAYS_PER_YEAR;
- }
- Year += 1;
- }
- }
- return Days;
- }
- INT
- ComputeYearForDays (
- PLONG Days
- )
- /*++
- Routine Description:
- This routine calculates the year given a number of days from the epoch.
- Arguments:
- Days - Supplies a pointer to the number of days since the epoch. On
- completion, this will contain the number of remaining days after the
- years have been subtracted.
- Return Value:
- Returns the year that the day resides in.
- --*/
- {
- LONG RemainingDays;
- INT Year;
- Year = TIME_ZONE_EPOCH_YEAR;
- RemainingDays = *Days;
- //
- // Subtract off any years after the epoch.
- //
- while (RemainingDays > 0) {
- if (IS_LEAP_YEAR(Year)) {
- RemainingDays -= DAYS_PER_LEAP_YEAR;
- } else {
- RemainingDays -= DAYS_PER_YEAR;
- }
- Year += 1;
- }
- //
- // The subtraction may have gone one too far, or the days may have
- // started negative. Either way, get the days up to a non-negative value.
- //
- while (RemainingDays < 0) {
- Year -= 1;
- if (IS_LEAP_YEAR(Year)) {
- RemainingDays += DAYS_PER_LEAP_YEAR;
- } else {
- RemainingDays += DAYS_PER_YEAR;
- }
- }
- *Days = RemainingDays;
- return Year;
- }
- PSTR
- TimeZoneGetString (
- PLIST_ENTRY ListHead,
- ULONG Offset
- )
- /*++
- Routine Description:
- This routine returns the string at a given string table offset.
- Arguments:
- ListHead - Supplies a pointer to the head of the list to search.
- Offset - Supplies the string table offset value to get.
- Return Value:
- Returns a pointer to the string on success.
- NULL on failure.
- --*/
- {
- PLIST_ENTRY CurrentEntry;
- PTZC_STRING String;
- CurrentEntry = ListHead->Next;
- while (CurrentEntry != ListHead) {
- String = LIST_VALUE(CurrentEntry, TZC_STRING, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- if (String->Offset == Offset) {
- return String->String;
- }
- }
- return NULL;
- }
- INT
- TimeZoneAddString (
- PSTR String,
- PULONG Offset
- )
- /*++
- Routine Description:
- This routine adds a string to the string table, reusing strings if possible.
- Arguments:
- String - Supplies a pointer to the string to add. A copy of this string
- will be made.
- Offset - Supplies a pointer where the string offset will be returned on
- success.
- Return Value:
- 0 on success.
- ENOMEM on allocation failure.
- --*/
- {
- INT Result;
- Result = TimeZoneAddStringToList(String,
- &TimeZoneStringList,
- &TimeZoneNextStringOffset,
- TRUE,
- Offset);
- return Result;
- }
- INT
- TimeZoneAddRuleString (
- PSTR String,
- PULONG Index
- )
- /*++
- Routine Description:
- This routine adds a string to the rule string table, reusing strings if
- possible.
- Arguments:
- String - Supplies a pointer to the string to add. A copy of this string
- will be made.
- Index - Supplies a pointer where the rule index will be returned on success.
- Return Value:
- 0 on success.
- ENOMEM on allocation failure.
- --*/
- {
- INT Result;
- Result = TimeZoneAddStringToList(String,
- &TimeZoneRuleStringList,
- &TimeZoneNextRuleNumber,
- FALSE,
- Index);
- return Result;
- }
- INT
- TimeZoneAddStringToList (
- PSTR String,
- PLIST_ENTRY ListHead,
- PULONG ListSize,
- BOOL TrackSize,
- PULONG Offset
- )
- /*++
- Routine Description:
- This routine adds a string to the the given string table.
- Arguments:
- String - Supplies a pointer to the string to add. A copy of this string
- will be made.
- ListHead - Supplies a pointer to the head of the list to add it to.
- ListSize - Supplies a pointer to the size of the list, which will be
- updated.
- TrackSize - Supplies a boolean indicating whether the list size tracks
- the total string size (TRUE) or the element count (FALSE).
- Offset - Supplies a pointer where the offset will be returned on success.
- Return Value:
- 0 on success.
- ENOMEM on allocation failure.
- --*/
- {
- PLIST_ENTRY CurrentEntry;
- PTZC_STRING StringEntry;
- //
- // First search for the string.
- //
- CurrentEntry = ListHead->Next;
- while (CurrentEntry != ListHead) {
- StringEntry = LIST_VALUE(CurrentEntry, TZC_STRING, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- if (strcmp(StringEntry->String, String) == 0) {
- *Offset = StringEntry->Offset;
- return 0;
- }
- }
- //
- // No string entry was found, create a new one.
- //
- StringEntry = malloc(sizeof(TZC_STRING));
- if (StringEntry == NULL) {
- return ENOMEM;
- }
- StringEntry->String = strdup(String);
- if (StringEntry->String == NULL) {
- free(StringEntry);
- return ENOMEM;
- }
- StringEntry->Offset = *ListSize;
- if (TrackSize != FALSE) {
- *ListSize += strlen(String) + 1;
- } else {
- *ListSize += 1;
- }
- INSERT_BEFORE(&(StringEntry->ListEntry), ListHead);
- *Offset = StringEntry->Offset;
- return 0;
- }
- VOID
- TimeZoneCompressEntries (
- PLIST_ENTRY ZoneEntryList,
- PULONG ZoneEntryCount,
- PTZC_ZONE Zone
- )
- /*++
- Routine Description:
- This routine attempts to compress the zone entries by finding a repeat
- earlier. This routine requires that the zone entries be at the end of the
- list.
- Arguments:
- ZoneEntryList - Supplies a pointer to the head of the list of zone entries.
- ZoneEntryCount - Supplies a pointer to the count of entries on the list,
- which may be updated.
- Zone - Supplies a pointer to the zone owning the entries.
- Return Value:
- None.
- --*/
- {
- ULONG Count;
- PLIST_ENTRY CurrentEntry;
- PTZC_ZONE_ENTRY Entry;
- PTZC_ZONE_ENTRY Potential;
- ULONG SearchCount;
- PTZC_ZONE_ENTRY SearchEntry;
- PTZC_ZONE_ENTRY Start;
- Count = Zone->ZoneEntryCount;
- CurrentEntry = ZoneEntryList->Previous;
- for (SearchCount = 0; SearchCount < Count - 1; SearchCount += 1) {
- CurrentEntry = CurrentEntry->Previous;
- }
- Entry = LIST_VALUE(CurrentEntry, TZC_ZONE_ENTRY, ListEntry);
- assert(Zone->ZoneEntryIndex == Entry->Index);
- SearchEntry = Entry;
- SearchCount = 0;
- Start = NULL;
- CurrentEntry = ZoneEntryList->Next;
- while (CurrentEntry != ZoneEntryList) {
- Potential = LIST_VALUE(CurrentEntry, TZC_ZONE_ENTRY, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- //
- // Stop if the element itself was found.
- //
- if (Potential == Entry) {
- break;
- }
- //
- // If this entry matches, then advance the search.
- //
- if ((Potential->RulesNameIndex == SearchEntry->RulesNameIndex) &&
- (Potential->Save == SearchEntry->Save) &&
- (Potential->FormatOffset == SearchEntry->FormatOffset) &&
- (Potential->Until == SearchEntry->Until)) {
- if (SearchCount == 0) {
- Start = Potential;
- }
- SearchCount += 1;
- if (SearchCount == Count) {
- break;
- }
- //
- // Move to the next one, and continue scanning.
- //
- SearchEntry = LIST_VALUE(SearchEntry->ListEntry.Next,
- TZC_ZONE_ENTRY,
- ListEntry);
- //
- // No match, reset.
- //
- } else {
- //
- // Redo this one against the first entry.
- //
- if (SearchCount != 0) {
- CurrentEntry = CurrentEntry->Previous;
- }
- SearchEntry = Entry;
- SearchCount = 0;
- }
- }
- if (SearchCount != Count) {
- return;
- }
- Zone->ZoneEntryIndex = Start->Index;
- //
- // Destroy the redundant elements.
- //
- CurrentEntry = &(Entry->ListEntry);
- while (CurrentEntry != ZoneEntryList) {
- CurrentEntry = CurrentEntry->Next;
- LIST_REMOVE(&(Entry->ListEntry));
- DestroyTimeZoneEntry(Entry);
- *ZoneEntryCount -= 1;
- Entry = LIST_VALUE(CurrentEntry, TZC_ZONE_ENTRY, ListEntry);
- }
- return;
- }
|