Python-2.7.3/Modules/main.c

No issues found

  1 /* Python interpreter main program */
  2 
  3 #include "Python.h"
  4 #include "osdefs.h"
  5 #include "code.h" /* For CO_FUTURE_DIVISION */
  6 #include "import.h"
  7 
  8 #ifdef __VMS
  9 #include <unixlib.h>
 10 #endif
 11 
 12 #if defined(MS_WINDOWS) || defined(__CYGWIN__)
 13 #ifdef HAVE_FCNTL_H
 14 #include <fcntl.h>
 15 #endif
 16 #endif
 17 
 18 #if (defined(PYOS_OS2) && !defined(PYCC_GCC)) || defined(MS_WINDOWS)
 19 #define PYTHONHOMEHELP "<prefix>\\lib"
 20 #else
 21 #if defined(PYOS_OS2) && defined(PYCC_GCC)
 22 #define PYTHONHOMEHELP "<prefix>/Lib"
 23 #else
 24 #define PYTHONHOMEHELP "<prefix>/pythonX.X"
 25 #endif
 26 #endif
 27 
 28 #include "pygetopt.h"
 29 
 30 #define COPYRIGHT \
 31     "Type \"help\", \"copyright\", \"credits\" or \"license\" " \
 32     "for more information."
 33 
 34 #ifdef __cplusplus
 35 extern "C" {
 36 #endif
 37 
 38 /* For Py_GetArgcArgv(); set by main() */
 39 static char **orig_argv;
 40 static int  orig_argc;
 41 
 42 /* command line options */
 43 #define BASE_OPTS "3bBc:dEhiJm:OQ:RsStuUvVW:xX?"
 44 
 45 #ifndef RISCOS
 46 #define PROGRAM_OPTS BASE_OPTS
 47 #else /*RISCOS*/
 48 /* extra option saying that we are running under a special task window
 49    frontend; especially my_readline will behave different */
 50 #define PROGRAM_OPTS BASE_OPTS "w"
 51 /* corresponding flag */
 52 extern int Py_RISCOSWimpFlag;
 53 #endif /*RISCOS*/
 54 
 55 /* Short usage message (with %s for argv0) */
 56 static char *usage_line =
 57 "usage: %s [option] ... [-c cmd | -m mod | file | -] [arg] ...\n";
 58 
 59 /* Long usage message, split into parts < 512 bytes */
 60 static char *usage_1 = "\
 61 Options and arguments (and corresponding environment variables):\n\
 62 -B     : don't write .py[co] files on import; also PYTHONDONTWRITEBYTECODE=x\n\
 63 -c cmd : program passed in as string (terminates option list)\n\
 64 -d     : debug output from parser; also PYTHONDEBUG=x\n\
 65 -E     : ignore PYTHON* environment variables (such as PYTHONPATH)\n\
 66 -h     : print this help message and exit (also --help)\n\
 67 -i     : inspect interactively after running script; forces a prompt even\n\
 68 ";
 69 static char *usage_2 = "\
 70          if stdin does not appear to be a terminal; also PYTHONINSPECT=x\n\
 71 -m mod : run library module as a script (terminates option list)\n\
 72 -O     : optimize generated bytecode slightly; also PYTHONOPTIMIZE=x\n\
 73 -OO    : remove doc-strings in addition to the -O optimizations\n\
 74 -R     : use a pseudo-random salt to make hash() values of various types be\n\
 75          unpredictable between separate invocations of the interpreter, as\n\
 76          a defense against denial-of-service attacks\n\
 77 -Q arg : division options: -Qold (default), -Qwarn, -Qwarnall, -Qnew\n\
 78 -s     : don't add user site directory to sys.path; also PYTHONNOUSERSITE\n\
 79 -S     : don't imply 'import site' on initialization\n\
 80 -t     : issue warnings about inconsistent tab usage (-tt: issue errors)\n\
 81 ";
 82 static char *usage_3 = "\
 83 -u     : unbuffered binary stdout and stderr; also PYTHONUNBUFFERED=x\n\
 84          see man page for details on internal buffering relating to '-u'\n\
 85 -v     : verbose (trace import statements); also PYTHONVERBOSE=x\n\
 86          can be supplied multiple times to increase verbosity\n\
 87 -V     : print the Python version number and exit (also --version)\n\
 88 -W arg : warning control; arg is action:message:category:module:lineno\n\
 89          also PYTHONWARNINGS=arg\n\
 90 -x     : skip first line of source, allowing use of non-Unix forms of #!cmd\n\
 91 ";
 92 static char *usage_4 = "\
 93 -3     : warn about Python 3.x incompatibilities that 2to3 cannot trivially fix\n\
 94 file   : program read from script file\n\
 95 -      : program read from stdin (default; interactive mode if a tty)\n\
 96 arg ...: arguments passed to program in sys.argv[1:]\n\n\
 97 Other environment variables:\n\
 98 PYTHONSTARTUP: file executed on interactive startup (no default)\n\
 99 PYTHONPATH   : '%c'-separated list of directories prefixed to the\n\
100                default module search path.  The result is sys.path.\n\
101 ";
102 static char *usage_5 = "\
103 PYTHONHOME   : alternate <prefix> directory (or <prefix>%c<exec_prefix>).\n\
104                The default module search path uses %s.\n\
105 PYTHONCASEOK : ignore case in 'import' statements (Windows).\n\
106 PYTHONIOENCODING: Encoding[:errors] used for stdin/stdout/stderr.\n\
107 ";
108 static char *usage_6 = "\
109 PYTHONHASHSEED: if this variable is set to 'random', the effect is the same\n\
110    as specifying the -R option: a random value is used to seed the hashes of\n\
111    str, bytes and datetime objects.  It can also be set to an integer\n\
112    in the range [0,4294967295] to get hash values with a predictable seed.\n\
113 ";
114 
115 
116 static int
117 usage(int exitcode, char* program)
118 {
119     FILE *f = exitcode ? stderr : stdout;
120 
121     fprintf(f, usage_line, program);
122     if (exitcode)
123         fprintf(f, "Try `python -h' for more information.\n");
124     else {
125         fputs(usage_1, f);
126         fputs(usage_2, f);
127         fputs(usage_3, f);
128         fprintf(f, usage_4, DELIM);
129         fprintf(f, usage_5, DELIM, PYTHONHOMEHELP);
130         fputs(usage_6, f);
131     }
132 #if defined(__VMS)
133     if (exitcode == 0) {
134         /* suppress 'error' message */
135         return 1;
136     }
137     else {
138         /* STS$M_INHIB_MSG + SS$_ABORT */
139         return 0x1000002c;
140     }
141 #else
142     return exitcode;
143 #endif
144     /*NOTREACHED*/
145 }
146 
147 static void RunStartupFile(PyCompilerFlags *cf)
148 {
149     char *startup = Py_GETENV("PYTHONSTARTUP");
150     if (startup != NULL && startup[0] != '\0') {
151         FILE *fp = fopen(startup, "r");
152         if (fp != NULL) {
153             (void) PyRun_SimpleFileExFlags(fp, startup, 0, cf);
154             PyErr_Clear();
155             fclose(fp);
156            } else {
157                     int save_errno;
158                     save_errno = errno;
159                     PySys_WriteStderr("Could not open PYTHONSTARTUP\n");
160                     errno = save_errno;
161                     PyErr_SetFromErrnoWithFilename(PyExc_IOError,
162                                                    startup);
163                     PyErr_Print();
164                     PyErr_Clear();
165         }
166     }
167 }
168 
169 
170 static int RunModule(char *module, int set_argv0)
171 {
172     PyObject *runpy, *runmodule, *runargs, *result;
173     runpy = PyImport_ImportModule("runpy");
174     if (runpy == NULL) {
175         fprintf(stderr, "Could not import runpy module\n");
176         return -1;
177     }
178     runmodule = PyObject_GetAttrString(runpy, "_run_module_as_main");
179     if (runmodule == NULL) {
180         fprintf(stderr, "Could not access runpy._run_module_as_main\n");
181         Py_DECREF(runpy);
182         return -1;
183     }
184     runargs = Py_BuildValue("(si)", module, set_argv0);
185     if (runargs == NULL) {
186         fprintf(stderr,
187             "Could not create arguments for runpy._run_module_as_main\n");
188         Py_DECREF(runpy);
189         Py_DECREF(runmodule);
190         return -1;
191     }
192     result = PyObject_Call(runmodule, runargs, NULL);
193     if (result == NULL) {
194         PyErr_Print();
195     }
196     Py_DECREF(runpy);
197     Py_DECREF(runmodule);
198     Py_DECREF(runargs);
199     if (result == NULL) {
200         return -1;
201     }
202     Py_DECREF(result);
203     return 0;
204 }
205 
206 static int RunMainFromImporter(char *filename)
207 {
208     PyObject *argv0 = NULL, *importer = NULL;
209 
210     if ((argv0 = PyString_FromString(filename)) &&
211         (importer = PyImport_GetImporter(argv0)) &&
212         (importer->ob_type != &PyNullImporter_Type))
213     {
214              /* argv0 is usable as an import source, so
215                     put it in sys.path[0] and import __main__ */
216         PyObject *sys_path = NULL;
217         if ((sys_path = PySys_GetObject("path")) &&
218             !PyList_SetItem(sys_path, 0, argv0))
219         {
220             Py_INCREF(argv0);
221             Py_DECREF(importer);
222             sys_path = NULL;
223             return RunModule("__main__", 0) != 0;
224         }
225     }
226     Py_XDECREF(argv0);
227     Py_XDECREF(importer);
228     if (PyErr_Occurred()) {
229         PyErr_Print();
230         return 1;
231     }
232     return -1;
233 }
234 
235 
236 /* Main program */
237 
238 int
239 Py_Main(int argc, char **argv)
240 {
241     int c;
242     int sts;
243     char *command = NULL;
244     char *filename = NULL;
245     char *module = NULL;
246     FILE *fp = stdin;
247     char *p;
248     int unbuffered = 0;
249     int skipfirstline = 0;
250     int stdin_is_interactive = 0;
251     int help = 0;
252     int version = 0;
253     int saw_unbuffered_flag = 0;
254     PyCompilerFlags cf;
255 
256     cf.cf_flags = 0;
257 
258     orig_argc = argc;           /* For Py_GetArgcArgv() */
259     orig_argv = argv;
260 
261 #ifdef RISCOS
262     Py_RISCOSWimpFlag = 0;
263 #endif
264 
265     /* Hash randomization needed early for all string operations
266        (including -W and -X options). */
267     while ((c = _PyOS_GetOpt(argc, argv, PROGRAM_OPTS)) != EOF) {
268         if (c == 'm' || c == 'c') {
269             /* -c / -m is the last option: following arguments are
270                not interpreter options. */
271             break;
272         }
273         switch (c) {
274         case 'E':
275             Py_IgnoreEnvironmentFlag++;
276             break;
277         case 'R':
278             Py_HashRandomizationFlag++;
279             break;
280         }
281     }
282     /* The variable is only tested for existence here; _PyRandom_Init will
283        check its value further. */
284     if (!Py_HashRandomizationFlag &&
285         (p = Py_GETENV("PYTHONHASHSEED")) && *p != '\0')
286         Py_HashRandomizationFlag = 1;
287 
288     _PyRandom_Init();
289 
290     PySys_ResetWarnOptions();
291     _PyOS_ResetGetOpt();
292 
293     while ((c = _PyOS_GetOpt(argc, argv, PROGRAM_OPTS)) != EOF) {
294         if (c == 'c') {
295             /* -c is the last option; following arguments
296                that look like options are left for the
297                command to interpret. */
298             command = (char *)malloc(strlen(_PyOS_optarg) + 2);
299             if (command == NULL)
300                 Py_FatalError(
301                    "not enough memory to copy -c argument");
302             strcpy(command, _PyOS_optarg);
303             strcat(command, "\n");
304             break;
305         }
306 
307         if (c == 'm') {
308             /* -m is the last option; following arguments
309                that look like options are left for the
310                module to interpret. */
311             module = (char *)malloc(strlen(_PyOS_optarg) + 2);
312             if (module == NULL)
313                 Py_FatalError(
314                    "not enough memory to copy -m argument");
315             strcpy(module, _PyOS_optarg);
316             break;
317         }
318 
319         switch (c) {
320         case 'b':
321             Py_BytesWarningFlag++;
322             break;
323 
324         case 'd':
325             Py_DebugFlag++;
326             break;
327 
328         case '3':
329             Py_Py3kWarningFlag++;
330             if (!Py_DivisionWarningFlag)
331                 Py_DivisionWarningFlag = 1;
332             break;
333 
334         case 'Q':
335             if (strcmp(_PyOS_optarg, "old") == 0) {
336                 Py_DivisionWarningFlag = 0;
337                 break;
338             }
339             if (strcmp(_PyOS_optarg, "warn") == 0) {
340                 Py_DivisionWarningFlag = 1;
341                 break;
342             }
343             if (strcmp(_PyOS_optarg, "warnall") == 0) {
344                 Py_DivisionWarningFlag = 2;
345                 break;
346             }
347             if (strcmp(_PyOS_optarg, "new") == 0) {
348                 /* This only affects __main__ */
349                 cf.cf_flags |= CO_FUTURE_DIVISION;
350                 /* And this tells the eval loop to treat
351                    BINARY_DIVIDE as BINARY_TRUE_DIVIDE */
352                 _Py_QnewFlag = 1;
353                 break;
354             }
355             fprintf(stderr,
356                 "-Q option should be `-Qold', "
357                 "`-Qwarn', `-Qwarnall', or `-Qnew' only\n");
358             return usage(2, argv[0]);
359             /* NOTREACHED */
360 
361         case 'i':
362             Py_InspectFlag++;
363             Py_InteractiveFlag++;
364             break;
365 
366         /* case 'J': reserved for Jython */
367 
368         case 'O':
369             Py_OptimizeFlag++;
370             break;
371 
372         case 'B':
373             Py_DontWriteBytecodeFlag++;
374             break;
375 
376         case 's':
377             Py_NoUserSiteDirectory++;
378             break;
379 
380         case 'S':
381             Py_NoSiteFlag++;
382             break;
383 
384         case 'E':
385             /* Already handled above */
386             break;
387 
388         case 't':
389             Py_TabcheckFlag++;
390             break;
391 
392         case 'u':
393             unbuffered++;
394             saw_unbuffered_flag = 1;
395             break;
396 
397         case 'v':
398             Py_VerboseFlag++;
399             break;
400 
401 #ifdef RISCOS
402         case 'w':
403             Py_RISCOSWimpFlag = 1;
404             break;
405 #endif
406 
407         case 'x':
408             skipfirstline = 1;
409             break;
410 
411         /* case 'X': reserved for implementation-specific arguments */
412 
413         case 'U':
414             Py_UnicodeFlag++;
415             break;
416         case 'h':
417         case '?':
418             help++;
419             break;
420         case 'V':
421             version++;
422             break;
423 
424         case 'W':
425             PySys_AddWarnOption(_PyOS_optarg);
426             break;
427 
428         case 'R':
429             /* Already handled above */
430             break;
431 
432         /* This space reserved for other options */
433 
434         default:
435             return usage(2, argv[0]);
436             /*NOTREACHED*/
437 
438         }
439     }
440 
441     if (help)
442         return usage(0, argv[0]);
443 
444     if (version) {
445         fprintf(stderr, "Python %s\n", PY_VERSION);
446         return 0;
447     }
448 
449     if (Py_Py3kWarningFlag && !Py_TabcheckFlag)
450         /* -3 implies -t (but not -tt) */
451         Py_TabcheckFlag = 1;
452 
453     if (!Py_InspectFlag &&
454         (p = Py_GETENV("PYTHONINSPECT")) && *p != '\0')
455         Py_InspectFlag = 1;
456     if (!saw_unbuffered_flag &&
457         (p = Py_GETENV("PYTHONUNBUFFERED")) && *p != '\0')
458         unbuffered = 1;
459 
460     if (!Py_NoUserSiteDirectory &&
461         (p = Py_GETENV("PYTHONNOUSERSITE")) && *p != '\0')
462         Py_NoUserSiteDirectory = 1;
463 
464     if ((p = Py_GETENV("PYTHONWARNINGS")) && *p != '\0') {
465         char *buf, *warning;
466 
467         buf = (char *)malloc(strlen(p) + 1);
468         if (buf == NULL)
469             Py_FatalError(
470                "not enough memory to copy PYTHONWARNINGS");
471         strcpy(buf, p);
472         for (warning = strtok(buf, ",");
473              warning != NULL;
474              warning = strtok(NULL, ","))
475             PySys_AddWarnOption(warning);
476         free(buf);
477     }
478 
479     if (command == NULL && module == NULL && _PyOS_optind < argc &&
480         strcmp(argv[_PyOS_optind], "-") != 0)
481     {
482 #ifdef __VMS
483         filename = decc$translate_vms(argv[_PyOS_optind]);
484         if (filename == (char *)0 || filename == (char *)-1)
485             filename = argv[_PyOS_optind];
486 
487 #else
488         filename = argv[_PyOS_optind];
489 #endif
490     }
491 
492     stdin_is_interactive = Py_FdIsInteractive(stdin, (char *)0);
493 
494     if (unbuffered) {
495 #if defined(MS_WINDOWS) || defined(__CYGWIN__)
496         _setmode(fileno(stdin), O_BINARY);
497         _setmode(fileno(stdout), O_BINARY);
498 #endif
499 #ifdef HAVE_SETVBUF
500         setvbuf(stdin,  (char *)NULL, _IONBF, BUFSIZ);
501         setvbuf(stdout, (char *)NULL, _IONBF, BUFSIZ);
502         setvbuf(stderr, (char *)NULL, _IONBF, BUFSIZ);
503 #else /* !HAVE_SETVBUF */
504         setbuf(stdin,  (char *)NULL);
505         setbuf(stdout, (char *)NULL);
506         setbuf(stderr, (char *)NULL);
507 #endif /* !HAVE_SETVBUF */
508     }
509     else if (Py_InteractiveFlag) {
510 #ifdef MS_WINDOWS
511         /* Doesn't have to have line-buffered -- use unbuffered */
512         /* Any set[v]buf(stdin, ...) screws up Tkinter :-( */
513         setvbuf(stdout, (char *)NULL, _IONBF, BUFSIZ);
514 #else /* !MS_WINDOWS */
515 #ifdef HAVE_SETVBUF
516         setvbuf(stdin,  (char *)NULL, _IOLBF, BUFSIZ);
517         setvbuf(stdout, (char *)NULL, _IOLBF, BUFSIZ);
518 #endif /* HAVE_SETVBUF */
519 #endif /* !MS_WINDOWS */
520         /* Leave stderr alone - it should be unbuffered anyway. */
521     }
522 #ifdef __VMS
523     else {
524         setvbuf (stdout, (char *)NULL, _IOLBF, BUFSIZ);
525     }
526 #endif /* __VMS */
527 
528 #ifdef __APPLE__
529     /* On MacOS X, when the Python interpreter is embedded in an
530        application bundle, it gets executed by a bootstrapping script
531        that does os.execve() with an argv[0] that's different from the
532        actual Python executable. This is needed to keep the Finder happy,
533        or rather, to work around Apple's overly strict requirements of
534        the process name. However, we still need a usable sys.executable,
535        so the actual executable path is passed in an environment variable.
536        See Lib/plat-mac/bundlebuiler.py for details about the bootstrap
537        script. */
538     if ((p = Py_GETENV("PYTHONEXECUTABLE")) && *p != '\0')
539         Py_SetProgramName(p);
540     else
541         Py_SetProgramName(argv[0]);
542 #else
543     Py_SetProgramName(argv[0]);
544 #endif
545     Py_Initialize();
546 
547     if (Py_VerboseFlag ||
548         (command == NULL && filename == NULL && module == NULL && stdin_is_interactive)) {
549         fprintf(stderr, "Python %s on %s\n",
550             Py_GetVersion(), Py_GetPlatform());
551         if (!Py_NoSiteFlag)
552             fprintf(stderr, "%s\n", COPYRIGHT);
553     }
554 
555     if (command != NULL) {
556         /* Backup _PyOS_optind and force sys.argv[0] = '-c' */
557         _PyOS_optind--;
558         argv[_PyOS_optind] = "-c";
559     }
560 
561     if (module != NULL) {
562         /* Backup _PyOS_optind and force sys.argv[0] = '-c'
563            so that PySys_SetArgv correctly sets sys.path[0] to ''
564            rather than looking for a file called "-m". See
565            tracker issue #8202 for details. */
566         _PyOS_optind--;
567         argv[_PyOS_optind] = "-c";
568     }
569 
570     PySys_SetArgv(argc-_PyOS_optind, argv+_PyOS_optind);
571 
572     if ((Py_InspectFlag || (command == NULL && filename == NULL && module == NULL)) &&
573         isatty(fileno(stdin))) {
574         PyObject *v;
575         v = PyImport_ImportModule("readline");
576         if (v == NULL)
577             PyErr_Clear();
578         else
579             Py_DECREF(v);
580     }
581 
582     if (command) {
583         sts = PyRun_SimpleStringFlags(command, &cf) != 0;
584         free(command);
585     } else if (module) {
586         sts = RunModule(module, 1);
587         free(module);
588     }
589     else {
590 
591         if (filename == NULL && stdin_is_interactive) {
592             Py_InspectFlag = 0; /* do exit on SystemExit */
593             RunStartupFile(&cf);
594         }
595         /* XXX */
596 
597         sts = -1;               /* keep track of whether we've already run __main__ */
598 
599         if (filename != NULL) {
600             sts = RunMainFromImporter(filename);
601         }
602 
603         if (sts==-1 && filename!=NULL) {
604             if ((fp = fopen(filename, "r")) == NULL) {
605                 fprintf(stderr, "%s: can't open file '%s': [Errno %d] %s\n",
606                     argv[0], filename, errno, strerror(errno));
607 
608                 return 2;
609             }
610             else if (skipfirstline) {
611                 int ch;
612                 /* Push back first newline so line numbers
613                    remain the same */
614                 while ((ch = getc(fp)) != EOF) {
615                     if (ch == '\n') {
616                         (void)ungetc(ch, fp);
617                         break;
618                     }
619                 }
620             }
621             {
622                 /* XXX: does this work on Win/Win64? (see posix_fstat) */
623                 struct stat sb;
624                 if (fstat(fileno(fp), &sb) == 0 &&
625                     S_ISDIR(sb.st_mode)) {
626                     fprintf(stderr, "%s: '%s' is a directory, cannot continue\n", argv[0], filename);
627                     fclose(fp);
628                     return 1;
629                 }
630             }
631         }
632 
633         if (sts==-1) {
634             /* call pending calls like signal handlers (SIGINT) */
635             if (Py_MakePendingCalls() == -1) {
636                 PyErr_Print();
637                 sts = 1;
638             } else {
639                 sts = PyRun_AnyFileExFlags(
640                     fp,
641                     filename == NULL ? "<stdin>" : filename,
642                     filename != NULL, &cf) != 0;
643             }
644         }
645 
646     }
647 
648     /* Check this environment variable at the end, to give programs the
649      * opportunity to set it from Python.
650      */
651     if (!Py_InspectFlag &&
652         (p = Py_GETENV("PYTHONINSPECT")) && *p != '\0')
653     {
654         Py_InspectFlag = 1;
655     }
656 
657     if (Py_InspectFlag && stdin_is_interactive &&
658         (filename != NULL || command != NULL || module != NULL)) {
659         Py_InspectFlag = 0;
660         /* XXX */
661         sts = PyRun_AnyFileFlags(stdin, "<stdin>", &cf) != 0;
662     }
663 
664     Py_Finalize();
665 #ifdef RISCOS
666     if (Py_RISCOSWimpFlag)
667         fprintf(stderr, "\x0cq\x0c"); /* make frontend quit */
668 #endif
669 
670 #ifdef __INSURE__
671     /* Insure++ is a memory analysis tool that aids in discovering
672      * memory leaks and other memory problems.  On Python exit, the
673      * interned string dictionary is flagged as being in use at exit
674      * (which it is).  Under normal circumstances, this is fine because
675      * the memory will be automatically reclaimed by the system.  Under
676      * memory debugging, it's a huge source of useless noise, so we
677      * trade off slower shutdown for less distraction in the memory
678      * reports.  -baw
679      */
680     _Py_ReleaseInternedStrings();
681 #endif /* __INSURE__ */
682 
683     return sts;
684 }
685 
686 /* this is gonna seem *real weird*, but if you put some other code between
687    Py_Main() and Py_GetArgcArgv() you will need to adjust the test in the
688    while statement in Misc/gdbinit:ppystack */
689 
690 /* Make the *original* argc/argv available to other modules.
691    This is rare, but it is needed by the secureware extension. */
692 
693 void
694 Py_GetArgcArgv(int *argc, char ***argv)
695 {
696     *argc = orig_argc;
697     *argv = orig_argv;
698 }
699 
700 #ifdef __cplusplus
701 }
702 #endif