home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: SysTools / SysTools.zip / pmcron03.zip / cronmain.c < prev    next >
C/C++ Source or Header  |  1996-05-09  |  67KB  |  1,393 lines

  1. /* Copyright (c) 1995 Florian Große-Coosmann, RCS section at the eof         */
  2. /* This module holds the main stuff of the server process.                   */
  3. /* All functions depending on the frontend type are stored either in         */
  4. /* cron_det.c or cron_pm.c, little exception: main at the end of this module.*/
  5. #define INCL_NOCOMMON
  6. #define INCL_WINSHELLDATA
  7. #define INCL_WINWINDOWMGR
  8. #define INCL_WINMESSAGEMGR
  9. #define INCL_WINFRAMEMGR
  10. #define INCL_WINSYS
  11.  
  12. #define INCL_DOSSEMAPHORES
  13. #define INCL_DOSRESOURCES
  14. #define INCL_DOSERRORS
  15. #define INCL_DOSMISC
  16. #define INCL_DOSFILEMGR
  17. #define INCL_DOSPROCESS
  18. #define INCL_DOSMODULEMGR
  19. #define INCL_DOSNLS
  20.  
  21. #include <os2.h>
  22.  
  23. #include <string.h>
  24. #include <stddef.h>
  25. #include <io.h>
  26. #include <stdarg.h>
  27. #include <sys/wait.h>
  28. #include <signal.h>
  29. #include <sys/types.h>
  30. #include <dirent.h>
  31. #include <fnmatch.h>
  32. #include <ctype.h>
  33. #include <locale.h>
  34. #include <fcntl.h>
  35. #include <share.h>
  36. #include <sys/stat.h>
  37. #include <sys/ioctl.h>
  38. #include "server.h"
  39.  
  40. #ifndef DATEFMT_MM_DD_YY
  41. #  define DATEFMT_MM_DD_YY  0x0000
  42. #  define DATEFMT_DD_MM_YY  0x0001
  43. #  define DATEFMT_YY_MM_DD  0x0002
  44. #endif
  45.  
  46. #define ALL_FILES (FILE_NORMAL | FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM | \
  47.                    FILE_DIRECTORY | FILE_ARCHIVED)
  48.  
  49.                                         /* these values are missed in the    */
  50.                                         /* os2emx.h file, token from bsedos.h*/
  51. #ifndef PT_FULLSCREEN
  52. #  define PT_FULLSCREEN          0      /* Full screen application           */
  53. #  define PT_REALMODE            1      /* Real mode process                 */
  54. #  define PT_WINDOWABLEVIO       2      /* VIO windowable application        */
  55. #  define PT_PM                  3      /* Presentation Manager application  */
  56. #  define PT_DETACHED            4      /* Detached application              */
  57. #endif
  58.  
  59.  
  60. HAB hab;                                /* the following four values are for */
  61. HMQ hmq;                                /* PM only                           */
  62. HWND hwndFrame = (HWND) 0;
  63. HWND hwndClient = (HWND) 0;
  64.  
  65. char   CurrFilename[CCHMAXPATH] = "???";/* user modifiable values            */
  66. RECTL  FrameRect,SaveFrameRect;         /* PM only                           */
  67. ULONG  ProgramFlags;
  68. ULONG  Threshold;
  69. UCHAR  MaxOutputs;
  70. UCHAR  CurrOutput;
  71. CHAR   Crontabs[CCHMAXPATH];
  72. CHAR   TCPIP_Allow[CCHMAXPATH];
  73. CHAR   OutputPath[CCHMAXPATH];
  74. USHORT Language;
  75. USHORT CurrentLanguage;
  76.  
  77. const LANGUAGE Languages[] = {
  78.    {ENGLISH,   LC_C_UK,       "English"},
  79.    {ENGLISH,   LC_C_USA,      "English"},
  80.    {GERMAN,    LC_C_GERMANY,  "Deutsch"},
  81.    {FRENCH,    LC_C_FRANCE,   "Français"}
  82. };
  83. const unsigned LanguagesCount = sizeof(Languages) / sizeof(Languages[0]);
  84.  
  85. LONG  Colors[4];                        /* used colors, see codes in server.h*/
  86.                                         /* only used when running under PM   */
  87. char Fontname[2 * FACESIZE + sizeof("10.")]; /* INI-file saveable font name  */
  88. ULONG LastDayStarted = 0;               /* last day crond has run            */
  89.  
  90. int   GlobalStop = 0;                   /* flag: shall we stop the program?  */
  91. int   TCPIPAvail = 0;                   /* is TCP/IP available?              */
  92. int   RunningUnderPM = 0;               /* the name of the flag says it      */
  93. ULONG ReWritePrfFlags = 0;              /* flags what has changed            */
  94. CHAR  DefIOPath[CCHMAXPATH];            /* path of the program               */
  95. char  Delims[] = "\\/:";                /* path delimiter                    */
  96. FILE *out = NULL;                       /* current output file               */
  97. int   cronhandle = -1;                  /* the handle of the Crontabs file   */
  98. HEV   CronSem = (HEV) 0;                /* event semaphore to show the       */
  99.                                         /* cron daemon that things have been */
  100.                                         /* changed                           */
  101.  
  102. #ifdef DEBUG
  103. FILE *DebugFile = NULL;
  104. #endif
  105.                                         /* front end related values:         */
  106. void (*NewProgramStatus)(ULONG NewStatus);   /* show new program state       */
  107. void (*NewStartTime)(time_t time);      /* show next job start time          */
  108. void (*NewOutputFile)(void);            /* show the display output file      */
  109. void (*JobsModified)(void);             /* advise a changed job list         */
  110. void (*StopPM)(void);                   /* stop PM window                    */
  111. void (*Error)(const char *s,HWND dummy);  /* show error string               */
  112. void (*FatalError)(const char *s,HWND dummy);   /* show error string and die */
  113. void (*Stop)(int code);                 /* signal handler: SIGTERM, ^C,^Break*/
  114. int SetJumpEnabled = 0;                 /* flag: can we use longjmp          */
  115. jmp_buf PrgEnd;                         /* jump if signaled                  */
  116.  
  117.  
  118. static char *szUNKNOWN = "There was an access to an unknown string.\r\n"
  119.                          "Please, inform the author, thank you.\r\n";
  120. static const char PrfDaemon[] = "Cron Daemon";
  121. static const char PrfScreen[] = "Screen Position";
  122. static const char PrfFlags[] = "Flags";
  123. static const char PrfNumMax[] = "Maximum Output";
  124. static const char PrfNumAct[] = "Current Output";
  125. static const char PrfThreshold[] = "Threshold";
  126. static const char PrfOutput[] = "Outputpath";
  127. static const char PrfTCPIPAllow[] = "TCP/IP Allow File";
  128. static const char PrfCrontabs[] = "Crontabs";
  129. static const char PrfLanguage[] = "Language";
  130. static const char PrfColors[] = "Colors";
  131. static const char PrfFont[] = "Font";
  132. static const char PrfDate[] = "Date";
  133. static const char CronDaemonClass[] = "Cron-Daemon-Class";
  134. static TID CronTid = (TID) -1;
  135. static HMTX WriteSem = (HMTX) 0;
  136. static RECTL ScreenRect;
  137.  
  138. static char FilenameMaskSearch[] = "Cron000?.out";
  139. #define SEARCH_DIGIT_POS (sizeof("Cron000") - 1)
  140. static char FilenameMaskSet[] = "Cron000%u.out";
  141.  
  142. /*****************************************************************************/
  143. /*  function name : Get                                                      */
  144. /*                                                                           */
  145. /*  arguments     : ID of a resource string, see IDS_... in cron_id.h        */
  146. /*                                                                           */
  147. /*  return value  : the resource string or a default string, never NULL      */
  148. /*                                                                           */
  149. /*  description   : Gets a resource string from the resource file appendix   */
  150. /*                  of the program file. All resource strings will be        */
  151. /*                  buffered. Thus, there are only some read operations      */
  152. /*                  while running. You should not change the returned        */
  153. /*                  string.                                                  */
  154. /*                  The Language specifier is used to get the locale         */
  155. /*                  specific string. You must not specify it.                */
  156. /*                                                                           */
  157. /*  note          : Only the strings between the                             */
  158. /*                  IDS_firstnumber_mustbe_first_IDS and the                 */
  159. /*                  IDS_lastnumber_mustbe_last_IDS will be loaded.           */
  160. /*                  Otherwise, the function returns the default string.      */
  161. /*****************************************************************************/
  162. const char *Get(ULONG IDS)
  163.                                         /* string resources are clustered in */
  164.                                         /* 16 element portions, we'll buffer */
  165.                                         /* all elements of a buffer. Thus we */
  166.                                         /* need more space and have to align */
  167.                                         /* the ID numbers.                   */
  168.                                         /* compare these values with the IDs */
  169.                                         /* found in cron_id.h                */
  170. #define FIRSTVALID (IDS_Title & 0xFFFFFFF0)
  171. #define LASTVALID  (IDS_lastnumber_mustbe_last_IDS | 0xF)
  172. {
  173.    ULONG err,AssignNum,LoadID;
  174.    char *buf;
  175.    unsigned char len;
  176.    unsigned i;
  177.  
  178.    static char *StringTable[LASTVALID - FIRSTVALID + 1]; /*buffer for strings*/
  179.    static int first = 1;
  180.  
  181.    if (first) {                         /* initialize the items to NULL      */
  182.       memset(StringTable,0,sizeof(StringTable));
  183.       first = 0;
  184.    }
  185.  
  186.    if ((IDS < FIRSTVALID) ||            /* no chance to find the string in   */
  187.        (IDS > LASTVALID))               /* the resource                      */
  188.       return(szUNKNOWN);
  189.                                         /* it must be in the resource file   */
  190.    IDS -= FIRSTVALID;                   /* align ID to the first element no. */
  191.    if (StringTable[IDS] != NULL)        /* element in buffer?                */
  192.       return(StringTable[IDS]);
  193.                                         /* unknown string, try to load       */
  194.                                         /* resource ID for the system        */
  195.    LoadID = ((IDS + FIRSTVALID + CurrentLanguage) / 16) + 1;
  196.    AssignNum = IDS & 0xFFFFFFF0;        /* first element in the cluster      */
  197.    if ((err = DosGetResource((HMODULE) 0, /* get the resource, the system    */
  198.                              RT_STRING, /* will allocate a new buffer for us */
  199.                              LoadID,
  200.                              (PPVOID) &buf)) == 0) {
  201.       buf += 2;                         /* there is an USHORT at the begin of*/
  202.                                         /* the resource (I think so :-( )    */
  203.       for (i = 0;i < 16;i++) {
  204.          len = *((unsigned char *) buf++);   /* take the length (UCHAR)      */
  205.          StringTable[AssignNum + i] = buf;   /* assign buffer element        */
  206.          buf += len;                    /* add the length                    */
  207.       }
  208.    } else                               /* resource not loaded?              */
  209.       for (i = 0;i < 16;i++)            /* apply default string              */
  210.          StringTable[AssignNum + i] = szUNKNOWN;
  211.    if (StringTable[IDS] == NULL)        /* should never happen...            */
  212.       StringTable[IDS] = szUNKNOWN;
  213.    return(StringTable[IDS]);
  214. #undef LASTVALID
  215. #undef FIRSTVALID
  216. }
  217.  
  218. /*****************************************************************************/
  219. /*  function name : GetTime                                                  */
  220. /*                                                                           */
  221. /*  arguments     : pointer to a buffer, the length of the buffer und a      */
  222. /*                  time.                                                    */
  223. /*                                                                           */
  224. /*  description   : puts the date and time of the third argument into the    */
  225. /*                  buffer in the default form of the current country.       */
  226. /*                  The country info is gotten from the system on first      */
  227. /*                  time.                                                    */
  228. /*****************************************************************************/
  229. void GetTime(char *buf,size_t bufsize,time_t t)
  230. {
  231.    static char formatstring[30] = "";
  232.    if (formatstring[0] == 0) {
  233.       COUNTRYCODE cc = {0,0};
  234.       COUNTRYINFO ci;
  235.       ULONG done;
  236.       char *date;
  237.       char *time;
  238.       strcpy(formatstring,"%d.%m.%Y %H:%M:%S"); /* default for Germany and   */
  239.                                         /* other european states             */
  240.       if ((DosQueryCtryInfo(sizeof(ci),&cc,&ci,&done) != 0) &&
  241.           (done >= offsetof(COUNTRYINFO,fsTimeFmt) + sizeof(ci.fsTimeFmt))) {
  242.          if (ci.fsDateFmt == DATEFMT_MM_DD_YY)
  243.             date = "%%m%s%%d%s%%Y ";    /* e.g. %m.%d.%Y                     */
  244.          else if (ci.fsDateFmt == DATEFMT_YY_MM_DD)
  245.             date = "%%Y%s%%m%s%%d ";
  246.          else
  247.             date = "%%d%s%%m%s%%Y ";
  248.          if (ci.fsTimeFmt)              /* 24 hour clock                     */
  249.             time = "%%H%s%%M%s%%S";
  250.          else
  251.             time = "%%I%s%%M%s%%S%%p";
  252.          sprintf(formatstring,
  253.                  date,
  254.                  ci.szDateSeparator,
  255.                  ci.szDateSeparator,
  256.                  ci.szDateSeparator);
  257.          sprintf(formatstring + strlen(formatstring),
  258.                  time,
  259.                  ci.szTimeSeparator,
  260.                  ci.szTimeSeparator,
  261.                  ci.szTimeSeparator);
  262.       }
  263.    }
  264.    strftime(buf,bufsize,formatstring,localtime(&t));
  265. }
  266.  
  267. /*****************************************************************************/
  268. /*  function name : GetTimeString                                            */
  269. /*                                                                           */
  270. /*  return value  : pointer to a static buffer holding the current date      */
  271. /*                  and time string with " : " appended.                     */
  272. /*                                                                           */
  273. /*  description   : see return value. You should not change the return       */
  274. /*                  value.                                                   */
  275. /*****************************************************************************/
  276. const char *GetTimeString(void)
  277. {
  278.    time_t t;
  279.    static char buf[40];
  280.    time(&t);
  281.    GetTime(buf,sizeof(buf) - 4,t);
  282.    strcat(buf," : ");
  283.    return(buf);
  284. }
  285.  
  286. /*****************************************************************************/
  287. /*  function name : BlockOutput                                              */
  288. /*                                                                           */
  289. /*  description   : the calling thread gets the exclusive access to the      */
  290. /*                  current output file.                                     */
  291. /*                                                                           */
  292. /*  note          : call UnBlockOutput as soon as possible                   */
  293. /*****************************************************************************/
  294. void BlockOutput(void)
  295. {
  296.    ULONG err;
  297.    if (WriteSem == (HMTX) 0)            /* not yet allocated? OK             */
  298.       return;
  299.    do {
  300.       err = DosRequestMutexSem(WriteSem,SEM_INDEFINITE_WAIT);
  301.    } while ((err != 0) && (err != ERROR_INTERRUPT));
  302. }
  303.  
  304. /*****************************************************************************/
  305. /*  function name : UnBlockOutput                                            */
  306. /*                                                                           */
  307. /*  description   : releases the exclusive access to the current output      */
  308. /*                  file.                                                    */
  309. /*                                                                           */
  310. /*  note          : should only called by the thread that has called         */
  311. /*                  BlockOutput                                              */
  312. /*****************************************************************************/
  313. void UnBlockOutput(void)
  314. {
  315.    DosReleaseMutexSem(WriteSem);
  316. }
  317.  
  318. /*****************************************************************************/
  319. /*  function name : Message                                                  */
  320. /*                                                                           */
  321. /*  arguments     : see printf                                               */
  322. /*                                                                           */
  323. /*  description   : prints the date and time and the message to the          */
  324. /*                  current output file. The written data is flushed to      */
  325. /*                  disk physically. Is the output file grows beyond the     */
  326. /*                  threshold a new output file is generated.                */
  327. /*****************************************************************************/
  328. void Message(const char *fmt,...)
  329. {
  330.    va_list marker;
  331.    if ((ProgramFlags & PRG_OUTPUT_IS_NUL) || (out == NULL)) /* nothing to do */
  332.       return;
  333.    BlockOutput();
  334.    if (filelength(fileno(out)) >= Threshold)
  335.       NextOutputFile(0);
  336.    fputs(GetTimeString(),out);
  337.    va_start(marker,fmt);
  338.    vfprintf(out,fmt,marker);
  339.    va_end(marker);
  340.    fflush(out);
  341.    UnBlockOutput();
  342. }
  343.  
  344. /*****************************************************************************/
  345. /*  function name : GetError                                                 */
  346. /*                                                                           */
  347. /*  arguments     : errno code                                               */
  348. /*                                                                           */
  349. /*  return value  : the appropriate errno string                             */
  350. /*                                                                           */
  351. /*  description   : resolves the current errno to a string. The errno        */
  352. /*                  string is not terminated by a newline. You should not    */
  353. /*                  change the return value.                                 */
  354. /*****************************************************************************/
  355. const char *GetError(int errnocode)
  356. {
  357.    static char strerrorbuf[128];
  358.    char *ptr;
  359.    ptr = strerror(errnocode);
  360.    if (strlen(ptr) >= sizeof(strerrorbuf))
  361.       memcpy(strerrorbuf,ptr,sizeof(strerrorbuf));
  362.    else
  363.       strcpy(strerrorbuf,ptr);
  364.    strerrorbuf[sizeof(strerrorbuf) - 1] = 0;
  365.    if ((ptr = strpbrk(strerrorbuf,"\r\n")) != NULL)
  366.       *ptr = 0;
  367.    return(strerrorbuf);
  368. }
  369.  
  370. /*****************************************************************************/
  371. /*  function name : Perror                                                   */
  372. /*                                                                           */
  373. /*  arguments     : see perror                                               */
  374. /*                                                                           */
  375. /*  description   : see perror but the output is preceeded by the current    */
  376. /*                  date and time and is written to the current output       */
  377. /*                  file.                                                    */
  378. /*****************************************************************************/
  379. void Perror(const char *s)
  380. {
  381.    if ((s != NULL) && (*s != '\0'))
  382.       Message("%s: %s\n",
  383.               s,
  384.               GetError(errno));
  385.    else
  386.       Message("%s\n",GetError(errno));
  387. }
  388.  
  389. /*****************************************************************************/
  390. /*  function name : ResolvOS2Err                                             */
  391. /*                                                                           */
  392. /*  arguments     : OS/2 error code, NULL terminated list of strings         */
  393. /*                  describing the error conditions or files.                */
  394. /*                                                                           */
  395. /*  return value  : the appropriate error string                             */
  396. /*                                                                           */
  397. /*  description   : resolves the error code to a string. The string is       */
  398. /*                  generated by the system. You should not change the       */
  399. /*                  return value.                                            */
  400. /*****************************************************************************/
  401. const char *ResolvOS2Err(ULONG msgno,const char *name,...)
  402. {
  403.    static CHAR msgbuf[513];             /* should be enough                  */
  404.    CHAR *names[10];                     /* maximum by system                 */
  405.    ULONG count = 0;
  406.    ULONG done;
  407.    va_list marker;
  408.    va_start(marker,name);
  409.    while ((count < 10) && (name != NULL)) {
  410.       names[count++] = (CHAR *) name;
  411.       name = va_arg(marker,CHAR *);
  412.    }
  413.    va_end(marker);
  414.    msgbuf[0] = 0;                       /* good default in case of an error  */
  415.    DosGetMessage(names,count,msgbuf,sizeof(msgbuf),msgno,"oso001.msg",&done);
  416.    return((char *) msgbuf);
  417. }
  418.  
  419. /*****************************************************************************/
  420. /*  function name : ChildDies                                                */
  421. /*                                                                           */
  422. /*  arguments     : signal number generated by the runtime system            */
  423. /*                                                                           */
  424. /*  description   : This is a signal routine. This routine is called by a    */
  425. /*                  SIGCHLD. It looks for the child which has died and       */
  426. /*                  calls EndProcess with the pid of the child. Then,        */
  427. /*                  the cron thread is awaken to reap the child.             */
  428. /*                                                                           */
  429. /*  note          : don't call directly, thread save?                        */
  430. /*****************************************************************************/
  431. void ChildDies(int code)
  432. {
  433.    int status;
  434.    int pid;
  435.    while ((pid = waitpid(-1,&status,WNOHANG)) > 0) {  /* never block!        */
  436.       EndProcess(pid,
  437.                  WIFEXITED(status) ? 0 : WTERMSIG(status),
  438.                  WEXITSTATUS(status));
  439.    }
  440.    signal(code,SIG_ACK);
  441.    DosPostEventSem(CronSem);
  442. }
  443.  
  444. /*****************************************************************************/
  445. /*  function name : PipeErr                                                  */
  446. /*                                                                           */
  447. /*  arguments     : signal number generated by the runtime system            */
  448. /*                                                                           */
  449. /*  description   : This is a signal routine. This routine is called by a    */
  450. /*                  SIG_PIPE. The signal is ignored.                         */
  451. /*                                                                           */
  452. /*  note          : don't call directly. We don't set signal(SIGPIPE,        */
  453. /*                  SIG_IGN) because the pipe thread should awake and        */
  454. /*                  get an ERROR_INTERRUPT by the system to reconnect the    */
  455. /*                  pipe.                                                    */
  456. /*****************************************************************************/
  457. void PipeErr(int code)
  458. {
  459.    signal(code,SIG_ACK);
  460. }
  461.  
  462. /*****************************************************************************/
  463. /*  function name : FullName                                                 */
  464. /*                                                                           */
  465. /*  arguments     : path or filename                                         */
  466. /*                                                                           */
  467. /*  description   : generates the fully qualified pathname of the            */
  468. /*                  argument. If an error occures the buffer is unchanged.   */
  469. /*                                                                           */
  470. /*  note          : the buffer Filename should be big enough, use a size     */
  471. /*                  of CCHMAXPATH.                                           */
  472. /*****************************************************************************/
  473. void FullName(char *Filename)
  474. {
  475.    size_t len;
  476.    char buf[CCHMAXPATH];
  477.    if (strlen(Filename) == 0)           /* argument == "" ? assume current   */
  478.       strcpy(Filename,".");             /* directory                         */
  479.  
  480.    if (DosQueryPathInfo(Filename,
  481.                         FIL_QUERYFULLNAME,
  482.                         buf,
  483.                         sizeof(buf)) == 0) {
  484.       if (strnicmp(buf,"\\DEV\\",5) == 0) {  /* don't expand character device*/
  485.          strcpy(Filename,buf + 5);      /* names, instead, eat up the leading*/
  486.          return;                        /* '\DEV\'                           */
  487.       }
  488.       strcpy(Filename,buf);
  489.    } else
  490.       return;
  491.    if ((len = strlen(Filename)) == 0)   /* should never happen...            */
  492.       return;
  493.  
  494.    if ((len == 3) && (Filename[1] == ':'))   /* don't cut the trailing back- */
  495.       return;                           /* slash of "?:\"                    */
  496.    if (Filename[len - 1] == '\\')       /* cut trailing backslash            */
  497.       Filename[len - 1] = '\0';
  498. }
  499.  
  500. /*****************************************************************************/
  501. /*  function name : AppendPathComponent                                      */
  502. /*                                                                           */
  503. /*  arguments     : pathname and a new component of the path or filename     */
  504. /*                                                                           */
  505. /*  description   : append to the first argument the second one. Delimits    */
  506. /*                  them by a backslash if needed.                           */
  507. /*                                                                           */
  508. /*  note          : the first buffer should be big enough, use a size of     */
  509. /*                  CCHMAXPATH.                                              */
  510. /*****************************************************************************/
  511. void AppendPathComponent(char *path,const char *component)
  512. {
  513.    size_t len = strlen(path);
  514.    if (len == 0)
  515.       strcpy(path,component);
  516.    else if (strchr(Delims,path[len - 1]) != NULL)  /* Be careful! suppose    */
  517.       strcat(path,component);           /* path == "A:", component == "file" */
  518.    else {
  519.       strcat(path,"\\");
  520.       strcat(path,component);
  521.    }
  522. }
  523.  
  524. /*****************************************************************************/
  525. /*  function name : CheckOutputPath                                          */
  526. /*                                                                           */
  527. /*  arguments     : pathname                                                 */
  528. /*                                                                           */
  529. /*  return value  : 1, if the pathname looks fine to be an output path,      */
  530. /*                  0 if not.                                                */
  531. /*                                                                           */
  532. /*  description   : checks wether the argument is a directory and doesn't    */
  533. /*                  has nondestroyable entries of the form "Cron000?.out".   */
  534. /*****************************************************************************/
  535. int CheckOutputPath(char *s)
  536. {
  537.    DIR *dir;
  538.    struct dirent *ent;
  539.    int retval = 1;
  540.    char buf[CCHMAXPATH];
  541.    if (strlen(s) >= CCHMAXPATH - 14)
  542.       return(0);
  543.    FullName(s);
  544.    strcpy(buf,s);
  545.    if ((dir = opendir(buf)) == NULL)    /* no directory?                     */
  546.       return(0);
  547.    while ((ent = readdir(dir)) != NULL) {
  548.       if (fnmatch(FilenameMaskSearch,ent->d_name,_FNM_OS2 | _FNM_IGNORECASE)
  549.                                                                           != 0)
  550.          continue;
  551.       if (!isdigit(ent->d_name[SEARCH_DIGIT_POS]))
  552.          continue;
  553.       if (ent->d_attr & (A_RONLY | A_HIDDEN | A_SYSTEM | A_DIR)) {
  554.          retval = 0;
  555.          break;
  556.       }
  557.    }
  558.    closedir(dir);
  559.    return(retval);
  560. }
  561.  
  562. /*****************************************************************************/
  563. /*  function name : NextOutputFile                                           */
  564. /*                                                                           */
  565. /*  arguments     : flag, if the output file must be opened in append mode   */
  566. /*                                                                           */
  567. /*  return value  : handle of the new output file,-1 on error                */
  568. /*                                                                           */
  569. /*  description   : opens a new (or old) output file. The function uses      */
  570. /*                  the variables OutputPath, CurrOutput und MaxOutputs to   */
  571. /*                  determine the correct filename. New data is appended     */
  572. /*                  to the current output file, if Append is set. This is    */
  573. /*                  the normal behavior while starting the program. While    */
  574. /*                  running and changing the output file all data in the     */
  575. /*                  new output file is destroyed.                            */
  576. /*                  If the program flag PRG_OUTPUT_IS_NUL is set the         */
  577. /*                  output file is closed und 0 is returned.                 */
  578. /*                  This function takes care of the Threshold.               */
  579. /*                                                                           */
  580. /*  note          : Please, don't use the returned handle, use the global    */
  581. /*                  variable out instead.                                    */
  582. /*****************************************************************************/
  583. int NextOutputFile(int Append)
  584. {
  585.    int handle;
  586.    int AddFlags;
  587.    FILE *file,*hlpfile;
  588.    unsigned short i,done;
  589.    char Filename[CCHMAXPATH];
  590.    char *ptr;
  591.  
  592.    if (ProgramFlags & PRG_OUTPUT_IS_NUL) {   /* ignore all output?           */
  593.       if (out != NULL)
  594.          fclose(out);
  595.       out = NULL;
  596.       strcpy(CurrFilename,"nul");
  597.       NewOutputFile();
  598.       return(0);
  599.    }
  600.  
  601.    if (Append)                          /* Open in append mode?              */
  602.       AddFlags = O_APPEND;
  603.    else
  604.       AddFlags = O_TRUNC | O_APPEND;    /* Yes, we append too, but we trunc  */
  605.                                         /* the file before                   */
  606.    i = CurrOutput;                      /* current output file number        */
  607.    if (!Append)                         /* new file? use valid next number   */
  608.       if (++i >= MaxOutputs)
  609.          i = 0;
  610.    strcpy(Filename,OutputPath);
  611.    AppendPathComponent(Filename,"?");   /* place a dummy as filename into buf*/
  612.    ptr = Filename + strlen(Filename) - 1; /* thus we avoid problems with     */
  613.                                         /* paths with/without trailing back- */
  614.                                         /* slashs                            */
  615.    for (done = 0;done < MaxOutputs;done++) { /* step through all valid names */
  616.       sprintf(ptr,FilenameMaskSet,i);
  617.       if ((handle = sopen(Filename,
  618.                           AddFlags | O_CREAT | O_TEXT | O_WRONLY |
  619.                                                        O_SYNC | O_NOINHERIT,
  620.                           SH_DENYWR,
  621.                           S_IREAD | S_IWRITE)) != -1) {
  622.          if ((filelength(handle) >= Threshold) ||  /* append mode?           */
  623.              ((file = fdopen(handle,"at")) == NULL)) {   /* we need a FILE   */
  624.             close(handle);
  625.             if (++i >= MaxOutputs)
  626.                i = 0;
  627.             AddFlags = O_TRUNC | O_APPEND;   /* trunc next file              */
  628.             continue;
  629.          }
  630.          fseek(file,0,SEEK_END);        /* there are problems with fdopen    */
  631.          if (i != CurrOutput) {         /* New output number?                */
  632.             CurrOutput = (UCHAR) i;
  633.             ReWritePrfFlags |= REWRITE_CURRENT;
  634.             ReWritePrf();
  635.          }
  636.          hlpfile = out;                 /* save old file, don't ever use     */
  637.          out = file;                    /* BlockOutput, thus make the change */
  638.                                         /* atomically                        */
  639.          if (hlpfile != NULL)
  640.             fclose(hlpfile);
  641.          strcpy(CurrFilename,Filename);
  642.          NewOutputFile();
  643.          return(handle);
  644.       }
  645.       if (++i >= MaxOutputs)            /* error opening file, check next    */
  646.          i = 0;
  647.       AddFlags = O_TRUNC | O_APPEND;    /* trunc next file                   */
  648.    }
  649.    return(-1);
  650. }
  651.  
  652. /*****************************************************************************/
  653. /*  function name : ReadCrontabs                                             */
  654. /*                                                                           */
  655. /*  arguments     : new name of the Crontabs file                            */
  656. /*                                                                           */
  657. /*  return value  : 1 on success, 0 otherwise                                */
  658. /*                                                                           */
  659. /*  description   : reads a Crontabs file and builds a complete new          */
  660. /*                  joblist. If this is done sucessfully, the default list   */
  661. /*                  is set to the new one and the old one is deleted.        */
  662. /*                  The cron thread is informed of the new list.             */
  663. /*                                                                           */
  664. /*  note          : only "nul" and regular files are allowed.                */
  665. /*                  In opposite of the unix version we have permanent        */
  666. /*                  access to the file. This is the right manner! Now we     */
  667. /*                  can sleep for a long time and we don't need to reread    */
  668. /*                  the Crontabs file every minute thus saving processor     */
  669. /*                  time.                                                    */
  670. /*****************************************************************************/
  671. int ReReadCrontabs(char *s)
  672. {
  673.    LIST_ENTRY NewHead, oldhead;
  674.    int handle,oldhandle,htype = HT_DEV_OTHER;
  675.  
  676.    if ((handle = sopen(s,
  677.                        O_BINARY | O_RDWR | O_CREAT | O_NOINHERIT,SH_DENYWR,
  678.                        S_IREAD | S_IWRITE)) == -1)
  679.       return(0);
  680.    ioctl(handle,FGETHTYPE,&htype);
  681.    if ((htype != HT_FILE) && (htype != HT_DEV_NUL)) { /* not "nul" or regular*/
  682.       close(handle);                    /* file? We won't read "KBD$" !!!    */
  683.       return(0);
  684.    }
  685.    memset(&NewHead,0,sizeof(NewHead));  /* build the new list                */
  686.    if (ReadInFile(handle,&NewHead) != 0) {   /* error interpreting file?     */
  687.       close(handle);
  688.       return(0);
  689.    }
  690.  
  691.    BlockProcess();                      /* we want to change the joblist     */
  692.    oldhandle = cronhandle;              /* save old settings                 */
  693.    oldhead = ListHead;
  694.    cronhandle = handle;                 /* setup the newer ones              */
  695.    ListHead = NewHead;
  696.    UnBlockProcess();                    /* OK, delete old resources but first*/
  697.                                         /* tell the cron thread to compute   */
  698.    if (CronSem != (HEV) 0)              /* the next starting time...         */
  699.       DosPostEventSem(CronSem);
  700.    JobsModified();                      /* the list is modified              */
  701.  
  702.    if (oldhandle != -1)                 /* close the old list                */
  703.       close(oldhandle);
  704.    while (DeleteListEntry(oldhead.next,&oldhead) == 0)   /* delete the       */
  705.       ;                                 /* complete old list                 */
  706.    return(1);
  707. }
  708.  
  709.  
  710. /*****************************************************************************/
  711. /*  function name : ReadPrf                                                  */
  712. /*                                                                           */
  713. /*  arguments     : name of the profile subentry (see Prf...[] at top)       */
  714. /*                  buffer for data                                          */
  715. /*                  minimum length of data to accept                         */
  716. /*                  maximum length of data to accept                         */
  717. /*                                                                           */
  718. /*  return value  : size of read data or 0 on error                          */
  719. /*                                                                           */
  720. /*  description   : reads from getenv(USER_INI) the supplied subentry of     */
  721. /*                  the entry PrfDaemon. It only accepts data lengths in a   */
  722. /*                  distict intervall.                                       */
  723. /*                                                                           */
  724. /*  note          : don't call direct, use ReadProfileData instead           */
  725. /*****************************************************************************/
  726. static ULONG ReadPrf(PCSZ Itemname,VOID *buf,ULONG minsize,ULONG maxsize)
  727. {
  728.    ULONG size = maxsize;
  729.    if (!PrfQueryProfileData(HINI_USERPROFILE,
  730.                             PrfDaemon,
  731.                             Itemname,
  732.                             buf,
  733.                             &size))
  734.       return(0);
  735.    if (size < minsize)
  736.       return(0);
  737.    return(size);
  738. }
  739.  
  740. /*****************************************************************************/
  741. /*  function name : WritePrf                                                 */
  742. /*                                                                           */
  743. /*  arguments     : name of the profile subentry (see Prf...[] at top)       */
  744. /*                  buffer of data                                           */
  745. /*                  length of buffer                                         */
  746. /*                                                                           */
  747. /*  return value  : 1 on success, 0 on error                                 */
  748. /*                                                                           */
  749. /*  description   : writes to getenv(USER_INI) the supplied subentry of      */
  750. /*                  the entry PrfDaemon.                                     */
  751. /*****************************************************************************/
  752. static int WritePrf(PCSZ Itemname,VOID *buf,ULONG size)
  753. {
  754.    if (PrfWriteProfileData(HINI_USERPROFILE,
  755.                            PrfDaemon,
  756.                            Itemname,
  757.                            buf,
  758.                            size) == TRUE)
  759.       return(1);
  760.    return(0);
  761. }
  762.  
  763. /*****************************************************************************/
  764. /*  function name : ReWriteOnePrf                                            */
  765. /*                                                                           */
  766. /*  arguments     : mask of the profile subentry which has to be written,    */
  767. /*                  see REWRITE_... flags in the header file                 */
  768. /*                                                                           */
  769. /*  return value  : 1 if successful, 0 otherwise                             */
  770. /*                                                                           */
  771. /*  description   : writes one entry of the profile data to the              */
  772. /*                  getenv(USER_INI) file.                                   */
  773. /*                                                                           */
  774. /*  note          : don't call direct, use ReWritePrf instead.               */
  775. /*****************************************************************************/
  776. static int ReWriteOnePrf(ULONG num)
  777. {
  778.    switch (num) {
  779.       case REWRITE_SCREEN:              /* PM only                           */
  780.          if (RunningUnderPM)
  781.             return(WritePrf(PrfScreen,&FrameRect,sizeof(FrameRect)));
  782.          else
  783.             return(1);
  784.       case REWRITE_FLAGS:
  785.          return(WritePrf(PrfFlags,&ProgramFlags,sizeof(ProgramFlags)));
  786.       case REWRITE_MAXIMUM:
  787.          return(WritePrf(PrfNumMax,&MaxOutputs,sizeof(MaxOutputs)));
  788.       case REWRITE_CURRENT:
  789.          return(WritePrf(PrfNumAct,&CurrOutput,sizeof(CurrOutput)));
  790.       case REWRITE_THRESHOLD:
  791.          return(WritePrf(PrfThreshold,&Threshold,sizeof(Threshold)));
  792.       case REWRITE_OUTPUTPATH:
  793.          return(WritePrf(PrfOutput,OutputPath,strlen(OutputPath) + 1));
  794.       case REWRITE_TCPIP_ALLOW:
  795.          return(WritePrf(PrfTCPIPAllow,TCPIP_Allow,strlen(TCPIP_Allow) + 1));
  796.       case REWRITE_CRONFILENAME:
  797.          return(WritePrf(PrfCrontabs,Crontabs,strlen(Crontabs) + 1));
  798.       case REWRITE_LANGUAGE:
  799.          return(WritePrf(PrfLanguage,&Language,sizeof(Language)));
  800.       case REWRITE_COLORS:
  801.          return(WritePrf(PrfColors,&Colors,sizeof(Colors)));
  802.       case REWRITE_FONT:
  803.          return(WritePrf(PrfFont,Fontname,strlen(Fontname) + 1));
  804.       case REWRITE_DATE:
  805.          return(WritePrf(PrfDate,&LastDayStarted,sizeof(LastDayStarted)));
  806.    }
  807.    return(1);                           /* default is success since some     */
  808. }                                       /* possible flags are not used       */
  809.  
  810. /*****************************************************************************/
  811. /*  function name : ReWritePrf                                               */
  812. /*                                                                           */
  813. /*  return value  : 1 if successful, 0 otherwise                             */
  814. /*                                                                           */
  815. /*  description   : writes all changed profile datas to the                  */
  816. /*                  getenv(USER_INI) file. This function should be called    */
  817. /*                  if any of the profile data changes. Maybe this           */
  818. /*                  function should be called periodically if one of the     */
  819. /*                  ReWritePrfFlags is set. After writing the flags are      */
  820. /*                  updated avoiding write overhead.                         */
  821. /*****************************************************************************/
  822. int ReWritePrf(void)
  823. {
  824.    int retval = 1;                      /* assume no errors                  */
  825.    ULONG i;
  826.    for (i = 1ul;i != 0;i <<= 1)
  827.       if (ReWritePrfFlags & i) {
  828.          if (!ReWriteOnePrf(i))
  829.             retval = 0;
  830.          else
  831.             ReWritePrfFlags &= ~i;
  832.       }
  833.    return(retval);
  834. }
  835.  
  836. /*****************************************************************************/
  837. /*  function name : ReadProfileData                                          */
  838. /*                                                                           */
  839. /*  description   : reads in all needed entries from the getenv(USER_INI)    */
  840. /*                  file. If there are problems (never initialized,          */
  841. /*                  invalid filenames, etc.) we set default values           */
  842. /*                  allowing starting up the program.                        */
  843. /*                  This function is quiet a bit awful. Be careful not to    */
  844. /*                  run into race conditions!                                */
  845. /*                                                                           */
  846. /*  note          : this function is called once at start of program         */
  847. /*****************************************************************************/
  848. void ReadProfileData(void)
  849. {
  850.    int ok;
  851.    if (RunningUnderPM) {
  852.                                         /* query desktop size                */
  853.       if (!WinQueryWindowRect(HWND_DESKTOP,&ScreenRect))
  854.          FatalWinError((HWND) 0);
  855.                                         /* query frame size                  */
  856.       if (!ReadPrf(PrfScreen,&FrameRect,sizeof(FrameRect),sizeof(FrameRect))) {
  857.                                         /* center the window sized (300,100) */
  858.          FrameRect.xRight = 300;
  859.          FrameRect.yTop = 100;
  860.          FrameRect.xLeft = (ScreenRect.xRight - 300) / 2;
  861.          FrameRect.yBottom = (ScreenRect.yTop - 100) / 2;
  862.          ReWritePrfFlags |= REWRITE_SCREEN;
  863.       } else {
  864.                                         /* put window into the visible region*/
  865.          if (FrameRect.xLeft < -5)      /* little tolerance                  */
  866.             FrameRect.xLeft = 0;
  867.          if (FrameRect.yBottom < -5)
  868.             FrameRect.yBottom = 0;
  869.          if (FrameRect.xLeft + FrameRect.xRight > ScreenRect.xRight + 5)
  870.             FrameRect.xLeft = ScreenRect.xRight - FrameRect.xRight;
  871.          if (FrameRect.yBottom + FrameRect.yTop > ScreenRect.yTop + 5)
  872.             FrameRect.yBottom = ScreenRect.yTop - FrameRect.yTop;
  873.       }
  874.       SaveFrameRect = FrameRect;
  875.  
  876.       if (!ReadPrf(PrfColors,Colors,sizeof(Colors),sizeof(Colors))) {
  877.          Colors[MYCLR_SYSFOREGROUND] = Colors[MYCLR_FOREGROUND] =
  878.                             WinQuerySysColor(HWND_DESKTOP,SYSCLR_WINDOWTEXT,0);
  879.          Colors[MYCLR_SYSBACKGROUND] = Colors[MYCLR_BACKGROUND] =
  880.                                 WinQuerySysColor(HWND_DESKTOP,SYSCLR_WINDOW,0);
  881.          ReWritePrfFlags |= REWRITE_COLORS;
  882.       }
  883.       if (!ReadPrf(PrfFont,Fontname,2,sizeof(Fontname)))
  884.          strcpy(Fontname,"");
  885.    }
  886.                                         /* first try to read the flags       */
  887.    if (!ReadPrf(PrfFlags,&ProgramFlags,sizeof(ProgramFlags),
  888.                                                        sizeof(ProgramFlags))) {
  889.       ReWritePrfFlags |= REWRITE_FLAGS;
  890.       ProgramFlags = PRG_OUTPUT_IS_NUL; /* default: avoid output files       */
  891.    } else
  892.       ProgramFlags &= PRG_ALLFLAGS;     /* kill illegal flags                */
  893.  
  894.    if (!ReadPrf(PrfCrontabs,Crontabs,2,sizeof(Crontabs))) { /* read Crontabs */
  895.       ReWritePrfFlags |= REWRITE_CRONFILENAME;  /* name                      */
  896.       strcpy(Crontabs,DefIOPath);       /* this is the default path          */
  897.       AppendPathComponent(Crontabs,"Crontabs");
  898.    }
  899.    if (!ReReadCrontabs(Crontabs)) {    /* error reading Crontabs file?       */
  900.       ok = 0;
  901.       if ((ReWritePrfFlags & REWRITE_CRONFILENAME) == 0) {
  902.          strcpy(Crontabs,DefIOPath);    /* perhaps a destroyed Crontabs      */
  903.          AppendPathComponent(Crontabs,"Crontabs");
  904.          if (ReReadCrontabs(Crontabs)) {
  905.             Error(Get(IDS_DefaultCrontabsFile),(HWND) 0);
  906.             ok = 1;
  907.          }
  908.       }
  909.       if (!ok) {                        /* Crontabs still bad?               */
  910.          strcpy(Crontabs,"nul");
  911.          if (!ReReadCrontabs(Crontabs))
  912.             FatalError(Get(IDS_NoCrontabsFile),(HWND) 0);
  913.          else
  914.             Error(Get(IDS_ErrCrontabsFile),(HWND) 0);
  915.       }
  916.    }
  917.    if ((!ReadPrf(PrfNumMax,&MaxOutputs,sizeof(MaxOutputs),sizeof(MaxOutputs)))||
  918.        ((MaxOutputs > 10) || (MaxOutputs < 2))) {
  919.       ReWritePrfFlags |= REWRITE_MAXIMUM;
  920.       MaxOutputs = 10;
  921.    }
  922.    if ((!ReadPrf(PrfNumAct,&CurrOutput,sizeof(CurrOutput),sizeof(CurrOutput)))||
  923.        (CurrOutput > MaxOutputs)) {
  924.       ReWritePrfFlags |= REWRITE_CURRENT;
  925.       CurrOutput = 0;
  926.    }
  927.    if (!ReadPrf(PrfThreshold,&Threshold,sizeof(Threshold),sizeof(Threshold))) {
  928.       ReWritePrfFlags |= REWRITE_THRESHOLD;
  929.       Threshold = 0x4000;
  930.    }
  931.                                         /* we need the above values! don't   */
  932.                                         /* set the following more to the top */
  933.                                         /* of the file                       */
  934.    if (!ReadPrf(PrfOutput,OutputPath,2,sizeof(OutputPath))) {
  935.       ReWritePrfFlags |= REWRITE_OUTPUTPATH;
  936.       strcpy(OutputPath,DefIOPath);
  937.    }
  938.    if (!CheckOutputPath(OutputPath)) {  /* output path not acceptable?       */
  939.       if (ReWritePrfFlags & REWRITE_OUTPUTPATH) /* already default path?     */
  940.          Error(Get(IDS_NoOutputFile),(HWND) 0); /* no output                 */
  941.       else {
  942.          ReWritePrfFlags |= REWRITE_OUTPUTPATH;
  943.          strcpy(OutputPath,DefIOPath);
  944.          if (!CheckOutputPath(OutputPath))
  945.             Error(Get(IDS_NoOutputFile),(HWND) 0);
  946.          else
  947.             Error(Get(IDS_DefaultOutput),(HWND) 0);
  948.       }
  949.    }
  950.    BlockOutput();                       /* setup the output file             */
  951.    if (NextOutputFile(1) == -1)         /* can't set any output file?        */
  952.       Error(Get(IDS_NoOutputFile),(HWND) 0);
  953.    UnBlockOutput();
  954.    if (!ReadPrf(PrfTCPIPAllow,&TCPIP_Allow,2,sizeof(TCPIP_Allow))) {
  955.       ReWritePrfFlags |= REWRITE_TCPIP_ALLOW;
  956.       strcpy(TCPIP_Allow,"nul");
  957.    }
  958.    if (!ReadAllow(TCPIP_Allow)) {
  959.       if (ReWritePrfFlags & REWRITE_TCPIP_ALLOW)   /* already default file   */
  960.          Error(Get(IDS_NoTCPIPFile),(HWND) 0);
  961.       else {
  962.          ReWritePrfFlags |= REWRITE_TCPIP_ALLOW;
  963.          strcpy(TCPIP_Allow,"nul");
  964.          if (!ReadAllow(TCPIP_Allow))
  965.             Error(Get(IDS_NoTCPIPFile),(HWND) 0);
  966.          else
  967.             Error(Get(IDS_DefaultTCPIPFile),(HWND) 0);
  968.       }
  969.    }
  970.  
  971.    if (!ReadPrf(PrfDate,&LastDayStarted,sizeof(LastDayStarted),
  972.                                                      sizeof(LastDayStarted)))
  973.       LastDayStarted = 0ul;             /* no need to write changes          */
  974.  
  975.    if (ReWritePrfFlags)                 /* rewrite changed settings          */
  976.       ReWritePrf();
  977. }
  978.  
  979. /*****************************************************************************/
  980. /*  function name : PipeThread                                               */
  981. /*                                                                           */
  982. /*  arguments     : not needed, must be void *                               */
  983. /*                                                                           */
  984. /*  description   : this is a thread function. It processes all requests     */
  985. /*                  to the already opened pipe.                              */
  986. /*                  Stops running if GlobalStop is set. The function         */
  987. /*                  requests an automatic disconnect by the cron thread      */
  988. /*                  avoiding any user to block the pipe for a long time.     */
  989. /*                                                                           */
  990. /*  note          : should be called via _beginthread                        */
  991. /*****************************************************************************/
  992. void PipeThread(void *dummy)
  993. {
  994.    ULONG err;
  995.  
  996.    do {                                 /* while (!GlobalStop)               */
  997.       if (GlobalStop)                   /* check at mostly every place...    */
  998.          break;
  999.       err = DosConnectNPipe(pipehandle);  /* wait for a client               */
  1000.       if (err) {                        /* maybe ERROR_INTERRUPT?            */
  1001.          if (GlobalStop)
  1002.             break;
  1003.          DosDisConnectNPipe(pipehandle);  /* maybe open/close, this must     */
  1004.                                         /* be done                           */
  1005.          continue;
  1006.       }
  1007.       NewProgramStatus(IDS_StatusCommunication);
  1008.       AutoDisConnect = time(NULL) + 60; /* let the pipe close after a minute */
  1009.       DosPostEventSem(CronSem);         /* cron thread shall kill the pipe   */
  1010.                                         /* after AutoDisConnect              */
  1011.       ProcessRequest(&pipehandle,PipeName);  /* do the work                  */
  1012.       if ((AutoDisConnect != (time_t) -1l) && (!GlobalStop))
  1013.          WaitClientDisConnect();        /* wait for the client to close the  */
  1014.                                         /* pipe. Otherwise, the client may   */
  1015.                                         /* loose some data send.             */
  1016.                                         /* ANSWER ME: Don't we need to wait  */
  1017.                                         /* for the client is we set the      */
  1018.                                         /* NP_NOWRITEBEHIND-Flag while       */
  1019.                                         /* creating the pipe?                */
  1020.  
  1021.       AutoDisConnect = (time_t) -1l;    /* don't let close the pipe          */
  1022.       NewProgramStatus(IDS_StatusNormal);
  1023.       if (GlobalStop)
  1024.          break;
  1025.       DosDisConnectNPipe(pipehandle);
  1026.       DosPostEventSem(CronSem);         /* inform the cron thread that there */
  1027.                                         /* is no need to close the pipe any  */
  1028.                                         /* longer                            */
  1029.    } while (!GlobalStop);
  1030. }
  1031.  
  1032. #ifdef DEBUG
  1033. /*****************************************************************************/
  1034. /*  function name : Dputs                                                    */
  1035. /*                                                                           */
  1036. /*  arguments     : see puts                                                 */
  1037. /*                                                                           */
  1038. /*  description   : writes the string to the debugfile in case of            */
  1039. /*                  debugging                                                */
  1040. /*                                                                           */
  1041. /*  note          : you can't use puts since stdout isn't a valid output     */
  1042. /*                  file. Use this function instead.                         */
  1043. /*****************************************************************************/
  1044. void Dputs(char *s)
  1045. {
  1046.    if (DebugFile != NULL)
  1047.       fprintf(DebugFile,"%s\n",s);
  1048. }
  1049.  
  1050. /*****************************************************************************/
  1051. /*  function name : Dprintf                                                  */
  1052. /*                                                                           */
  1053. /*  arguments     : see printf                                               */
  1054. /*                                                                           */
  1055. /*  description   : writes the data to the debugfile in case of              */
  1056. /*                  debugging                                                */
  1057. /*                                                                           */
  1058. /*  note          : you can't use printf since stdout isn't a valid          */
  1059. /*                  output file. Use this function instead.                  */
  1060. /*****************************************************************************/
  1061. void Dprintf(char *fmt,...)
  1062. {
  1063.    va_list marker;
  1064.    if (DebugFile == NULL)
  1065.       return;
  1066.    va_start(marker,fmt);
  1067.    vfprintf(DebugFile,fmt,marker);
  1068.    va_end(marker);
  1069. }
  1070. #endif
  1071.  
  1072. int main(int argc,char **argv)
  1073. {
  1074.    ULONG flFrameFlags;
  1075.    QMSG qmsg;
  1076.    ULONG id,err;
  1077.    static CHAR FullName[CCHMAXPATH];
  1078.    PTIB ptib;
  1079.    PPIB ppib;
  1080.    HPIPE hp;
  1081.    int i;
  1082.    char FailBuf[257],*ptr;
  1083.    static RESULTCODES res;
  1084.  
  1085.    if ((_emx_env & 0x0200) == 0) {      /* this must be done first           */
  1086.       fputs("This program can't run under DOS",stdout);
  1087.       return(-1);
  1088.    }
  1089.  
  1090.                                         /* are we running as a PM appl?      */
  1091.                                         /* we must set the signal handlers   */
  1092.                                         /* to the correct value              */
  1093.    if (DosGetInfoBlocks(&ptib,&ppib) != 0) { /* query module handle and pgm  */
  1094.       fputs("error calling DosGetInfoBlocks",stdout); /* type                */
  1095.       return(-1);
  1096.    }
  1097.  
  1098.    RunningUnderPM = (ppib->pib_ultype == PT_PM) ? 1 : 0;
  1099.  
  1100.    if (RunningUnderPM) {
  1101.       NewProgramStatus = PMNewProgramStatus;
  1102.       NewStartTime     = PMNewStartTime;
  1103.       NewOutputFile    = PMNewOutputFile;
  1104.       StopPM           = PMStopPM;
  1105.       Error            = PMError;
  1106.       FatalError       = PMFatalError;
  1107.       JobsModified     = PMJobsModified;
  1108.       Stop             = PMStop;
  1109.    } else {
  1110.       NewProgramStatus = DetachedNewProgramStatus;
  1111.       NewStartTime     = DetachedNewStartTime;
  1112.       NewOutputFile    = DetachedNewOutputFile;
  1113.       StopPM           = DetachedStopPM;
  1114.       Error            = DetachedError;
  1115.       FatalError       = DetachedFatalError;
  1116.       JobsModified     = DetachedJobsModified;
  1117.       Stop             = DetachedStop;
  1118.    }
  1119.  
  1120.    signal(SIGINT,Stop);
  1121.    signal(SIGBREAK,Stop);
  1122.    signal(SIGTERM,Stop);
  1123.    signal(SIGCHLD,ChildDies);
  1124.    signal(SIGPIPE,PipeErr);
  1125.  
  1126.    DosError(FERR_DISABLEHARDERR | FERR_ENABLEEXCEPTION);
  1127.  
  1128.    err = ReadPrf(PrfLanguage,&Language,sizeof(Language),sizeof(Language));
  1129.    if (err == 0) {                      /* The entry has not been found      */
  1130.       Language = ENGLISH;               /* Default in case of an error       */
  1131.       ptr = getenv("LANG");
  1132.       if (ptr != NULL) {
  1133.          for (i = 0;i < (int) LanguagesCount;i++)
  1134.             if (strcmp(Languages[i].LANG_descr,ptr) == 0)
  1135.                Language = Languages[i].code;
  1136.       }
  1137.       ReWritePrfFlags |= REWRITE_LANGUAGE;
  1138.    } else if (err == sizeof(Language)) {
  1139.                                         /* Valid language code?              */
  1140.       for (i = 0;i < (int) LanguagesCount;i++)
  1141.          if (Languages[i].code == Language)
  1142.             break;
  1143.       if (i >= (int) LanguagesCount) {  /* Unknown value                     */
  1144.          Language = ENGLISH;
  1145.          ReWritePrfFlags |= REWRITE_LANGUAGE;
  1146.       }
  1147.    } else {                             /* Entry corrupted, use English      */
  1148.       Language = ENGLISH;
  1149.       ReWritePrfFlags |= REWRITE_LANGUAGE;
  1150.    }
  1151.    if (ReWritePrfFlags & REWRITE_LANGUAGE)
  1152.       ReWritePrf();
  1153.    CurrentLanguage = Language;
  1154.  
  1155.    for (id = IDS_Title;id <= IDS_lastnumber_mustbe_last_IDS;id++) {
  1156.                                         /* preload all strings...            */
  1157. #ifdef CHECK_STRINGS                    /* can't use Dprintf or printf       */
  1158.       fprintf(stdout,"string of ID %lu is >%s<\n",id,Get(id));
  1159. #else
  1160.       Get(id);
  1161. #endif
  1162.    }
  1163.                                         /* check if some needed IDS have been*/
  1164.                                         /* loaded                            */
  1165.    if ((Get(IDS_Title) == szUNKNOWN) ||
  1166.        (Get(IDS_ErrInitialize) == szUNKNOWN)) {
  1167.       fputs("Can't load the resource strings, inform author or\n"
  1168.             "correct the IDs in the resource file appendage of the program.\n",
  1169.             stdout);
  1170.       return(-1);
  1171.    }
  1172.  
  1173.    if (RunningUnderPM) {                /* initialize PM                     */
  1174.       if ((hab = WinInitialize(0)) == (HAB) 0) {
  1175.          fputs(Get(IDS_ErrInitialize),stdout);
  1176.          return(-1);
  1177.       }
  1178.       if ((hmq = WinCreateMsgQueue(hab,0)) == (HMQ) 0) {
  1179.          fputs(Get(IDS_ErrMsgQueue),stdout);
  1180.          return(-1);
  1181.       }
  1182.    }
  1183.                                         /* Well, the whole stuff consists of */
  1184.                                         /* the following terms:              */
  1185.                                         /* 1) handle 0 is open on "nul" and  */
  1186.                                         /*    stdin points to this handle    */
  1187.                                         /* 2) handle 1 and 2 are reserved for*/
  1188.                                         /*    job redirection and should     */
  1189.                                         /*    point to "nul", too.           */
  1190.                                         /* 3) don't believe, these FILE's are*/
  1191.                                         /*    open! They are not in case of  */
  1192.                                         /*    PM!                            */
  1193.    fclose(stdin);                       /* fclose the standard file explicit */
  1194.    fclose(stdout);                      /*                                   */
  1195.    fclose(stderr);                      /*                                   */
  1196.    fcloseall();                         /* fclose all other                  */
  1197.    for (i = 0;i < 100;i++)              /* safety first!                     */
  1198.       close(i);
  1199.    if ((freopen("nul","r+b",stdin)  != stdin) ||   /* reassign standard files*/
  1200.        (freopen("nul","r+b",stdout) != stdout) ||
  1201.        (freopen("nul","r+b",stderr) != stderr))
  1202.       FatalError(Get(IDS_ErrReassignStdIO),(HWND) 0);
  1203. #ifdef DEBUG
  1204.                                         /* in case of debugging we open a    */
  1205.    if ((DebugFile = fopen("crond.out","at")) != NULL) {  /* helper file      */
  1206.                                         /* don't let childs inherit this file*/
  1207.       fcntl(fileno(DebugFile),F_SETFD,
  1208.                               fcntl(fileno(DebugFile),F_GETFD,0) | FD_CLOEXEC);
  1209.       setvbuf(DebugFile,NULL,_IONBF,0);
  1210.    }
  1211. #endif
  1212.  
  1213.    if (IsPipeThere())
  1214.       FatalError(Get(IDS_AlreadyStarted),(HWND) 0);
  1215.  
  1216.    CheckTCPIPAvail();
  1217.  
  1218.                                         /* setup needed semaphores           */
  1219.    if ((DosCreateMutexSem(NULL, &ThreadSem, 0, FALSE) != 0) ||
  1220.        (DosCreateMutexSem(NULL, &WriteSem,  0, FALSE) != 0) ||
  1221.        (DosCreateEventSem(NULL, &CronSem,   0, FALSE) != 0))
  1222.       FatalError(Get(IDS_ErrCreateSem),(HWND) 0);
  1223.  
  1224.                                         /* query own filename                */
  1225.    if (DosQueryModuleName(ppib->pib_hmte,sizeof(FullName),FullName) != 0)
  1226.       FatalError(Get(IDS_ErrQMod),(HWND) 0);
  1227.  
  1228.    strcpy(DefIOPath,FullName);          /* prepare default IO path           */
  1229.  
  1230.    ptr = DefIOPath;
  1231.    while (strpbrk(ptr,Delims) != NULL)
  1232.       ptr = strpbrk(ptr,Delims) + 1;
  1233.    *ptr = '\0';
  1234.  
  1235.    ReadProfileData();                   /* setup all saved profile data      */
  1236.  
  1237.    if (RunningUnderPM) {
  1238.       if (!WinRegisterClass(hab,
  1239.                             CronDaemonClass,
  1240.                             ClientWndProc,
  1241.                             CS_SIZEREDRAW | CS_MOVENOTIFY,
  1242.                             0L))
  1243.          FatalWinError((HWND) 0);
  1244.  
  1245.       flFrameFlags = FCF_STANDARD & ~FCF_ACCELTABLE;
  1246.       hwndFrame = WinCreateStdWindow(HWND_DESKTOP,
  1247.                                      0,
  1248.                                      &flFrameFlags,
  1249.                                      CronDaemonClass,
  1250.                                      Get(IDS_Title),
  1251.                                      0l,
  1252.                                      0,
  1253.                                      NLS(ID_CRON),
  1254.                                      &hwndClient);
  1255.       if (hwndFrame == (HWND) 0)
  1256.          FatalWinError((HWND) 0);
  1257.    } else {
  1258. #ifdef DEBUG
  1259.       if ((getenv("RUN_FOREGROUND") == NULL) &&
  1260.           (ppib->pib_ultype != PT_DETACHED)) {
  1261. #else
  1262.       if (ppib->pib_ultype != PT_DETACHED) { /* not detached?                */
  1263. #endif
  1264.          fcloseall();                   /* the now starting process needs    */
  1265.          close(cronhandle);             /* exclusive access to all files     */
  1266.          FailBuf[0] = 0;
  1267.          if ((err = DosExecPgm(FailBuf,
  1268.                                sizeof(FailBuf),
  1269.                                EXEC_BACKGROUND,
  1270.                                ppib->pib_pchcmd,
  1271.                                ppib->pib_pchenv,
  1272.                                &res,
  1273.                                FullName)) != 0) {
  1274.             out = fopen("SCREEN$","w+t"); /* out has been closed             */
  1275.             Message(Get(IDS_CantCallMyself),
  1276.                     err,
  1277.                     ResolvOS2Err(err,(FailBuf[0] != 0) ? FailBuf : FullName,
  1278.                                                                         NULL));
  1279.             exit(-1);
  1280.          }
  1281.          return(0);
  1282.       }
  1283.    }
  1284.                                         /* setup pipe                        */
  1285.    if ((DosCreateNPipe(PipeName,
  1286.                        &hp,
  1287.                        NP_ACCESS_DUPLEX | NP_NOINHERIT | NP_WRITEBEHIND,
  1288.                        NP_WAIT | NP_READMODE_BYTE | NP_TYPE_BYTE | 1,
  1289.                        MAX_IO,
  1290.                        MAX_IO,
  1291.                        500) != 0) ||
  1292.        (pipehandle = _imphandle(hp)) == -1)
  1293.       FatalError(Get(IDS_ErrNamedPipe),(HWND) 0);
  1294.    setmode(pipehandle,O_BINARY);
  1295.  
  1296.                                         /* start cron thread                 */
  1297.    if ((CronTid = (TID) _beginthread(CronDaemon,NULL,0x4000,NULL)) == (TID) -1)
  1298.       FatalError(Get(IDS_ErrStartDaemon),(HWND) 0);
  1299.  
  1300.  
  1301.    if (_beginthread(PipeThread,NULL,0x4000,NULL) == (TID) -1)
  1302.       FatalError(Get(IDS_ErrStartPipe),(HWND) 0);
  1303.  
  1304.    if (TCPIPAvail)
  1305.       if (_beginthread(SocketThread,NULL,0x4000,NULL) == (TID) -1)
  1306.          FatalError(Get(IDS_ErrStartSocket),(HWND) 0);   /* there is not     */
  1307.                                         /* enough memory to start the thread.*/
  1308.                                         /* if we continue we'll run into     */
  1309.                                         /* this problem later. I think it's  */
  1310.                                         /* better to terminate.              */
  1311.  
  1312.    if (RunningUnderPM) {
  1313.       ULONG fl = 0;
  1314.       if (ProgramFlags & PRG_START_MAXIMIZED)
  1315.          fl = SWP_MAXIMIZE;
  1316.       else if (ProgramFlags & PRG_START_MINIMIZED)
  1317.          fl = SWP_MINIMIZE;
  1318.       WinSetWindowPos(hwndFrame,0,
  1319.                       FrameRect.xLeft, FrameRect.yBottom,
  1320.                       FrameRect.xRight,FrameRect.yTop,
  1321.                       SWP_DEACTIVATE | SWP_MOVE | SWP_SIZE | SWP_SHOW | fl);
  1322.    }
  1323.  
  1324.    Message(Get(IDS_CronStarts));
  1325.  
  1326.  
  1327.    if (RunningUnderPM) {
  1328.       if (!setjmp(PrgEnd)) {
  1329.          SetJumpEnabled = 1;            /* allow the use of longjmp          */
  1330.          while (WinGetMsg(hab,&qmsg,0L,0,0))
  1331.             WinDispatchMsg(hab,&qmsg);
  1332.       }
  1333.       SetPosOfWindow(hwndFrame);
  1334.       WinDestroyWindow(hwndFrame);
  1335.       WinDestroyMsgQueue(hmq);
  1336.       WinTerminate(hab);
  1337.    } else {
  1338.       if (!setjmp(PrgEnd)) {
  1339.          SetJumpEnabled = 1;            /* allow the use of longjmp          */
  1340.          while (!GlobalStop)            /* wait until program should stop or */
  1341.             if (DosWaitThread(&CronTid,DCWW_WAIT) == 0) {
  1342.                CronTid = (TID) -1;
  1343.                break;
  1344.             }
  1345.       }
  1346.    }
  1347.  
  1348.    if (CronTid != (TID) -1)             /* cron thread still alive?          */
  1349.       if (DosWaitThread(&CronTid,DCWW_WAIT) != 0)  /* may occur              */
  1350.          DosSleep(1000);                /* be patient, GlobalStop is set...  */
  1351.  
  1352.    Message(Get(IDS_CronEnds));
  1353.  
  1354.    return(0);
  1355. }
  1356.  
  1357. /* RCS depending informations
  1358.  *
  1359.  * $Name: Version121 $
  1360.  *
  1361.  * $Log: cronmain.c $
  1362.  * Revision 1.7  1996/05/09 10:05:47  Florian
  1363.  * Problems with blanks in the help filename fixed.
  1364.  *
  1365.  * Revision 1.6  1995/10/18 09:46:07  Florian
  1366.  * Used font, colors and window state and current date are stored in the
  1367.  * INI-file now.
  1368.  * Better handling of the CHILD signal.
  1369.  * Added support of the French language.
  1370.  *
  1371.  * Revision 1.5  1995/08/03 07:43:23  Florian
  1372.  * NLS supported (English and German)
  1373.  *
  1374.  * Revision 1.4  1995/03/15 09:07:34  Florian
  1375.  * Some minor bugs fixed.
  1376.  * TCP/IP support added.
  1377.  *
  1378.  * Revision 1.3  1995/03/06 11:52:06  Florian
  1379.  * Many bugs fixed.
  1380.  * Fully supported online help.
  1381.  * Notebook layout revised.
  1382.  *
  1383.  * Revision 1.2  1995/02/20 12:53:23  Florian
  1384.  * All dialogs are placed into a notebook.
  1385.  * Some bugs fixed.
  1386.  *
  1387.  * Revision 1.1  1995/02/03 10:42:33  Florian
  1388.  * Initial revision
  1389.  *
  1390.  *
  1391.  */
  1392. static char rcsid[] = "@(#)$Id: cronmain.c 1.7 1996/05/09 10:05:47 Florian Exp $";
  1393.