tzcomp.c 84 KB

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