/* code to open local or fetch FITS files for skyview from STScI or ESO. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xephem.h" #define MAXDSSFOV (30.) /* max field size we retreive, arcmins*/ #define MINDSSFOV (5.) /* min field size we retreive, arcmins*/ static int sf_readFile (char *name); static void sf_create (void); static void initFSB (Widget w); static void initPubShared (Widget rc_w, Widget fsb_w); static void sf_save_cb (Widget w, XtPointer client, XtPointer call); static void save_file (void); static void sf_open_cb (Widget w, XtPointer client, XtPointer call); static void sf_close_cb (Widget w, XtPointer client, XtPointer call); static void sf_help_cb (Widget w, XtPointer client, XtPointer call); static void sf_setdate_cb (Widget w, XtPointer client, XtPointer call); static void sf_setSaveName (char *newfn); static char *bname (char *buf); static void eso_fits (void); static void stsci_fits (void); static void fits_read_icb (XtPointer client, int *fd, XtInputId *id); static int fitsObs (double *mjdp); static void sf_setObsDate (void); static int prepOpen (char fn[], char errmsg[]); static XtInputId read_iid; /* set while working on reading from socket */ static XtIntervalId read_to; /* callback to poll for read cancel */ static void fits_read_to (XtPointer client, XtIntervalId *id); static void fits_read_abort (FImage *fip); static int fr_socket; /* FITS reading socket */ static XE_SSL_FD fr_ssl_fd; /* FITS reading ssl descriptor */ static Widget sf_w; /* main dialog */ static Widget savefn_w; /* TF for save filename */ static Widget stsci_w; /* TB for STScI, else ESO */ static Widget fsb_w; /* FSB for opening a file */ static Widget hdr_w; /* ScrolledText for the FITS header */ static Widget autoname_w; /* TB for whether to auto set save filename */ static Widget obsdate_w; /* label for obs date string */ static Widget setobsdate_w; /* PB to set main to obs date */ static Widget dss1_w; /* TB set to use DSS 1 */ static Widget dss2r_w; /* TB set to use DSS 2 red */ static Widget dss2b_w; /* TB set to use DSS 2 blue */ #define FWDT 1234 /* FITS file poll interval, ms */ static int fw_isFifo (char *name); static void fw_to (XtPointer client, XtIntervalId *id); static void fw_icb (XtPointer client, int *fd, XtInputId *id); static void fw_cb (Widget w, XtPointer client, XtPointer call); static void fw_on (int whether); static XtIntervalId fw_tid; /* used to poll for file naming FITS file */ static XtInputId fw_iid; /* used to monitor FIFO for name of FITS file */ static Widget fwfn_w; /* TF holding Watch file name */ static Widget fw_w; /* TB whether to watch for FITS file */ static int fw_fd; /* file watch fifo id */ /* which survey */ typedef enum { DSS_1, DSS_2R, DSS_2B } Survey; static Survey whichSurvey (void); #define FCPP 500 /* FITS read cancel poll period, ms */ static char fitsp[] = "FITSpattern"; /* resource name of FITS file pattern */ #if defined (__NUTC__) static char gexe[] = "gunzip.exe"; /* gunzip executable */ #else static char gexe[] = "gunzip"; /* gunzip executable */ #endif static char gcmd[] = "gunzip"; /* gunzip command's argv[0] */ static char skyfitscategory[] = "Sky View -- FITS"; /* Save category */ /* called to manage the fits dialog. */ void sf_manage() { if (!sf_w) { sf_create(); si_create(); } XtManageChild(sf_w); } /* called to unmanage the fits dialog. */ void sf_unmanage() { if (!sf_w) return; XtUnmanageChild (sf_w); } /* return 1 if dialog is up, else 0. */ int sf_ismanaged() { return (sf_w && XtIsManaged(sf_w)); } /* called to put up or remove the watch cursor. */ void sf_cursor (Cursor c) { Window win; if (sf_w && (win = XtWindow(sf_w)) != 0) { Display *dsp = XtDisplay(sf_w); if (c) XDefineCursor (dsp, win, c); else XUndefineCursor (dsp, win); } } /* install fip as the new current image with the given name. * N.B. fip memory is made persistent, do not reset on return * last argument determines whether contrast and WCS are set automatically. */ void sf_newFITS (FImage *fip, char name[], int autocon) { /* install fip as current image */ si_newfim (fip, name, autocon); /* set save name from image center, if enabled */ if (XmToggleButtonGetState(autoname_w)) sf_setSaveName (name); /* set image date */ sf_setObsDate (); } /* fill the hdr_w scrolled text with the FITS header entries. * keep hdrl up to date. */ void sf_showHeader (fip) FImage *fip; { char *header; int i; if (!sf_w) { sf_create(); si_create(); } /* room for each FITS line, with nl and a final END and \0 */ header = malloc ((fip->nvar+1)*(FITS_HCOLS+1) + 1); if (!header) { xe_msg (0, "No memory to display FITS header"); return; } /* copy from fip->var to header, adding \n after each line */ for (i = 0; i < fip->nvar; i++) { memcpy(header + i*(FITS_HCOLS+1), fip->var[i], FITS_HCOLS); header[(i+1)*(FITS_HCOLS+1)-1] = '\n'; } /* add END and '\0' to make it a real string */ (void) sprintf (&header[i*(FITS_HCOLS+1)], "END"); XmTextSetString (hdr_w, header); free (header); /* scroll to the top */ XmTextShowPosition (hdr_w, (XmTextPosition)0); } /* return copies of the current filename and OBJECT or TARGET keywords. * if either can not be determined, the returned string will be 0 length. * N.B. we assume the caller supplies "enough" space. */ void sf_getName (fn, on) char *fn; /* filename */ char *on; /* object name */ { FImage *fip; char *savefn; int i, n; fip = si_getFImage (); if (!fip) { *fn = *on = '\0'; return; } savefn = XmTextFieldGetString (savefn_w); n = strlen (savefn); for (i = n-1; i >= 0 && savefn[i] != '/' && savefn[i] != '\\'; --i) continue; strcpy (fn, &savefn[i+1]); XtFree (savefn); if (getStringFITS (fip, "OBJECT", on) < 0 && getStringFITS (fip, "TARGET", on) < 0) *on = '\0'; } /* t00fri: include possibility to read .fth compressed files */ static int prepOpen (fn, errmsg) char fn[]; char errmsg[]; { int fd; int l; l = strlen (fn); if (l < 4 || strcmp(fn+l-4, ".fth")) { /* just open directly */ fd = openh (fn, O_RDONLY); if (fd < 0) strcpy (errmsg, syserrstr()); } else { /* ends with .fth so need to run through fdecompress * TODO: this is a really lazy way to do it -- */ char cmd[2048]; char tmp[2048]; int s; tempfilename (tmp, "xefts", ".fth"); sprintf (cmd, "cp %s %s; fdecompress -r %s", fn, tmp, tmp); s = system (cmd); if (s != 0) { sprintf (errmsg, "Can not execute `%s' ", cmd); if (s < 0) strcat (errmsg, syserrstr()); fd = -1; } else { tmp[strlen(tmp)-1] = 's'; fd = openh (tmp, O_RDONLY); (void) unlink (tmp); /* once open, remove the .fts copy */ if (fd < 0) sprintf (errmsg, "Can not decompress %s: %s", tmp, syserrstr()); } } return (fd); } /* open and read a FITS file. * if all ok return 0, else return -1. */ static int sf_readFile (name) char *name; { char buf[1024]; FImage fim, *fip = &fim; char errmsg[1024]; int fd; int s; /* open the fits file */ fd = prepOpen (name, errmsg); if (fd < 0) { xe_msg (1, "%s: %s", name, errmsg); return(-1); } /* read in */ s = readFITS (fd, fip, buf); close (fd); if (s < 0) { xe_msg (1, "%s: %s", name, buf); return(-1); } /* ok!*/ sf_newFITS (fip, name, 1); return (0); } /* create, but do not manage, the FITS file dialog */ static void sf_create() { Widget tf_w, bf_w; Widget rc_w, rb_w; Widget go_w; Widget pw_w; Widget h_w; Widget w; Arg args[20]; int n; /* create form */ n = 0; XtSetArg (args[n], XmNautoUnmanage, False); n++; XtSetArg (args[n], XmNallowResize, True); n++; XtSetArg (args[n], XmNverticalSpacing, 5); n++; XtSetArg (args[n], XmNmarginWidth, 5); n++; XtSetArg (args[n], XmNcolormap, xe_cm); n++; XtSetArg (args[n], XmNdefaultPosition, False); n++; sf_w = XmCreateFormDialog (svshell_w, "SkyFITS", args, n); set_something (sf_w, XmNcolormap, (XtArgVal)xe_cm); XtAddCallback (sf_w, XmNhelpCallback, sf_help_cb, NULL); sr_reg (XtParent(sf_w), "XEphem*SkyFITS.x", skyfitscategory, 0); sr_reg (XtParent(sf_w), "XEphem*SkyFITS.y", skyfitscategory, 0); /* set some stuff in the parent DialogShell. * setting XmNdialogTitle in the Form didn't work.. */ n = 0; XtSetArg (args[n], XmNtitle, "xephem Sky FITS"); n++; XtSetValues (XtParent(sf_w), args, n); /* top and bottom halves are in their own forms, then * each form is in a paned window */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; pw_w = XmCreatePanedWindow (sf_w, "FITSPW", args, n); XtManageChild (pw_w); /* the top form */ n = 0; tf_w = XmCreateForm (pw_w, "TF", args, n); XtManageChild (tf_w); /* controls to fetch networked images */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNtopOffset, 6); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++; go_w = XmCreatePushButton (tf_w, "Get", args, n); wtip (go_w, "Retrieve image of Sky View center over Internet"); XtAddCallback (go_w, XmNactivateCallback, sf_go_cb, NULL); XtManageChild (go_w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNtopOffset, 8); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNleftWidget, go_w); n++; XtSetArg (args[n], XmNleftOffset, 4); n++; XtSetArg (args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++; w = XmCreateLabel (tf_w, "GL", args, n); set_xmstring (w,XmNlabelString,"Digitized Sky Survey image:"); XtManageChild (w); /* institution selection */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, go_w); n++; XtSetArg (args[n], XmNtopOffset, 3); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNleftPosition, 3); n++; w = XmCreateLabel (tf_w, "From:", args, n); XtManageChild (w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, go_w); n++; XtSetArg (args[n], XmNtopOffset, 1); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNleftPosition, 25); n++; XtSetArg (args[n], XmNmarginHeight, 0); n++; XtSetArg (args[n], XmNpacking, XmPACK_TIGHT); n++; XtSetArg (args[n], XmNspacing, 6); n++; XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++; rb_w = XmCreateRadioBox (tf_w, "GRB", args, n); XtManageChild (rb_w); n = 0; XtSetArg (args[n], XmNspacing, 4); n++; stsci_w = XmCreateToggleButton (rb_w, "STScI", args, n); wtip (stsci_w, "Get image from Maryland USA"); XtManageChild (stsci_w); sr_reg (stsci_w, NULL, skyfitscategory, 1); /* stsci sets logic */ n = 0; XtSetArg (args[n], XmNspacing, 4); n++; XtSetArg(args[n],XmNset,!XmToggleButtonGetState(stsci_w)); n++; w = XmCreateToggleButton (rb_w, "ESO", args, n); wtip (stsci_w, "Get image from Germany"); XtManageChild (w); /* survey selection */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, rb_w); n++; XtSetArg (args[n], XmNtopOffset, 1); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNleftPosition, 3); n++; w = XmCreateLabel (tf_w, "Survey:", args, n); XtManageChild (w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, rb_w); n++; XtSetArg (args[n], XmNtopOffset, 0); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNleftPosition, 25); n++; XtSetArg (args[n], XmNmarginHeight, 0); n++; XtSetArg (args[n], XmNpacking, XmPACK_TIGHT); n++; XtSetArg (args[n], XmNspacing, 6); n++; XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++; rb_w = XmCreateRadioBox (tf_w, "GRB", args, n); XtManageChild (rb_w); n = 0; XtSetArg (args[n], XmNspacing, 4); n++; dss1_w = XmCreateToggleButton (rb_w, "DSS1", args, n); set_xmstring (dss1_w, XmNlabelString, "DSS 1"); wtip (dss1_w, "Original DSS"); XtManageChild (dss1_w); sr_reg (dss1_w, NULL, skyfitscategory, 1); n = 0; XtSetArg (args[n], XmNspacing, 4); n++; dss2r_w = XmCreateToggleButton (rb_w, "DSS2R", args, n); set_xmstring (dss2r_w, XmNlabelString, "DSS 2R"); wtip (dss2r_w, "DSS 2, Red band (90% complete)"); XtManageChild (dss2r_w); sr_reg (dss2r_w, NULL, skyfitscategory, 1); n = 0; XtSetArg (args[n], XmNspacing, 4); n++; dss2b_w = XmCreateToggleButton (rb_w, "DSS2B", args, n); set_xmstring (dss2b_w, XmNlabelString, "DSS 2B"); wtip (dss2b_w, "DSS 2, Blue band (50% complete)"); XtManageChild (dss2b_w); sr_reg (dss2b_w, NULL, skyfitscategory, 1); /* header, with possible date */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, rb_w); n++; XtSetArg (args[n], XmNtopOffset, 10); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++; h_w = XmCreateLabel (tf_w, "Lab", args, n); set_xmstring (h_w, XmNlabelString, "FITS Header:"); XtManageChild (h_w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, rb_w); n++; XtSetArg (args[n], XmNtopOffset, 10); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; setobsdate_w = XmCreatePushButton (tf_w, "SO", args, n); set_xmstring (setobsdate_w, XmNlabelString, "Set time"); XtAddCallback (setobsdate_w, XmNactivateCallback, sf_setdate_cb, 0); wtip(setobsdate_w,"Set main XEphem time to this Observation time"); XtManageChild (setobsdate_w); XtSetSensitive (setobsdate_w, False); /* set true when have date */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, rb_w); n++; XtSetArg (args[n], XmNtopOffset, 10); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNleftWidget, h_w); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNrightWidget, setobsdate_w); n++; XtSetArg (args[n], XmNalignment, XmALIGNMENT_CENTER); n++; obsdate_w = XmCreateLabel (tf_w, "ObsDate", args, n); set_xmstring (obsdate_w, XmNlabelString, " "); wtip(obsdate_w, "Best-guess of time of Observation"); XtManageChild (obsdate_w); /* scrolled text in which to display the header */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, setobsdate_w); n++; XtSetArg (args[n], XmNtopOffset, 2); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomOffset, 10); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNautoShowCursorPosition, False); n++; XtSetArg (args[n], XmNeditMode, XmMULTI_LINE_EDIT); n++; XtSetArg (args[n], XmNeditable, False); n++; XtSetArg (args[n], XmNcursorPositionVisible, False); n++; hdr_w = XmCreateScrolledText (tf_w, "Header", args, n); wtip (hdr_w, "Scrolled text area containing FITS File header"); XtManageChild (hdr_w); /* the bottom form */ n = 0; bf_w = XmCreateForm (pw_w, "BF", args, n); XtManageChild (bf_w); /* auto listen */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNtopOffset, 16); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; w = XmCreateLabel (bf_w, "FFWL", args, n); set_xmstring (w, XmNlabelString, "File watch:"); XtManageChild (w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNtopOffset, 15); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNleftPosition, 45); n++; fw_w = XmCreateToggleButton (bf_w, "Watch", args, n); /* N.B. don't sr_reg because that can trigger before SV ever up */ XtAddCallback (fw_w, XmNvalueChangedCallback, fw_cb, NULL); wtip (fw_w, "Whether to watch this file for name of FITS file to load"); XtManageChild (fw_w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, fw_w); n++; XtSetArg (args[n], XmNtopOffset, 2); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; fwfn_w = XmCreateTextField (bf_w, "WatchFile", args, n); defaultTextFN (fwfn_w, 0, getPrivateDir(), "watch.txt"); sr_reg (fwfn_w, NULL, skyfitscategory, 1); wtip (fwfn_w,"Name of file to watch for name of FITS file to load"); XtManageChild (fwfn_w); /* label, go PB, Auto name TB and TF for saving a file */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, fwfn_w); n++; XtSetArg (args[n], XmNtopOffset, 16); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; w = XmCreateLabel (bf_w, "Save", args, n); set_xmstring (w, XmNlabelString, "Save as:"); XtManageChild (w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, fwfn_w); n++; XtSetArg (args[n], XmNtopOffset, 15); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNleftPosition, 45); n++; w = XmCreatePushButton (bf_w, "Save", args, n); set_xmstring (w, XmNlabelString, "Save now"); XtAddCallback (w, XmNactivateCallback, sf_save_cb, NULL); wtip (w, "Save the current image to the file named below"); XtManageChild (w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, fwfn_w); n++; XtSetArg (args[n], XmNtopOffset, 15); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; autoname_w = XmCreateToggleButton (bf_w, "AutoName", args, n); set_xmstring (autoname_w, XmNlabelString, "Auto name"); XtManageChild (autoname_w); wtip (autoname_w, "When on, automatically chooses a filename based on RA and Dec"); sr_reg (autoname_w, NULL, skyfitscategory, 1); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, autoname_w); n++; XtSetArg (args[n], XmNtopOffset, 2); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; savefn_w = XmCreateTextField (bf_w, "SaveFN", args, n); defaultTextFN (savefn_w, 0, getPrivateDir(), "xxx.fts"); XtAddCallback (savefn_w, XmNactivateCallback, sf_save_cb, NULL); wtip (savefn_w, "Enter name of file to write, then press Enter"); XtManageChild (savefn_w); /* the Open FSB */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, savefn_w); n++; XtSetArg (args[n], XmNtopOffset, 16); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++; w = XmCreateLabel (bf_w, "Lab", args, n); set_xmstring (w, XmNlabelString, "Open:"); XtManageChild (w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, savefn_w); n++; XtSetArg (args[n], XmNtopOffset, 14); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++; XtSetArg (args[n], XmNspacing, 5); n++; rc_w = XmCreateRowColumn (bf_w, "USRB", args, n); XtManageChild (rc_w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, rc_w); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; /* t00fri: keeps FILE scrolled list width correct */ XtSetArg (args[n], XmNresizePolicy, XmRESIZE_NONE); n++; fsb_w = XmCreateFileSelectionBox (bf_w, "FSB", args, n); XtManageChild (fsb_w); initFSB(fsb_w); initPubShared (rc_w, fsb_w); } /* init the directory and pattern resources of the given FileSelectionBox. * we try to pull these from the basic program resources. */ static void initFSB (fsb_w) Widget fsb_w; { Widget w; /* set default dir and pattern */ set_xmstring (fsb_w, XmNdirectory, getPrivateDir()); set_xmstring (fsb_w, XmNpattern, getXRes (fitsp, "*.f*t*")); /* change some button labels. * N.B. can't add tips because these are really Gadgets. */ w = XmFileSelectionBoxGetChild (fsb_w, XmDIALOG_OK_BUTTON); set_xmstring (w, XmNlabelString, "Open"); w = XmFileSelectionBoxGetChild (fsb_w, XmDIALOG_CANCEL_BUTTON); set_xmstring (w, XmNlabelString, "Close"); /* some other tips */ w = XmFileSelectionBoxGetChild (fsb_w, XmDIALOG_FILTER_TEXT); wtip (w, "Current directory and pattern; press `Filter' to rescan"); w = XmFileSelectionBoxGetChild (fsb_w, XmDIALOG_TEXT); wtip (w, "FITS file name to be read if press `Open'"); /* connect an Open handler */ XtAddCallback (fsb_w, XmNokCallback, sf_open_cb, NULL); /* connect a Close handler */ XtAddCallback (fsb_w, XmNcancelCallback, sf_close_cb, NULL); /* connect a Help handler */ XtAddCallback (fsb_w, XmNhelpCallback, sf_help_cb, NULL); } /* callback from the Public dir PB */ /* ARGSUSED */ static void sharedDirCB (w, client, call) Widget w; XtPointer client; XtPointer call; { Widget fsb_w = (Widget)client; char buf[1024]; (void) sprintf (buf, "%s/fits", getShareDir()); set_xmstring (fsb_w, XmNdirectory, expand_home(buf)); } /* callback from the Private dir PB */ /* ARGSUSED */ static void privateDirCB (w, client, call) Widget w; XtPointer client; XtPointer call; { Widget fsb_w = (Widget)client; set_xmstring (fsb_w, XmNdirectory, getPrivateDir()); } /* build a set of PB in RC rc_w so that they * set the XmNdirectory resource in the FSB fsb_w and invoke the Filter. */ static void initPubShared (rc_w, fsb_w) Widget rc_w, fsb_w; { Arg args[20]; char tip[1024]; Widget w; int n; n = 0; w = XmCreateLabel (rc_w, "Dir", args, n); set_xmstring (w, XmNlabelString, "Look in:"); XtManageChild (w); n = 0; w = XmCreatePushButton (rc_w, "Private", args, n); XtAddCallback(w, XmNactivateCallback, privateDirCB, (XtPointer)fsb_w); sprintf (tip, "Set directory to %s", getPrivateDir()); wtip (w, XtNewString(tip)); XtManageChild (w); n = 0; w = XmCreatePushButton (rc_w, "Shared", args, n); XtAddCallback(w, XmNactivateCallback, sharedDirCB, (XtPointer)fsb_w); sprintf (tip, "Set directory to %s/fits", getShareDir()); wtip (w, XtNewString(tip)); XtManageChild (w); } /* called when Watch TB changes */ /* ARGSUSED */ static void fw_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { fw_on (XmToggleButtonGetState(w)); } /* called when Get PB or toolbar PB is hit */ /* ARGSUSED */ void sf_go_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { if (!sf_w) { sf_create(); si_create(); } if (read_iid) { xe_msg (1, "DSS download is already in progress."); return; } if (XmToggleButtonGetState(stsci_w)) stsci_fits(); else eso_fits(); } /* called when CR is hit in the Save text field or the Save PB is hit */ /* ARGSUSED */ static void sf_save_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { char *fn; if (!si_getFImage ()) { xe_msg (1, "No current FITS file to save"); return; } fn = XmTextFieldGetString (savefn_w); if (!fn || (int)strlen(fn) < 1) { xe_msg (1, "Please specify a filename"); XtFree (fn); return; } if (existsh (fn) == 0 && confirm()) { char buf[1024]; (void) sprintf (buf, "%s exists:\nOk to overwrite?", bname(fn)); query (sf_w, buf, "Yes -- Overwrite", "No -- Cancel", NULL, save_file, NULL, NULL); } else save_file(); XtFree (fn); } /* save to file named in savefn_w. * we already know everything is ok to just do it now. */ static void save_file() { FImage *fip; char buf[1024]; char *fn; int fd; fn = XmTextFieldGetString (savefn_w); fd = openh (fn, O_CREAT|O_WRONLY, 0666); if (fd < 0) { xe_msg (1, "%s: %s", fn, syserrstr()); XtFree (fn); return; } fip = si_getFImage (); if (!fip) { printf ("FImage disappeared in save_file\n"); abort(); } si_setContrast(fip); if (writeFITS (fd, fip, buf, 1) < 0) { xe_msg (1, "%s: %s", fn, buf); } else { xe_msg (confirm(), "%s:\nwritten successfully", fn); } (void) close (fd); XtFree (fn); } /* called when a file selected by the FSB is to be opened */ static void /* ARGSUSED */ sf_open_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { XmFileSelectionBoxCallbackStruct *s = (XmFileSelectionBoxCallbackStruct *)call; char *sp; if (s->reason != XmCR_OK) { printf ("%s: Unknown reason = 0x%x\n", "sf_open_cb()", s->reason); abort(); } watch_cursor(1); XmStringGetLtoR (s->value, XmSTRING_DEFAULT_CHARSET, &sp); sf_readFile (sp); XtFree (sp); watch_cursor(0); } /* ARGSUSED */ static void sf_close_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { XtUnmanageChild (sf_w); } /* ARGSUSED */ static void sf_help_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { static char *msg[] = { "Read in local FITS files or read from Network.", "Resulting image will be displayed in Sky View." }; hlp_dialog ("Sky FITS", msg, sizeof(msg)/sizeof(msg[0])); } /* callback to set main time to match FITS */ /* ARGSUSED */ static void sf_setdate_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { double newmjd; if (fitsObs(&newmjd) == 0) mm_newcaldate(newmjd); } /* set savefn_w to newfn. * preserve any existing directory. */ static void sf_setSaveName(newfn) char *newfn; { char buf[1024]; char *fn; fn = XmTextFieldGetString (savefn_w); (void) sprintf (buf, "%.*s%s", (int)(bname(fn)-fn), fn, bname(newfn)); XtFree (fn); XmTextFieldSetString (savefn_w, buf); } /* return pointer to basename portion of filename fn. */ static char * bname (fn) char *fn; { char *base; if (!(base = strrchr(fn,'/')) && !(base = strrchr(fn,'\\'))) base = fn; else base++; /* skip the / */ return (base); } /* return 0 if find the string str in buf, else -1 */ static int chk4str (str, buf) char str[]; char buf[]; { int l = strlen (str); while (*buf != '\0') if (strncmp (str, buf++, l) == 0) return (0); return (-1); } /* return 0 if have gunzip else -1 */ static int chk_gunzip() { #define NOGZEXIT 88 /* any impossible gzip exit value */ static int know = 1; /* 0 or -1 when know for sure */ int wstatus; sigset_t oss, nss; int pid; /* only really test once */ if (know <= 0) return (know); /* main program reaps children with a SIGCHLD handler to avoid * zombies. we want to temporarily prevent that here so we can wait. */ sigemptyset (&nss); sigaddset (&nss, SIGCHLD); sigprocmask (SIG_BLOCK, &nss, &oss); /* fork/exec and see how it exits */ pid = fork(); if (pid < 0) return (know = -1); if (pid == 0) { /* new process: exec gunzip reading /dev/null else exit NOGZEXIT */ int nullfd = open ("/dev/null", O_RDWR); if (nullfd < 0) _exit(NOGZEXIT); dup2 (nullfd, 0); dup2 (nullfd, 1); dup2 (nullfd, 2); execlp (gexe, gcmd, NULL); /* does not return if works */ _exit(NOGZEXIT); } /* parent waits for exit status */ know = (waitpid (pid, &wstatus, 0) == pid && WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != NOGZEXIT) ? 0 : -1; /* resume allowing SIGCHLD */ sigprocmask (SIG_SETMASK, &oss, NULL); return (know); } /* return 1 if have/want to use gunzip, else 0 */ static int use_gunzip() { if (chk_gunzip() < 0) { xe_msg (1,"Can not find %s.\nProceeding without compression", gcmd); return (0); } return (1); } /* setup the pipe between gunzip and xephem to decompress the data. * return pid if ok, else -1. */ static int setup_gunzip_pipe(int sockfd) { int gzfd[2]; /* file descriptors for gunzip pipe */ int pid; int errfd; /* make the pipe to gunzip */ if (pipe(gzfd) < 0) { xe_msg (1, "Can not make pipe for gunzip"); return (-1); } /* fork/exec gunzip */ switch((pid = fork())) { case 0: /* child: put gunzip between socket and us */ close (gzfd[0]); /* do not need read side of pipe */ dup2 (sockfd, 0); /* socket becomes gunzip's stdin */ close (sockfd); /* do not need after dup */ dup2 (gzfd[1], 1); /* write side of pipe becomes gunzip's stdout */ close (gzfd[1]); /* do not need after dup */ errfd = open ("/dev/null", O_RDWR); if (errfd >= 0) { dup2 (errfd, 2);/* dump gunzip's stderr */ close (errfd); } execlp (gexe, gcmd, "-c", NULL); /*should work, already checked*/ _exit(1); /* exit in case gunzip disappeared */ break; /* :) */ case -1: /* fork failed */ xe_msg (1, "Can not fork for gunzip"); return (-1); default: /* parent */ break; } /* put gunzip between the socket and us */ close (gzfd[1]); /* do not need write side of pipe */ dup2 (gzfd[0], sockfd); /* read side of pipe masquarades as socket */ close (gzfd[0]); /* do not need after dup */ /* return gunzip's pid */ return (pid); } /* setup the pipe between ssl decryption and xephem to decrypt the data. * return pid if ok, else -1. */ static int setup_ssldecryption_pipe(int sockfd) { int ssldecryptfd[2]; /* file descriptors for ssl decryption pipe */ int pid; unsigned char buf[2048]; int nr; /* make the pipe to ssl decryption */ if (pipe(ssldecryptfd) < 0) { xe_msg (1, "Can not make pipe for ssl decryption"); return (-1); } /* fork and start ssl decryption */ switch((pid = fork())) { case 0: /* child: put ssl decryption between socket and us */ close (ssldecryptfd[0]); /* do not need read side of pipe */ while ((nr = SSL_read (fr_ssl_fd.ssl, buf, sizeof(buf))) > 0) { write (ssldecryptfd[1], buf, nr); } close (ssldecryptfd[1]); /* close when ssl decryption finishes */ SSL_free (fr_ssl_fd.ssl); close (fr_ssl_fd.fd); _exit(EXIT_SUCCESS); /* exit when ssl decryption finishes */ break; /* :) */ case -1: /* fork failed */ xe_msg (1, "Can not fork for ssl decryption"); return (-1); default: /* parent */ break; } /* put ssl decryption between the socket and us */ close (ssldecryptfd[1]); /* do not need write side of pipe */ dup2 (ssldecryptfd[0], sockfd); /* read side of pipe masquarades as socket */ close (ssldecryptfd[0]); /* do not need after dup */ /* return pid of ssl decryption */ return (pid); } static Survey whichSurvey() { if (XmToggleButtonGetState(dss2r_w)) return (DSS_2R); if (XmToggleButtonGetState(dss2b_w)) return (DSS_2B); return (DSS_1); } /* start an input stream reading a FITS image from ESO */ static void eso_fits() { static char host[] = "archive.eso.org"; static FImage fim, *fip = &fim; double fov, alt, az, ra, dec; char rastr[32], *rap, decstr[32], *decp; char buf[2048], msg[1024]; char *survey; int gzpid; int aamode; int sawfits; int ssldecryptpid; ssldecryptpid = 0; memset(&fr_ssl_fd, 0, sizeof(fr_ssl_fd)); /* do not turn off watch until completely finished */ watch_cursor (1); /* let user abort */ stopd_up(); /* init fip (not reset because we copy the malloc'd fields to fim) */ initFImage (fip); /* find current skyview center and size, in degrees */ sv_getcenter (&aamode, &fov, &alt, &az, &ra, &dec); fov = 60*raddeg(fov); ra = radhr (ra); dec = raddeg (dec); if (fov > MAXDSSFOV) fov = MAXDSSFOV; if (fov < MINDSSFOV) fov = MINDSSFOV; /* get desired survey */ switch (whichSurvey()) { default: case DSS_1: survey = "DSS1"; break; case DSS_2R: survey = "DSS2-red"; break; case DSS_2B: survey = "DSS2-blue"; break; } /* format and send the request. * N.B. ESO can't tolerate leading blanks in ra dec specs */ fs_sexa (rastr, ra, 2, 3600); for (rap = rastr; *rap == ' '; rap++) continue; fs_sexa (decstr, dec, 3, 3600); for (decp = decstr; *decp == ' '; decp++) continue; (void) sprintf (buf, "GET https://%s/dss/dss?ra=%s&dec=%s&equinox=J2000&Sky-Survey=%s&mime-type=%s&x=%.0f&y=%.0f HTTP/1.0\r\nUser-Agent: xephem/%s\r\n\r\n", host, rap, decp, survey, use_gunzip() ? "display/gz-fits" : "application/x-fits", fov, fov, PATCHLEVEL); xe_msg (0, "Command to %s:\n%s", host, buf); fr_socket = httpsGET (host, buf, msg, &fr_ssl_fd); if (fr_socket < 0) { xe_msg (1, "https get: %s", msg); stopd_down(); watch_cursor (0); return; } /* switch on ssl decryption */ ssldecryptpid = setup_ssldecryption_pipe(fr_socket); if (ssldecryptpid < 0) { watch_cursor (0); close (fr_socket); stopd_down(); return; } /* read back the header -- ends with a blank line */ sawfits = 0; while (recvline (fr_socket, buf, sizeof(buf)) > 1) { xe_msg (0, "Rcv: %s", buf); if (chk4str ("application/x-fits", buf) == 0 || chk4str ("image/x-fits", buf) == 0) sawfits = 1; } /* if do not see a fits file, show what we do find */ if (!sawfits) { xe_msg (0, " "); xe_msg (1, "Message from server in File->System log"); xe_msg (0, "------------------"); while (recvline (fr_socket, buf, sizeof(buf)) > 0) xe_msg (0, "Rcv: %s", buf); xe_msg (0, "------------------"); xe_msg (0, "End of Message from server"); msg_manage(); watch_cursor (0); close (fr_socket); stopd_down(); return; } /* possibly connect via gunzip -- weird if have gunzip but can't */ if (use_gunzip()) { gzpid = setup_gunzip_pipe(fr_socket); if (gzpid < 0) { if (ssldecryptpid > 0) kill (ssldecryptpid, SIGTERM); watch_cursor (0); close (fr_socket); stopd_down(); return; } } else gzpid = -1; /* read the FITS header portion */ if (readFITSHeader (fr_socket, fip, buf) < 0) { watch_cursor (0); xe_msg (1, "%s", buf); resetFImage (fip); close (fr_socket); if (ssldecryptpid > 0) kill (ssldecryptpid, SIGTERM); if (gzpid > 0) kill (gzpid, SIGTERM); stopd_down(); return; } /* ok, start reading the pixels and give user a way to abort */ pm_up(); /* for your viewing pleasure */ read_iid = XtAppAddInput (xe_app, fr_socket, (XtPointer)XtInputReadMask, fits_read_icb, (XtPointer)fip); read_to = XtAppAddTimeOut (xe_app, FCPP, fits_read_to, (XtPointer)(fip)); } /* start an input stream reading a FITS image from STScI */ static void stsci_fits() { static char host[] = "archive.stsci.edu"; static FImage fim, *fip = &fim; double fov, alt, az, ra, dec; char buf[1024], msg[1024]; char *survey; int gzpid; int aamode; int sawfits; int ssldecryptpid; ssldecryptpid = 0; memset(&fr_ssl_fd, 0, sizeof(fr_ssl_fd)); /* do not turn off watch until completely finished */ watch_cursor (1); /* let user abort */ stopd_up(); /* init fip (not reset because we copy the malloc'd fields to fim) */ initFImage (fip); /* find current skyview center and size, in degrees */ sv_getcenter (&aamode, &fov, &alt, &az, &ra, &dec); fov = 60*raddeg(fov); ra = raddeg (ra); dec = raddeg (dec); if (fov > MAXDSSFOV) fov = MAXDSSFOV; if (fov < MINDSSFOV) fov = MINDSSFOV; /* get desired survey */ switch (whichSurvey()) { default: case DSS_1: survey = "1"; break; case DSS_2R: survey = "2r"; break; case DSS_2B: survey = "2b"; break; } /* format and send the request */ (void) sprintf(buf,"GET https://%s/cgi-bin/dss_search?ra=%.5f&dec=%.5f&equinox=J2000&v=%s&height=%.0f&width=%.0f&format=FITS&compression=%s&version=3 HTTP/1.0\r\nUser-Agent: xephem/%s\r\n\r\n", host, ra, dec, survey, fov, fov, use_gunzip() ? "gz" : "NONE", PATCHLEVEL); xe_msg (0, "Command to %s:\n%s", host, buf); fr_socket = httpsGET (host, buf, msg, &fr_ssl_fd); if (fr_socket < 0) { xe_msg (1, "https get: %s", msg); stopd_down(); watch_cursor (0); return; } /* switch on ssl decryption */ ssldecryptpid = setup_ssldecryption_pipe(fr_socket); if (ssldecryptpid < 0) { watch_cursor (0); close (fr_socket); stopd_down(); return; } /* read back the header -- ends with a blank line */ sawfits = 0; while (recvline (fr_socket, buf, sizeof(buf)) > 1) { xe_msg (0, "Rcv: %s", buf); if (chk4str ("image/x-fits", buf) == 0) sawfits = 1; } /* if do not see a fits file, show what we do find */ if (!sawfits) { xe_msg (0, " "); xe_msg (1, "Message from server in File -> System log"); xe_msg (0, "------------------"); while (recvline (fr_socket, buf, sizeof(buf)) > 0) xe_msg (0, "%s", buf); xe_msg (0, "------------------"); xe_msg (0, "End of Message from server"); msg_manage(); watch_cursor (0); close (fr_socket); stopd_down(); return; } /* possibly connect via gunzip -- weird if have gunzip but can't */ if (use_gunzip()) { gzpid = setup_gunzip_pipe(fr_socket); if (gzpid < 0) { if (ssldecryptpid > 0) kill (ssldecryptpid, SIGTERM); watch_cursor (0); close (fr_socket); stopd_down(); return; } } else gzpid = -1; /* read the FITS header portion */ if (readFITSHeader (fr_socket, fip, buf) < 0) { watch_cursor (0); xe_msg (1, "%s", buf); resetFImage (fip); close (fr_socket); if (ssldecryptpid > 0) kill (ssldecryptpid, SIGTERM); if (gzpid > 0) kill (gzpid, SIGTERM); stopd_down(); return; } /* ok, start reading the pixels and give user a way to abort */ pm_up(); /* for your viewing pleasure */ read_iid = XtAppAddInput (xe_app, fr_socket, (XtPointer)XtInputReadMask, fits_read_icb, (XtPointer)fip); read_to = XtAppAddTimeOut (xe_app, FCPP, fits_read_to, (XtPointer)(fip)); } /* called whenever there is more data available on the sockfd. * client is *FImage being accumulated. */ static void fits_read_icb (client, fd, id) XtPointer client; int *fd; XtInputId *id; { FImage *fip = (FImage *)client; int sockfd = *fd; double ra, dec; char buf[1024]; /* read another chunk */ if (readIncFITS (sockfd, fip, buf) < 0) { xe_msg (1, "%s", buf); fits_read_abort (fip); return; } /* report progress */ pm_set (fip->nbytes * 100 / fip->totbytes); XmUpdateDisplay (toplevel_w); /* keep going if expecting more */ if (fip->nbytes < fip->totbytes) return; /* finished reading */ stopd_down(); pm_down(); close (sockfd); XtRemoveInput (read_iid); read_iid = (XtInputId)0; XtRemoveTimeOut (read_to); read_to = (XtIntervalId)0; /* YES! */ /* give it a name */ if (xy2RADec (fip, fip->sw/2.0, fip->sh/2.0, &ra, &dec) < 0) { /* no headers! use time I guess */ struct tm *tp; time_t t0; time (&t0); tp = gmtime (&t0); if (!tp) localtime (&t0); sprintf (buf, "%04d%02d%02dT%02d%02d%02d.fts", tp->tm_year+1900, tp->tm_mon+1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec); } else { int dneg, rh, rm, dd, dm; ra = radhr(ra); dec = raddeg(dec); if ((dneg = (dec < 0))) dec = -dec; rh = (int)floor(ra); rm = (int)floor((ra - rh)*60.0 + 0.5); if (rm == 60) { if (++rh == 24) rh = 0; rm = 0; } dd = (int)floor(dec); dm = (int)floor((dec - dd)*60.0 + 0.5); if (dm == 60) { dd++; dm = 0; } (void) sprintf (buf, "%02d%02d%c%02d%02d.fts", rh, rm, dneg ? '-' : '+', dd, dm); } /* commit and display */ sf_newFITS (fip, buf, 1); /* N.B. copies fip .. do not resetFImage */ watch_cursor (0); } /* called to poll cancelling FITS file download * client is *FImage being accumulated. */ static void fits_read_to (XtPointer client, XtIntervalId *id) { FImage *fip = (FImage *)client; if (stopd_check() < 0) { fits_read_abort (fip); } else { read_to = XtAppAddTimeOut (xe_app, FCPP, fits_read_to, (XtPointer)(fip)); } } static void fits_read_abort (FImage *fip) { resetFImage (fip); close (fr_socket); XtRemoveInput (read_iid); read_iid = (XtInputId)0; XtRemoveTimeOut (read_to); read_to = (XtIntervalId)0; stopd_down(); pm_down(); watch_cursor (0); } /* poke around in the headers and try to find the mjd of the observation. * return 0 if think we found something, else -1 */ static int fitsObs(mjdp) double *mjdp; { FImage *fip = si_getFImage(); char buf[128]; double x; if (!fip) return (-1); if (getRealFITS (fip, "JD", &x) == 0) { *mjdp = x - MJD0; return (0); } if (getRealFITS (fip, "EPOCH", &x) == 0) { year_mjd (x, mjdp); return (0); } if (getStringFITS (fip, "DATE-OBS", buf) == 0) { /* try ISO 8601 then a few guesses */ int a, b, c, d, e, f; if (sscanf (buf, "%d-%d-%dT%d:%d:%d", &a, &b, &c, &d, &e, &f) == 6){ double day = c + (d + ((e + f/60.0)/60.0))/24.0; cal_mjd (b, day, a, mjdp); return (0); } if (sscanf (buf, "%d%*[/-]%d%*[/-]%d", &a, &b, &c) == 3) { if (a > 1900) { /* yyyy-mm-dd? */ cal_mjd (b, (double)c, a, mjdp); return (0); } else if (a <= 12 && b <= 31 && c < 100) { /* mm-dd-yy? */ c += (c < 50 ? 2000 : 1900); cal_mjd (a, (double)b, c, mjdp); return (0); } } } return (-1); } /* get and display the time of observation from the current FITS image */ static void sf_setObsDate() { double objsmjd; if (fitsObs(&objsmjd) == 0) { int mm, yy, d, h, m, s; double dd, dh, dm, ds; char buf[128]; mjd_cal (objsmjd, &mm, &dd, &yy); d = (int)dd; dh = (dd - d)*24.; h = (int)dh; dm = (dh - h)*60.; m = (int)dm; ds = (dm - m)*60.; if (ds > 59.5) { s = 0; if (++m == 60) { m = 0; h += 1; /* TODO: roll date if hits 24 */ } } else s = (int)ds; sprintf (buf, "%d-%d-%d %02d:%02d:%02d", yy, mm, d, h, m, s); set_xmstring (obsdate_w, XmNlabelString, buf); XtSetSensitive (setobsdate_w, True); } else { set_xmstring (obsdate_w, XmNlabelString, " "); XtSetSensitive (setobsdate_w, False); } } /* turn on or off file watching. */ static void fw_on (whether) int whether; { /* turn everything off */ if (fw_tid) { XtRemoveTimeOut (fw_tid); fw_tid = 0; } if (fw_iid) { close (fw_fd); XtRemoveInput (fw_iid); fw_iid = 0; } /* then maybe restart */ if (whether) { char *txt, wfn[1024]; /* clean scrubbed file name to watch */ txt = XmTextFieldGetString (fwfn_w); strcpy (wfn, expand_home(txt)); XtFree (txt); /* start timer or input depending on whether fifo */ if (fw_isFifo(wfn)) { fw_fd = open (wfn, O_RDWR); if (fw_fd < 0) { char msg[1024]; sprintf (msg, "%s: %s", wfn, syserrstr()); XmToggleButtonSetState (fw_w, False, False); } else { fw_iid = XtAppAddInput (xe_app, fw_fd, (XtPointer)XtInputReadMask, fw_icb, NULL); } } else { fw_tid = XtAppAddTimeOut (xe_app, 0, fw_to, 0); } } } /* called periodically to check whether file in fwfn_w names a FITS file * to load. when it does, load the named file and delete the watch file * as a simple form of ACK. */ static void fw_to (client, id) XtPointer client; XtIntervalId *id; { char wfn[512], ffn[512]; char *txt, *nl; int wfd, nr; /* try to open watch file */ txt = XmTextFieldGetString (fwfn_w); strcpy (wfn, expand_home(txt)); XtFree (txt); wfd = open (wfn, O_RDONLY|O_NONBLOCK); if (wfd < 0) goto again; /* read it to get name of FITS file */ nr = read (wfd, ffn, sizeof(ffn)); close (wfd); if (nr <= 0) goto again; ffn[nr] = '\0'; nl = strchr (ffn, '\n'); if (nl) *nl = '\0'; strcpy (ffn, expand_home(ffn)); /* display and remove */ sv_manage(); sf_readFile (ffn); remove (wfn); again: fw_tid = XtAppAddTimeOut (xe_app, FWDT, fw_to, 0); } /* called whenever the FITS filename fifo might have something to read. */ static void fw_icb (client, fdp, id) XtPointer client; int *fdp; XtInputId *id; { char *nl, ffn[1024]; int nr; nr = read (fw_fd, ffn, sizeof(ffn)); if (nr <= 0) { if (nr < 0) sprintf (ffn, "FITS Watch fifo: %s", syserrstr()); else sprintf (ffn, "EOF from Watch fifo."); strcat (ffn, "\nTurning FITS Watching off"); xe_msg (1, ffn); XmToggleButtonSetState (fw_w, False, True); /* let it clean up */ } ffn[nr] = '\0'; nl = strchr (ffn, '\n'); if (nl) *nl = '\0'; strcpy (ffn, expand_home(ffn)); /* display */ sv_manage(); sf_readFile (ffn); } /* return whether fn claims to be a fifo */ static int fw_isFifo (fn) char *fn; { struct stat st; return (!stat (fn, &st) && (st.st_mode & S_IFIFO)); }