source: trunk/WRF.COMMON/WRFV3/external/io_grib1/MEL_grib1/gribputpds.c @ 2759

Last change on this file since 2759 was 2759, checked in by aslmd, 2 years ago

adding unmodified code from WRFV3.0.1.1, expurged from useless data +1M size

File size: 18.2 KB
Line 
1/* FILENAME:   gribputpds.c */
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5#include <stdlib.h>
6#ifdef XT3_Catamount
7#include <features.h>
8#else
9#include <netinet/in.h>
10#endif
11#include "dprints.h"            /* for dprints */
12#include "gribfuncs.h"          /* prototypes */
13
14/*
15*
16****************************************************************************
17* A.  FUNCTION:  gribputpds
18*       Use the information provided to create a Product Defn Section of
19*       the GRIB format and store it in the GRIB_HDR structure;
20*
21*    INTERFACE:
22*       int gribputpds (Data_Input, User_Input, pPDS_Input, ppgrib_hdr, errmsg)
23*
24*    ARGUMENTS (I=input, O=output, I&O=input and output):
25*      (I)  DATA_INPUT Data_Input;
26*           Structure containing info of this field (ids used in the Sections)
27*      (I)  USER_INPUT User_Input;
28*           Structure containing encoder configuration data
29*      (O)  PDS_INPUT  *pPDS_Input;
30*           points to an empty Structure;   to be filled with PDS info
31*           retrieved from  Data_Input and User_Input.
32*     (I&O) GRIB_HDR **ppgrib_hdr;
33*           points to Grib Header Structure;  May already have 1 or more
34*           GRIB sections in it;  Will have PDS appended to its 'entire_msg',
35*           'pds_ptr', 'pds_len' and 'mesg_len' updated also.
36*      (O)  char *errmsg;
37*           empty array, returned filled if error occurred
38*
39*    RETURN CODE:
40*       0>  no errors;
41*           Grib Header structure now has PDS newly appended to its
42*           entire_msg buffer, its sections length, message length,
43*           and section pointers are updated.
44*       1>  error, errmsg filled;
45*           failed to make storage for PDS_GRIB, or
46*           failed to enlarge 'entire_msg' to hold new PDS block;
47*      99>  error in create_inpPDS() or inp2grib_PDS(); errmsg filled;
48****************************************************************************/
49
50#if PROTOTYPE_NEEDED
51int gribputpds ( DATA_INPUT     Data_Input,
52                USER_INPUT      User_Input,
53                PDS_INPUT       *pPDS_Input,
54                GRIB_HDR        **ppgrib_hdr,
55                char            *errmsg)
56#else
57int gribputpds ( Data_Input, User_Input, pPDS_Input, ppgrib_hdr, errmsg)
58                DATA_INPUT      Data_Input;
59                USER_INPUT      User_Input;
60                PDS_INPUT       *pPDS_Input;
61                GRIB_HDR        **ppgrib_hdr;
62                char            *errmsg;
63#endif
64{
65   PDS_GRIB  *pPDS_Grib=0; /* true GRIB format for pds */
66   GRIB_HDR  *gh;          /* working var */
67   int       stat= 0;      /* status */
68   long      newsize=0L;   /* size of msg after adding PDS block */
69   /*void      create_inpPDS ();
70   int       inp2grib_PDS ();*/
71   char      *func= "GribPutPDS";
72
73  DPRINT1("\nEntering %s()......\n", func);
74/*
75*
76* A.1       FUNCTION create_inpPDS              !void
77*           !create internal struct PDS_INPUT from DATA_INPUT & USER_INPUT
78*/
79   create_inpPDS (Data_Input, User_Input, pPDS_Input);
80
81/*
82*
83* A.2       MALLOC local struct PDS_GRIB, clear it out;
84*           IF (fails) THEN
85*               SET bad stat
86*               RETURN
87*           ELSE
88*               CLEAR out the struct
89*           ENDIF
90*/
91  if ( !(pPDS_Grib= (PDS_GRIB *)malloc(sizeof(PDS_GRIB))) )
92    {   
93     sprintf(errmsg,"%s:  failed storage for PDS_GRIB\n",func);
94     stat=1; goto BYE; 
95    }
96   else memset ((void *)pPDS_Grib, '\0', sizeof(PDS_GRIB));
97
98/*
99*
100* A.3       FUNCTION inp2grib_PDS   
101*           !convert internal PDS_INPUT to true Grib format PDS_GRIB
102*           IF (error) THEN
103*               SAVE error from func in stat
104*               RETURN
105*           ENDIF
106*/
107   if (stat = inp2grib_PDS (pPDS_Input, &pPDS_Grib, errmsg))
108         { upd_child_errmsg (func, errmsg);
109           goto BYE; 
110         }
111/*
112*
113* A.4       CALCULATE new msg length after adding new PDS
114*/
115  DPRINT0("putting Pds into Grib Hdr struct\n");
116
117  gh= *ppgrib_hdr;
118  newsize= gh->msg_length + sizeof(PDS_GRIB);
119
120/*
121*
122* A.5       IF gribhdr's buffer is too small AND
123*               FUCTION Expand_gribhdr failed
124*           THEN
125*               SET stat = 1
126*               RETURN with error   !errmsg filled
127*           ENDIF
128*/
129     if (newsize > gh->abs_size
130        && Expand_gribhdr (gh, newsize, errmsg) !=0)
131        {
132        stat = 1;
133        upd_child_errmsg (func, errmsg);
134        goto BYE;
135        }
136
137/*
138*
139* A.6       COPY Pds and its info into Grib Header
140*           !copy PDS_GRIB struct to the end of Entire_msg array;
141*           !store pds pointer and length
142*           !update msg length
143*/
144   gh->pds_ptr= gh->entire_msg + gh->msg_length;
145   memcpy ((void *) gh->pds_ptr, (void *) pPDS_Grib, sizeof (PDS_GRIB));
146   gh->pds_len    = sizeof(PDS_GRIB);
147   gh->msg_length += gh->pds_len;
148
149   DPRINT1 ("copied PDS_GRIB(%ld) bytes from pPDS_Grib to PDS_PTR\n",
150        sizeof(PDS_GRIB));
151
152/*
153*
154* A.7       FREE up local struct PDS_GRIB
155*
156* A.8       RETURN to caller with stat
157*/
158BYE:
159   if (pPDS_Grib!=NULL) free (pPDS_Grib);
160   DPRINT2 ("Leaving %s(), stat=%d\n", func,stat);
161   return stat;
162/*
163*
164* END OF FUNCTION
165*
166*
167*/
168}
169
170/*
171*
172*********************************************************************
173* B. FUNCTION:  create_inpPDS
174*        Fill the internal Product Defn Section structure with info
175*        retrieved from the 2 input structures DATA_INPUT and USER_INPUT.
176*
177*    INTERFACE:
178*        void  create_inpPDS (Data_Input, User_Input, pPDS_Input)
179*
180*    ARGUMENTS (I=input, O=output, I&O=input and output):
181*      (I)  DATA_INPUT Data_Input;   holds ids to be used in Sections
182*      (I)  USER_INPUT User_Input;   holds encoder configuration info
183*      (O)  PDS_INPUT *pPDS_Input;   pre-allocated structure to be filled;
184*
185*    RETURN CODE:   none;
186**********************************************************************
187*/
188#if PROTOTYPE_NEEDED
189void  create_inpPDS ( DATA_INPUT Data_Input, USER_INPUT User_Input,
190                        PDS_INPUT *pPDS_Input)
191#else
192void  create_inpPDS ( Data_Input, User_Input, pPDS_Input)
193                        DATA_INPUT Data_Input; 
194                        USER_INPUT User_Input;
195                        PDS_INPUT *pPDS_Input;
196#endif
197{
198int i;
199
200   DPRINT0 ( "Entering create_inpPDS ()\n" );
201/*
202*
203* B.1       LOAD info from struct USER_INPUT into struct PDS_INPUT
204*/
205
206/* assigns the values from USER_INPUT to PDS_Input */
207   pPDS_Input->usEd_num = (unsigned short) 1; /* GRIB Edition num */
208   pPDS_Input->usParm_tbl = User_Input.usParm_tbl; /* GRIB TblVersion num */
209   pPDS_Input->usSub_tbl = User_Input.usSub_tbl; /* Local TblVersion num */
210   pPDS_Input->usCenter_id = User_Input.usCenter_id; /* Originating Ctr-Tbl0*/
211   pPDS_Input->usProc_id = Data_Input.usProc_id; /* Model id */
212   pPDS_Input->usGrid_id = Data_Input.usGrid_id; /* Grid id num */
213   pPDS_Input->usGds_bms_id = User_Input.usGds_bms_id; /* GDS/BMS flag-Tbl1 */
214   pPDS_Input->usParm_id = Data_Input.usParm_id; /* Parameter& Units id -Tbl2 */
215   pPDS_Input->usParm_sub = Data_Input.usParm_sub_id;/* Sub-Tblentry for Tbl2 */
216   pPDS_Input->usLevel_id = Data_Input.usLevel_id; /* Type of level/layer-Tbl3*/ /* Height, pressure of level 1  and 2 */ 
217   pPDS_Input->usHeight1 =(unsigned short)Data_Input.nLvl_1 ; 
218   pPDS_Input->usHeight2 =(unsigned short)Data_Input.nLvl_2 ; 
219   pPDS_Input->usYear =(unsigned short)( Data_Input.nYear % 100 ); /* Year */
220   pPDS_Input->usMonth =(unsigned short)Data_Input.nMonth; /* Month */
221   pPDS_Input->usDay =(unsigned short)Data_Input.nDay;/* Day of month */
222   pPDS_Input->usHour =(unsigned short)Data_Input.nHour; /* Hour of day */
223   pPDS_Input->usMinute =(unsigned short)Data_Input.nMinute; /* Minute of hour*/
224   pPDS_Input->usSecond =(unsigned short)Data_Input.nSecond; /* Secs of Min */
225   pPDS_Input->usFcst_unit_id = Data_Input.usFcst_id; /*ForecastTime unit-Tbl4*/
226   /* Period of time (tau)- 0 for analysis */
227   pPDS_Input->usP1 = Data_Input.usFcst_per1;
228   /* Period of time between analyses */
229   pPDS_Input->usP2 = Data_Input.usFcst_per2;
230   /* Time range indicator-Tbl5 */
231   pPDS_Input->usTime_range = Data_Input.usTime_range_id;
232   /* Num in average */
233   pPDS_Input->usTime_range_avg = Data_Input.usTime_range_avg;
234   /* Num missing from average */
235   pPDS_Input->usTime_range_mis = Data_Input.usTime_range_mis; 
236
237   /* Century of reference time */
238   if (Data_Input.nYear % 100 == 0) {
239     pPDS_Input->usCentury = (unsigned short)( Data_Input.nYear / 100 );
240     pPDS_Input->usYear += 100;
241   } else {
242     pPDS_Input->usCentury =(unsigned short)( Data_Input.nYear / 100 + 1);
243   }
244
245   /* Decimal scale factor */
246   pPDS_Input->sDec_sc_fctr = (short) Data_Input.nDec_sc_fctr;
247   /* reserved bytes */
248   for ( i=0 ; i< 12 ; i++)pPDS_Input->ausZero[i] = 0; /* Reserved- Set to 0 */
249   /* Oct-26 was reserved, now holds Sub-Center Id */
250   pPDS_Input->usCenter_sub =  User_Input.usCenter_sub;
251   /* Oct-41:  show that Grib Extensions are used */
252   pPDS_Input->usExt_flag = (unsigned short)EXTENSION_FLAG; 
253   /* Tracking ID for data set */
254   pPDS_Input->usTrack_num = User_Input.usTrack_num; 
255
256   /* WSI Extended PDS section: Used for extended and higher resolution time periods */
257   pPDS_Input->PDS_41 = Data_Input.PDS_41;
258   pPDS_Input->PDS_42 = Data_Input.PDS_42;
259   pPDS_Input->PDS_46 = Data_Input.PDS_46;
260   pPDS_Input->PDS_47 = Data_Input.PDS_47;
261   pPDS_Input->PDS_51 = Data_Input.PDS_51;
262   pPDS_Input->PDS_52 = Data_Input.PDS_52;
263
264/*
265*
266* B.2       ASSIGN size of PDS_GRIB into uslength of struct PDS_INPUT
267            ** If encoding MEL GRIB messages, Pds Length should be 46 and
268            Octet 41 should equal the Extension Flag.
269*/
270   pPDS_Input->uslength = sizeof(PDS_GRIB);
271
272/*
273*
274* B.3       DEBUG Print
275*/
276   DPRINT1("\t create_inpPDS:  uslength = %u (Size of PDS_GRIB)\n",
277   pPDS_Input->uslength );
278   DPRINT1("\t create_inpPDS:  usEd_num = %u\n", pPDS_Input->usEd_num );
279   DPRINT1("\t create_inpPDS:  usParm_tbl = %u\n", pPDS_Input->usParm_tbl );
280   DPRINT1("\t create_inpPDS:  usSub_tbl = %u\n", pPDS_Input->usSub_tbl);
281   DPRINT1("\t create_inpPDS:  usCenter_id = %u\n", pPDS_Input->usCenter_id );
282   DPRINT2("\t create_inpPDS:  usCenter_sub Oct26=%u, usExt_flag Oct41=%u\n", 
283        pPDS_Input->usCenter_sub, pPDS_Input->usExt_flag);
284   DPRINT1("\t create_inpPDS:  usProc_id = %u\n", pPDS_Input->usProc_id );
285   DPRINT1("\t create_inpPDS:  usGrid_id = %u\n", pPDS_Input->usGrid_id );
286   DPRINT1("\t create_inpPDS:  usGds_bms_id = %u\n", pPDS_Input->usGds_bms_id);
287   DPRINT1("\t create_inpPDS:  usParm_id = %u\n", pPDS_Input->usParm_id );
288   DPRINT1("\t create_inpPDS:  usParm_sub = %u\n", pPDS_Input->usParm_sub);
289   DPRINT1("\t create_inpPDS:  usLevel_id = %u\n", pPDS_Input->usLevel_id );
290   DPRINT1("\t create_inpPDS:  usHeight1 = %u\n", pPDS_Input->usHeight1 );
291   DPRINT1("\t create_inpPDS:  usHeight2 = %u\n", pPDS_Input->usHeight2 );
292   DPRINT1("\t create_inpPDS:  usCentury = %u\n", pPDS_Input->usCentury );
293   DPRINT1("\t create_inpPDS:  usYear = %u\n", pPDS_Input->usYear );
294   DPRINT1("\t create_inpPDS:  usDay = %u\n", pPDS_Input->usDay );
295   DPRINT1("\t create_inpPDS:  usHour = %u\n", pPDS_Input->usHour );
296   DPRINT1("\t create_inpPDS:  usMinute = %u\n", pPDS_Input->usMinute );
297   DPRINT1("\t create_inpPDS:  usSecond = %u\n", pPDS_Input->usSecond );
298   DPRINT1("\t create_inpPDS:  usP1 = %u\n", pPDS_Input->usP1);
299   DPRINT1("\t create_inpPDS:  sDec_sc_fctr  = %d\n", pPDS_Input->sDec_sc_fctr);
300   DPRINT1("\t create_inpPDS:  usTrack_num = %u\n", pPDS_Input->usTrack_num);
301   DPRINT0("\t create_inpPDS:  WSI Extended PDS Section: \n");
302   DPRINT1("\t create_inpPDS:  PDS_41 (Forecast time 1 unit id): %u\n",pPDS_Input->PDS_41);
303   DPRINT1("\t create_inpPDS:  PDS_42 (Forecast time 1): %u\n",pPDS_Input->PDS_42);
304   DPRINT1("\t create_inpPDS:  PDS_46 (Forecast time 2 unit id): %u\n",pPDS_Input->PDS_46);
305   DPRINT1("\t create_inpPDS:  PDS_47 (Forecast time 2 unit id): %u\n",pPDS_Input->PDS_47);
306   DPRINT1("\t create_inpPDS:  PDS_51 (Time range indicator): %u\n",pPDS_Input->PDS_51);
307   DPRINT1("\t create_inpPDS:  PDS_52 (Top of atm): %u\n",pPDS_Input->PDS_52);
308
309   
310/*
311*
312* B.4       RETURN w/nothing
313*/
314   DPRINT0 ("Exiting create_inpPDS with no errors;\n"); 
315
316/*
317*
318* END OF FUNCTION
319*
320*
321*/
322}
323
324/*
325*
326****************************************************************************
327* C. FUNCTION:  inp2grib_PDS
328*      Use the data from the internal structure to fill the Product
329*      Definition Section structure.
330*
331*    INTERFACE:
332*      int inp2grib_PDS ( pPDS_Input, ppPDS_Grib, errmsg)
333*
334*    ARGUMENTS (I=input, O=output, I&O=input and output):
335*      (I)  PDS_INPUT *pPDS_Input;
336*           internal PDS structure, used for input
337*      (O)  PDS_GRIB  **ppPDS_GRIB ;
338*           pre-allocated structure to be filled;
339*      (O)  char *errmsg;
340*           empty array, returned filled if error occured;
341*
342*   RETURN CODE:
343*      0> no errors;  PDS_GRIB filled;
344*     99> unexpected null pointers, Errmsg filled;
345****************************************************************************/
346
347#if PROTOTYPE_NEEDED
348int inp2grib_PDS ( PDS_INPUT    *pPDS_Input,
349                PDS_GRIB        **ppPDS_Grib,
350                char            *errmsg)
351#else
352int inp2grib_PDS ( pPDS_Input, ppPDS_Grib, errmsg)
353                PDS_INPUT       *pPDS_Input;
354                PDS_GRIB        **ppPDS_Grib;
355                char            *errmsg;
356#endif
357{
358   char             *func= "inp2grib_PDS";
359   unsigned char    ach3bytes[3];
360   unsigned long    ulPDS_length = 0;
361   short            sDec_sc_fctr = 0;
362   int              nStatus = 0;
363   int              i;          /* loop counter */
364   long             lTemp;      /* working var */
365   PDS_GRIB         *tpds;      /* true grib pds, working var */
366   short            tmp_byte2;  /* working var */
367   long             tmp_byte4;  /* working var */
368
369   DPRINT0  ( "Entering inp2grib_PDS......\n" );
370
371/*
372*
373* C.1       IF (either Internal PDS_INPUT or True Grib PDS_GRIB is null) THEN
374*              SET status = 99;
375*              RETURN
376*           ENDIF
377*/
378      if ( !ppPDS_Grib  || !pPDS_Input) {
379           sprintf(errmsg,
380           "%s: either PDS_GRIB /PDS_INPUT/or both are Null\n",func);
381           nStatus= 99;
382           goto BYE;
383        }
384
385/*
386*
387* C.2       ASSIGN local ptr to point to PDS_GRIB struct;
388*/
389      tpds = *ppPDS_Grib;
390
391/*
392*
393* C.3       CREATE true Grib struct PDS_GRIB from internal PDS_INPUT
394*/
395      tpds->chParm_tbl =  ( unsigned char ) pPDS_Input->usParm_tbl;
396
397      /* Commented out by Todd Hutchinson, WSI, when Extended PDS was replaced */
398      /* tpds->chSub_tbl =  ( unsigned char ) pPDS_Input->usSub_tbl; */
399
400      tpds->chCenter_id = ( unsigned char ) pPDS_Input->usCenter_id;
401      tpds->chProc_id = ( unsigned char ) pPDS_Input->usProc_id;
402      tpds->chGrid_id = ( unsigned char ) pPDS_Input->usGrid_id;
403      tpds->chGds_bms_id = ( unsigned char ) pPDS_Input->usGds_bms_id;
404      tpds->chParm_id = ( unsigned char ) pPDS_Input->usParm_id;
405
406      /* Commented out by Todd Hutchinson, WSI, when Extended PDS was replaced */
407      /* tpds->chParm_sub= (unsigned char )  pPDS_Input->usParm_sub; */
408
409      tpds->chLevel_id = ( unsigned char ) pPDS_Input->usLevel_id;
410
411      switch(pPDS_Input->usLevel_id){
412        case   1:  /* surface(of the Earth, includes sea surface) level  */
413        case   2:  /* cloud base level  */
414        case   3:  /* cloud top level  */
415        case   4:  /* 0 deg C isotherm level */
416        case   5:  /* adiabatic condensation level  */
417        case   6:  /* maximum wind speed level  */
418        case   7:  /* tropopause level  */
419        case   8:  /* nominal top of atmosphere level  */
420        case   9:  /* sea bottom level  */
421        case 100:  /* isobaric level            */
422        case 103:  /* fixed height level        */
423        case 105:  /* fixed height above ground */
424        case 107:  /* sigma level               */
425        case 111:  /* depth below land surface  */
426        case 113:  /* isentropic (theta) level  */
427        case 115:  /* sigma-z level  */
428        case 119:  /* Eta Level */
429        case 125:  /* height level above ground (high precision) */
430        case 160:  /* depth below sea level     */
431        case 200:  /* entire atmosphere considered as a single layer */
432        case 201:  /* entire ocean considered as a single layer */
433        case 212:  /* low cloud bottom level */
434        case 213:  /* low cloud top level */
435        case 222:  /* middle cloud bottom level */
436        case 223:  /* middle cloud top level */
437        case 232:  /* high cloud bottom level */
438        case 233:  /* high cloud top level */
439          set_bytes(pPDS_Input->usHeight1, 2, tpds->achHeight);
440          break;
441        default:
442          set_bytes(pPDS_Input->usHeight2, 1, (tpds->achHeight));
443          set_bytes(pPDS_Input->usHeight1, 1, (tpds->achHeight)+1);
444          break;
445      }
446
447      tpds->chYear = ( unsigned char ) pPDS_Input->usYear;
448      tpds->chMonth = ( unsigned char ) pPDS_Input->usMonth;
449      tpds->chDay = ( unsigned char ) pPDS_Input->usDay;
450      tpds->chHour = ( unsigned char ) pPDS_Input->usHour;
451      tpds->chMinute = ( unsigned char ) pPDS_Input->usMinute;
452      tpds->chFcst_unit_id = ( unsigned char ) pPDS_Input->usFcst_unit_id;
453      tpds->chP1 = ( unsigned char ) pPDS_Input->usP1;
454      tpds->chP2 = ( unsigned char ) pPDS_Input->usP2;
455      tpds->chTime_range = ( unsigned char ) pPDS_Input->usTime_range;
456
457      set_bytes(pPDS_Input->usTime_range_avg,2,tpds->achTime_range_avg);
458
459      tpds->chTime_range_mis = ( unsigned char ) pPDS_Input->usTime_range_mis;
460      tpds->chCentury = ( unsigned char ) pPDS_Input->usCentury;
461      tpds->chCenter_sub = ( unsigned char ) pPDS_Input->usCenter_sub;
462      DPRINT1("Octet-26:  tpds->usCenter_sub= %d\n", (int)tpds->chCenter_sub);
463
464      set_bytes(pPDS_Input->sDec_sc_fctr,2,tpds->achDec_sc_fctr);
465
466      for(i=0;i<12;++i)
467        tpds->achZero[i]= ( unsigned char ) pPDS_Input->ausZero[i];
468
469      /* Commented out by Todd Hutchinson, WSI, when Extended PDS was replaced */
470      /*
471      tpds->chExt_flag = (unsigned char) pPDS_Input->usExt_flag;
472      DPRINT1("Octet41:   tpds->chExt_flag= %d\n", (int)tpds->chExt_flag);
473
474      tpds->chSecond = ( unsigned char ) pPDS_Input->usSecond;
475      memcpy((void *)tpds->chTrack_num,
476                (void *)&(pPDS_Input->usTrack_num), 2);
477      */
478     
479      /* Added by Todd Hutchinson, WSI.  Extended WSI PDS section */
480
481      tpds->PDS_41 = (unsigned char)pPDS_Input->PDS_41;
482
483      set_bytes(pPDS_Input->PDS_42,4,tpds->PDS_42);
484
485      tpds->PDS_46 = (unsigned char)pPDS_Input->PDS_46;
486
487      set_bytes(pPDS_Input->PDS_47,4,tpds->PDS_47);
488
489      tpds->PDS_51 = (unsigned char)pPDS_Input->PDS_51;
490
491      set_bytes(pPDS_Input->PDS_52,2,tpds->PDS_52);
492
493      ulPDS_length= sizeof (PDS_GRIB);
494      DPRINT1 ( "\t length of PDS_GRIB is %d\n", ulPDS_length );
495
496      set_bytes(ulPDS_length, 3, tpds->achPDS_length);
497
498     HDR_PRINT("encoded PDS", (unsigned char*)tpds, (int)ulPDS_length); 
499/*
500*
501* C.4       RETURN with Status
502*/
503BYE:
504      DPRINT1 ( "Exiting inp2grib_PDS(), stat=%d\n", nStatus);
505      return ( nStatus );
506/*
507*
508* END OF FUNCTION
509*
510*/ 
511}
Note: See TracBrowser for help on using the repository browser.