1 /****************************************************************************
4 * This module contains the entry routine for the raytracer and the code to
5 * parse the parameters on the command line.
7 * from Persistence of Vision(tm) Ray Tracer
8 * Copyright 1996,1999 Persistence of Vision Team
9 *---------------------------------------------------------------------------
10 * NOTICE: This source code file is provided so that users may experiment
11 * with enhancements to POV-Ray and to port the software to platforms other
12 * than those supported by the POV-Ray Team. There are strict rules under
13 * which you are permitted to use this file. The rules are in the file
14 * named POVLEGAL.DOC which should be distributed with this file.
15 * If POVLEGAL.DOC is not available or for more info please contact the POV-Ray
16 * Team Coordinator by email to team-coord@povray.org or visit us on the web at
17 * http://www.povray.org. The latest version of POV-Ray may be found at this site.
19 * This program is based on the popular DKB raytracer version 2.12.
20 * DKBTrace was originally written by David K. Buck.
21 * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
23 *****************************************************************************/
26 #include <time.h> /* BP */
27 #include "frame.h" /* common to ALL modules in this program */
69 #include "userio.h" /*Error,Warning,Init_Text_Streams*/
74 /*****************************************************************************
75 * Local preprocessor defines
76 ******************************************************************************/
78 /* Flags for the variable store. */
85 /*****************************************************************************
87 ******************************************************************************/
91 /*****************************************************************************
93 ******************************************************************************/
99 /* Options and display stuff. */
105 int Abort_Test_Every
;
107 int Experimental_Flag
;
109 /* Current stage of the program. */
113 /* Flag if -h option will show help screens. */
117 /* File and parsing stuff. */
120 COUNTER stats
[MaxStat
];
121 COUNTER totalstats
[MaxStat
];
123 int Num_Echo_Lines
; /* May make user setable later - CEY*/
124 int Echo_Line_Length
; /* May make user setable later - CEY*/
129 FILE_HANDLE
*Output_File_Handle
;
131 char Actual_Output_Name
[FILE_NAME_LENGTH
];
135 time_t tstart
, tstop
;
136 DBL tparse
, trender
, tparse_total
, trender_total
;
138 /* Variable used by vector macros. */
141 volatile int Stop_Flag
;
143 /* Flag if close_all() was already called. */
144 static int closed_flag
;
148 char *Option_String_Ptr
;
151 /*****************************************************************************
153 ******************************************************************************/
155 static void init_vars (void);
156 static void destroy_libraries (void);
157 static void fix_up_rendering_window (void);
158 static void fix_up_animation_values (void);
159 static void fix_up_scene_name (void);
160 static void set_output_file_handle (void);
161 static void setup_output_file_name (void);
162 static void open_output_file (void);
163 static void FrameRender (void);
164 static void init_statistics (COUNTER
*);
165 static void sum_statistics (COUNTER
*, COUNTER
*);
166 static void variable_store (int Flag
);
167 static int Has_Extension (char *name
);
168 static unsigned closest_power_of_2 (unsigned theNumber
);
169 static void init_shellouts (void);
170 static void destroy_shellouts (void);
173 /*****************************************************************************
197 ******************************************************************************/
200 #ifdef NOCMDLINE /* a main() by any other name... */
202 MAIN_RETURN_TYPE
alt_main()
204 MAIN_RETURN_TYPE
main()
208 MAIN_RETURN_TYPE
alt_main(int argc
, char **argv
)
210 MAIN_RETURN_TYPE
main(int argc
, char **argv
)
212 #endif /* ...would be a lot less hassle!! :-) AAC */
217 SHELLRET Pre_Scene_Result
, Frame_Result
;
219 /* Attention all ALTMAIN people! See comments attached to this function*/
222 /* Startup povray. */
223 Stage
= STAGE_STARTUP
;
226 /* Print banner and credit info. */
227 Stage
= STAGE_BANNER
;
232 /* Print help screens. */
236 Print_Help_Screens();
240 /* Initialize variables. */
243 Stage
= STAGE_ENVIRONMENT
;
247 Stage
= STAGE_INI_FILE
;
249 /* Read parameters from POVRAY.INI */
253 /* Parse the command line parameters */
255 Stage
= STAGE_COMMAND_LINE
;
257 Help_Available
= (argc
== 2);
259 for (i
= 1 ; i
< argc
; i
++ )
261 parse_option_line(argv
[i
]);
265 /* Strip path and extension off input name to create scene name */
266 fix_up_scene_name ();
268 /* Redirect text streams [SCD 2/95] */
271 /* Write .INI file [SCD 2/95] */
276 /* Make sure clock is okay, validate animation parameters */
277 fix_up_animation_values();
279 /* Fix-up rendering window values if necessary. */
280 fix_up_rendering_window();
282 /* Set output file handle for options screen. */
283 set_output_file_handle();
285 /* Print options used. */
289 /* VARIOUS INITIALIZATION THAT ONLY NEEDS TO BE DONE 1/EXECUTION */
291 /* Set up noise-tables. */
294 Diff_Clock
= opts
.FrameSeq
.FinalClock
- opts
.FrameSeq
.InitialClock
;
296 if (opts
.Options
& CYCLIC_ANIMATION
)
298 Diff_Frame
= opts
.FrameSeq
.FinalFrame
- opts
.FrameSeq
.InitialFrame
+ 1;
302 Diff_Frame
= opts
.FrameSeq
.FinalFrame
- opts
.FrameSeq
.InitialFrame
;
305 Clock_Delta
= ((Diff_Frame
==0)?0:Diff_Clock
/Diff_Frame
);
309 /* Execute the first shell-out command */
310 Pre_Scene_Result
=(POV_SHELLOUT_CAST
)POV_SHELLOUT(PRE_SCENE_SHL
);
312 /* Loop over each frame */
314 if (Pre_Scene_Result
!= ALL_SKIP_RET
)
316 if (Pre_Scene_Result
!= SKIP_ONCE_RET
)
318 for (opts
.FrameSeq
.FrameNumber
= opts
.FrameSeq
.InitialFrame
,
319 opts
.FrameSeq
.Clock_Value
= opts
.FrameSeq
.InitialClock
;
321 opts
.FrameSeq
.FrameNumber
<= opts
.FrameSeq
.FinalFrame
;
323 opts
.FrameSeq
.FrameNumber
++,
324 opts
.FrameSeq
.Clock_Value
+= Clock_Delta
)
326 setup_output_file_name();
328 /* Execute a shell-out command before tracing */
330 Frame_Result
=(POV_SHELLOUT_CAST
)POV_SHELLOUT(PRE_FRAME_SHL
);
332 if (Frame_Result
== ALL_SKIP_RET
)
337 if (Frame_Result
!= SKIP_ONCE_RET
)
341 /* Execute a shell-out command after tracing */
343 Frame_Result
= (POV_SHELLOUT_CAST
)POV_SHELLOUT(POST_FRAME_SHL
);
345 if ((Frame_Result
==SKIP_ONCE_RET
) || (Frame_Result
==ALL_SKIP_RET
))
352 /* Print total stats ... */
354 if(opts
.FrameSeq
.FrameType
==FT_MULTIPLE_FRAME
)
356 Statistics("\nTotal Statistics");
358 opts
.FrameSeq
.FrameNumber
--;
360 PRINT_STATS(totalstats
);
362 opts
.FrameSeq
.FrameNumber
++;
366 /* Execute the final shell-out command */
368 POV_SHELLOUT(POST_SCENE_SHL
);
375 MAIN_RETURN_STATEMENT
379 /*****************************************************************************
397 * Do all that is necessary for rendering a single frame, including parsing
401 * Feb 1996: Make sure we are displaying when doing a mosaic preview [AED]
403 ******************************************************************************/
405 static void FrameRender()
407 unsigned long hours
, minutes
;
408 DBL seconds
, t_total
;
410 /* Store start time for parse. */
413 /* Parse the scene file. */
414 Status_Info("\n\nParsing...");
418 Init_Random_Generators();
422 Destroy_Random_Generators();
426 if (opts
.Options
& RADIOSITY
)
428 Experimental_Flag
|= EF_RADIOS
;
431 if (Experimental_Flag
)
433 Warning(0.0,"Warning: This rendering uses the following experimental features:\n");
434 if (Experimental_Flag
& EF_RADIOS
)
436 Warning(0.0," radiosity");
438 Warning(0.0,".\nThe design and implementation of these features is likely to\n");
439 Warning(0.0,"change in future versions of POV-Ray. Full backward compatibility\n");
440 Warning(0.0,"with the current implementation is NOT guaranteed.\n");
445 /* Switch off standard anti-aliasing. */
447 if ((Frame
.Camera
->Aperture
!= 0.0) && (Frame
.Camera
->Blur_Samples
> 0))
449 opts
.Options
&= ~ANTIALIAS
;
451 Warning(0.0, "Focal blur is used. Standard antialiasing is switched off.\n");
454 /* Create the bounding box hierarchy. */
456 Stage
= STAGE_SLAB_BUILDING
;
460 Status_Info("\nCreating bounding slabs.");
463 /* Init module specific stuff. */
464 Initialize_Atmosphere_Code();
465 Initialize_BBox_Code();
466 Initialize_Lighting_Code();
467 Initialize_Mesh_Code();
468 Initialize_VLBuffer_Code();
469 Initialize_Radiosity_Code();
471 /* Always call this to print number of objects. */
472 Build_Bounding_Slabs(&Root_Object
);
474 /* Create the vista buffer. */
475 Build_Vista_Buffer();
477 /* Create the light buffers. */
478 Build_Light_Buffers();
480 /* Create blob queue. */
483 /* Save variable values. */
484 variable_store(STORE
);
486 /* Open output file and if we are continuing an interrupted trace,
487 * read in the previous file settings and any data there. This has to
488 * be done before any image-size related allocations, since the settings
489 * in a resumed file take precedence over that specified by the user. [AED]
493 /* Start the display. */
494 if (opts
.Options
& DISPLAY
)
496 Status_Info ("\nDisplaying...");
498 POV_DISPLAY_INIT(Frame
.Screen_Width
, Frame
.Screen_Height
);
500 Display_Started
= TRUE
;
502 /* Display vista tree. */
506 /* Get things ready for ray tracing (misc init, mem alloc) */
507 Initialize_Renderer();
509 /* This had to be taken out of open_output_file() because we don't have
510 * the final image size until the output file has been opened, so we can't
511 * initialize the display until we know this, which in turn means we can't
512 * read the rendered part before the display is initialized. [AED]
514 if ((opts
.Options
& DISKWRITE
) && (opts
.Options
& CONTINUE_TRACE
))
516 Read_Rendered_Part(Actual_Output_Name
);
518 if (opts
.Last_Line
> Frame
.Screen_Height
)
519 opts
.Last_Line
= Frame
.Screen_Height
;
521 if (opts
.Last_Column
> Frame
.Screen_Width
)
522 opts
.Last_Column
= Frame
.Screen_Width
;
526 /* Get parsing time. */
528 tparse
= TIME_ELAPSED
530 /* Get total parsing time. */
531 tparse_total
+= tparse
;
533 /* Store start time for trace. */
536 if(opts
.FrameSeq
.FrameType
==FT_MULTIPLE_FRAME
)
538 t_total
=tparse_total
+trender_total
;
539 SPLIT_TIME(t_total
,&hours
,&minutes
,&seconds
);
540 Render_Info("\n %02ld:%02ld:%02.0f so far, ",hours
,minutes
,seconds
);
541 Render_Info("Rendering frame %d, going to %d.",
542 opts
.FrameSeq
.FrameNumber
, opts
.FrameSeq
.FinalFrame
);
545 Stage
= STAGE_RENDERING
;
549 Status_Info ("\nRendering...\r");
551 /* Macro for setting up any special FP options */
554 /* Ok, go for it - trace the picture. */
556 /* If radiosity preview has been done, we are continuing a trace, so it
557 * is important NOT to do the preview, even if the user requests it, as it
558 * will cause discontinuities in radiosity shading by (probably) calculating
559 * a few more radiosity values.
561 if ( !opts
.Radiosity_Preview_Done
)
563 if ( opts
.Options
& RADIOSITY
)
565 /* Note that radiosity REQUIRES a mosaic preview prior to main scan */
567 Start_Tracing_Mosaic_Smooth(opts
.PreviewGridSize_Start
, opts
.PreviewGridSize_End
);
571 if (opts
.Options
& PREVIEW
&& opts
.Options
& DISPLAY
)
573 Start_Tracing_Mosaic_Preview(opts
.PreviewGridSize_Start
, opts
.PreviewGridSize_End
);
578 switch (opts
.Tracing_Method
)
582 Start_Adaptive_Tracing();
589 Start_Non_Adaptive_Tracing();
594 /* Record time so well spent before file close so it can be in comments */
596 trender
= TIME_ELAPSED
598 /* Close out our file */
599 if (Output_File_Handle
)
601 Close_File(Output_File_Handle
);
604 Stage
= STAGE_SHUTDOWN
;
608 /* DESTROY lots of stuff */
609 Deinitialize_Atmosphere_Code();
610 Deinitialize_BBox_Code();
611 Deinitialize_Lighting_Code();
612 Deinitialize_Mesh_Code();
613 Deinitialize_VLBuffer_Code();
614 Deinitialize_Radiosity_Code();
615 Destroy_Blob_Queue();
616 Destroy_Light_Buffers();
617 Destroy_Vista_Buffer();
618 Destroy_Bounding_Slabs();
620 Terminate_Renderer();
622 Free_Iteration_Stack();
626 /* Get total render time. */
627 trender_total
+= trender
;
631 if ((opts
.Options
& DISPLAY
) && Display_Started
)
635 Display_Started
= FALSE
;
638 if (opts
.histogram_on
)
639 write_histogram (opts
.Histogram_File_Name
) ;
641 Status_Info("\nDone Tracing");
643 /* Print stats ... */
646 if(opts
.FrameSeq
.FrameType
==FT_MULTIPLE_FRAME
)
649 sum_statistics(totalstats
, stats
);
651 /* ... and then clear them for the next frame */
652 init_statistics(stats
);
655 /* Restore variable values. */
656 variable_store(RESTORE
);
662 /*****************************************************************************
666 * fix_up_rendering_window
680 * Fix wrong window and mosaic preview values.
686 ******************************************************************************/
688 static void fix_up_rendering_window()
692 if (opts
.First_Column_Percent
> 0.0)
693 opts
.First_Column
= (int) (Frame
.Screen_Width
* opts
.First_Column_Percent
);
695 if (opts
.First_Line_Percent
> 0.0)
696 opts
.First_Line
= (int) (Frame
.Screen_Height
* opts
.First_Line_Percent
);
698 /* The decrements are a fudge factor that used to be in OPTIN.C
699 * but it messed up Write_INI_File so its moved here.
702 if (opts
.First_Column
<= 0)
703 opts
.First_Column
= 0;
707 if (opts
.First_Line
<= 0)
712 if ((opts
.Last_Column
== -1) && (opts
.Last_Column_Percent
<= 1.0))
713 opts
.Last_Column
= (int) (Frame
.Screen_Width
* opts
.Last_Column_Percent
);
715 if ((opts
.Last_Line
== -1) && (opts
.Last_Line_Percent
<= 1.0))
716 opts
.Last_Line
= (int) (Frame
.Screen_Height
* opts
.Last_Line_Percent
);
718 if (opts
.Last_Line
== -1)
719 opts
.Last_Line
= Frame
.Screen_Height
;
721 if (opts
.Last_Column
== -1)
722 opts
.Last_Column
= Frame
.Screen_Width
;
724 if (opts
.Last_Column
< 0 || opts
.Last_Column
> Frame
.Screen_Width
)
725 opts
.Last_Column
= Frame
.Screen_Width
;
727 if (opts
.Last_Line
> Frame
.Screen_Height
)
728 opts
.Last_Line
= Frame
.Screen_Height
;
730 /* Fix up Mosaic Preview values */
731 opts
.PreviewGridSize_Start
=max(1,opts
.PreviewGridSize_Start
);
732 opts
.PreviewGridSize_End
=max(1,opts
.PreviewGridSize_End
);
734 if ((temp
=closest_power_of_2((unsigned)opts
.PreviewGridSize_Start
))!=opts
.PreviewGridSize_Start
)
736 Warning(0.0,"Preview_Start_Size must be a power of 2. Changing to %d.\n",temp
);
737 opts
.PreviewGridSize_Start
=temp
;
740 if ((temp
=closest_power_of_2((unsigned)opts
.PreviewGridSize_End
))!=opts
.PreviewGridSize_End
)
742 Warning(0.0,"Preview_End_Size must be a power of 2. Changing to %d.\n",temp
);
743 opts
.PreviewGridSize_End
=temp
;
746 /* End must be less than or equal to start */
747 if (opts
.PreviewGridSize_End
> opts
.PreviewGridSize_Start
)
748 opts
.PreviewGridSize_End
= opts
.PreviewGridSize_Start
;
750 if (opts
.PreviewGridSize_Start
> 1)
752 opts
.PreviewGridSize_End
=max(opts
.PreviewGridSize_End
,2);
753 opts
.Options
|= PREVIEW
;
757 opts
.Options
&= ~PREVIEW
;
760 /* Set histogram size here so it is available for Print_Options, and
761 * make sure that it has an integer number of pixels/bucket. */
762 if (opts
.histogram_on
)
764 if (opts
.histogram_x
== 0 || opts
.histogram_x
> Frame
.Screen_Width
)
765 opts
.histogram_x
= Frame
.Screen_Width
;
766 else if (opts
.histogram_x
< Frame
.Screen_Width
)
767 opts
.histogram_x
= Frame
.Screen_Width
/ ((Frame
.Screen_Width
+
768 opts
.histogram_x
- 1) / opts
.histogram_x
);
770 if (opts
.histogram_y
== 0 || opts
.histogram_y
> Frame
.Screen_Height
)
771 opts
.histogram_y
= Frame
.Screen_Height
;
772 else if (opts
.histogram_y
< Frame
.Screen_Height
)
773 opts
.histogram_y
= Frame
.Screen_Height
/ ((Frame
.Screen_Height
+
774 opts
.histogram_y
- 1) /opts
.histogram_y
);
778 /*****************************************************************************
782 * fix_up_animation_values
796 * Validate animation parameters, compute subset values
802 ******************************************************************************/
803 static void fix_up_animation_values()
808 float ClockPerFrameIncr
;
811 if (opts
.FrameSeq
.FinalFrame
!= -1)
813 opts
.FrameSeq
.FrameType
= FT_MULTIPLE_FRAME
;
815 if (opts
.FrameSeq
.Clock_Value
!= 0.0)
817 Warning(0.0,"Attempted to set single clock value in multi frame\nanimation. Clock value overridden.\n");
822 if (opts
.FrameSeq
.Clock_Value
!= 0.0)
824 opts
.FrameSeq
.FrameType
= FT_SINGLE_FRAME
;
828 if (opts
.FrameSeq
.FrameType
== FT_SINGLE_FRAME
)
831 * These are dummy values that will work for single_frame,
832 * even in an animation loop.
835 opts
.FrameSeq
.InitialFrame
= 0;
836 opts
.FrameSeq
.FinalFrame
= 0;
837 opts
.FrameSeq
.InitialClock
= opts
.FrameSeq
.Clock_Value
;
838 opts
.FrameSeq
.FinalClock
= 0.0;
842 /* FrameType==FT_MULTIPLE_FRAME */
844 if(opts
.FrameSeq
.InitialFrame
== -1)
846 opts
.FrameSeq
.InitialFrame
= 1;
849 if (opts
.FrameSeq
.FinalFrame
< opts
.FrameSeq
.InitialFrame
)
851 Error("Final frame %d is less than Start Frame %d.\n",
852 opts
.FrameSeq
.FinalFrame
, opts
.FrameSeq
.InitialFrame
);
855 ClockDiff
= opts
.FrameSeq
.FinalClock
-opts
.FrameSeq
.InitialClock
;
857 if (opts
.Options
& CYCLIC_ANIMATION
)
859 FrameDiff
= opts
.FrameSeq
.FinalFrame
-opts
.FrameSeq
.InitialFrame
+1;
863 FrameDiff
= opts
.FrameSeq
.FinalFrame
-opts
.FrameSeq
.InitialFrame
;
866 ClockPerFrameIncr
= (FrameDiff
== 0) ? 0 : (ClockDiff
/FrameDiff
);
868 /* Calculate width, which is an integer log10 */
870 NumFrames
= opts
.FrameSeq
.FinalFrame
;
872 opts
.FrameSeq
.FrameNumWidth
= 1;
874 while (NumFrames
>= 10)
876 opts
.FrameSeq
.FrameNumWidth
++;
878 NumFrames
= NumFrames
/ 10;
881 if (opts
.FrameSeq
.FrameNumWidth
> POV_NAME_MAX
-1)
883 Error("Can't render %d frames requiring %d chars with %d width filename.\n",
884 opts
.FrameSeq
.FinalFrame
- opts
.FrameSeq
.InitialFrame
+ 1,
885 opts
.FrameSeq
.FrameNumWidth
, POV_NAME_MAX
);
888 /* STARTING FRAME SUBSET */
890 if (opts
.FrameSeq
.SubsetStartPercent
!= DBL_VALUE_UNSET
)
892 FrameIncr
= FrameDiff
* opts
.FrameSeq
.SubsetStartPercent
+ 0.5; /* w/rounding */
894 opts
.FrameSeq
.SubsetStartFrame
= opts
.FrameSeq
.InitialFrame
+ FrameIncr
;
897 if (opts
.FrameSeq
.SubsetStartFrame
!= INT_VALUE_UNSET
)
899 NumFrames
= opts
.FrameSeq
.SubsetStartFrame
- opts
.FrameSeq
.InitialFrame
;
901 opts
.FrameSeq
.InitialFrame
= opts
.FrameSeq
.SubsetStartFrame
;
902 opts
.FrameSeq
.InitialClock
= opts
.FrameSeq
.InitialClock
+ NumFrames
* ClockPerFrameIncr
;
905 /* ENDING FRAME SUBSET */
907 if (opts
.FrameSeq
.SubsetEndPercent
!= DBL_VALUE_UNSET
)
910 * By this time, we have possibly lost InitialFrame, so we calculate
911 * it via FinalFrame-FrameDiff
914 FrameIncr
= FrameDiff
* opts
.FrameSeq
.SubsetEndPercent
+ 0.5; /* w/rounding */
916 opts
.FrameSeq
.SubsetEndFrame
= (opts
.FrameSeq
.FinalFrame
- FrameDiff
) + FrameIncr
;
919 if (opts
.FrameSeq
.SubsetEndFrame
!= INT_VALUE_UNSET
)
921 NumFrames
= opts
.FrameSeq
.FinalFrame
- opts
.FrameSeq
.SubsetEndFrame
;
923 opts
.FrameSeq
.FinalFrame
= opts
.FrameSeq
.SubsetEndFrame
;
924 opts
.FrameSeq
.FinalClock
= opts
.FrameSeq
.FinalClock
- NumFrames
* ClockPerFrameIncr
;
928 * Now that we have everything calculated, we check FinalFrame
929 * and InitialFrame one more time, in case the subsets messed them up
932 if (opts
.FrameSeq
.FinalFrame
< opts
.FrameSeq
.InitialFrame
)
934 Error("Final frame %d is less than Start Frame %d\ndue to bad subset specification.\n",
935 opts
.FrameSeq
.FinalFrame
, opts
.FrameSeq
.InitialFrame
);
939 /* Needed for pre-render shellout fixup */
941 opts
.FrameSeq
.FrameNumber
= opts
.FrameSeq
.InitialFrame
;
942 opts
.FrameSeq
.Clock_Value
= opts
.FrameSeq
.InitialClock
;
945 /*****************************************************************************
963 * Strip path and extention of input file to create scene name
967 ******************************************************************************/
969 static void fix_up_scene_name()
972 char temp
[FILE_NAME_LENGTH
];
974 if ((l
=strlen(opts
.Input_File_Name
)-1)<1)
976 strcpy(opts
.Scene_Name
,opts
.Input_File_Name
);
980 strcpy(temp
,opts
.Input_File_Name
);
983 if (temp
[i
]==FILENAME_SEPARATOR
)
996 while ((i
>0) && (temp
[i
]!=FILENAME_SEPARATOR
))
998 if (temp
[i
]==FILENAME_SEPARATOR
)
1000 strcpy(opts
.Scene_Name
,&(temp
[i
]));
1003 /*****************************************************************************
1007 * set_output_file_handle
1021 * Set the output file handle according to the file type used.
1025 * Oct 95 - Removed test where the output file handle was only set if
1026 * output_to_file was TRUE. The output file handle structure
1027 * contains a pointer to read line, which is used by the continue
1028 * trace option. If you tried a continue trace with file output
1029 * manually turned OFF, then a GPF would occur due to a call to a
1030 * NULL function pointer.
1032 ******************************************************************************/
1034 static void set_output_file_handle()
1036 char *def_ext
= NULL
;
1037 char temp
[FILE_NAME_LENGTH
];
1039 switch (opts
.OutputFormat
)
1043 case 'S' : Output_File_Handle
= GET_SYS_FILE_HANDLE(); def_ext
=SYS_DEF_EXT
; break;
1048 case 'C' : Output_File_Handle
= Get_Targa_File_Handle(); def_ext
=".tga"; break;
1051 case 'P' : Output_File_Handle
= Get_PPM_File_Handle(); def_ext
=".ppm"; break;
1054 case 'N' : Output_File_Handle
= Get_Png_File_Handle(); def_ext
=".png"; break;
1057 case 'D' : Error ("Dump format no longer supported.\n"); break;
1059 case 'R' : Error ("Raw format no longer supported.\n"); break;
1061 default : Error ("Unrecognized output file format %c.\n",
1065 Output_File_Handle
->file_type
= IMAGE_FTYPE
;
1067 strcpy(temp
,opts
.Output_File_Name
);
1069 POV_SPLIT_PATH(temp
,opts
.Output_Path
,opts
.Output_File_Name
);
1071 if (opts
.Output_File_Name
[0] == '\0')
1073 sprintf(opts
.Output_File_Name
, "%s%s",opts
.Scene_Name
,def_ext
);
1075 else if (!(opts
.Options
& TO_STDOUT
))
1077 if (!Has_Extension(opts
.Output_File_Name
))
1079 strcat(opts
.Output_File_Name
, def_ext
);
1083 strcpy(opts
.Output_Numbered_Name
,opts
.Output_File_Name
);
1086 /*****************************************************************************
1090 * setup_output_file_name
1104 * Determine the file name for this frame. For an animation, the frame
1105 * number is inserted into the file name.
1109 * Jan-97 [esp] Added conditional after getcwd, because Metrowerks getcwd
1110 * function appends a path separator on output.
1112 ******************************************************************************/
1113 static void setup_output_file_name()
1115 char number_string
[10];
1116 char separator_string
[2] = {FILENAME_SEPARATOR
, 0} ;
1118 int available_characters
;
1122 /* This will create the real name for the file */
1123 if(opts
.FrameSeq
.FrameType
!=FT_MULTIPLE_FRAME
||
1124 opts
.Options
& TO_STDOUT
)
1126 strcpy(opts
.Output_Numbered_Name
,opts
.Output_File_Name
);
1131 * This is the maximum number of characters that can be used of the
1132 * original filename. This will ensure that enough space is available
1133 * for the frame number in the filename
1136 available_characters
= POV_NAME_MAX
-opts
.FrameSeq
.FrameNumWidth
;
1138 plast_period
= strrchr(opts
.Output_File_Name
, '.');
1140 if (plast_period
== NULL
)
1142 Error("Illegal file name %s -- no extension.\n", opts
.Output_File_Name
);
1145 ilast_period
= plast_period
- opts
.Output_File_Name
;
1147 fname_chars
= ilast_period
;
1149 if (fname_chars
> available_characters
)
1151 /* Only give the warning once */
1153 if (opts
.FrameSeq
.FrameNumber
== opts
.FrameSeq
.InitialFrame
)
1155 Warning(0.0, "Need to cut the output filename by %d characters.\n",
1156 ilast_period
- available_characters
);
1159 fname_chars
= available_characters
;
1162 /* Perform actual generation of filename */
1164 strncpy(opts
.Output_Numbered_Name
, opts
.Output_File_Name
, (unsigned)fname_chars
);
1166 /* strncpy doesn't terminate if strlen(opts.Output_File_Name)<fname_chars */
1168 opts
.Output_Numbered_Name
[fname_chars
]='\0';
1170 sprintf(number_string
, "%0*d", opts
.FrameSeq
.FrameNumWidth
, opts
.FrameSeq
.FrameNumber
);
1172 strcat(opts
.Output_Numbered_Name
, number_string
);
1174 strcat(opts
.Output_Numbered_Name
, &opts
.Output_File_Name
[ilast_period
]);
1177 if (strlen (opts
.Output_Path
) == 0)
1179 getcwd (opts
.Output_Path
, sizeof (opts
.Output_Path
) - 1) ;
1180 /* on some systems (MacOS) getcwd adds the path separator on the end */
1181 /* so only add it if it isn't already there... [esp] */
1182 if (opts
.Output_Path
[strlen(opts
.Output_Path
)-1] != FILENAME_SEPARATOR
)
1183 strcat (opts
.Output_Path
, separator_string
) ;
1185 strncpy (Actual_Output_Name
,opts
.Output_Path
, sizeof (Actual_Output_Name
));
1186 strncat (Actual_Output_Name
,opts
.Output_Numbered_Name
, sizeof (Actual_Output_Name
));
1188 Debug_Info("P='%s',O='%s',A='%s',N='%s'\n",opts.Output_Path,
1189 opts.Output_Numbered_Name, Actual_Output_Name,opts.Output_Numbered_Name);
1194 /*****************************************************************************
1212 * Open file and read in previous image if continued trace is on.
1214 * GOTCHA : This saves a POINTER to the file name, so the file
1215 * name must exist over the entire life/use of the file
1221 ******************************************************************************/
1223 static void open_output_file()
1227 if (opts
.Options
& DISKWRITE
)
1229 Stage
= STAGE_FILE_INIT
;
1231 if (opts
.Options
& BUFFERED_OUTPUT
)
1233 Buffer_Size
=opts
.File_Buffer_Size
;
1240 if (opts
.Options
& CONTINUE_TRACE
)
1242 Stage
= STAGE_CONTINUING
;
1244 if (Open_File(Output_File_Handle
, Actual_Output_Name
,
1245 &Frame
.Screen_Width
, &Frame
.Screen_Height
, Buffer_Size
,
1248 Close_File(Output_File_Handle
);
1250 Warning (0.0,"Error opening continue trace output file.\n");
1252 Warning (0.0,"Opening new output file %s.\n",Actual_Output_Name
);
1254 /* Turn off continue trace */
1256 opts
.Options
&= ~CONTINUE_TRACE
;
1258 if (Open_File(Output_File_Handle
, Actual_Output_Name
,
1259 &Frame
.Screen_Width
, &Frame
.Screen_Height
, Buffer_Size
,
1262 Error ("Error opening output file.");
1268 if (Open_File(Output_File_Handle
, Actual_Output_Name
,
1269 &Frame
.Screen_Width
, &Frame
.Screen_Height
, Buffer_Size
,
1272 Error ("Error opening output file.");
1279 /*****************************************************************************
1297 * Initialize all global variables.
1303 ******************************************************************************/
1305 static void init_vars()
1308 opts
.Abort_Test_Counter
= Abort_Test_Every
;
1309 Abort_Test_Every
= 1;
1310 opts
.AntialiasDepth
= 3;
1311 opts
.Antialias_Threshold
= 0.3;
1312 opts
.BBox_Threshold
= 25;
1314 opts
.DisplayFormat
= '0';
1315 Display_Started
= FALSE
;
1316 opts
.File_Buffer_Size
= 0;
1317 opts
.First_Column
= 0;
1318 opts
.First_Column_Percent
= 0.0;
1319 opts
.First_Line
= 0;
1320 opts
.First_Line_Percent
= 0.0;
1321 Frame
.Screen_Height
= 100;
1322 Frame
.Screen_Width
= 100;
1325 opts
.JitterScale
= 1.0;
1326 opts
.Language_Version
= 3.1;
1327 opts
.Last_Column
= -1;
1328 opts
.Last_Column_Percent
= 1.0;
1329 opts
.Last_Line
= -1;
1330 opts
.Last_Line_Percent
= 1.0;
1331 opts
.PreviewGridSize_Start
= 1;
1332 opts
.PreviewGridSize_End
= 1;
1333 opts
.Library_Paths
[0] = NULL
;
1334 opts
.Library_Path_Index
= 0;
1335 Max_Intersections
= 64; /*128*/
1336 Number_Of_Files
= 0;
1337 Number_of_istacks
= 0;
1339 opts
.Options
= USE_VISTA_BUFFER
+ USE_LIGHT_BUFFER
+ JITTER
+
1340 DISKWRITE
+ REMOVE_BOUNDS
;
1341 opts
.OutputFormat
= DEFAULT_OUTPUT_FORMAT
;
1342 opts
.OutputQuality
= 8;
1343 Output_File_Handle
= NULL
;
1344 opts
.Output_Numbered_Name
[0]='\0';
1345 opts
.Output_File_Name
[0]='\0';
1346 opts
.Output_Path
[0]='\0';
1347 opts
.PaletteOption
= '3';
1349 opts
.Quality_Flags
= QUALITY_9
;
1350 opts
.DisplayGamma
= DEFAULT_DISPLAY_GAMMA
;
1353 * If DisplayGamma == 2.2, then GammaFactor == .45, which is what we want.
1355 opts
.GammaFactor
= DEFAULT_ASSUMED_GAMMA
/opts
.DisplayGamma
;
1357 opts
.FrameSeq
.FrameType
= FT_SINGLE_FRAME
;
1358 opts
.FrameSeq
.Clock_Value
= 0.0;
1359 opts
.FrameSeq
.InitialFrame
= 1;
1360 opts
.FrameSeq
.InitialClock
= 0.0;
1361 opts
.FrameSeq
.FinalFrame
= INT_VALUE_UNSET
;
1362 opts
.FrameSeq
.FrameNumWidth
= 0;
1363 opts
.FrameSeq
.FinalClock
= 1.0;
1364 opts
.FrameSeq
.SubsetStartFrame
= INT_VALUE_UNSET
;
1365 opts
.FrameSeq
.SubsetStartPercent
= DBL_VALUE_UNSET
;
1366 opts
.FrameSeq
.SubsetEndFrame
= INT_VALUE_UNSET
;
1367 opts
.FrameSeq
.SubsetEndPercent
= DBL_VALUE_UNSET
;
1368 opts
.FrameSeq
.Field_Render_Flag
= FALSE
;
1369 opts
.FrameSeq
.Odd_Field_Flag
= FALSE
;
1371 opts
.Radiosity_Brightness
= 3.3;
1372 opts
.Radiosity_Count
= 100;
1373 opts
.Radiosity_Dist_Max
= 0.; /* default calculated in Radiosity_Initialize */
1374 opts
.Radiosity_Error_Bound
= .4;
1375 opts
.Radiosity_Gray
= .5; /* degree to which gathered light is grayed */
1376 opts
.Radiosity_Low_Error_Factor
= .8;
1377 opts
.Radiosity_Min_Reuse
= .015;
1378 opts
.Radiosity_Nearest_Count
= 6;
1379 opts
.Radiosity_Recursion_Limit
= 1;
1380 opts
.Radiosity_Quality
= 6; /* Q-flag value for light gathering */
1381 opts
.Radiosity_File_ReadOnContinue
= 1;
1382 opts
.Radiosity_File_SaveWhileRendering
= 1;
1383 opts
.Radiosity_File_AlwaysReadAtStart
= 0;
1384 opts
.Radiosity_File_KeepOnAbort
= 1;
1385 opts
.Radiosity_File_KeepAlways
= 0;
1388 init_statistics(stats
);
1389 init_statistics(totalstats
);
1391 strcpy (opts
.Input_File_Name
, "OBJECT.POV");
1392 opts
.Scene_Name
[0]='\0';
1393 opts
.Ini_Output_File_Name
[0]='\0';
1394 opts
.Use_Slabs
=TRUE
;
1395 Num_Echo_Lines
= 5; /* May make user setable later - CEY*/
1396 Echo_Line_Length
= 180; /* May make user setable later - CEY*/
1398 closed_flag
= FALSE
;
1401 trender
= trender_total
= 0.0;
1402 tparse
= tparse_total
= 0.0;
1404 histogram_grid
= NULL
;
1405 opts
.histogram_on
= FALSE
;
1406 opts
.histogram_type
= NONE
;
1407 opts
.Histogram_File_Name
[0] = '\0';
1408 Histogram_File_Handle
= NULL
;
1410 * Note that late initialization of the histogram_x and histogram_y
1411 * variables is done in fix_up_rendering_window, if they aren't specified
1412 * on the command line. This is because they are based on the image
1413 * dimensions, and we can't be certain that we have this info at the
1414 * time we parse the histogram options in optin.c. [AED]
1416 opts
.histogram_x
= opts
.histogram_y
= 0 ;
1417 max_histogram_value
= 0 ;
1419 opts
.Tracing_Method
= 1;
1420 Experimental_Flag
= 0;
1421 Make_Pigment_Entries();
1425 /*****************************************************************************
1443 * Initialize statistics to 0
1449 ******************************************************************************/
1451 static void init_statistics(COUNTER
*pstats
)
1455 for(i
=0; i
<MaxStat
; i
++)
1456 Init_Counter(pstats
[i
]);
1459 /*****************************************************************************
1477 * Add current statistics to total statistics
1483 ******************************************************************************/
1485 static void sum_statistics(COUNTER
*ptotalstats
, COUNTER
*pstats
)
1490 for(i
=0; i
<MaxStat
; i
++)
1492 Add_Counter(tmp
,pstats
[i
],ptotalstats
[i
]);
1498 /*****************************************************************************
1506 * flag - flag telling wether to store or restore variables.
1518 * Store or restore variables whose value has to be the same for all
1519 * frames of an animation and who are changed during every frame.
1523 * May 1995 : Creation.
1525 ******************************************************************************/
1527 static void variable_store(int Flag
)
1529 static int STORE_First_Line
;
1535 STORE_First_Line
= opts
.First_Line
;
1541 opts
.First_Line
= STORE_First_Line
;
1547 Error("Unknown flag in variable_store().\n");
1551 /*****************************************************************************
1569 * Free library path memory.
1575 ******************************************************************************/
1577 static void destroy_libraries()
1581 for (i
= 0; i
< opts
.Library_Path_Index
; i
++)
1583 POV_FREE(opts
.Library_Paths
[i
]);
1585 opts
.Library_Paths
[i
] = NULL
;
1591 /*****************************************************************************
1609 * Close all the stuff that has been opened and free all allocated memory.
1615 ******************************************************************************/
1619 /* Only close things once */
1626 if (Output_File_Handle
!= NULL
)
1628 Close_File(Output_File_Handle
);
1630 POV_FREE(Output_File_Handle
);
1632 Output_File_Handle
= NULL
;
1635 destroy_shellouts();
1636 destroy_libraries();
1637 Destroy_Text_Streams();
1638 Free_Noise_Tables();
1639 Terminate_Renderer();
1640 Destroy_Bounding_Slabs();
1641 Destroy_Blob_Queue();
1642 Destroy_Vista_Buffer();
1643 Destroy_Light_Buffers();
1644 Destroy_Random_Generators();
1645 Deinitialize_Radiosity_Code();
1646 Free_Iteration_Stack();
1647 destroy_histogram();
1648 Deinitialize_Atmosphere_Code();
1649 Deinitialize_BBox_Code();
1650 Deinitialize_Lighting_Code();
1651 Deinitialize_Mesh_Code();
1652 Deinitialize_VLBuffer_Code();
1657 if ((opts
.Options
& DISPLAY
) && Display_Started
)
1667 /*****************************************************************************
1671 * POV_Std_Split_Time
1685 * Split time into hours, minutes and seconds.
1691 ******************************************************************************/
1693 void POV_Std_Split_Time(DBL time_dif
, unsigned long *hrs
, unsigned long *mins
, DBL
*secs
)
1695 *hrs
= (unsigned long)(time_dif
/ 3600.0);
1697 *mins
= (unsigned long)((time_dif
- (DBL
)(*hrs
* 3600)) / 60.0);
1699 *secs
= time_dif
- (DBL
)(*hrs
* 3600 + *mins
* 60);
1705 /*****************************************************************************
1723 * Since the stricmp function isn't available on all systems, we've
1724 * provided a simplified version of it here.
1730 ******************************************************************************/
1732 int pov_stricmp (char *s1
, char *s2
)
1736 while ((*s1
!= '\0') && (*s2
!= '\0'))
1741 c1
= (char)toupper(c1
);
1742 c2
= (char)toupper(c2
);
1773 /*****************************************************************************
1791 * Find a file in the search path.
1795 * Apr 1996: Don't add trailing FILENAME_SEPARATOR if we are immediately
1796 * following DRIVE_SEPARATOR because of Amiga probs. [AED]
1798 ******************************************************************************/
1800 FILE *Locate_File (char *filename
, char *mode
, char *ext1
, char *ext2
, char *buffer
, int err_flag
)
1803 char pathname
[FILE_NAME_LENGTH
];
1804 char file0
[FILE_NAME_LENGTH
];
1805 char file1
[FILE_NAME_LENGTH
];
1806 char file2
[FILE_NAME_LENGTH
];
1809 if (Has_Extension(filename
))
1815 if ((l1
= strlen(ext1
)) > 0)
1817 strcpy(file1
, filename
);
1818 strcat(file1
, ext1
);
1821 if ((l2
= strlen(ext2
)) > 0)
1823 strcpy(file2
, filename
);
1824 strcat(file2
, ext2
);
1828 /* Check the current directory first. */
1831 if ((f
= fopen(file1
, mode
)) != NULL
)
1833 POV_GET_FULL_PATH(f
,file1
,buffer
);
1839 if ((f
= fopen(file2
, mode
)) != NULL
)
1841 POV_GET_FULL_PATH(f
,file2
,buffer
);
1845 if ((f
= fopen(filename
, mode
)) != NULL
)
1847 POV_GET_FULL_PATH(f
,filename
,buffer
);
1851 for (i
= 0; i
< opts
.Library_Path_Index
; i
++)
1853 strcpy(file0
, opts
.Library_Paths
[i
]);
1854 file0
[strlen(file0
)+1] = '\0';
1855 if (file0
[strlen(file0
) - 1] != DRIVE_SEPARATOR
)
1856 file0
[strlen(file0
)] = FILENAME_SEPARATOR
;
1860 strcpy(pathname
, file0
);
1861 strcat(pathname
, file1
);
1862 if ((f
= fopen(pathname
, mode
)) != NULL
)
1864 POV_GET_FULL_PATH(f
,pathname
,buffer
);
1871 strcpy(pathname
, file0
);
1872 strcat(pathname
, file2
);
1873 if ((f
= fopen(pathname
, mode
)) != NULL
)
1875 POV_GET_FULL_PATH(f
,pathname
,buffer
);
1879 strcpy(pathname
, file0
);
1880 strcat(pathname
, filename
);
1881 if ((f
= fopen(pathname
, mode
)) != NULL
)
1883 POV_GET_FULL_PATH(f
,pathname
,buffer
);
1892 Error_Line("Could not find file '%s%s'\n",filename
,ext1
);
1896 Error_Line("Could not find file '%s'\n",filename
);
1903 /*****************************************************************************
1919 ******************************************************************************/
1921 static int Has_Extension (char *name
)
1927 p
=strrchr(name
, '.');
1931 if ((strlen(name
)-(p
-name
))<=4)
1941 /*****************************************************************************
1949 * template_command - the template command string to execute
1961 * Execute the command line described by the string being passed in
1967 ******************************************************************************/
1969 SHELLRET
pov_shellout (SHELLTYPE Type
)
1971 char real_command
[POV_MAX_CMD_LENGTH
];
1974 SHELLRET Return_Code
;
1976 char *template_command
;
1979 if ( opts
.Shellouts
== NULL
) return(IGNORE_RET
);
1981 template_command
=opts
.Shellouts
[Type
].Command
;
1983 if ((length
= strlen(template_command
)) == 0)
1990 case PRE_SCENE_SHL
: s
="pre-scene"; break;
1991 case PRE_FRAME_SHL
: s
="pre-frame"; break;
1992 case POST_FRAME_SHL
: s
="post-frame"; break;
1993 case POST_SCENE_SHL
: s
="post-scene"; break;
1994 case USER_ABORT_SHL
: s
="user about"; break;
1995 case FATAL_SHL
: s
="fatal error"; break;
1996 case MAX_SHL
: /* To remove warnings*/ break;
1999 Status_Info("\nPerforming %s shell-out command",s
);
2001 /* First, find the real command */
2003 for (i
= 0, j
= 0; i
< length
; )
2005 if (template_command
[i
] == '%')
2007 switch (toupper(template_command
[i
+1]))
2011 strncpy(&real_command
[j
], opts
.Output_Numbered_Name
,
2012 (unsigned)(l
=strlen(opts
.Output_Numbered_Name
)));
2018 strncpy(&real_command
[j
], opts
.Output_Path
,(unsigned)(l
=strlen(opts
.Output_Path
)));
2024 strncpy(&real_command
[j
], opts
.Scene_Name
, (unsigned)(l
=strlen(opts
.Scene_Name
)));
2030 sprintf(&real_command
[j
],"%d",opts
.FrameSeq
.FrameNumber
);
2031 l
= strlen(&real_command
[j
]);
2037 sprintf(&real_command
[j
],"%f",opts
.FrameSeq
.Clock_Value
);
2038 l
= strlen(&real_command
[j
]);
2044 sprintf(&real_command
[j
],"%d",Frame
.Screen_Height
);
2045 l
= strlen(&real_command
[j
]);
2051 sprintf(&real_command
[j
],"%d",Frame
.Screen_Width
);
2052 l
= strlen(&real_command
[j
]);
2058 real_command
[j
]='%';
2067 i
+=2; /* we used 2 characters of template_command */
2071 real_command
[j
++]=template_command
[i
++];
2075 real_command
[j
]='\0';
2077 Return_Code
=(POV_SHELLOUT_CAST
)POV_SYSTEM(real_command
);
2079 if (opts
.Shellouts
[Type
].Inverse
)
2081 Return_Code
=(POV_SHELLOUT_CAST
)(!((int)Return_Code
));
2086 if (Type
< USER_ABORT_SHL
)
2088 switch(opts
.Shellouts
[Type
].Ret
)
2092 Error("Fatal error returned from shellout command.");
2098 Check_User_Abort(TRUE
); /* the TRUE forces user abort */
2110 case ALL_SKIP_RET
: /* Added to remove warnings */
2115 return(opts
.Shellouts
[Type
].Ret
);
2121 /*****************************************************************************
2137 ******************************************************************************/
2139 static void init_shellouts()
2143 opts
.Shellouts
=(SHELLDATA
*)POV_MALLOC(sizeof(SHELLDATA
)*MAX_SHL
,"shellout data");
2145 for (i
=0; i
< MAX_SHL
; i
++)
2147 opts
.Shellouts
[i
].Ret
=IGNORE_RET
;
2148 opts
.Shellouts
[i
].Inverse
=FALSE
;
2149 opts
.Shellouts
[i
].Command
[0]='\0';
2153 /*****************************************************************************
2169 ******************************************************************************/
2171 static void destroy_shellouts()
2173 if (opts
.Shellouts
!= NULL
)
2175 POV_FREE(opts
.Shellouts
);
2178 opts
.Shellouts
=NULL
;
2182 /*****************************************************************************
2186 * closest_power_of_2
2190 * theNumber - the value to determine closest power of 2 for.
2196 * The closest power of two is returned, or zero if the
2197 * argument is less than or equal to zero.
2205 * Decription: Find the highest positive power of 2 that is
2206 * less than or equal to the number passed.
2219 * Aug 1994 : Created by Eduard.
2221 ******************************************************************************/
2223 static unsigned closest_power_of_2(unsigned theNumber
)
2225 int PowerOf2Counter
;
2227 /* do not handle zero or negative numbers for now */
2234 /* count the number in question down as we count up a power of 2 */
2236 PowerOf2Counter
= 1;
2238 while (theNumber
> 1)
2240 /* move our power of 2 counter bit up... */
2242 PowerOf2Counter
<<= 1;
2244 /* and reduce our test number by a factor of 2 two */
2249 return(PowerOf2Counter
);
2252 /*****************************************************************************
2268 * This routine does essential initialization that is required before any
2269 * POV_MALLOC-like routines may be called and before any text streams
2272 * If you are using alt_main and need access to any part of the generic code
2273 * before alt_main is called, you MUST call this routine first! Also note
2274 * that it is safe to call it twice. If you don't call it, alt_main will.
2275 * It won't hurt if you both do it.
2277 * NOTE: Terminate_POV de-initializes these features. Therefore you may
2278 * need to call it again between sucessive calls to alt_main. If you call
2279 * pre_init_povray but for some reason you abort and don't call alt_main,
2280 * then you should call Terminate_POV to clean up.
2283 * Nov 1995 : Created by CEY
2285 ******************************************************************************/
2287 void pre_init_povray()
2289 if (pre_init_flag
==1234)
2294 /* Initialize memory. */
2297 /* Initialize streams. In USERIO.C */
2298 Init_Text_Streams();
2302 pre_init_tokenizer ();
2307 void POV_Split_Path(char *s
,char *p
,char *f
)
2313 if ((l
=strrchr(p
,FILENAME_SEPARATOR
))==NULL
)
2315 if ((l
=strrchr(p
,DRIVE_SEPARATOR
))==NULL
)