1
0

tzcomp.c 91 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304
  1. /*++
  2. Copyright (c) 2013 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. tzcomp.c
  9. Abstract:
  10. This module implements the time zone compiler program, which translates
  11. time zone data into a binary format.
  12. Author:
  13. Evan Green 2-Aug-2013
  14. Environment:
  15. Build
  16. --*/
  17. //
  18. // ------------------------------------------------------------------- Includes
  19. //
  20. #include <minoca/lib/types.h>
  21. #include <minoca/lib/status.h>
  22. #include <minoca/lib/rtl.h>
  23. #include <minoca/lib/tzfmt.h>
  24. #include <assert.h>
  25. #include <ctype.h>
  26. #include <errno.h>
  27. #include <getopt.h>
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30. #include <string.h>
  31. //
  32. // --------------------------------------------------------------------- Macros
  33. //
  34. //
  35. // ---------------------------------------------------------------- Definitions
  36. //
  37. #define TIME_ZONE_COMPILER_VERSION_MAJOR 1
  38. #define TIME_ZONE_COMPILER_VERSION_MINOR 1
  39. #define TIME_ZONE_COMPILER_USAGE \
  40. "Usage: tzcomp [-p] [-f <zone>] [-o <outputfile>] [files...]\n" \
  41. "The tzcomp utility compiles standard time zone data files into a binary " \
  42. "format. Options are:\n\n" \
  43. " -o, --output=<file> -- Write the output to the given file rather \n" \
  44. " than the default file name \"" TIME_ZONE_DEFAULT_OUTPUT_FILE \
  45. "\".\n\n" \
  46. " -v, --verbose -- Print the parsed results coming from the input files." \
  47. "\n" \
  48. " -y, --year=<year> -- Write only zone information newer than the \n" \
  49. "given year.\n" \
  50. " -z, --zone=<zone> -- Produce output only for the time zone of the \n" \
  51. " given name.\n" \
  52. #define INITIAL_MALLOC_SIZE 32
  53. #define TIME_ZONE_DEFAULT_OUTPUT_FILE "tzdata"
  54. #define TZCOMP_OPTIONS_STRING "ho:vy:z:V"
  55. //
  56. // ------------------------------------------------------ Data Type Definitions
  57. //
  58. typedef enum _TZC_RULE_FIELD {
  59. RuleFieldMagic,
  60. RuleFieldName,
  61. RuleFieldFrom,
  62. RuleFieldTo,
  63. RuleFieldType,
  64. RuleFieldIn,
  65. RuleFieldOn,
  66. RuleFieldAt,
  67. RuleFieldSave,
  68. RuleFieldLetters,
  69. RuleFieldCount
  70. } TZC_RULE_FIELD, *PTZC_RULE_FIELD;
  71. typedef enum _TZC_ZONE_FIELD {
  72. ZoneFieldMagic,
  73. ZoneFieldName,
  74. ZoneFieldGmtOffset,
  75. ZoneFieldRules,
  76. ZoneFieldFormat,
  77. ZoneFieldUntilYear,
  78. ZoneFieldUntilMonth,
  79. ZoneFieldUntilDay,
  80. ZoneFieldUntilTime,
  81. ZoneFieldCount
  82. } TZC_ZONE_FIELD, *PTZC_ZONE_FIELD;
  83. typedef enum _TZC_LINK_FIELD {
  84. LinkFieldMagic,
  85. LinkFieldFrom,
  86. LinkFieldTo,
  87. LinkFieldCount
  88. } TZC_LINK_FIELD, *PTZC_LINK_FIELD;
  89. typedef enum _TZC_LEAP_FIELD {
  90. LeapFieldMagic,
  91. LeapFieldYear,
  92. LeapFieldMonth,
  93. LeapFieldDay,
  94. LeapFieldTime,
  95. LeapFieldCorrection,
  96. LeapFieldRollingOrStationary,
  97. LeapFieldCount
  98. } TZC_LEAP_FIELD, *PTZC_LEAP_FIELD;
  99. typedef struct _TZC_RULE {
  100. LIST_ENTRY ListEntry;
  101. ULONG NameIndex;
  102. SHORT From;
  103. SHORT To;
  104. TIME_ZONE_MONTH Month;
  105. TIME_ZONE_OCCASION On;
  106. LONG At;
  107. TIME_ZONE_LENS AtLens;
  108. LONG Save;
  109. ULONG LettersOffset;
  110. } TZC_RULE, *PTZC_RULE;
  111. typedef struct _TZC_ZONE {
  112. LIST_ENTRY ListEntry;
  113. ULONG NameOffset;
  114. ULONG ZoneEntryIndex;
  115. ULONG ZoneEntryCount;
  116. } TZC_ZONE, *PTZC_ZONE;
  117. typedef struct _TZC_LINK {
  118. LIST_ENTRY ListEntry;
  119. PSTR From;
  120. PSTR To;
  121. } TZC_LINK, *PTZC_LINK;
  122. typedef struct _TZC_ZONE_ENTRY {
  123. LIST_ENTRY ListEntry;
  124. ULONG Index;
  125. LONG GmtOffset;
  126. ULONG RulesNameIndex;
  127. LONG Save;
  128. ULONG FormatOffset;
  129. LONGLONG Until;
  130. } TZC_ZONE_ENTRY, *PTZC_ZONE_ENTRY;
  131. typedef struct _TZC_LEAP {
  132. LIST_ENTRY ListEntry;
  133. LONGLONG Date;
  134. CHAR Positive;
  135. CHAR LocalTime;
  136. } TZC_LEAP, *PTZC_LEAP;
  137. typedef struct _TZC_STRING {
  138. LIST_ENTRY ListEntry;
  139. ULONG Offset;
  140. PSTR String;
  141. } TZC_STRING, *PTZC_STRING;
  142. //
  143. // ----------------------------------------------- Internal Function Prototypes
  144. //
  145. INT
  146. ReadTimeZoneFile (
  147. PSTR FilePath
  148. );
  149. INT
  150. ProcessTimeZoneRule (
  151. PSTR *Fields,
  152. ULONG FieldCount
  153. );
  154. INT
  155. ProcessTimeZone (
  156. PSTR *Fields,
  157. ULONG FieldCount,
  158. PBOOL Continuation
  159. );
  160. INT
  161. ProcessTimeZoneLink (
  162. PSTR *Fields,
  163. ULONG FieldCount
  164. );
  165. INT
  166. ProcessTimeZoneLeap (
  167. PSTR *Fields,
  168. ULONG FieldCount
  169. );
  170. INT
  171. ReadTimeZoneFields (
  172. FILE *File,
  173. PVOID *LineBuffer,
  174. PULONG LineBufferSize,
  175. PSTR **Fields,
  176. PULONG FieldsSize,
  177. PULONG FieldCount,
  178. PBOOL EndOfFile
  179. );
  180. INT
  181. ReadTimeZoneLine (
  182. FILE *File,
  183. PVOID *LineBuffer,
  184. PULONG LineBufferSize,
  185. PBOOL EndOfFile
  186. );
  187. INT
  188. TranslateLinksToZones (
  189. );
  190. INT
  191. TimeZoneFilter (
  192. PSTR Name,
  193. INT Year
  194. );
  195. INT
  196. WriteTimeZoneData (
  197. PSTR FileName
  198. );
  199. VOID
  200. DestroyTimeZoneRule (
  201. PTZC_RULE Rule
  202. );
  203. VOID
  204. DestroyTimeZone (
  205. PTZC_ZONE Zone
  206. );
  207. VOID
  208. DestroyTimeZoneEntry (
  209. PTZC_ZONE_ENTRY ZoneEntry
  210. );
  211. VOID
  212. DestroyTimeZoneLink (
  213. PTZC_LINK Link
  214. );
  215. VOID
  216. DestroyTimeZoneLeap (
  217. PTZC_LEAP Leap
  218. );
  219. VOID
  220. DestroyTimeZoneStringList (
  221. PLIST_ENTRY ListHead
  222. );
  223. INT
  224. ParseTimeZoneRuleLimit (
  225. PSTR Field,
  226. SHORT OnlyValue,
  227. PSHORT Value
  228. );
  229. INT
  230. ParseTimeZoneMonth (
  231. PSTR Field,
  232. PTIME_ZONE_MONTH Value
  233. );
  234. INT
  235. ParseTimeZoneRuleWeekday (
  236. PSTR Field,
  237. PTIME_ZONE_WEEKDAY Value
  238. );
  239. INT
  240. ParseTimeZoneOccasion (
  241. PSTR Field,
  242. PTIME_ZONE_OCCASION Occasion
  243. );
  244. INT
  245. ParseTimeZoneTime (
  246. PSTR Field,
  247. PLONG Time,
  248. PTIME_ZONE_LENS Lens
  249. );
  250. VOID
  251. PrintTimeZoneRule (
  252. PTZC_RULE Rule
  253. );
  254. VOID
  255. PrintTimeZone (
  256. PTZC_ZONE Zone
  257. );
  258. VOID
  259. PrintTimeZoneLink (
  260. PTZC_LINK Link
  261. );
  262. VOID
  263. PrintTimeZoneLeap (
  264. PTZC_LEAP Leap
  265. );
  266. VOID
  267. PrintTimeZoneEntry (
  268. PTZC_ZONE_ENTRY ZoneEntry
  269. );
  270. VOID
  271. PrintTimeZoneTime (
  272. LONG Time,
  273. TIME_ZONE_LENS Lens
  274. );
  275. VOID
  276. PrintTimeZoneDate (
  277. LONGLONG Date
  278. );
  279. INT
  280. CalculateOccasionForDate (
  281. PTIME_ZONE_OCCASION Occasion,
  282. INT Year,
  283. TIME_ZONE_MONTH Month,
  284. PINT Date
  285. );
  286. INT
  287. CalculateWeekdayForMonth (
  288. INT Year,
  289. TIME_ZONE_MONTH Month,
  290. PTIME_ZONE_WEEKDAY Weekday
  291. );
  292. LONG
  293. ComputeDaysForYear (
  294. INT Year
  295. );
  296. INT
  297. ComputeYearForDays (
  298. PLONG Days
  299. );
  300. PSTR
  301. TimeZoneGetString (
  302. PLIST_ENTRY ListHead,
  303. ULONG Offset
  304. );
  305. INT
  306. TimeZoneAddString (
  307. PSTR String,
  308. PULONG Offset
  309. );
  310. INT
  311. TimeZoneAddRuleString (
  312. PSTR String,
  313. PULONG Index
  314. );
  315. INT
  316. TimeZoneAddStringToList (
  317. PSTR String,
  318. PLIST_ENTRY ListHead,
  319. PULONG ListSize,
  320. BOOL TrackSize,
  321. PULONG Offset
  322. );
  323. VOID
  324. TimeZoneCompressEntries (
  325. PLIST_ENTRY ZoneEntryList,
  326. PULONG ZoneEntryCount,
  327. PTZC_ZONE Zone
  328. );
  329. //
  330. // -------------------------------------------------------------------- Globals
  331. //
  332. struct option TzcompLongOptions[] = {
  333. {"output", required_argument, 0, 'o'},
  334. {"verbose", no_argument, 0, 'v'},
  335. {"year", required_argument, 0, 'y'},
  336. {"zone", required_argument, 0, 'z'},
  337. {"help", no_argument, 0, 'h'},
  338. {"version", no_argument, 0, 'V'},
  339. {NULL, 0, 0, 0},
  340. };
  341. LIST_ENTRY TimeZoneRuleList;
  342. LIST_ENTRY TimeZoneList;
  343. LIST_ENTRY TimeZoneEntryList;
  344. LIST_ENTRY TimeZoneLinkList;
  345. LIST_ENTRY TimeZoneLeapList;
  346. //
  347. // Store the string table and next free offset into it.
  348. //
  349. LIST_ENTRY TimeZoneStringList;
  350. ULONG TimeZoneNextStringOffset;
  351. //
  352. // Store the string table for the rule list (which will eventually be
  353. // discarded) and the next valid rule number.
  354. //
  355. LIST_ENTRY TimeZoneRuleStringList;
  356. ULONG TimeZoneNextRuleNumber;
  357. ULONG TimeZoneNextZoneEntryIndex;
  358. ULONG TimeZoneRuleCount;
  359. ULONG TimeZoneCount;
  360. ULONG TimeZoneLeapCount;
  361. PSTR TimeZoneMonthStrings[TimeZoneMonthCount] = {
  362. "January",
  363. "February",
  364. "March",
  365. "April",
  366. "May",
  367. "June",
  368. "July",
  369. "August",
  370. "September",
  371. "October",
  372. "November",
  373. "December"
  374. };
  375. PSTR TimeZoneAbbreviatedMonthStrings[TimeZoneMonthCount] = {
  376. "Jan",
  377. "Feb",
  378. "Mar",
  379. "Apr",
  380. "May",
  381. "Jun",
  382. "Jul",
  383. "Aug",
  384. "Sep",
  385. "Oct",
  386. "Nov",
  387. "Dec"
  388. };
  389. PSTR TimeZoneWeekdayStrings[TimeZoneWeekdayCount] = {
  390. "Sunday",
  391. "Monday",
  392. "Tuesday",
  393. "Wednesday",
  394. "Thursday",
  395. "Friday",
  396. "Saturday"
  397. };
  398. PSTR TimeZoneAbbreviatedWeekdayStrings[TimeZoneWeekdayCount] = {
  399. "Sun",
  400. "Mon",
  401. "Tue",
  402. "Wed",
  403. "Thu",
  404. "Fri",
  405. "Sat"
  406. };
  407. CHAR TimeZoneDaysPerMonth[2][TimeZoneMonthCount] = {
  408. {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
  409. {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
  410. };
  411. SHORT TimeZoneMonthDays[2][TimeZoneMonthCount] = {
  412. {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
  413. {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335},
  414. };
  415. //
  416. // ------------------------------------------------------------------ Functions
  417. //
  418. INT
  419. main (
  420. INT ArgumentCount,
  421. CHAR **Arguments
  422. )
  423. /*++
  424. Routine Description:
  425. This routine is the main entry point for the time zone compiler program.
  426. Arguments:
  427. ArgumentCount - Supplies the number of command line arguments the program
  428. was invoked with.
  429. Arguments - Supplies a tokenized array of command line arguments.
  430. Return Value:
  431. Returns an integer exit code. 0 for success, nonzero otherwise.
  432. --*/
  433. {
  434. PSTR Argument;
  435. INT ArgumentIndex;
  436. PLIST_ENTRY CurrentEntry;
  437. PSTR FilterZone;
  438. PTZC_LEAP Leap;
  439. PTZC_LINK Link;
  440. INT Option;
  441. PSTR OutputName;
  442. BOOL PrintParsedEntries;
  443. INT Result;
  444. PTZC_RULE Rule;
  445. ULONG UnusedIndex;
  446. INT YearFilter;
  447. PTZC_ZONE Zone;
  448. PTZC_ZONE_ENTRY ZoneEntry;
  449. INITIALIZE_LIST_HEAD(&TimeZoneRuleList);
  450. INITIALIZE_LIST_HEAD(&TimeZoneList);
  451. INITIALIZE_LIST_HEAD(&TimeZoneEntryList);
  452. INITIALIZE_LIST_HEAD(&TimeZoneLinkList);
  453. INITIALIZE_LIST_HEAD(&TimeZoneLeapList);
  454. INITIALIZE_LIST_HEAD(&TimeZoneStringList);
  455. INITIALIZE_LIST_HEAD(&TimeZoneRuleStringList);
  456. FilterZone = NULL;
  457. OutputName = TIME_ZONE_DEFAULT_OUTPUT_FILE;
  458. PrintParsedEntries = FALSE;
  459. Result = 0;
  460. YearFilter = 0;
  461. if (ArgumentCount <= 1) {
  462. fprintf(stderr, TIME_ZONE_COMPILER_USAGE);
  463. return 1;
  464. }
  465. //
  466. // Add the null strings to the first positions.
  467. //
  468. TimeZoneAddString("", &UnusedIndex);
  469. TimeZoneAddRuleString("", &UnusedIndex);
  470. //
  471. // Loop through the arguments.
  472. //
  473. while (TRUE) {
  474. Option = getopt_long(ArgumentCount,
  475. Arguments,
  476. TZCOMP_OPTIONS_STRING,
  477. TzcompLongOptions,
  478. NULL);
  479. if (Option == -1) {
  480. break;
  481. }
  482. if ((Option == '?') || (Option == ':')) {
  483. Result = 1;
  484. goto MainEnd;
  485. }
  486. switch (Option) {
  487. case 'o':
  488. OutputName = optarg;
  489. break;
  490. case 'v':
  491. PrintParsedEntries = TRUE;
  492. break;
  493. case 'y':
  494. YearFilter = strtoul(optarg, NULL, 10);
  495. if ((YearFilter <= 0) || (YearFilter > 9999)) {
  496. fprintf(stderr, "Invalid year %s\n", optarg);
  497. Result = 1;
  498. goto MainEnd;
  499. }
  500. break;
  501. case 'z':
  502. FilterZone = optarg;
  503. break;
  504. case 'h':
  505. printf(TIME_ZONE_COMPILER_USAGE);
  506. return 1;
  507. case 'V':
  508. printf("Tzcomp version %d.%d\n",
  509. TIME_ZONE_COMPILER_VERSION_MAJOR,
  510. TIME_ZONE_COMPILER_VERSION_MINOR);
  511. return 1;
  512. default:
  513. assert(FALSE);
  514. Result = 1;
  515. goto MainEnd;
  516. }
  517. }
  518. ArgumentIndex = optind;
  519. while (ArgumentIndex < ArgumentCount) {
  520. Argument = Arguments[ArgumentIndex];
  521. Result = ReadTimeZoneFile(Argument);
  522. if (Result != 0) {
  523. fprintf(stderr,
  524. "tzcomp: Failed to process time zone data file %s.\n",
  525. Argument);
  526. goto MainEnd;
  527. }
  528. ArgumentIndex += 1;
  529. }
  530. Result = TranslateLinksToZones();
  531. if (Result != 0) {
  532. goto MainEnd;
  533. }
  534. //
  535. // Filter for the requested name and/or years if requested.
  536. //
  537. if ((FilterZone != NULL) || (YearFilter != 0)) {
  538. Result = TimeZoneFilter(FilterZone, YearFilter);
  539. if (Result != 0) {
  540. fprintf(stderr,
  541. "tzcomp: Error: Failed to filter time zone: %s.\n",
  542. strerror(errno));
  543. goto MainEnd;
  544. }
  545. }
  546. //
  547. // If requested, print all the parsed entries.
  548. //
  549. if (PrintParsedEntries != FALSE) {
  550. CurrentEntry = TimeZoneRuleList.Next;
  551. while (CurrentEntry != &TimeZoneRuleList) {
  552. Rule = LIST_VALUE(CurrentEntry, TZC_RULE, ListEntry);
  553. CurrentEntry = CurrentEntry->Next;
  554. PrintTimeZoneRule(Rule);
  555. }
  556. CurrentEntry = TimeZoneList.Next;
  557. while (CurrentEntry != &TimeZoneList) {
  558. Zone = LIST_VALUE(CurrentEntry, TZC_ZONE, ListEntry);
  559. CurrentEntry = CurrentEntry->Next;
  560. PrintTimeZone(Zone);
  561. }
  562. CurrentEntry = TimeZoneLinkList.Next;
  563. while (CurrentEntry != &TimeZoneLinkList) {
  564. Link = LIST_VALUE(CurrentEntry, TZC_LINK, ListEntry);
  565. CurrentEntry = CurrentEntry->Next;
  566. PrintTimeZoneLink(Link);
  567. }
  568. CurrentEntry = TimeZoneLeapList.Next;
  569. while (CurrentEntry != &TimeZoneLeapList) {
  570. Leap = LIST_VALUE(CurrentEntry, TZC_LEAP, ListEntry);
  571. CurrentEntry = CurrentEntry->Next;
  572. PrintTimeZoneLeap(Leap);
  573. }
  574. }
  575. Result = WriteTimeZoneData(OutputName);
  576. if (Result != 0) {
  577. fprintf(stderr,
  578. "tzcomp: Error: Failed to write time zone data: %s.\n",
  579. strerror(errno));
  580. goto MainEnd;
  581. }
  582. MainEnd:
  583. while (LIST_EMPTY(&TimeZoneRuleList) == FALSE) {
  584. Rule = LIST_VALUE(TimeZoneRuleList.Next, TZC_RULE, ListEntry);
  585. LIST_REMOVE(&(Rule->ListEntry));
  586. DestroyTimeZoneRule(Rule);
  587. }
  588. while (LIST_EMPTY(&TimeZoneList) == FALSE) {
  589. Zone = LIST_VALUE(TimeZoneList.Next, TZC_ZONE, ListEntry);
  590. LIST_REMOVE(&(Zone->ListEntry));
  591. DestroyTimeZone(Zone);
  592. }
  593. while (LIST_EMPTY(&TimeZoneLinkList) == FALSE) {
  594. Link = LIST_VALUE(TimeZoneLinkList.Next, TZC_LINK, ListEntry);
  595. LIST_REMOVE(&(Link->ListEntry));
  596. DestroyTimeZoneLink(Link);
  597. }
  598. while (LIST_EMPTY(&TimeZoneLeapList) == FALSE) {
  599. Leap = LIST_VALUE(TimeZoneLeapList.Next, TZC_LEAP, ListEntry);
  600. LIST_REMOVE(&(Leap->ListEntry));
  601. DestroyTimeZoneLeap(Leap);
  602. }
  603. while (LIST_EMPTY(&TimeZoneEntryList) == FALSE) {
  604. ZoneEntry = LIST_VALUE(TimeZoneEntryList.Next,
  605. TZC_ZONE_ENTRY,
  606. ListEntry);
  607. LIST_REMOVE(&(ZoneEntry->ListEntry));
  608. DestroyTimeZoneEntry(ZoneEntry);
  609. }
  610. DestroyTimeZoneStringList(&TimeZoneStringList);
  611. DestroyTimeZoneStringList(&TimeZoneRuleStringList);
  612. return Result;
  613. }
  614. //
  615. // --------------------------------------------------------- Internal Functions
  616. //
  617. INT
  618. ReadTimeZoneFile (
  619. PSTR FilePath
  620. )
  621. /*++
  622. Routine Description:
  623. This routine reads in time zone data from a file.
  624. Arguments:
  625. FilePath - Supplies a pointer to the file path of the data to read in.
  626. Return Value:
  627. 0 on success.
  628. Non-zero on failure.
  629. --*/
  630. {
  631. BOOL EndOfFile;
  632. ULONG FieldCount;
  633. PSTR *Fields;
  634. ULONG FieldsSize;
  635. FILE *File;
  636. PVOID LineBuffer;
  637. ULONG LineBufferSize;
  638. ULONG LineNumber;
  639. INT Result;
  640. BOOL ZoneContinuation;
  641. EndOfFile = FALSE;
  642. FieldsSize = 0;
  643. Fields = NULL;
  644. LineBuffer = NULL;
  645. LineBufferSize = 0;
  646. ZoneContinuation = FALSE;
  647. File = fopen(FilePath, "rb");
  648. if (File == NULL) {
  649. fprintf(stderr,
  650. "tzcomp: Failed to open %s: %s\n",
  651. FilePath,
  652. strerror(errno));
  653. Result = errno;
  654. goto ReadTimeZoneFileEnd;
  655. }
  656. //
  657. // Loop reading and processing lines.
  658. //
  659. LineNumber = 1;
  660. while (TRUE) {
  661. Result = ReadTimeZoneFields(File,
  662. &LineBuffer,
  663. &LineBufferSize,
  664. &Fields,
  665. &FieldsSize,
  666. &FieldCount,
  667. &EndOfFile);
  668. if (Result != 0) {
  669. fprintf(stderr,
  670. "tzcomp: Failed to read line %s:%d: %s\n",
  671. FilePath,
  672. LineNumber,
  673. strerror(Result));
  674. goto ReadTimeZoneFileEnd;
  675. }
  676. //
  677. // Process the line according to its type.
  678. //
  679. if (FieldCount != 0) {
  680. Result = 0;
  681. if ((ZoneContinuation != FALSE) ||
  682. (strcasecmp(Fields[0], "Zone") == 0)) {
  683. Result = ProcessTimeZone(Fields, FieldCount, &ZoneContinuation);
  684. } else if (strcasecmp(Fields[0], "Rule") == 0) {
  685. Result = ProcessTimeZoneRule(Fields, FieldCount);
  686. } else if (strcasecmp(Fields[0], "Link") == 0) {
  687. Result = ProcessTimeZoneLink(Fields, FieldCount);
  688. } else if (strcasecmp(Fields[0], "Leap") == 0) {
  689. Result = ProcessTimeZoneLeap(Fields, FieldCount);
  690. }
  691. if (Result != 0) {
  692. fprintf(stderr,
  693. "tzcomp: Failed to process line %s:%d: %s.\n",
  694. FilePath,
  695. LineNumber,
  696. strerror(Result));
  697. goto ReadTimeZoneFileEnd;
  698. }
  699. }
  700. if (EndOfFile != FALSE) {
  701. break;
  702. }
  703. LineNumber += 1;
  704. }
  705. Result = 0;
  706. ReadTimeZoneFileEnd:
  707. if (File != NULL) {
  708. fclose(File);
  709. }
  710. if (LineBuffer != NULL) {
  711. free(LineBuffer);
  712. }
  713. if (Fields != NULL) {
  714. free(Fields);
  715. }
  716. return Result;
  717. }
  718. INT
  719. ProcessTimeZoneRule (
  720. PSTR *Fields,
  721. ULONG FieldCount
  722. )
  723. /*++
  724. Routine Description:
  725. This routine processes a time zone rule line.
  726. Arguments:
  727. Fields - Supplies a pointer to the fields in the line.
  728. FieldCount - Supplies the number of elements in the fields array.
  729. Return Value:
  730. 0 on success.
  731. Non-zero on failure.
  732. --*/
  733. {
  734. INT Result;
  735. PTZC_RULE Rule;
  736. //
  737. // Validate the number of fields and the first field.
  738. //
  739. assert(strcasecmp(Fields[RuleFieldMagic], "Rule") == 0);
  740. Rule = NULL;
  741. if (FieldCount != RuleFieldCount) {
  742. fprintf(stderr,
  743. "tzcomp: Expected %d fields in a Rule, got %d.\n",
  744. RuleFieldCount,
  745. FieldCount);
  746. Result = EILSEQ;
  747. goto ProcessTimeZoneRuleEnd;
  748. }
  749. //
  750. // Allocate the new rule structure.
  751. //
  752. Rule = malloc(sizeof(TZC_RULE));
  753. if (Rule == NULL) {
  754. Result = ENOMEM;
  755. goto ProcessTimeZoneRuleEnd;
  756. }
  757. memset(Rule, 0, sizeof(TZC_RULE));
  758. //
  759. // Copy the name.
  760. //
  761. Result = TimeZoneAddRuleString(Fields[RuleFieldName], &(Rule->NameIndex));
  762. if (Result != 0) {
  763. goto ProcessTimeZoneRuleEnd;
  764. }
  765. //
  766. // Parse the FROM and TO years.
  767. //
  768. Result = ParseTimeZoneRuleLimit(Fields[RuleFieldFrom],
  769. MIN_TIME_ZONE_YEAR,
  770. &(Rule->From));
  771. if (Result != 0) {
  772. fprintf(stderr,
  773. "Failed to process Rule FROM: %s\n",
  774. Fields[RuleFieldFrom]);
  775. goto ProcessTimeZoneRuleEnd;
  776. }
  777. Result = ParseTimeZoneRuleLimit(Fields[RuleFieldTo],
  778. Rule->From,
  779. &(Rule->To));
  780. if (Result != 0) {
  781. fprintf(stderr,
  782. "Failed to process Rule TO: %s\n",
  783. Fields[RuleFieldTo]);
  784. goto ProcessTimeZoneRuleEnd;
  785. }
  786. //
  787. // Skip over the type field.
  788. //
  789. if (strcmp(Fields[RuleFieldType], "-") != 0) {
  790. fprintf(stderr,
  791. "Warning: Ignoring rule type %s.\n",
  792. Fields[RuleFieldType]);
  793. }
  794. //
  795. // Parse the IN month.
  796. //
  797. Result = ParseTimeZoneMonth(Fields[RuleFieldIn], &(Rule->Month));
  798. if (Result != 0) {
  799. fprintf(stderr,
  800. "Failed to process Rule IN: %s\n",
  801. Fields[RuleFieldIn]);
  802. goto ProcessTimeZoneRuleEnd;
  803. }
  804. //
  805. // Parse the ON occasion.
  806. //
  807. Result = ParseTimeZoneOccasion(Fields[RuleFieldOn], &(Rule->On));
  808. if (Result != 0) {
  809. fprintf(stderr,
  810. "Failed to process Rule ON: %s\n",
  811. Fields[RuleFieldOn]);
  812. goto ProcessTimeZoneRuleEnd;
  813. }
  814. //
  815. // Parse the AT time.
  816. //
  817. Result = ParseTimeZoneTime(Fields[RuleFieldAt],
  818. &(Rule->At),
  819. &(Rule->AtLens));
  820. if (Result != 0) {
  821. fprintf(stderr,
  822. "Failed to process Rule AT: %s\n",
  823. Fields[RuleFieldAt]);
  824. goto ProcessTimeZoneRuleEnd;
  825. }
  826. //
  827. // Parse the SAVE time.
  828. //
  829. Result = ParseTimeZoneTime(Fields[RuleFieldSave], &(Rule->Save), NULL);
  830. if (Result != 0) {
  831. fprintf(stderr,
  832. "Failed to process Rule SAVE: %s\n",
  833. Fields[RuleFieldSave]);
  834. goto ProcessTimeZoneRuleEnd;
  835. }
  836. //
  837. // Copy the letters.
  838. //
  839. Result = TimeZoneAddString(Fields[RuleFieldLetters],
  840. &(Rule->LettersOffset));
  841. if (Result != 0) {
  842. goto ProcessTimeZoneRuleEnd;
  843. }
  844. INSERT_BEFORE(&(Rule->ListEntry), &TimeZoneRuleList);
  845. TimeZoneRuleCount += 1;
  846. Result = 0;
  847. ProcessTimeZoneRuleEnd:
  848. if (Result != 0) {
  849. if (Rule != NULL) {
  850. DestroyTimeZoneRule(Rule);
  851. }
  852. }
  853. return Result;
  854. }
  855. INT
  856. ProcessTimeZone (
  857. PSTR *Fields,
  858. ULONG FieldCount,
  859. PBOOL Continuation
  860. )
  861. /*++
  862. Routine Description:
  863. This routine processes a time zone line.
  864. Arguments:
  865. Fields - Supplies a pointer to the fields in the line.
  866. FieldCount - Supplies the number of elements in the fields array.
  867. Continuation - Supplies a pointer that on input contains whether or not
  868. this zone line is a continuation line. On output, this variable will
  869. be set to indicate whether another continuation line is expected.
  870. Return Value:
  871. 0 on success.
  872. Non-zero on failure.
  873. --*/
  874. {
  875. PSTR AfterScan;
  876. INT Day;
  877. PSTR Field;
  878. ULONG FieldOffset;
  879. INT Leap;
  880. TIME_ZONE_MONTH Month;
  881. TIME_ZONE_OCCASION Occasion;
  882. INT Result;
  883. LONG ScannedValue;
  884. TIME_ZONE_LENS UntilLens;
  885. LONG UntilTime;
  886. BOOL UntilValid;
  887. INT Year;
  888. PTZC_ZONE Zone;
  889. PTZC_ZONE_ENTRY ZoneEntry;
  890. UntilValid = FALSE;
  891. Zone = NULL;
  892. ZoneEntry = NULL;
  893. //
  894. // If this is a continuation, then get the most recent zone and set the
  895. // field offset since the "Zone" and Name fields will be missing.
  896. //
  897. if (*Continuation != FALSE) {
  898. FieldOffset = ZoneFieldGmtOffset;
  899. if (FieldCount < ZoneFieldFormat - FieldOffset) {
  900. fprintf(stderr,
  901. "Error: Not enough fields for zone continuation.\n");
  902. Result = EILSEQ;
  903. goto ProcessTimeZoneEnd;
  904. }
  905. Zone = LIST_VALUE(TimeZoneList.Previous, TZC_ZONE, ListEntry);
  906. //
  907. // This is not a continuation.
  908. //
  909. } else {
  910. assert(strcasecmp(Fields[ZoneFieldMagic], "Zone") == 0);
  911. FieldOffset = 0;
  912. if (FieldCount < ZoneFieldRules) {
  913. fprintf(stderr,
  914. "Error: Not enough fields for zone line.\n");
  915. Result = EILSEQ;
  916. goto ProcessTimeZoneEnd;
  917. }
  918. Zone = malloc(sizeof(TZC_ZONE));
  919. if (Zone == NULL) {
  920. Result = ENOMEM;
  921. goto ProcessTimeZoneEnd;
  922. }
  923. memset(Zone, 0, sizeof(TZC_ZONE));
  924. //
  925. // Copy the name.
  926. //
  927. Result = TimeZoneAddString(Fields[ZoneFieldName], &(Zone->NameOffset));
  928. if (Result != 0) {
  929. free(Zone);
  930. goto ProcessTimeZoneEnd;
  931. }
  932. Zone->ZoneEntryIndex = TimeZoneNextZoneEntryIndex;
  933. INSERT_BEFORE(&(Zone->ListEntry), &TimeZoneList);
  934. TimeZoneCount += 1;
  935. }
  936. //
  937. // Create the zone entry.
  938. //
  939. ZoneEntry = malloc(sizeof(TZC_ZONE_ENTRY));
  940. if (ZoneEntry == NULL) {
  941. Result = ENOMEM;
  942. goto ProcessTimeZoneEnd;
  943. }
  944. memset(ZoneEntry, 0, sizeof(TZC_ZONE_ENTRY));
  945. ZoneEntry->Until = MAX_TIME_ZONE_DATE;
  946. ZoneEntry->Index = TimeZoneNextZoneEntryIndex;
  947. TimeZoneNextZoneEntryIndex += 1;
  948. //
  949. // Get the GMT offset time.
  950. //
  951. Field = Fields[ZoneFieldGmtOffset - FieldOffset];
  952. Result = ParseTimeZoneTime(Field, &(ZoneEntry->GmtOffset), NULL);
  953. if (Result != 0) {
  954. fprintf(stderr,
  955. "Error: Failed to parse Zone GMTOFFSET %s.\n",
  956. Field);
  957. goto ProcessTimeZoneEnd;
  958. }
  959. //
  960. // Get the rules string. If it's a -, then this zone is always in
  961. // standard time. Otherwise, it could be a save value (like 1:00) or
  962. // the name of a set of rules.
  963. //
  964. Field = Fields[ZoneFieldRules - FieldOffset];
  965. ZoneEntry->RulesNameIndex = -1;
  966. if (strcmp(Field, "-") != 0) {
  967. if ((*Field == '-') || (isdigit(*Field))) {
  968. Result = ParseTimeZoneTime(Field, &(ZoneEntry->Save), NULL);
  969. if (Result != 0) {
  970. fprintf(stderr,
  971. "Error: Failed to parse Zone SAVE %s.\n",
  972. Field);
  973. goto ProcessTimeZoneEnd;
  974. }
  975. } else {
  976. Result = TimeZoneAddRuleString(Field, &(ZoneEntry->RulesNameIndex));
  977. if (Result != 0) {
  978. goto ProcessTimeZoneEnd;
  979. }
  980. }
  981. }
  982. //
  983. // Copy the format string.
  984. //
  985. Field = Fields[ZoneFieldFormat - FieldOffset];
  986. Result = TimeZoneAddString(Field, &(ZoneEntry->FormatOffset));
  987. if (Result != 0) {
  988. goto ProcessTimeZoneEnd;
  989. }
  990. //
  991. // If there's no until year, then this is done.
  992. //
  993. if (FieldCount <= ZoneFieldUntilYear - FieldOffset) {
  994. Result = 0;
  995. goto ProcessTimeZoneEnd;
  996. }
  997. Field = Fields[ZoneFieldUntilYear - FieldOffset];
  998. ScannedValue = strtol(Field, &AfterScan, 10);
  999. if ((ScannedValue <= MIN_TIME_ZONE_YEAR) ||
  1000. (ScannedValue >= MAX_TIME_ZONE_YEAR) ||
  1001. (AfterScan == Field)) {
  1002. fprintf(stderr,
  1003. "Error: Failed to parse Zone UNTIL YEAR %s.\n",
  1004. Field);
  1005. Result = EILSEQ;
  1006. goto ProcessTimeZoneEnd;
  1007. }
  1008. Year = ScannedValue;
  1009. UntilValid = TRUE;
  1010. ZoneEntry->Until = (LONGLONG)ComputeDaysForYear(Year) * SECONDS_PER_DAY;
  1011. //
  1012. // If there's no until month, this is done.
  1013. //
  1014. if (FieldCount <= ZoneFieldUntilMonth - FieldOffset) {
  1015. Result = 0;
  1016. goto ProcessTimeZoneEnd;
  1017. }
  1018. Leap = 0;
  1019. if (IS_LEAP_YEAR(ScannedValue)) {
  1020. Leap = 1;
  1021. }
  1022. Field = Fields[ZoneFieldUntilMonth - FieldOffset];
  1023. Result = ParseTimeZoneMonth(Field, &Month);
  1024. if (Result != 0) {
  1025. fprintf(stderr,
  1026. "Error: Failed to parse Zone UNTIL MONTH %s.\n",
  1027. Field);
  1028. goto ProcessTimeZoneEnd;
  1029. }
  1030. ZoneEntry->Until += TimeZoneMonthDays[Leap][Month] * SECONDS_PER_DAY;
  1031. //
  1032. // If there is no until day, this is done.
  1033. //
  1034. if (FieldCount <= ZoneFieldUntilDay - FieldOffset) {
  1035. Result = 0;
  1036. goto ProcessTimeZoneEnd;
  1037. }
  1038. Field = Fields[ZoneFieldUntilDay - FieldOffset];
  1039. //
  1040. // The day portion of the until field can apparently either be a number or
  1041. // an occasion.
  1042. //
  1043. if (isdigit(*Field)) {
  1044. ScannedValue = strtol(Field, &AfterScan, 10);
  1045. if ((ScannedValue <= 0) || (ScannedValue > 31) ||
  1046. (AfterScan == Field)) {
  1047. fprintf(stderr,
  1048. "Error: Failed to parse Zone UNTIL DAY %s.\n",
  1049. Field);
  1050. Result = EILSEQ;
  1051. goto ProcessTimeZoneEnd;
  1052. }
  1053. Day = ScannedValue;
  1054. } else {
  1055. memset(&Occasion, 0, sizeof(TIME_ZONE_OCCASION));
  1056. Result = ParseTimeZoneOccasion(Field, &Occasion);
  1057. if (Result != 0) {
  1058. fprintf(stderr,
  1059. "Error: Failed to parse Zone UNTIL DAY (occasion) %s.\n",
  1060. Field);
  1061. goto ProcessTimeZoneEnd;
  1062. }
  1063. Result = CalculateOccasionForDate(&Occasion, Year, Month, &Day);
  1064. if (Result != 0) {
  1065. fprintf(stderr, "Error: Zone UNTIL DAY occasion does not exist.\n");
  1066. goto ProcessTimeZoneEnd;
  1067. }
  1068. }
  1069. ZoneEntry->Until += (Day - 1) * SECONDS_PER_DAY;
  1070. //
  1071. // Finally, if there is no time, this is done.
  1072. //
  1073. if (FieldCount <= ZoneFieldUntilTime - FieldOffset) {
  1074. Result = 0;
  1075. goto ProcessTimeZoneEnd;
  1076. }
  1077. Field = Fields[ZoneFieldUntilTime - FieldOffset];
  1078. Result = ParseTimeZoneTime(Field, &UntilTime, &UntilLens);
  1079. if (Result != 0) {
  1080. fprintf(stderr,
  1081. "Error: Failed to parse Zone UNTIL TIME %s.\n",
  1082. Field);
  1083. goto ProcessTimeZoneEnd;
  1084. }
  1085. ZoneEntry->Until += UntilTime;
  1086. if ((UntilLens == TimeZoneLensLocalTime) ||
  1087. (UntilLens == TimeZoneLensLocalStandardTime)) {
  1088. ZoneEntry->Until += ZoneEntry->GmtOffset;
  1089. if (UntilLens == TimeZoneLensLocalTime) {
  1090. ZoneEntry->Until += ZoneEntry->Save;
  1091. }
  1092. }
  1093. ProcessTimeZoneEnd:
  1094. if (Result != 0) {
  1095. if (ZoneEntry != NULL) {
  1096. DestroyTimeZoneEntry(ZoneEntry);
  1097. }
  1098. } else {
  1099. assert((ZoneEntry != NULL) && (Zone != NULL));
  1100. Zone->ZoneEntryCount += 1;
  1101. INSERT_BEFORE(&(ZoneEntry->ListEntry), &TimeZoneEntryList);
  1102. //
  1103. // If this is the last zone entry, attempt to compress by finding the
  1104. // same zone entries elsewhere.
  1105. //
  1106. if (UntilValid == FALSE) {
  1107. TimeZoneCompressEntries(&TimeZoneEntryList,
  1108. &TimeZoneNextZoneEntryIndex,
  1109. Zone);
  1110. }
  1111. }
  1112. *Continuation = UntilValid;
  1113. return Result;
  1114. }
  1115. INT
  1116. ProcessTimeZoneLink (
  1117. PSTR *Fields,
  1118. ULONG FieldCount
  1119. )
  1120. /*++
  1121. Routine Description:
  1122. This routine processes a time zone link line.
  1123. Arguments:
  1124. Fields - Supplies a pointer to the fields in the line.
  1125. FieldCount - Supplies the number of elements in the fields array.
  1126. Return Value:
  1127. 0 on success.
  1128. Non-zero on failure.
  1129. --*/
  1130. {
  1131. PTZC_LINK Link;
  1132. INT Result;
  1133. Link = NULL;
  1134. Result = ENOMEM;
  1135. if (FieldCount != LinkFieldCount) {
  1136. fprintf(stderr,
  1137. "Error: Link should have had %d fields, had %d.\n",
  1138. LinkFieldCount,
  1139. FieldCount);
  1140. return EILSEQ;
  1141. }
  1142. assert(strcasecmp(Fields[LinkFieldMagic], "Link") == 0);
  1143. Link = malloc(sizeof(TZC_LINK));
  1144. if (Link == NULL) {
  1145. goto ProcessTimeZoneLinkEnd;
  1146. }
  1147. memset(Link, 0, sizeof(TZC_LINK));
  1148. Link->From = strdup(Fields[LinkFieldFrom]);
  1149. if (Link->From == NULL) {
  1150. goto ProcessTimeZoneLinkEnd;
  1151. }
  1152. Link->To = strdup(Fields[LinkFieldTo]);
  1153. if (Link->To == NULL) {
  1154. goto ProcessTimeZoneLinkEnd;
  1155. }
  1156. INSERT_BEFORE(&(Link->ListEntry), &TimeZoneLinkList);
  1157. Result = 0;
  1158. ProcessTimeZoneLinkEnd:
  1159. if (Result != 0) {
  1160. if (Link != NULL) {
  1161. DestroyTimeZoneLink(Link);
  1162. }
  1163. }
  1164. return Result;
  1165. }
  1166. INT
  1167. ProcessTimeZoneLeap (
  1168. PSTR *Fields,
  1169. ULONG FieldCount
  1170. )
  1171. /*++
  1172. Routine Description:
  1173. This routine processes a time zone leap second line.
  1174. Arguments:
  1175. Fields - Supplies a pointer to the fields in the line.
  1176. FieldCount - Supplies the number of elements in the fields array.
  1177. Return Value:
  1178. 0 on success.
  1179. Non-zero on failure.
  1180. --*/
  1181. {
  1182. PSTR AfterScan;
  1183. INT Day;
  1184. PSTR Field;
  1185. PTZC_LEAP Leap;
  1186. INT LeapYear;
  1187. TIME_ZONE_MONTH Month;
  1188. INT Result;
  1189. LONG ScannedValue;
  1190. LONG Time;
  1191. INT Year;
  1192. Leap = NULL;
  1193. Result = ENOMEM;
  1194. if (FieldCount != LeapFieldCount) {
  1195. fprintf(stderr,
  1196. "Error: Link should have had %d fields, had %d.\n",
  1197. LinkFieldCount,
  1198. FieldCount);
  1199. return EILSEQ;
  1200. }
  1201. assert(strcasecmp(Fields[LeapFieldMagic], "Leap") == 0);
  1202. Leap = malloc(sizeof(TZC_LEAP));
  1203. if (Leap == NULL) {
  1204. goto ProcessTimeZoneLeapEnd;
  1205. }
  1206. memset(Leap, 0, sizeof(TZC_LEAP));
  1207. //
  1208. // Process the year.
  1209. //
  1210. Field = Fields[LeapFieldYear];
  1211. ScannedValue = strtol(Field, &AfterScan, 10);
  1212. if ((ScannedValue <= MIN_TIME_ZONE_YEAR) ||
  1213. (ScannedValue >= MAX_TIME_ZONE_YEAR) ||
  1214. (AfterScan == Field)) {
  1215. fprintf(stderr,
  1216. "Error: Failed to parse Leap YEAR %s.\n",
  1217. Field);
  1218. Result = EILSEQ;
  1219. goto ProcessTimeZoneLeapEnd;
  1220. }
  1221. Year = ScannedValue;
  1222. Leap->Date = (LONGLONG)ComputeDaysForYear(Year) * SECONDS_PER_DAY;
  1223. //
  1224. // Process the month.
  1225. //
  1226. LeapYear = 0;
  1227. if (IS_LEAP_YEAR(Year)) {
  1228. LeapYear = 1;
  1229. }
  1230. Field = Fields[LeapFieldMonth];
  1231. Result = ParseTimeZoneMonth(Field, &Month);
  1232. if (Result != 0) {
  1233. fprintf(stderr,
  1234. "Error: Failed to parse Leap MONTH %s.\n",
  1235. Field);
  1236. goto ProcessTimeZoneLeapEnd;
  1237. }
  1238. Leap->Date += TimeZoneMonthDays[LeapYear][Month] * SECONDS_PER_DAY;
  1239. //
  1240. // Process the month day.
  1241. //
  1242. Field = Fields[LeapFieldDay];
  1243. ScannedValue = strtol(Field, &AfterScan, 10);
  1244. if ((ScannedValue <= 0) || (ScannedValue > 31) ||
  1245. (AfterScan == Field)) {
  1246. fprintf(stderr,
  1247. "Error: Failed to parse Leap DAY %s.\n",
  1248. Field);
  1249. Result = EILSEQ;
  1250. goto ProcessTimeZoneLeapEnd;
  1251. }
  1252. Day = ScannedValue;
  1253. Leap->Date += (Day - 1) * SECONDS_PER_DAY;
  1254. //
  1255. // Process the time.
  1256. //
  1257. Field = Fields[LeapFieldTime];
  1258. Result = ParseTimeZoneTime(Field, &Time, NULL);
  1259. if (Result != 0) {
  1260. fprintf(stderr,
  1261. "Error: Failed to parse Leap TIME %s.\n",
  1262. Field);
  1263. goto ProcessTimeZoneLeapEnd;
  1264. }
  1265. Leap->Date += Time;
  1266. //
  1267. // Process the correction, which should be + or -.
  1268. //
  1269. Field = Fields[LeapFieldCorrection];
  1270. if (strcmp(Field, "+") == 0) {
  1271. Leap->Positive = TRUE;
  1272. } else if (strcmp(Field, "-") == 0) {
  1273. Leap->Positive = FALSE;
  1274. } else {
  1275. fprintf(stderr,
  1276. "Error: Failed to parse Leap CORRECTION %s.\n",
  1277. Field);
  1278. Result = EILSEQ;
  1279. goto ProcessTimeZoneLeapEnd;
  1280. }
  1281. //
  1282. // Process the Rolling/Stationary bit, which should be R or S.
  1283. //
  1284. Field = Fields[LeapFieldRollingOrStationary];
  1285. if (strcasecmp(Field, "R") == 0) {
  1286. Leap->LocalTime = TRUE;
  1287. } else if (strcasecmp(Field, "S") == 0) {
  1288. Leap->LocalTime = FALSE;
  1289. } else {
  1290. fprintf(stderr,
  1291. "Error: Failed to parse Leap R/S %s.\n",
  1292. Field);
  1293. Result = EILSEQ;
  1294. goto ProcessTimeZoneLeapEnd;
  1295. }
  1296. INSERT_BEFORE(&(Leap->ListEntry), &TimeZoneLeapList);
  1297. TimeZoneLeapCount += 1;
  1298. Result = 0;
  1299. ProcessTimeZoneLeapEnd:
  1300. if (Result != 0) {
  1301. if (Leap != NULL) {
  1302. DestroyTimeZoneLeap(Leap);
  1303. }
  1304. }
  1305. return Result;
  1306. }
  1307. INT
  1308. ReadTimeZoneFields (
  1309. FILE *File,
  1310. PVOID *LineBuffer,
  1311. PULONG LineBufferSize,
  1312. PSTR **Fields,
  1313. PULONG FieldsSize,
  1314. PULONG FieldCount,
  1315. PBOOL EndOfFile
  1316. )
  1317. /*++
  1318. Routine Description:
  1319. This routine reads a line in from the time zone file.
  1320. Arguments:
  1321. File - Supplies the file stream to read from.
  1322. LineBuffer - Supplies a pointer that on input points to an allocated buffer
  1323. (which can be null). On output, this buffer will be used for the
  1324. field values, and potentially realloced.
  1325. LineBufferSize - Supplies a pointer that on input contains the size of the
  1326. line buffer in bytes. On output this value will be updated to reflect
  1327. the new buffer allocation size.
  1328. Fields - Supplies a pointer that on input contains a pointer to an array
  1329. of pointers to strings. On output, the pointers to the various fields
  1330. of the line will be returned here. The entire buffer may be realloced
  1331. for large field counts.
  1332. FieldsSize - Supplies a pointer that on input contains the size of the
  1333. fields buffer in bytes. This value will be updated on output.
  1334. FieldCount - Supplies a pointer where the number of fields in this line
  1335. will be returned.
  1336. EndOfFile - Supplies a pointer where a boolean will be returned indicating
  1337. if the end of the input file was reached.
  1338. Return Value:
  1339. 0 on success.
  1340. Non-zero on failure.
  1341. --*/
  1342. {
  1343. ULONG ElementCount;
  1344. ULONG FieldsCapacity;
  1345. BOOL InQuote;
  1346. PSTR Line;
  1347. PSTR *LineFields;
  1348. PVOID NewBuffer;
  1349. ULONG NewCapacity;
  1350. INT Result;
  1351. ElementCount = 0;
  1352. FieldsCapacity = *FieldsSize;
  1353. LineFields = *Fields;
  1354. Result = ReadTimeZoneLine(File, LineBuffer, LineBufferSize, EndOfFile);
  1355. if (Result != 0) {
  1356. goto ReadTimeZoneFieldsEnd;
  1357. }
  1358. //
  1359. // Loop delimiting fields.
  1360. //
  1361. Line = *LineBuffer;
  1362. while (*Line != '\0') {
  1363. //
  1364. // Swoop past leading spaces.
  1365. //
  1366. while (isspace(*Line)) {
  1367. Line += 1;
  1368. }
  1369. //
  1370. // If the next character is a terminator or # (comment), then this line
  1371. // is toast.
  1372. //
  1373. if ((*Line == '\0') || (*Line == '#')) {
  1374. break;
  1375. }
  1376. //
  1377. // Reallocate the fields buffer if needed.
  1378. //
  1379. if ((ElementCount * sizeof(PSTR)) >= FieldsCapacity) {
  1380. NewCapacity = FieldsCapacity;
  1381. if (NewCapacity == 0) {
  1382. NewCapacity = INITIAL_MALLOC_SIZE;
  1383. } else {
  1384. NewCapacity *= 2;
  1385. }
  1386. assert(NewCapacity > (ElementCount * sizeof(PSTR)));
  1387. NewBuffer = realloc(LineFields, NewCapacity);
  1388. if (NewBuffer == NULL) {
  1389. Result = ENOMEM;
  1390. goto ReadTimeZoneFieldsEnd;
  1391. }
  1392. FieldsCapacity = NewCapacity;
  1393. LineFields = NewBuffer;
  1394. }
  1395. //
  1396. // Set the entry in the fields array.
  1397. //
  1398. LineFields[ElementCount] = Line;
  1399. ElementCount += 1;
  1400. //
  1401. // Find the end of the field.
  1402. //
  1403. InQuote = FALSE;
  1404. while ((*Line != '\0') && ((!isspace(*Line)) || (InQuote != FALSE))) {
  1405. if (InQuote != FALSE) {
  1406. if (*Line == '"') {
  1407. InQuote = FALSE;
  1408. }
  1409. } else {
  1410. if (*Line == '"') {
  1411. InQuote = TRUE;
  1412. }
  1413. }
  1414. Line += 1;
  1415. }
  1416. //
  1417. // Terminate the field.
  1418. //
  1419. if (*Line == '\0') {
  1420. break;
  1421. }
  1422. *Line = '\0';
  1423. Line += 1;
  1424. }
  1425. Result = 0;
  1426. ReadTimeZoneFieldsEnd:
  1427. *FieldCount = ElementCount;
  1428. *FieldsSize = FieldsCapacity;
  1429. *Fields = LineFields;
  1430. return Result;
  1431. }
  1432. INT
  1433. ReadTimeZoneLine (
  1434. FILE *File,
  1435. PVOID *LineBuffer,
  1436. PULONG LineBufferSize,
  1437. PBOOL EndOfFile
  1438. )
  1439. /*++
  1440. Routine Description:
  1441. This routine reads a line in from the time zone file.
  1442. Arguments:
  1443. File - Supplies the file stream to read from.
  1444. LineBuffer - Supplies a pointer that on input points to an allocated buffer
  1445. (which can be null). On output, this buffer will contain the null
  1446. terminated line.
  1447. LineBufferSize - Supplies a pointer that on input contains the size of the
  1448. line buffer in bytes. On output this value will be updated to reflect
  1449. the new buffer allocation size.
  1450. EndOfFile - Supplies a pointer where a boolean will be returned indicating
  1451. if the end of file was hit.
  1452. Return Value:
  1453. 0 on success.
  1454. Non-zero on failure.
  1455. --*/
  1456. {
  1457. INT Character;
  1458. BOOL EndOfLine;
  1459. ULONG Length;
  1460. PSTR Line;
  1461. ULONG LineCapacity;
  1462. PVOID NewBuffer;
  1463. ULONG NewLineCapacity;
  1464. INT Result;
  1465. *EndOfFile = FALSE;
  1466. EndOfLine = FALSE;
  1467. Line = *LineBuffer;
  1468. LineCapacity = *LineBufferSize;
  1469. Length = 0;
  1470. while (TRUE) {
  1471. //
  1472. // Read a character from the file.
  1473. //
  1474. Character = fgetc(File);
  1475. if (Character == EOF) {
  1476. if (feof(File) != 0) {
  1477. *EndOfFile = TRUE;
  1478. EndOfLine = TRUE;
  1479. Character = '\0';
  1480. } else {
  1481. fprintf(stderr, "Error reading file: %s.\n", strerror(errno));
  1482. Result = errno;
  1483. goto ReadTimeZoneLineEnd;
  1484. }
  1485. }
  1486. //
  1487. // Reallocate the buffer if it's too small to hold this character.
  1488. //
  1489. if (Length >= LineCapacity) {
  1490. NewLineCapacity = LineCapacity;
  1491. if (NewLineCapacity == 0) {
  1492. NewLineCapacity = INITIAL_MALLOC_SIZE;
  1493. } else {
  1494. NewLineCapacity *= 2;
  1495. }
  1496. assert(NewLineCapacity > Length);
  1497. NewBuffer = realloc(Line, NewLineCapacity);
  1498. if (NewBuffer == NULL) {
  1499. Result = ENOMEM;
  1500. goto ReadTimeZoneLineEnd;
  1501. }
  1502. LineCapacity = NewLineCapacity;
  1503. Line = NewBuffer;
  1504. }
  1505. //
  1506. // Terminate if this is a newline.
  1507. //
  1508. if (Character == '\n') {
  1509. Character = '\0';
  1510. EndOfLine = TRUE;
  1511. }
  1512. //
  1513. // Add the line to the buffer.
  1514. //
  1515. Line[Length] = Character;
  1516. Length += 1;
  1517. if (EndOfLine != FALSE) {
  1518. break;
  1519. }
  1520. }
  1521. Result = 0;
  1522. ReadTimeZoneLineEnd:
  1523. *LineBuffer = Line;
  1524. *LineBufferSize = LineCapacity;
  1525. return Result;
  1526. }
  1527. INT
  1528. TranslateLinksToZones (
  1529. )
  1530. /*++
  1531. Routine Description:
  1532. This routine converts time zone links into time zone structures.
  1533. Arguments:
  1534. None.
  1535. Return Value:
  1536. 0 on success.
  1537. ENOMEM on allocation failure.
  1538. --*/
  1539. {
  1540. PLIST_ENTRY CurrentEntry;
  1541. PLIST_ENTRY CurrentZoneEntry;
  1542. PTZC_ZONE DestinationZone;
  1543. PTZC_LINK Link;
  1544. INT Result;
  1545. PTZC_ZONE Zone;
  1546. PSTR ZoneName;
  1547. CurrentEntry = TimeZoneLinkList.Next;
  1548. while (CurrentEntry != &TimeZoneLinkList) {
  1549. Link = LIST_VALUE(CurrentEntry, TZC_LINK, ListEntry);
  1550. CurrentEntry = CurrentEntry->Next;
  1551. //
  1552. // Loop through all the zones looking for the destination.
  1553. //
  1554. DestinationZone = NULL;
  1555. CurrentZoneEntry = TimeZoneList.Next;
  1556. while (CurrentZoneEntry != &TimeZoneList) {
  1557. DestinationZone = LIST_VALUE(CurrentZoneEntry, TZC_ZONE, ListEntry);
  1558. CurrentZoneEntry = CurrentZoneEntry->Next;
  1559. ZoneName = TimeZoneGetString(&TimeZoneStringList,
  1560. DestinationZone->NameOffset);
  1561. if (strcmp(ZoneName, Link->From) == 0) {
  1562. break;
  1563. }
  1564. DestinationZone = NULL;
  1565. }
  1566. if (DestinationZone == NULL) {
  1567. fprintf(stderr,
  1568. "tzcomp: Warning: Link destination time zone %s not "
  1569. "found. Source (%s).\n",
  1570. Link->From,
  1571. Link->To);
  1572. continue;
  1573. }
  1574. //
  1575. // Create a time zone structure and initialize it based on the
  1576. // destination.
  1577. //
  1578. Zone = malloc(sizeof(TZC_ZONE));
  1579. if (Zone == NULL) {
  1580. return ENOMEM;
  1581. }
  1582. memset(Zone, 0, sizeof(TZC_ZONE));
  1583. Result = TimeZoneAddString(Link->To, &(Zone->NameOffset));
  1584. if (Result != 0) {
  1585. free(Zone);
  1586. return Result;
  1587. }
  1588. Zone->ZoneEntryIndex = DestinationZone->ZoneEntryIndex;
  1589. Zone->ZoneEntryCount = DestinationZone->ZoneEntryCount;
  1590. INSERT_BEFORE(&(Zone->ListEntry), &TimeZoneList);
  1591. TimeZoneCount += 1;
  1592. }
  1593. return 0;
  1594. }
  1595. INT
  1596. TimeZoneFilter (
  1597. PSTR Name,
  1598. INT Year
  1599. )
  1600. /*++
  1601. Routine Description:
  1602. This routine removes all time zone data from the global list except for
  1603. that that matches the given time zone name and/or starts after the given
  1604. year.
  1605. Arguments:
  1606. Name - Supplies an optional pointer to a string containing the name of the
  1607. time zone to keep.
  1608. Year - Supplies an optional year before which to exclude any time zone
  1609. entries.
  1610. Return Value:
  1611. 0 on success.
  1612. Returns an error number on failure.
  1613. --*/
  1614. {
  1615. PLIST_ENTRY CurrentEntry;
  1616. PLIST_ENTRY CurrentZoneEntry;
  1617. ULONG CurrentZoneEntryCount;
  1618. ULONG EntryIndex;
  1619. PSTR FormatString;
  1620. PSTR LettersString;
  1621. LIST_ENTRY NewEntryList;
  1622. LIST_ENTRY NewRuleList;
  1623. LIST_ENTRY NewStringList;
  1624. ULONG NewStringListSize;
  1625. PTZC_ZONE_ENTRY NewZoneEntry;
  1626. LIST_ENTRY NewZoneList;
  1627. PTZC_ZONE RemoveZone;
  1628. INT Result;
  1629. PTZC_RULE Rule;
  1630. ULONG RuleCount;
  1631. PLIST_ENTRY RuleEntry;
  1632. LONGLONG Until;
  1633. ULONG UnusedOffset;
  1634. PTZC_ZONE Zone;
  1635. PTZC_ZONE_ENTRY ZoneEntry;
  1636. ULONG ZoneEntryCount;
  1637. PSTR ZoneName;
  1638. ULONG ZonesAdded;
  1639. INITIALIZE_LIST_HEAD(&NewEntryList);
  1640. INITIALIZE_LIST_HEAD(&NewRuleList);
  1641. INITIALIZE_LIST_HEAD(&NewStringList);
  1642. INITIALIZE_LIST_HEAD(&NewZoneList);
  1643. NewStringListSize = 0;
  1644. RuleCount = 0;
  1645. Zone = NULL;
  1646. ZoneEntryCount = 0;
  1647. ZoneName = NULL;
  1648. Until = (LONGLONG)ComputeDaysForYear(Year) * SECONDS_PER_DAY;
  1649. TimeZoneAddStringToList("",
  1650. &NewStringList,
  1651. &NewStringListSize,
  1652. TRUE,
  1653. &UnusedOffset);
  1654. //
  1655. // Loop looking for time zones that match the name and the year.
  1656. //
  1657. ZonesAdded = 0;
  1658. CurrentZoneEntry = TimeZoneList.Next;
  1659. while (CurrentZoneEntry != &TimeZoneList) {
  1660. Zone = LIST_VALUE(CurrentZoneEntry, TZC_ZONE, ListEntry);
  1661. CurrentZoneEntry = CurrentZoneEntry->Next;
  1662. ZoneName = TimeZoneGetString(&TimeZoneStringList, Zone->NameOffset);
  1663. if (Name != NULL) {
  1664. if (strcasecmp(Name, ZoneName) != 0) {
  1665. continue;
  1666. }
  1667. }
  1668. //
  1669. // Find the starting zone entry.
  1670. //
  1671. CurrentEntry = TimeZoneEntryList.Next;
  1672. for (EntryIndex = 0;
  1673. EntryIndex < Zone->ZoneEntryIndex;
  1674. EntryIndex += 1) {
  1675. assert(CurrentEntry != &TimeZoneEntryList);
  1676. CurrentEntry = CurrentEntry->Next;
  1677. }
  1678. //
  1679. // Pull the zone entries onto the new list.
  1680. //
  1681. CurrentZoneEntryCount = 0;
  1682. Zone->ZoneEntryIndex = ZoneEntryCount;
  1683. for (EntryIndex = 0;
  1684. EntryIndex < Zone->ZoneEntryCount;
  1685. EntryIndex += 1) {
  1686. ZoneEntry = LIST_VALUE(CurrentEntry, TZC_ZONE_ENTRY, ListEntry);
  1687. assert(CurrentEntry != &TimeZoneEntryList);
  1688. CurrentEntry = CurrentEntry->Next;
  1689. //
  1690. // Skip an entry that is too old.
  1691. //
  1692. if (ZoneEntry->Until <= Until) {
  1693. continue;
  1694. }
  1695. NewZoneEntry = malloc(sizeof(TZC_ZONE_ENTRY));
  1696. if (NewZoneEntry == NULL) {
  1697. Result = ENOMEM;
  1698. goto TimeZoneFilterByNameEnd;
  1699. }
  1700. memcpy(NewZoneEntry, ZoneEntry, sizeof(TZC_ZONE_ENTRY));
  1701. INSERT_BEFORE(&(NewZoneEntry->ListEntry), &NewEntryList);
  1702. NewZoneEntry->Index = ZoneEntryCount;
  1703. ZoneEntryCount += 1;
  1704. CurrentZoneEntryCount += 1;
  1705. FormatString = TimeZoneGetString(&TimeZoneStringList,
  1706. ZoneEntry->FormatOffset);
  1707. Result = TimeZoneAddStringToList(FormatString,
  1708. &NewStringList,
  1709. &NewStringListSize,
  1710. TRUE,
  1711. &(NewZoneEntry->FormatOffset));
  1712. if (Result != 0) {
  1713. goto TimeZoneFilterByNameEnd;
  1714. }
  1715. //
  1716. // Loop through and pull off any rules that apply to this zone
  1717. // entry.
  1718. //
  1719. if (NewZoneEntry->RulesNameIndex != -1) {
  1720. RuleEntry = TimeZoneRuleList.Next;
  1721. while (RuleEntry != &TimeZoneRuleList) {
  1722. Rule = LIST_VALUE(RuleEntry, TZC_RULE, ListEntry);
  1723. RuleEntry = RuleEntry->Next;
  1724. if (Rule->NameIndex == NewZoneEntry->RulesNameIndex) {
  1725. if (Rule->To <= Year) {
  1726. continue;
  1727. }
  1728. LettersString = TimeZoneGetString(&TimeZoneStringList,
  1729. Rule->LettersOffset);
  1730. Result = TimeZoneAddStringToList(
  1731. LettersString,
  1732. &NewStringList,
  1733. &NewStringListSize,
  1734. TRUE,
  1735. &(Rule->LettersOffset));
  1736. if (Result != 0) {
  1737. goto TimeZoneFilterByNameEnd;
  1738. }
  1739. LIST_REMOVE(&(Rule->ListEntry));
  1740. INSERT_BEFORE(&(Rule->ListEntry), &NewRuleList);
  1741. RuleCount += 1;
  1742. }
  1743. }
  1744. }
  1745. }
  1746. if (CurrentZoneEntryCount != 0) {
  1747. Zone->ZoneEntryCount = CurrentZoneEntryCount;
  1748. LIST_REMOVE(&(Zone->ListEntry));
  1749. INSERT_BEFORE(&(Zone->ListEntry), &NewZoneList);
  1750. ZonesAdded += 1;
  1751. //
  1752. // Add the zone name to the string table.
  1753. //
  1754. Result = TimeZoneAddStringToList(ZoneName,
  1755. &NewStringList,
  1756. &NewStringListSize,
  1757. TRUE,
  1758. &(Zone->NameOffset));
  1759. if (Result != 0) {
  1760. goto TimeZoneFilterByNameEnd;
  1761. }
  1762. TimeZoneCompressEntries(&NewEntryList,
  1763. &ZoneEntryCount,
  1764. Zone);
  1765. }
  1766. }
  1767. //
  1768. // Fail if the time zone was not found.
  1769. //
  1770. if (ZonesAdded == 0) {
  1771. Result = EINVAL;
  1772. if (Name != NULL) {
  1773. fprintf(stderr,
  1774. "Error: Could not find time zone \"%s\" after year %d.\n",
  1775. Name,
  1776. Year);
  1777. } else {
  1778. fprintf(stderr,
  1779. "Error: No time zones after year %d.\n",
  1780. Year);
  1781. }
  1782. goto TimeZoneFilterByNameEnd;
  1783. }
  1784. //
  1785. // Destroy all the other zones, rules, and zone entries.
  1786. //
  1787. while (LIST_EMPTY(&TimeZoneRuleList) == FALSE) {
  1788. Rule = LIST_VALUE(TimeZoneRuleList.Next, TZC_RULE, ListEntry);
  1789. LIST_REMOVE(&(Rule->ListEntry));
  1790. DestroyTimeZoneRule(Rule);
  1791. }
  1792. while (LIST_EMPTY(&TimeZoneList) == FALSE) {
  1793. RemoveZone = LIST_VALUE(TimeZoneList.Next, TZC_ZONE, ListEntry);
  1794. LIST_REMOVE(&(RemoveZone->ListEntry));
  1795. DestroyTimeZone(RemoveZone);
  1796. }
  1797. while (LIST_EMPTY(&TimeZoneEntryList) == FALSE) {
  1798. ZoneEntry = LIST_VALUE(TimeZoneEntryList.Next,
  1799. TZC_ZONE_ENTRY,
  1800. ListEntry);
  1801. LIST_REMOVE(&(ZoneEntry->ListEntry));
  1802. DestroyTimeZoneEntry(ZoneEntry);
  1803. }
  1804. DestroyTimeZoneStringList(&TimeZoneStringList);
  1805. //
  1806. // Now insert all entries from the local lists onto the global lists.
  1807. //
  1808. if (!LIST_EMPTY(&NewRuleList)) {
  1809. MOVE_LIST(&NewRuleList, &TimeZoneRuleList);
  1810. INITIALIZE_LIST_HEAD(&NewRuleList);
  1811. }
  1812. TimeZoneRuleCount = RuleCount;
  1813. if (!LIST_EMPTY(&NewEntryList)) {
  1814. MOVE_LIST(&NewEntryList, &TimeZoneEntryList);
  1815. INITIALIZE_LIST_HEAD(&NewEntryList);
  1816. }
  1817. TimeZoneNextZoneEntryIndex = ZoneEntryCount;
  1818. if (!LIST_EMPTY(&NewStringList)) {
  1819. MOVE_LIST(&NewStringList, &TimeZoneStringList);
  1820. INITIALIZE_LIST_HEAD(&NewStringList);
  1821. }
  1822. TimeZoneNextStringOffset = NewStringListSize;
  1823. if (!LIST_EMPTY(&NewZoneList)) {
  1824. MOVE_LIST(&NewZoneList, &TimeZoneList);
  1825. INITIALIZE_LIST_HEAD(&NewZoneList);
  1826. }
  1827. TimeZoneCount = ZonesAdded;
  1828. Result = 0;
  1829. TimeZoneFilterByNameEnd:
  1830. while (LIST_EMPTY(&NewRuleList) == FALSE) {
  1831. Rule = LIST_VALUE(NewRuleList.Next, TZC_RULE, ListEntry);
  1832. LIST_REMOVE(&(Rule->ListEntry));
  1833. DestroyTimeZoneRule(Rule);
  1834. }
  1835. while (LIST_EMPTY(&NewEntryList) == FALSE) {
  1836. ZoneEntry = LIST_VALUE(NewEntryList.Next,
  1837. TZC_ZONE_ENTRY,
  1838. ListEntry);
  1839. LIST_REMOVE(&(ZoneEntry->ListEntry));
  1840. DestroyTimeZoneEntry(ZoneEntry);
  1841. }
  1842. while (LIST_EMPTY(&NewZoneList) == FALSE) {
  1843. Zone = LIST_VALUE(NewZoneList.Next, TZC_ZONE, ListEntry);
  1844. LIST_REMOVE(&(Zone->ListEntry));
  1845. DestroyTimeZone(Zone);
  1846. }
  1847. DestroyTimeZoneStringList(&NewStringList);
  1848. return Result;
  1849. }
  1850. INT
  1851. WriteTimeZoneData (
  1852. PSTR FileName
  1853. )
  1854. /*++
  1855. Routine Description:
  1856. This routine writes the time zone data out to a file in binary format.
  1857. Arguments:
  1858. FileName - Supplies a pointer to a string containing the name of the file
  1859. to write.
  1860. Return Value:
  1861. 0 on success.
  1862. Returns an error number on failure.
  1863. --*/
  1864. {
  1865. size_t BytesWritten;
  1866. PLIST_ENTRY CurrentEntry;
  1867. ULONG ElementsWritten;
  1868. FILE *File;
  1869. TIME_ZONE_LEAP_SECOND FileLeap;
  1870. TIME_ZONE_RULE FileRule;
  1871. TIME_ZONE FileZone;
  1872. TIME_ZONE_ENTRY FileZoneEntry;
  1873. TIME_ZONE_HEADER Header;
  1874. PTZC_LEAP Leap;
  1875. INT Result;
  1876. PTZC_RULE Rule;
  1877. PTZC_STRING String;
  1878. size_t StringLength;
  1879. PTZC_ZONE Zone;
  1880. PTZC_ZONE_ENTRY ZoneEntry;
  1881. memset(&Header, 0, sizeof(TIME_ZONE_HEADER));
  1882. File = fopen(FileName, "wb");
  1883. if (File == NULL) {
  1884. Result = errno;
  1885. fprintf(stderr,
  1886. "tzcomp: Failed to open output file \"%s\": %s.\n",
  1887. FileName,
  1888. strerror(Result));
  1889. goto WriteTimeZoneDataEnd;
  1890. }
  1891. //
  1892. // Write out the header.
  1893. //
  1894. Header.Magic = TIME_ZONE_HEADER_MAGIC;
  1895. Header.RuleOffset = sizeof(TIME_ZONE_HEADER);
  1896. Header.RuleCount = TimeZoneRuleCount;
  1897. Header.ZoneOffset = Header.RuleOffset +
  1898. (Header.RuleCount * sizeof(TIME_ZONE_RULE));
  1899. Header.ZoneCount = TimeZoneCount;
  1900. Header.ZoneEntryOffset = Header.ZoneOffset +
  1901. (Header.ZoneCount * sizeof(TIME_ZONE));
  1902. Header.ZoneEntryCount = TimeZoneNextZoneEntryIndex;
  1903. Header.LeapOffset = Header.ZoneEntryOffset +
  1904. (Header.ZoneEntryCount * sizeof(TIME_ZONE_ENTRY));
  1905. Header.LeapCount = TimeZoneLeapCount;
  1906. Header.StringsOffset = Header.LeapOffset +
  1907. (Header.LeapCount * sizeof(TIME_ZONE_LEAP_SECOND));
  1908. Header.StringsSize = TimeZoneNextStringOffset;
  1909. BytesWritten = fwrite(&Header, 1, sizeof(TIME_ZONE_HEADER), File);
  1910. if (BytesWritten <= 0) {
  1911. Result = errno;
  1912. fprintf(stderr, "tzcomp: Write error: %s.\n", strerror(Result));
  1913. goto WriteTimeZoneDataEnd;
  1914. }
  1915. assert(ftell(File) == Header.RuleOffset);
  1916. //
  1917. // Write out the rules.
  1918. //
  1919. memset(&FileRule, 0, sizeof(TIME_ZONE_RULE));
  1920. ElementsWritten = 0;
  1921. CurrentEntry = TimeZoneRuleList.Next;
  1922. while (CurrentEntry != &TimeZoneRuleList) {
  1923. Rule = LIST_VALUE(CurrentEntry, TZC_RULE, ListEntry);
  1924. CurrentEntry = CurrentEntry->Next;
  1925. FileRule.Number = Rule->NameIndex;
  1926. FileRule.From = Rule->From;
  1927. FileRule.To = Rule->To;
  1928. FileRule.Month = Rule->Month;
  1929. FileRule.On = Rule->On;
  1930. FileRule.At = Rule->At;
  1931. FileRule.AtLens = Rule->AtLens;
  1932. FileRule.Save = Rule->Save;
  1933. FileRule.Letters = Rule->LettersOffset;
  1934. BytesWritten = fwrite(&FileRule, 1, sizeof(TIME_ZONE_RULE), File);
  1935. if (BytesWritten <= 0) {
  1936. Result = errno;
  1937. fprintf(stderr, "tzcomp: Write error: %s.\n", strerror(Result));
  1938. goto WriteTimeZoneDataEnd;
  1939. }
  1940. ElementsWritten += 1;
  1941. }
  1942. assert(ElementsWritten == Header.RuleCount);
  1943. assert(ftell(File) == Header.ZoneOffset);
  1944. //
  1945. // Write out the zones.
  1946. //
  1947. memset(&FileZone, 0, sizeof(TIME_ZONE));
  1948. ElementsWritten = 0;
  1949. CurrentEntry = TimeZoneList.Next;
  1950. while (CurrentEntry != &TimeZoneList) {
  1951. Zone = LIST_VALUE(CurrentEntry, TZC_ZONE, ListEntry);
  1952. CurrentEntry = CurrentEntry->Next;
  1953. FileZone.Name = Zone->NameOffset;
  1954. FileZone.EntryIndex = Zone->ZoneEntryIndex;
  1955. FileZone.EntryCount = Zone->ZoneEntryCount;
  1956. BytesWritten = fwrite(&FileZone, 1, sizeof(TIME_ZONE), File);
  1957. if (BytesWritten <= 0) {
  1958. Result = errno;
  1959. fprintf(stderr, "tzcomp: Write error: %s.\n", strerror(Result));
  1960. goto WriteTimeZoneDataEnd;
  1961. }
  1962. ElementsWritten += 1;
  1963. }
  1964. assert(ElementsWritten == Header.ZoneCount);
  1965. assert(ftell(File) == Header.ZoneEntryOffset);
  1966. //
  1967. // Write out the zone entries.
  1968. //
  1969. memset(&FileZoneEntry, 0, sizeof(TIME_ZONE_ENTRY));
  1970. ElementsWritten = 0;
  1971. CurrentEntry = TimeZoneEntryList.Next;
  1972. while (CurrentEntry != &TimeZoneEntryList) {
  1973. ZoneEntry = LIST_VALUE(CurrentEntry, TZC_ZONE_ENTRY, ListEntry);
  1974. CurrentEntry = CurrentEntry->Next;
  1975. FileZoneEntry.GmtOffset = ZoneEntry->GmtOffset;
  1976. FileZoneEntry.Rules = ZoneEntry->RulesNameIndex;
  1977. FileZoneEntry.Save = ZoneEntry->Save;
  1978. FileZoneEntry.Format = ZoneEntry->FormatOffset;
  1979. FileZoneEntry.Until = ZoneEntry->Until;
  1980. BytesWritten = fwrite(&FileZoneEntry, 1, sizeof(TIME_ZONE_ENTRY), File);
  1981. if (BytesWritten <= 0) {
  1982. Result = errno;
  1983. fprintf(stderr, "tzcomp: Write error: %s.\n", strerror(Result));
  1984. goto WriteTimeZoneDataEnd;
  1985. }
  1986. ElementsWritten += 1;
  1987. }
  1988. assert(ElementsWritten == Header.ZoneEntryCount);
  1989. assert(ftell(File) == Header.LeapOffset);
  1990. //
  1991. // Write out the leap seconds.
  1992. //
  1993. memset(&FileLeap, 0, sizeof(TIME_ZONE_LEAP_SECOND));
  1994. ElementsWritten = 0;
  1995. CurrentEntry = TimeZoneLeapList.Next;
  1996. while (CurrentEntry != &TimeZoneLeapList) {
  1997. Leap = LIST_VALUE(CurrentEntry, TZC_LEAP, ListEntry);
  1998. CurrentEntry = CurrentEntry->Next;
  1999. FileLeap.Date = Leap->Date;
  2000. FileLeap.Positive = Leap->Positive;
  2001. FileLeap.LocalTime = Leap->LocalTime;
  2002. BytesWritten = fwrite(&FileLeap,
  2003. 1,
  2004. sizeof(TIME_ZONE_LEAP_SECOND),
  2005. File);
  2006. if (BytesWritten <= 0) {
  2007. Result = errno;
  2008. fprintf(stderr, "tzcomp: Write error: %s.\n", strerror(Result));
  2009. goto WriteTimeZoneDataEnd;
  2010. }
  2011. ElementsWritten += 1;
  2012. }
  2013. assert(ElementsWritten == Header.LeapCount);
  2014. assert(ftell(File) == Header.StringsOffset);
  2015. //
  2016. // Write out the string table.
  2017. //
  2018. ElementsWritten = 0;
  2019. CurrentEntry = TimeZoneStringList.Next;
  2020. while (CurrentEntry != &TimeZoneStringList) {
  2021. String = LIST_VALUE(CurrentEntry, TZC_STRING, ListEntry);
  2022. CurrentEntry = CurrentEntry->Next;
  2023. StringLength = strlen(String->String) + 1;
  2024. BytesWritten = fwrite(String->String, 1, StringLength, File);
  2025. if (BytesWritten <= 0) {
  2026. Result = errno;
  2027. fprintf(stderr, "tzcomp: Write error: %s.\n", strerror(Result));
  2028. goto WriteTimeZoneDataEnd;
  2029. }
  2030. ElementsWritten += StringLength;
  2031. }
  2032. assert(ElementsWritten == Header.StringsSize);
  2033. Result = 0;
  2034. WriteTimeZoneDataEnd:
  2035. if (File != NULL) {
  2036. fclose(File);
  2037. }
  2038. return Result;
  2039. }
  2040. VOID
  2041. DestroyTimeZoneRule (
  2042. PTZC_RULE Rule
  2043. )
  2044. /*++
  2045. Routine Description:
  2046. This routine frees all memory associated with a time zone rule. This
  2047. routine assumes the rule has already been pulled off of any list it was on.
  2048. Arguments:
  2049. Rule - Supplies a pointer to the rule to destroy.
  2050. Return Value:
  2051. None.
  2052. --*/
  2053. {
  2054. free(Rule);
  2055. return;
  2056. }
  2057. VOID
  2058. DestroyTimeZone (
  2059. PTZC_ZONE Zone
  2060. )
  2061. /*++
  2062. Routine Description:
  2063. This routine destroys a time zone (the structure, of course). This routine
  2064. assumes the zone has already been pulled off of any list it was on.
  2065. Arguments:
  2066. Zone - Supplies a pointer to the zone to destroy.
  2067. Return Value:
  2068. None.
  2069. --*/
  2070. {
  2071. free(Zone);
  2072. return;
  2073. }
  2074. VOID
  2075. DestroyTimeZoneEntry (
  2076. PTZC_ZONE_ENTRY ZoneEntry
  2077. )
  2078. /*++
  2079. Routine Description:
  2080. This routine destroys a time zone entry. This routine assumes the entry has
  2081. already been pulled off of any list it was on.
  2082. Arguments:
  2083. ZoneEntry - Supplies a pointer to the zone entry to destroy.
  2084. Return Value:
  2085. None.
  2086. --*/
  2087. {
  2088. free(ZoneEntry);
  2089. return;
  2090. }
  2091. VOID
  2092. DestroyTimeZoneLink (
  2093. PTZC_LINK Link
  2094. )
  2095. /*++
  2096. Routine Description:
  2097. This routine destroys a time zone link. This routine assumes the structure
  2098. has already been pulled off of any list it was on.
  2099. Arguments:
  2100. Link - Supplies a pointer to the zone link to destroy.
  2101. Return Value:
  2102. None.
  2103. --*/
  2104. {
  2105. if (Link->From != NULL) {
  2106. free(Link->From);
  2107. }
  2108. if (Link->To != NULL) {
  2109. free(Link->To);
  2110. }
  2111. free(Link);
  2112. return;
  2113. }
  2114. VOID
  2115. DestroyTimeZoneLeap (
  2116. PTZC_LEAP Leap
  2117. )
  2118. /*++
  2119. Routine Description:
  2120. This routine destroys a time zone leap second structure. This routine
  2121. assumes the structure has already been pulled off of any list it was on.
  2122. Arguments:
  2123. Leap - Supplies a pointer to the leap second to destroy.
  2124. Return Value:
  2125. None.
  2126. --*/
  2127. {
  2128. free(Leap);
  2129. return;
  2130. }
  2131. VOID
  2132. DestroyTimeZoneStringList (
  2133. PLIST_ENTRY ListHead
  2134. )
  2135. /*++
  2136. Routine Description:
  2137. This routine destroys a time zone string list.
  2138. Arguments:
  2139. ListHead - Supplies a pointer to the head of the list.
  2140. Return Value:
  2141. None.
  2142. --*/
  2143. {
  2144. PTZC_STRING StringEntry;
  2145. //
  2146. // First search for the string.
  2147. //
  2148. while (LIST_EMPTY(ListHead) == FALSE) {
  2149. StringEntry = LIST_VALUE(ListHead->Next, TZC_STRING, ListEntry);
  2150. LIST_REMOVE(&(StringEntry->ListEntry));
  2151. free(StringEntry->String);
  2152. free(StringEntry);
  2153. }
  2154. return;
  2155. }
  2156. INT
  2157. ParseTimeZoneRuleLimit (
  2158. PSTR Field,
  2159. SHORT OnlyValue,
  2160. PSHORT Value
  2161. )
  2162. /*++
  2163. Routine Description:
  2164. This routine parses a rule limit (the FROM and TO fields of the rule).
  2165. Valid values are a year, Minimum, Maximum, and Only (with abbreviations).
  2166. Arguments:
  2167. Field - Supplies a pointer to the field.
  2168. OnlyValue - Supplies the value to return if the field has "only" in it.
  2169. Value - Supplies a pointer where the value will be returned on success.
  2170. Return Value:
  2171. 0 on success.
  2172. Non-zero on failure.
  2173. --*/
  2174. {
  2175. PSTR AfterScan;
  2176. INT Result;
  2177. LONG ScannedValue;
  2178. Result = 0;
  2179. if ((strcasecmp(Field, "Minimum") == 0) ||
  2180. (strcasecmp(Field, "Min") == 0)) {
  2181. *Value = MIN_TIME_ZONE_YEAR;
  2182. } else if ((strcasecmp(Field, "Maximum") == 0) ||
  2183. (strcasecmp(Field, "Max") == 0)) {
  2184. *Value = MAX_TIME_ZONE_YEAR;
  2185. } else if (strcasecmp(Field, "Only") == 0) {
  2186. *Value = OnlyValue;
  2187. } else {
  2188. ScannedValue = strtol(Field, &AfterScan, 10);
  2189. if ((ScannedValue < MIN_TIME_ZONE_YEAR) ||
  2190. (ScannedValue > MAX_TIME_ZONE_YEAR) ||
  2191. (AfterScan == Field)) {
  2192. fprintf(stderr, "Error: Cannot parse rule limit %s.\n", Field);
  2193. Result = EILSEQ;
  2194. }
  2195. *Value = ScannedValue;
  2196. }
  2197. return Result;
  2198. }
  2199. INT
  2200. ParseTimeZoneMonth (
  2201. PSTR Field,
  2202. PTIME_ZONE_MONTH Value
  2203. )
  2204. /*++
  2205. Routine Description:
  2206. This routine parses a month (such as in the Rule IN column).
  2207. Arguments:
  2208. Field - Supplies a pointer to the field.
  2209. Value - Supplies a pointer where the value will be returned on success.
  2210. Return Value:
  2211. 0 on success.
  2212. Non-zero on failure.
  2213. --*/
  2214. {
  2215. TIME_ZONE_MONTH Month;
  2216. for (Month = TimeZoneMonthJanuary;
  2217. Month <= TimeZoneMonthDecember;
  2218. Month += 1) {
  2219. if ((strcasecmp(Field, TimeZoneMonthStrings[Month]) == 0) ||
  2220. (strcasecmp(Field, TimeZoneAbbreviatedMonthStrings[Month]) == 0)) {
  2221. *Value = Month;
  2222. return 0;
  2223. }
  2224. }
  2225. *Value = TimeZoneMonthCount;
  2226. return EILSEQ;
  2227. }
  2228. INT
  2229. ParseTimeZoneRuleWeekday (
  2230. PSTR Field,
  2231. PTIME_ZONE_WEEKDAY Value
  2232. )
  2233. /*++
  2234. Routine Description:
  2235. This routine parses a rule weekday (buried in the ON column).
  2236. Arguments:
  2237. Field - Supplies a pointer to the field.
  2238. Value - Supplies a pointer where the value will be returned on success.
  2239. Return Value:
  2240. 0 on success.
  2241. Non-zero on failure.
  2242. --*/
  2243. {
  2244. TIME_ZONE_WEEKDAY Weekday;
  2245. for (Weekday = TimeZoneWeekdaySunday;
  2246. Weekday <= TimeZoneWeekdaySaturday;
  2247. Weekday += 1) {
  2248. if ((strcasecmp(Field, TimeZoneWeekdayStrings[Weekday]) == 0) ||
  2249. (strcasecmp(Field, TimeZoneAbbreviatedWeekdayStrings[Weekday]) ==
  2250. 0)) {
  2251. *Value = Weekday;
  2252. return 0;
  2253. }
  2254. }
  2255. *Value = TimeZoneWeekdayCount;
  2256. return EILSEQ;
  2257. }
  2258. INT
  2259. ParseTimeZoneOccasion (
  2260. PSTR Field,
  2261. PTIME_ZONE_OCCASION Occasion
  2262. )
  2263. /*++
  2264. Routine Description:
  2265. This routine parses an occasion (such as in the Rule ON column).
  2266. Arguments:
  2267. Field - Supplies a pointer to the field.
  2268. Occasion - Supplies a pointer where the result will be returned on success.
  2269. Return Value:
  2270. 0 on success.
  2271. Non-zero on failure.
  2272. --*/
  2273. {
  2274. PSTR AfterScan;
  2275. CHAR Comparator;
  2276. PSTR Equals;
  2277. CHAR LastString[5];
  2278. INT Result;
  2279. LONG ScanResult;
  2280. TIME_ZONE_WEEKDAY Weekday;
  2281. PSTR WeekdayString;
  2282. Result = EILSEQ;
  2283. memset(Occasion, 0, sizeof(TIME_ZONE_OCCASION));
  2284. memcpy(LastString, Field, sizeof(LastString) - 1);
  2285. LastString[sizeof(LastString) - 1] = '\0';
  2286. //
  2287. // If the field starts with a digit, it's just a straight up month date.
  2288. //
  2289. if ((*Field >= '0') && (*Field <= '9')) {
  2290. ScanResult = strtol(Field, &AfterScan, 10);
  2291. if ((AfterScan == Field) || (ScanResult < 0) || (ScanResult > 31)) {
  2292. fprintf(stderr,
  2293. "Error: Unable to scan occasion month date %s.\n",
  2294. Field);
  2295. return Result;
  2296. }
  2297. Occasion->Type = TimeZoneOccasionMonthDate;
  2298. Occasion->MonthDay = (CHAR)ScanResult;
  2299. //
  2300. // If the field starts with "last", then it's the last weekday in a given
  2301. // month.
  2302. //
  2303. } else if (strcasecmp(LastString, "Last") == 0) {
  2304. WeekdayString = Field + 4;
  2305. if (*WeekdayString == '-') {
  2306. WeekdayString += 1;
  2307. }
  2308. Result = ParseTimeZoneRuleWeekday(WeekdayString, &Weekday);
  2309. if (Result != 0) {
  2310. return Result;
  2311. }
  2312. Occasion->Type = TimeZoneOccasionLastWeekday;
  2313. Occasion->Weekday = Weekday;
  2314. //
  2315. // If the field has a = in it, then it's the last weekday >= a month date or
  2316. // the first weekday <= a month date.
  2317. //
  2318. } else if (strchr(Field, '=') != NULL) {
  2319. Equals = strchr(Field, '=');
  2320. if (Equals == Field) {
  2321. fprintf(stderr, "Error: Unable to scan occasion %s.\n", Field);
  2322. return Result;
  2323. }
  2324. Comparator = *(Equals - 1);
  2325. if (Comparator == '>') {
  2326. Occasion->Type = TimeZoneOccasionGreaterOrEqualWeekday;
  2327. } else if (Comparator == '<') {
  2328. Occasion->Type = TimeZoneOccasionLessOrEqualWeekday;
  2329. } else {
  2330. fprintf(stderr, "Error: Unable to scan occasion %s.\n", Field);
  2331. return Result;
  2332. }
  2333. //
  2334. // Scan the month date.
  2335. //
  2336. ScanResult = strtol(Equals + 1, &AfterScan, 10);
  2337. if ((AfterScan == Field) || (ScanResult < 0) || (ScanResult > 31)) {
  2338. fprintf(stderr,
  2339. "Error: Unable to scan occasion month date %s.\n",
  2340. Field);
  2341. return Result;
  2342. }
  2343. Occasion->MonthDay = (CHAR)ScanResult;
  2344. //
  2345. // Terminate and scan the weekday.
  2346. //
  2347. *(Equals - 1) = '\0';
  2348. Result = ParseTimeZoneRuleWeekday(Field, &Weekday);
  2349. if (Result != 0) {
  2350. return Result;
  2351. }
  2352. Occasion->Weekday = Weekday;
  2353. //
  2354. // This is unrecognized.
  2355. //
  2356. } else {
  2357. fprintf(stderr, "Error: Unable to scan occasion %s.\n", Field);
  2358. return Result;
  2359. }
  2360. Result = 0;
  2361. return Result;
  2362. }
  2363. INT
  2364. ParseTimeZoneTime (
  2365. PSTR Field,
  2366. PLONG Time,
  2367. PTIME_ZONE_LENS Lens
  2368. )
  2369. /*++
  2370. Routine Description:
  2371. This routine parses a rule time (in the Rule ON column).
  2372. Arguments:
  2373. Field - Supplies a pointer to the field.
  2374. Time - Supplies a pointer where the time in seconds will be returned.
  2375. Lens - Supplies an optional pointer where the lens under which to view this
  2376. time.
  2377. Return Value:
  2378. 0 on success.
  2379. Non-zero on failure.
  2380. --*/
  2381. {
  2382. PSTR AfterScan;
  2383. BOOL Negative;
  2384. PSTR OriginalField;
  2385. INT Result;
  2386. LONG ScannedValue;
  2387. TIME_ZONE_LENS TimeLens;
  2388. TimeLens = TimeZoneLensLocalTime;
  2389. *Time = 0;
  2390. OriginalField = Field;
  2391. Result = EILSEQ;
  2392. Negative = FALSE;
  2393. if (*Field == '-') {
  2394. Negative = TRUE;
  2395. Field += 1;
  2396. }
  2397. //
  2398. // Parse some hours.
  2399. //
  2400. ScannedValue = strtol(Field, &AfterScan, 10);
  2401. if ((ScannedValue < 0) || ((AfterScan == Field) && (Negative == FALSE))) {
  2402. goto ParseTimeZoneTimeEnd;
  2403. }
  2404. *Time = ScannedValue * SECONDS_PER_HOUR;
  2405. Field = AfterScan;
  2406. //
  2407. // Parse some optional minutes.
  2408. //
  2409. if (*Field == ':') {
  2410. Field += 1;
  2411. ScannedValue = strtol(Field, &AfterScan, 10);
  2412. if ((ScannedValue < 0) || (AfterScan == Field)) {
  2413. goto ParseTimeZoneTimeEnd;
  2414. }
  2415. *Time += ScannedValue * SECONDS_PER_MINUTE;
  2416. Field = AfterScan;
  2417. //
  2418. // Parse some optonal seconds.
  2419. //
  2420. if (*Field == ':') {
  2421. Field += 1;
  2422. ScannedValue = strtol(Field, &AfterScan, 10);
  2423. if ((ScannedValue < 0) || (AfterScan == Field)) {
  2424. goto ParseTimeZoneTimeEnd;
  2425. }
  2426. *Time += ScannedValue;
  2427. Field = AfterScan;
  2428. }
  2429. }
  2430. //
  2431. // Parse an optional lens with which to understand this time.
  2432. //
  2433. if (*Field == 'w') {
  2434. TimeLens = TimeZoneLensLocalTime;
  2435. } else if (*Field == 's') {
  2436. TimeLens = TimeZoneLensLocalStandardTime;
  2437. } else if ((*Field == 'u') || (*Field == 'g') || (*Field == 'z')) {
  2438. TimeLens = TimeZoneLensUtc;
  2439. } else if (*Field != '\0') {
  2440. goto ParseTimeZoneTimeEnd;
  2441. }
  2442. if (Negative != FALSE) {
  2443. *Time = -*Time;
  2444. }
  2445. Result = 0;
  2446. ParseTimeZoneTimeEnd:
  2447. if (Lens != NULL) {
  2448. *Lens = TimeLens;
  2449. }
  2450. if (Result != 0) {
  2451. fprintf(stderr,
  2452. "Error: Failed to scan time field %s.\n",
  2453. OriginalField);
  2454. }
  2455. return Result;
  2456. }
  2457. VOID
  2458. PrintTimeZoneRule (
  2459. PTZC_RULE Rule
  2460. )
  2461. /*++
  2462. Routine Description:
  2463. This routine prints a time zone rule.
  2464. Arguments:
  2465. Rule - Supplies a pointer to the rule to print.
  2466. Return Value:
  2467. None.
  2468. --*/
  2469. {
  2470. INT Weekday;
  2471. printf("Rule %3d: %-13s %04d-%04d %-9s ",
  2472. Rule->NameIndex,
  2473. TimeZoneGetString(&TimeZoneRuleStringList, Rule->NameIndex),
  2474. Rule->From,
  2475. Rule->To,
  2476. TimeZoneMonthStrings[Rule->Month]);
  2477. Weekday = Rule->On.Weekday;
  2478. switch (Rule->On.Type) {
  2479. case TimeZoneOccasionMonthDate:
  2480. printf("%-7d ", Rule->On.MonthDay);
  2481. break;
  2482. case TimeZoneOccasionLastWeekday:
  2483. printf("Last%s ", TimeZoneAbbreviatedWeekdayStrings[Weekday]);
  2484. break;
  2485. case TimeZoneOccasionGreaterOrEqualWeekday:
  2486. printf("%s>=%-2d ",
  2487. TimeZoneAbbreviatedWeekdayStrings[Weekday],
  2488. Rule->On.MonthDay);
  2489. break;
  2490. case TimeZoneOccasionLessOrEqualWeekday:
  2491. printf("%s<=%-2d ",
  2492. TimeZoneAbbreviatedWeekdayStrings[Weekday],
  2493. Rule->On.MonthDay);
  2494. break;
  2495. default:
  2496. assert(FALSE);
  2497. break;
  2498. }
  2499. PrintTimeZoneTime(Rule->At, Rule->AtLens);
  2500. printf(" ");
  2501. PrintTimeZoneTime(Rule->Save, TimeZoneLensLocalTime);
  2502. printf(" %s\n",
  2503. TimeZoneGetString(&TimeZoneStringList, Rule->LettersOffset));
  2504. return;
  2505. }
  2506. VOID
  2507. PrintTimeZone (
  2508. PTZC_ZONE Zone
  2509. )
  2510. /*++
  2511. Routine Description:
  2512. This routine prints a time zone.
  2513. Arguments:
  2514. Zone - Supplies a pointer to the zone to print.
  2515. Return Value:
  2516. None.
  2517. --*/
  2518. {
  2519. PLIST_ENTRY CurrentEntry;
  2520. ULONG EntryIndex;
  2521. PTZC_ZONE_ENTRY ZoneEntry;
  2522. printf("Zone: %s (Entry index %d, count %d)\n",
  2523. TimeZoneGetString(&TimeZoneStringList, Zone->NameOffset),
  2524. Zone->ZoneEntryIndex,
  2525. Zone->ZoneEntryCount);
  2526. //
  2527. // Skip to the proper index in the list.
  2528. //
  2529. CurrentEntry = TimeZoneEntryList.Next;
  2530. for (EntryIndex = 0; EntryIndex < Zone->ZoneEntryIndex; EntryIndex += 1) {
  2531. assert(CurrentEntry != &TimeZoneEntryList);
  2532. CurrentEntry = CurrentEntry->Next;
  2533. }
  2534. for (EntryIndex = 0; EntryIndex < Zone->ZoneEntryCount; EntryIndex += 1) {
  2535. assert(CurrentEntry != &TimeZoneEntryList);
  2536. ZoneEntry = LIST_VALUE(CurrentEntry, TZC_ZONE_ENTRY, ListEntry);
  2537. CurrentEntry = CurrentEntry->Next;
  2538. printf(" ");
  2539. PrintTimeZoneEntry(ZoneEntry);
  2540. }
  2541. printf("\n");
  2542. return;
  2543. }
  2544. VOID
  2545. PrintTimeZoneEntry (
  2546. PTZC_ZONE_ENTRY ZoneEntry
  2547. )
  2548. /*++
  2549. Routine Description:
  2550. This routine prints a time zone entry.
  2551. Arguments:
  2552. ZoneEntry - Supplies a pointer to the zone entry to print.
  2553. Return Value:
  2554. None.
  2555. --*/
  2556. {
  2557. PSTR RuleName;
  2558. PrintTimeZoneTime(ZoneEntry->GmtOffset, TimeZoneLensLocalTime);
  2559. printf(" ");
  2560. if (ZoneEntry->RulesNameIndex != -1) {
  2561. RuleName = TimeZoneGetString(&TimeZoneRuleStringList,
  2562. ZoneEntry->RulesNameIndex);
  2563. printf("%-12s ", RuleName);
  2564. } else {
  2565. PrintTimeZoneTime(ZoneEntry->Save, TimeZoneLensLocalTime);
  2566. printf(" ");
  2567. }
  2568. printf("%-7s ",
  2569. TimeZoneGetString(&TimeZoneStringList, ZoneEntry->FormatOffset));
  2570. if (ZoneEntry->Until < MAX_TIME_ZONE_DATE) {
  2571. PrintTimeZoneDate(ZoneEntry->Until);
  2572. }
  2573. printf("\n");
  2574. return;
  2575. }
  2576. VOID
  2577. PrintTimeZoneLink (
  2578. PTZC_LINK Link
  2579. )
  2580. /*++
  2581. Routine Description:
  2582. This routine prints a time zone link.
  2583. Arguments:
  2584. Link - Supplies a pointer to the link to print.
  2585. Return Value:
  2586. None.
  2587. --*/
  2588. {
  2589. printf("Link: %s TO %s\n", Link->From, Link->To);
  2590. return;
  2591. }
  2592. VOID
  2593. PrintTimeZoneLeap (
  2594. PTZC_LEAP Leap
  2595. )
  2596. /*++
  2597. Routine Description:
  2598. This routine prints a time zone leap second.
  2599. Arguments:
  2600. Leap - Supplies a pointer to the leap second to print.
  2601. Return Value:
  2602. None.
  2603. --*/
  2604. {
  2605. CHAR Correction;
  2606. CHAR RollingOrStationary;
  2607. printf("Leap: ");
  2608. PrintTimeZoneDate(Leap->Date);
  2609. Correction = '-';
  2610. if (Leap->Positive != FALSE) {
  2611. Correction = '+';
  2612. }
  2613. RollingOrStationary = 'S';
  2614. if (Leap->LocalTime != FALSE) {
  2615. RollingOrStationary = 'R';
  2616. }
  2617. printf(" %c %c\n", Correction, RollingOrStationary);
  2618. return;
  2619. }
  2620. VOID
  2621. PrintTimeZoneTime (
  2622. LONG Time,
  2623. TIME_ZONE_LENS Lens
  2624. )
  2625. /*++
  2626. Routine Description:
  2627. This routine prints a time zone time.
  2628. Arguments:
  2629. Time - Supplies the time to print (in seconds).
  2630. Lens - Supplies a lens to print as well.
  2631. Return Value:
  2632. None.
  2633. --*/
  2634. {
  2635. LONG Hours;
  2636. INT Length;
  2637. CHAR LensCharacter;
  2638. LONG Minutes;
  2639. BOOL Negative;
  2640. LONG Seconds;
  2641. Length = 0;
  2642. Negative = FALSE;
  2643. if (Time < 0) {
  2644. Negative = TRUE;
  2645. Time = -Time;
  2646. }
  2647. Hours = Time / SECONDS_PER_HOUR;
  2648. Time -= Hours * SECONDS_PER_HOUR;
  2649. Minutes = Time / SECONDS_PER_MINUTE;
  2650. Time -= Minutes * SECONDS_PER_MINUTE;
  2651. Seconds = Time;
  2652. if (Negative != FALSE) {
  2653. printf("-");
  2654. Length += 1;
  2655. }
  2656. printf("%d:%02d", Hours, Minutes);
  2657. Length += 4;
  2658. if (Hours >= 10) {
  2659. Length += 1;
  2660. }
  2661. if (Seconds != 0) {
  2662. printf(":%02d", Seconds);
  2663. Length += 3;
  2664. }
  2665. switch (Lens) {
  2666. case TimeZoneLensLocalTime:
  2667. LensCharacter = ' ';
  2668. break;
  2669. case TimeZoneLensLocalStandardTime:
  2670. LensCharacter = 's';
  2671. break;
  2672. case TimeZoneLensUtc:
  2673. LensCharacter = 'u';
  2674. break;
  2675. default:
  2676. assert(FALSE);
  2677. LensCharacter = 'X';
  2678. break;
  2679. }
  2680. printf("%-*c", 10 - Length, LensCharacter);
  2681. return;
  2682. }
  2683. VOID
  2684. PrintTimeZoneDate (
  2685. LONGLONG Date
  2686. )
  2687. /*++
  2688. Routine Description:
  2689. This routine prints a time zone date.
  2690. Arguments:
  2691. Date - Supplies the date in seconds since the epoch.
  2692. Return Value:
  2693. None.
  2694. --*/
  2695. {
  2696. INT Day;
  2697. LONG Days;
  2698. INT Leap;
  2699. INT Month;
  2700. INT Year;
  2701. //
  2702. // Figure out and subtract off the year. Make the remainder positive (so
  2703. // that something like -1 becomes December 31, 2000.
  2704. //
  2705. Days = Date / SECONDS_PER_DAY;
  2706. Date -= (LONGLONG)Days * SECONDS_PER_DAY;
  2707. if (Date < 0) {
  2708. Date += SECONDS_PER_DAY;
  2709. Days -= 1;
  2710. }
  2711. Year = ComputeYearForDays(&Days);
  2712. Leap = 0;
  2713. if (IS_LEAP_YEAR(Year)) {
  2714. Leap = 1;
  2715. }
  2716. //
  2717. // Subtract off the months.
  2718. //
  2719. Month = 0;
  2720. Day = Days;
  2721. while (Day >= TimeZoneDaysPerMonth[Leap][Month]) {
  2722. Day -= TimeZoneDaysPerMonth[Leap][Month];
  2723. Month += 1;
  2724. assert(Month < TimeZoneMonthCount);
  2725. }
  2726. //
  2727. // Days of the month start with 1.
  2728. //
  2729. Day += 1;
  2730. assert(Date < SECONDS_PER_DAY);
  2731. printf("%04d", Year);
  2732. if ((Month != TimeZoneMonthJanuary) || (Day != 1) || (Date != 0)) {
  2733. printf(" %s %2d ",
  2734. TimeZoneAbbreviatedMonthStrings[Month],
  2735. Day);
  2736. PrintTimeZoneTime((LONG)Date, TimeZoneLensLocalTime);
  2737. } else {
  2738. printf("%8s", "");
  2739. }
  2740. return;
  2741. }
  2742. INT
  2743. CalculateOccasionForDate (
  2744. PTIME_ZONE_OCCASION Occasion,
  2745. INT Year,
  2746. TIME_ZONE_MONTH Month,
  2747. PINT Date
  2748. )
  2749. /*++
  2750. Routine Description:
  2751. This routine determines the day of the month for the given occasion.
  2752. Arguments:
  2753. Occasion - Supplies a pointer to the occasion.
  2754. Year - Supplies the year to calculate the occasion for.
  2755. Month - Supplies the month to calculate the occasion for.
  2756. Date - Supplies a pointer where the date (of the month) when the occasion
  2757. occurs will be returned.
  2758. Return Value:
  2759. 0 on success.
  2760. 1 if the occasion does not occur in the given year and month.
  2761. --*/
  2762. {
  2763. INT DaysInMonth;
  2764. INT Leap;
  2765. INT MonthDate;
  2766. INT Result;
  2767. TIME_ZONE_WEEKDAY Weekday;
  2768. Leap = 0;
  2769. if (IS_LEAP_YEAR(Year)) {
  2770. Leap = 1;
  2771. }
  2772. DaysInMonth = TimeZoneDaysPerMonth[Leap][Month];
  2773. if (Occasion->Type == TimeZoneOccasionMonthDate) {
  2774. if (Occasion->MonthDay < DaysInMonth) {
  2775. *Date = Occasion->MonthDay;
  2776. return 0;
  2777. }
  2778. return EINVAL;
  2779. }
  2780. //
  2781. // Calculate the weekday for the first of the month.
  2782. //
  2783. Result = CalculateWeekdayForMonth(Year, Month, &Weekday);
  2784. if (Result != 0) {
  2785. return Result;
  2786. }
  2787. MonthDate = 1;
  2788. //
  2789. // Calculate the first instance of the desired weekday.
  2790. //
  2791. if (Occasion->Weekday >= Weekday) {
  2792. MonthDate += Occasion->Weekday - Weekday;
  2793. } else {
  2794. MonthDate += DAYS_PER_WEEK - (Weekday - Occasion->Weekday);
  2795. }
  2796. switch (Occasion->Type) {
  2797. //
  2798. // Add a week as many times as possible.
  2799. //
  2800. case TimeZoneOccasionLastWeekday:
  2801. while (MonthDate + DAYS_PER_WEEK <= DaysInMonth) {
  2802. MonthDate += DAYS_PER_WEEK;
  2803. }
  2804. break;
  2805. //
  2806. // Add a week as long as it's less than the required minimum month day. If
  2807. // that pushes it over the month, then the occasion doesn't exist.
  2808. //
  2809. case TimeZoneOccasionGreaterOrEqualWeekday:
  2810. while (MonthDate < Occasion->MonthDay) {
  2811. MonthDate += DAYS_PER_WEEK;
  2812. }
  2813. if (MonthDate > DaysInMonth) {
  2814. return EINVAL;
  2815. }
  2816. break;
  2817. //
  2818. // If the first instance of that weekday is already too far, then the
  2819. // occasion doesn't exist. Otherwise, keep adding weeks as long as it's
  2820. // still under the limit.
  2821. //
  2822. case TimeZoneOccasionLessOrEqualWeekday:
  2823. if (MonthDate > Occasion->MonthDay) {
  2824. return EINVAL;
  2825. }
  2826. while (MonthDate + DAYS_PER_WEEK < Occasion->MonthDay) {
  2827. MonthDate += DAYS_PER_WEEK;
  2828. }
  2829. break;
  2830. default:
  2831. assert(FALSE);
  2832. return 1;
  2833. }
  2834. *Date = MonthDate;
  2835. return 0;
  2836. }
  2837. INT
  2838. CalculateWeekdayForMonth (
  2839. INT Year,
  2840. TIME_ZONE_MONTH Month,
  2841. PTIME_ZONE_WEEKDAY Weekday
  2842. )
  2843. /*++
  2844. Routine Description:
  2845. This routine calculates the weekday for the first of the month on the
  2846. given month and year.
  2847. Arguments:
  2848. Year - Supplies the year to calculate the weekday for.
  2849. Month - Supplies the month to calculate the weekday for.
  2850. Weekday - Supplies a pointer where the weekday will be returned on success.
  2851. Return Value:
  2852. 0 on success.
  2853. ERANGE if the result was out of range.
  2854. --*/
  2855. {
  2856. LONG Days;
  2857. INT Leap;
  2858. INT Modulo;
  2859. if ((Year > MAX_TIME_ZONE_YEAR) || (Year < MIN_TIME_ZONE_YEAR)) {
  2860. return ERANGE;
  2861. }
  2862. Days = ComputeDaysForYear(Year);
  2863. Leap = 0;
  2864. if (IS_LEAP_YEAR(Year)) {
  2865. Leap = 1;
  2866. }
  2867. Days += TimeZoneMonthDays[Leap][Month];
  2868. Modulo = ((TIME_ZONE_EPOCH_WEEKDAY + Days) % DAYS_PER_WEEK);
  2869. if (Modulo < 0) {
  2870. Modulo = DAYS_PER_WEEK + Modulo;
  2871. }
  2872. *Weekday = Modulo;
  2873. return 0;
  2874. }
  2875. LONG
  2876. ComputeDaysForYear (
  2877. INT Year
  2878. )
  2879. /*++
  2880. Routine Description:
  2881. This routine calculates the number of days for the given year, relative to
  2882. the epoch.
  2883. Arguments:
  2884. Year - Supplies the target year.
  2885. Return Value:
  2886. Returns the number of days since the epoch that January 1st of the given
  2887. year occurred.
  2888. --*/
  2889. {
  2890. LONG Days;
  2891. Days = 0;
  2892. if (Year >= TIME_ZONE_EPOCH_YEAR) {
  2893. while (Year > TIME_ZONE_EPOCH_YEAR) {
  2894. if (IS_LEAP_YEAR(Year)) {
  2895. Days += DAYS_PER_LEAP_YEAR;
  2896. } else {
  2897. Days += DAYS_PER_YEAR;
  2898. }
  2899. Year -= 1;
  2900. }
  2901. } else {
  2902. while (Year < TIME_ZONE_EPOCH_YEAR) {
  2903. if (IS_LEAP_YEAR(Year)) {
  2904. Days -= DAYS_PER_LEAP_YEAR;
  2905. } else {
  2906. Days -= DAYS_PER_YEAR;
  2907. }
  2908. Year += 1;
  2909. }
  2910. }
  2911. return Days;
  2912. }
  2913. INT
  2914. ComputeYearForDays (
  2915. PLONG Days
  2916. )
  2917. /*++
  2918. Routine Description:
  2919. This routine calculates the year given a number of days from the epoch.
  2920. Arguments:
  2921. Days - Supplies a pointer to the number of days since the epoch. On
  2922. completion, this will contain the number of remaining days after the
  2923. years have been subtracted.
  2924. Return Value:
  2925. Returns the year that the day resides in.
  2926. --*/
  2927. {
  2928. LONG RemainingDays;
  2929. INT Year;
  2930. Year = TIME_ZONE_EPOCH_YEAR;
  2931. RemainingDays = *Days;
  2932. //
  2933. // Subtract off any years after the epoch.
  2934. //
  2935. while (RemainingDays > 0) {
  2936. if (IS_LEAP_YEAR(Year)) {
  2937. RemainingDays -= DAYS_PER_LEAP_YEAR;
  2938. } else {
  2939. RemainingDays -= DAYS_PER_YEAR;
  2940. }
  2941. Year += 1;
  2942. }
  2943. //
  2944. // The subtraction may have gone one too far, or the days may have
  2945. // started negative. Either way, get the days up to a non-negative value.
  2946. //
  2947. while (RemainingDays < 0) {
  2948. Year -= 1;
  2949. if (IS_LEAP_YEAR(Year)) {
  2950. RemainingDays += DAYS_PER_LEAP_YEAR;
  2951. } else {
  2952. RemainingDays += DAYS_PER_YEAR;
  2953. }
  2954. }
  2955. *Days = RemainingDays;
  2956. return Year;
  2957. }
  2958. PSTR
  2959. TimeZoneGetString (
  2960. PLIST_ENTRY ListHead,
  2961. ULONG Offset
  2962. )
  2963. /*++
  2964. Routine Description:
  2965. This routine returns the string at a given string table offset.
  2966. Arguments:
  2967. ListHead - Supplies a pointer to the head of the list to search.
  2968. Offset - Supplies the string table offset value to get.
  2969. Return Value:
  2970. Returns a pointer to the string on success.
  2971. NULL on failure.
  2972. --*/
  2973. {
  2974. PLIST_ENTRY CurrentEntry;
  2975. PTZC_STRING String;
  2976. CurrentEntry = ListHead->Next;
  2977. while (CurrentEntry != ListHead) {
  2978. String = LIST_VALUE(CurrentEntry, TZC_STRING, ListEntry);
  2979. CurrentEntry = CurrentEntry->Next;
  2980. if (String->Offset == Offset) {
  2981. return String->String;
  2982. }
  2983. }
  2984. return NULL;
  2985. }
  2986. INT
  2987. TimeZoneAddString (
  2988. PSTR String,
  2989. PULONG Offset
  2990. )
  2991. /*++
  2992. Routine Description:
  2993. This routine adds a string to the string table, reusing strings if possible.
  2994. Arguments:
  2995. String - Supplies a pointer to the string to add. A copy of this string
  2996. will be made.
  2997. Offset - Supplies a pointer where the string offset will be returned on
  2998. success.
  2999. Return Value:
  3000. 0 on success.
  3001. ENOMEM on allocation failure.
  3002. --*/
  3003. {
  3004. INT Result;
  3005. Result = TimeZoneAddStringToList(String,
  3006. &TimeZoneStringList,
  3007. &TimeZoneNextStringOffset,
  3008. TRUE,
  3009. Offset);
  3010. return Result;
  3011. }
  3012. INT
  3013. TimeZoneAddRuleString (
  3014. PSTR String,
  3015. PULONG Index
  3016. )
  3017. /*++
  3018. Routine Description:
  3019. This routine adds a string to the rule string table, reusing strings if
  3020. possible.
  3021. Arguments:
  3022. String - Supplies a pointer to the string to add. A copy of this string
  3023. will be made.
  3024. Index - Supplies a pointer where the rule index will be returned on success.
  3025. Return Value:
  3026. 0 on success.
  3027. ENOMEM on allocation failure.
  3028. --*/
  3029. {
  3030. INT Result;
  3031. Result = TimeZoneAddStringToList(String,
  3032. &TimeZoneRuleStringList,
  3033. &TimeZoneNextRuleNumber,
  3034. FALSE,
  3035. Index);
  3036. return Result;
  3037. }
  3038. INT
  3039. TimeZoneAddStringToList (
  3040. PSTR String,
  3041. PLIST_ENTRY ListHead,
  3042. PULONG ListSize,
  3043. BOOL TrackSize,
  3044. PULONG Offset
  3045. )
  3046. /*++
  3047. Routine Description:
  3048. This routine adds a string to the the given string table.
  3049. Arguments:
  3050. String - Supplies a pointer to the string to add. A copy of this string
  3051. will be made.
  3052. ListHead - Supplies a pointer to the head of the list to add it to.
  3053. ListSize - Supplies a pointer to the size of the list, which will be
  3054. updated.
  3055. TrackSize - Supplies a boolean indicating whether the list size tracks
  3056. the total string size (TRUE) or the element count (FALSE).
  3057. Offset - Supplies a pointer where the offset will be returned on success.
  3058. Return Value:
  3059. 0 on success.
  3060. ENOMEM on allocation failure.
  3061. --*/
  3062. {
  3063. PLIST_ENTRY CurrentEntry;
  3064. PTZC_STRING StringEntry;
  3065. //
  3066. // First search for the string.
  3067. //
  3068. CurrentEntry = ListHead->Next;
  3069. while (CurrentEntry != ListHead) {
  3070. StringEntry = LIST_VALUE(CurrentEntry, TZC_STRING, ListEntry);
  3071. CurrentEntry = CurrentEntry->Next;
  3072. if (strcmp(StringEntry->String, String) == 0) {
  3073. *Offset = StringEntry->Offset;
  3074. return 0;
  3075. }
  3076. }
  3077. //
  3078. // No string entry was found, create a new one.
  3079. //
  3080. StringEntry = malloc(sizeof(TZC_STRING));
  3081. if (StringEntry == NULL) {
  3082. return ENOMEM;
  3083. }
  3084. StringEntry->String = strdup(String);
  3085. if (StringEntry->String == NULL) {
  3086. free(StringEntry);
  3087. return ENOMEM;
  3088. }
  3089. StringEntry->Offset = *ListSize;
  3090. if (TrackSize != FALSE) {
  3091. *ListSize += strlen(String) + 1;
  3092. } else {
  3093. *ListSize += 1;
  3094. }
  3095. INSERT_BEFORE(&(StringEntry->ListEntry), ListHead);
  3096. *Offset = StringEntry->Offset;
  3097. return 0;
  3098. }
  3099. VOID
  3100. TimeZoneCompressEntries (
  3101. PLIST_ENTRY ZoneEntryList,
  3102. PULONG ZoneEntryCount,
  3103. PTZC_ZONE Zone
  3104. )
  3105. /*++
  3106. Routine Description:
  3107. This routine attempts to compress the zone entries by finding a repeat
  3108. earlier. This routine requires that the zone entries be at the end of the
  3109. list.
  3110. Arguments:
  3111. ZoneEntryList - Supplies a pointer to the head of the list of zone entries.
  3112. ZoneEntryCount - Supplies a pointer to the count of entries on the list,
  3113. which may be updated.
  3114. Zone - Supplies a pointer to the zone owning the entries.
  3115. Return Value:
  3116. None.
  3117. --*/
  3118. {
  3119. ULONG Count;
  3120. PLIST_ENTRY CurrentEntry;
  3121. PTZC_ZONE_ENTRY Entry;
  3122. PTZC_ZONE_ENTRY Potential;
  3123. ULONG SearchCount;
  3124. PTZC_ZONE_ENTRY SearchEntry;
  3125. PTZC_ZONE_ENTRY Start;
  3126. Count = Zone->ZoneEntryCount;
  3127. CurrentEntry = ZoneEntryList->Previous;
  3128. for (SearchCount = 0; SearchCount < Count - 1; SearchCount += 1) {
  3129. CurrentEntry = CurrentEntry->Previous;
  3130. }
  3131. Entry = LIST_VALUE(CurrentEntry, TZC_ZONE_ENTRY, ListEntry);
  3132. assert(Zone->ZoneEntryIndex == Entry->Index);
  3133. SearchEntry = Entry;
  3134. SearchCount = 0;
  3135. Start = NULL;
  3136. CurrentEntry = ZoneEntryList->Next;
  3137. while (CurrentEntry != ZoneEntryList) {
  3138. Potential = LIST_VALUE(CurrentEntry, TZC_ZONE_ENTRY, ListEntry);
  3139. CurrentEntry = CurrentEntry->Next;
  3140. //
  3141. // Stop if the element itself was found.
  3142. //
  3143. if (Potential == Entry) {
  3144. break;
  3145. }
  3146. //
  3147. // If this entry matches, then advance the search.
  3148. //
  3149. if ((Potential->RulesNameIndex == SearchEntry->RulesNameIndex) &&
  3150. (Potential->Save == SearchEntry->Save) &&
  3151. (Potential->FormatOffset == SearchEntry->FormatOffset) &&
  3152. (Potential->Until == SearchEntry->Until)) {
  3153. if (SearchCount == 0) {
  3154. Start = Potential;
  3155. }
  3156. SearchCount += 1;
  3157. if (SearchCount == Count) {
  3158. break;
  3159. }
  3160. //
  3161. // Move to the next one, and continue scanning.
  3162. //
  3163. SearchEntry = LIST_VALUE(SearchEntry->ListEntry.Next,
  3164. TZC_ZONE_ENTRY,
  3165. ListEntry);
  3166. //
  3167. // No match, reset.
  3168. //
  3169. } else {
  3170. //
  3171. // Redo this one against the first entry.
  3172. //
  3173. if (SearchCount != 0) {
  3174. CurrentEntry = CurrentEntry->Previous;
  3175. }
  3176. SearchEntry = Entry;
  3177. SearchCount = 0;
  3178. }
  3179. }
  3180. if (SearchCount != Count) {
  3181. return;
  3182. }
  3183. Zone->ZoneEntryIndex = Start->Index;
  3184. //
  3185. // Destroy the redundant elements.
  3186. //
  3187. CurrentEntry = &(Entry->ListEntry);
  3188. while (CurrentEntry != ZoneEntryList) {
  3189. CurrentEntry = CurrentEntry->Next;
  3190. LIST_REMOVE(&(Entry->ListEntry));
  3191. DestroyTimeZoneEntry(Entry);
  3192. *ZoneEntryCount -= 1;
  3193. Entry = LIST_VALUE(CurrentEntry, TZC_ZONE_ENTRY, ListEntry);
  3194. }
  3195. return;
  3196. }