home *** CD-ROM | disk | FTP | other *** search
/ vsiftp.vmssoftware.com / VSIPUBLIC@vsiftp.vmssoftware.com.tar / FREEWARE / FREEWARE40.ZIP / xv310a / xvdir_vaxc.c < prev    next >
C/C++ Source or Header  |  1995-11-06  |  57KB  |  2,074 lines

  1. /* 
  2.  * xvdir.c - Directory changin', file i/o dialog box
  3.  *
  4.  * callable functions:
  5.  *
  6.  *   CreateDirW(geom,bwidth)-  creates the dirW window.  Doesn't map it.
  7.  *   DirBox(vis)            -  random processing based on value of 'vis'
  8.  *                             maps/unmaps window, etc.
  9.  *   ClickDirW()            -  handles mouse clicks in DirW
  10.  *   LoadCurrentDirectory() -  loads up current dir information for dirW
  11.  *   GetDirPath()           -  returns path that 'dirW' is looking at
  12.  *   DoSave()               -  calls appropriate save routines
  13.  *   SetDirFName()          -  sets the 'load/save-as' filename and default
  14.  *   GetDirFName()          -  gets the 'load/save-as' filename (no path)
  15.  *   SetDirSaveMode()       -  sets default format/color settings
  16.  *
  17.  *   InitPoll()             -  called whenever a file is first loaded
  18.  *   CheckPoll(int)         -  checks to see whether we should reload
  19.  */
  20.  
  21. #include "copyright.h"
  22.  
  23. #define NEEDSTIME      /* for CheckPoll */
  24. #define NEEDSDIR
  25. #include "xv.h"
  26.  
  27. #include "bits/d_load"
  28. #include "bits/d_save"
  29.  
  30. #ifndef VMS
  31. #include <pwd.h>       /* for getpwnam() prototype and passwd struct */
  32. #endif
  33.  
  34.  
  35. #define DIRWIDE  350               /* (fixed) size of directory window */
  36. #define DIRHIGH  400
  37.  
  38. #define NLINES   15                /* # of lines in list control (keep odd) */
  39. #define LISTWIDE 237               /* width of list window */
  40. #define BUTTW    60                /* width of buttons */
  41. #define BUTTH    24                /* height of buttons */
  42. #define DDWIDE  (LISTWIDE-80+15)   /* max width of dirMB */
  43. #define DNAMWIDE 252               /* width of 'file name' entry window */
  44. #define MAXDEEP  30                /* max num of directories in cwd path */
  45. #define MAXFNLEN 256               /* max len of filename being entered */
  46.  
  47. #define FMTLABEL "Format:"         /* label shown next to fmtMB */
  48. #define COLLABEL "Colors:"         /* label shown next to colMB */
  49. #define FMTWIDE  150               /* width of fmtMB */
  50. #define COLWIDE  150               /* width of colMB */
  51.  
  52. /* NOTE: make sure these match up with F_* definitions in xv.h */
  53. static char *saveColors[] = { "Full Color", 
  54.                   "Greyscale",
  55.                   "B/W Dithered",
  56.                   "Reduced Color" };
  57.  
  58. static char *saveFormats[] = { "GIF",
  59. #ifdef HAVE_JPEG
  60.                    "JPEG",
  61. #endif
  62. #ifdef HAVE_TIFF
  63.                    "TIFF",
  64. #endif
  65.                    "PostScript",
  66.                    "PBM/PGM/PPM (raw)",
  67.                    "PBM/PGM/PPM (ascii)",
  68.                    "X11 Bitmap",
  69.                    "XPM",
  70.                    "BMP",
  71.                    "Sun Rasterfile",
  72.                    "IRIS RGB",
  73.                    "Targa (24-bit)",
  74.                    "FITS",
  75.                    "PM",
  76.                    MBSEP,
  77.                    "Filename List"};
  78.  
  79.  
  80. static void arrangeButts     PARM((int));
  81. static void RedrawDList      PARM((int, SCRL *));
  82. static void changedDirMB     PARM((int));
  83. static int  dnamcmp          PARM((const void *, const void *));
  84. static int  FNameCdable      PARM((void));
  85. static void loadCWD          PARM((void));
  86. static int  cd_able          PARM((char *));
  87. static void scrollToFileName PARM((void));
  88. static void setFName         PARM((char *));
  89. static void showFName        PARM((void));
  90. static void changeSuffix     PARM((void));
  91. static int  autoComplete     PARM((void));
  92.  
  93. static byte *handleBWandReduced   PARM((byte *, int,int,int, int, int *, 
  94.                     byte **, byte **, byte **));
  95. static byte *handleNormSel        PARM((int *, int *, int *, int *));
  96.  
  97.  
  98. static char  *fnames[MAXNAMES];
  99. static int    numfnames = 0, ndirs = 0;
  100. static char   path[MAXPATHLEN+1];       /* '/' terminated */
  101. static char   loadpath[MAXPATHLEN+1];   /* '/' terminated */
  102. static char   savepath[MAXPATHLEN+1];   /* '/' terminated */
  103. static char  *dirs[MAXDEEP];            /* list of directory names */
  104. static char  *dirMBlist[MAXDEEP];       /* list of dir names in right order */
  105. static char  *lastdir;                  /* name of the directory we're in */
  106. static char   filename[MAXFNLEN+100];   /* filename being entered */
  107. static char   deffname[MAXFNLEN+100];   /* default filename */
  108.  
  109. static int    savemode;                 /* if 0 'load box', if 1 'save box' */
  110. static int    curPos, stPos, enPos;     /* filename textedit stuff */
  111. static MBUTT  dirMB;                    /* popup path menu */
  112. static MBUTT  fmtMB;                    /* 'format' menu button (Save only) */
  113. static MBUTT  colMB;                    /* 'colors' menu button (Save only) */
  114.  
  115. static Pixmap d_loadPix, d_savePix;
  116.  
  117. static int  haveoldinfo = 0;
  118. static int  oldformat, oldcolors;
  119. static char oldfname[MAXFNLEN+100];
  120.  
  121. /* the name of the file actually opened.  (the temp file if we are piping) */
  122. static char outFName[256];  
  123. static int  dopipe;
  124.  
  125.  
  126. /***************************************************/
  127. void CreateDirW(geom)
  128.      char *geom;
  129. {
  130.   int w, y;
  131.   
  132.   path[0] = '\0';
  133.  
  134.   xv_getwd(loadpath, sizeof(loadpath));
  135.   xv_getwd(savepath, sizeof(savepath));
  136.  
  137.   
  138.   dirW = CreateWindow("","XVdir", geom, DIRWIDE, DIRHIGH, infofg, infobg, 0);
  139.   if (!dirW) FatalError("couldn't create 'directory' window!");
  140.  
  141.   LSCreate(&dList, dirW, 10, 5 + 3*(6+LINEHIGH) + 6, LISTWIDE, 
  142.        LINEHIGH*NLINES, NLINES, fnames, numfnames, infofg, infobg, 
  143.        hicol, locol, RedrawDList, 1, 0);
  144.  
  145.   dnamW = XCreateSimpleWindow(theDisp, dirW, 80, dList.y + (int) dList.h + 30, 
  146.                   (u_int) DNAMWIDE+6, (u_int) LINEHIGH+5, 
  147.                   1, infofg, infobg);
  148.   if (!dnamW) FatalError("can't create name window");
  149.   XSelectInput(theDisp, dnamW, ExposureMask);
  150.  
  151.  
  152.   CBCreate(&browseCB,   dirW, DIRWIDE/2, dList.y + (int) dList.h + 6, 
  153.        "Browse", infofg, infobg, hicol,locol);
  154.  
  155.   CBCreate(&savenormCB, dirW, 220, dList.y + (int) dList.h + 6, 
  156.        "Normal Size", infofg, infobg,hicol,locol);
  157.  
  158.   CBCreate(&saveselCB,  dirW, 80,        dList.y + (int) dList.h + 6, 
  159.            "Selected Area", infofg, infobg,hicol,locol);
  160.  
  161.  
  162.   /* y-coordinates get filled in when window is opened */
  163.   BTCreate(&dbut[S_BOK],     dirW, 259, 0, 80, BUTTH, 
  164.        "Ok",        infofg, infobg,hicol,locol);
  165.   BTCreate(&dbut[S_BCANC],   dirW, 259, 0, 80, BUTTH, 
  166.        "Cancel",    infofg,infobg,hicol,locol);
  167.   BTCreate(&dbut[S_BRESCAN], dirW, 259, 0, 80, BUTTH, 
  168.        "Rescan",    infofg,infobg,hicol,locol);
  169.   BTCreate(&dbut[S_BOLDSET], dirW, 259, 0, 80, BUTTH, 
  170.        "Prev Set",  infofg,infobg,hicol,locol);
  171.   BTCreate(&dbut[S_BOLDNAM], dirW, 259, 0, 80, BUTTH, 
  172.        "Prev Name", infofg,infobg,hicol,locol);
  173.  
  174.   SetDirFName("");
  175.   XMapSubwindows(theDisp, dirW);
  176.   numfnames = 0;
  177.  
  178.  
  179.   /*
  180.    * create MBUTTs *after* calling XMapSubWindows() to keep popup unmapped
  181.    */
  182.  
  183.   MBCreate(&dirMB, dirW, 50, dList.y -(LINEHIGH+6), 
  184.        (u_int) DDWIDE, (u_int) LINEHIGH, NULL, NULL, 0,
  185.        infofg,infobg,hicol,locol);
  186.  
  187.   MBCreate(&fmtMB, dirW, DIRWIDE-FMTWIDE-10, 5,            
  188.        (u_int) FMTWIDE, (u_int) LINEHIGH, NULL, saveFormats, F_MAXFMTS, 
  189.        infofg,infobg,hicol,locol);
  190.   fmtMB.hascheck = 1;
  191.   MBSelect(&fmtMB, 0);
  192.  
  193.   MBCreate(&colMB, dirW, DIRWIDE-COLWIDE-10, 5+LINEHIGH+6, 
  194.        (u_int) COLWIDE, (u_int) LINEHIGH, NULL, saveColors, F_MAXCOLORS, 
  195.        infofg,infobg,hicol,locol);
  196.   colMB.hascheck = 1;
  197.   MBSelect(&colMB, 0);
  198.  
  199.  
  200.   d_loadPix = XCreatePixmapFromBitmapData(theDisp, dirW, 
  201.                  (char *) d_load_bits, d_load_width, d_load_height, 
  202.                       infofg, infobg, dispDEEP);
  203.  
  204.   d_savePix = XCreatePixmapFromBitmapData(theDisp, dirW, 
  205.                  (char *) d_save_bits, d_save_width, d_save_height, 
  206.                       infofg, infobg, dispDEEP);
  207.  
  208. }
  209.   
  210.  
  211. /***************************************************/
  212. void DirBox(mode)
  213.      int mode;
  214. {
  215.   static int firstclose = 1;
  216.  
  217.   if (!mode) {
  218.     if (savemode) strcpy(savepath, path);
  219.              else strcpy(loadpath, path);
  220.  
  221.     if (firstclose) {
  222.       strcpy(loadpath, path);
  223.       strcpy(savepath, path);
  224.       firstclose = 0;
  225.     }
  226.  
  227.     XUnmapWindow(theDisp, dirW);  /* close */
  228.   }
  229.  
  230.   else if (mode == BLOAD) {
  231.     strcpy(path, loadpath);
  232.     WaitCursor();  LoadCurrentDirectory();  SetCursors(-1);
  233.  
  234.     XStoreName(theDisp, dirW, "xv load");
  235.     XSetIconName(theDisp, dirW, "xv load");
  236.  
  237.     dbut[S_BLOADALL].str = "Load All";
  238.     BTSetActive(&dbut[S_BLOADALL], 1);
  239.  
  240.     arrangeButts(mode);
  241.  
  242.     MBSetActive(&fmtMB, 0);
  243.     MBSetActive(&colMB, 0);
  244.  
  245.     CenterMapWindow(dirW, dbut[S_BOK].x+30, dbut[S_BOK].y + BUTTH/2,
  246.             DIRWIDE, DIRHIGH);
  247.  
  248.     savemode = 0;
  249.   }
  250.  
  251.   else if (mode == BSAVE) {
  252.     strcpy(path, savepath);
  253.     WaitCursor();  LoadCurrentDirectory();  SetCursors(-1);
  254.  
  255.     XStoreName(theDisp, dirW, "xv save");
  256.     XSetIconName(theDisp, dirW, "xv save");
  257.  
  258.     dbut[S_BOLDSET].str = "Prev Set";
  259.  
  260.     arrangeButts(mode);
  261.  
  262.     BTSetActive(&dbut[S_BOLDSET], haveoldinfo);
  263.     BTSetActive(&dbut[S_BOLDNAM], haveoldinfo);
  264.     
  265.     CBSetActive(&saveselCB, HaveSelection());
  266.  
  267.     MBSetActive(&fmtMB, 1);
  268.     if (MBWhich(&fmtMB) == F_FILELIST) {
  269.       MBSetActive(&colMB,      0);
  270.       CBSetActive(&savenormCB, 0);
  271.     }
  272.     else {
  273.       MBSetActive(&colMB,      1);
  274.       CBSetActive(&savenormCB, 1);
  275.     }
  276.  
  277.     CenterMapWindow(dirW, dbut[S_BOK].x+30, dbut[S_BOK].y + BUTTH/2,
  278.             DIRWIDE, DIRHIGH);
  279.  
  280.     savemode = 1;
  281.   }
  282.  
  283.   scrollToFileName();
  284.  
  285.   dirUp = mode;
  286.   BTSetActive(&but[BLOAD], !dirUp);
  287.   BTSetActive(&but[BSAVE], !dirUp);
  288. }
  289.  
  290.  
  291. /***************************************************/
  292. static void arrangeButts(mode)
  293.      int mode;
  294. {
  295.   int i, nbts, ngaps, szdiff, top, gap;
  296.  
  297.   nbts = (mode==BLOAD) ? S_LOAD_NBUTTS : S_NBUTTS;
  298.   ngaps = nbts-1;
  299.  
  300.   szdiff = dList.h - (nbts * BUTTH);
  301.   gap    = szdiff / ngaps;
  302.  
  303.   if (gap>16) {
  304.     gap = 16;
  305.     top = dList.y + (dList.h - (nbts*BUTTH) - (ngaps*gap))/2;
  306.     
  307.     for (i=0; i<nbts; i++) dbut[i].y = top + i*(BUTTH+gap);
  308.   }
  309.   else {
  310.     for (i=0; i<nbts; i++) 
  311.       dbut[i].y = dList.y + ((dList.h-BUTTH)*i) / ngaps;
  312.   }
  313. }
  314.     
  315.  
  316.  
  317. /***************************************************/
  318. void RedrawDirW(x,y,w,h)
  319.      int x,y,w,h;
  320. {
  321.   int        i, ypos, txtw;
  322.   char       foo[30], *str;
  323.   XRectangle xr;
  324.  
  325.   if (dList.nstr==1) strcpy(foo,"1 file");
  326.                 else sprintf(foo,"%d files",dList.nstr);
  327.  
  328.   ypos = dList.y + dList.h + 8 + ASCENT;
  329.   XSetForeground(theDisp, theGC, infobg);
  330.   XFillRectangle(theDisp, dirW, theGC, 10, ypos-ASCENT, 
  331.          (u_int) DIRWIDE, (u_int) CHIGH);
  332.   XSetForeground(theDisp, theGC, infofg);
  333.   DrawString(dirW, 10, ypos, foo);
  334.  
  335.  
  336.   if (dirUp == BLOAD) str = "Load file:";  
  337.                  else str = "Save file:";
  338.   DrawString(dirW, 10, dList.y + (int) dList.h + 30 + 4 + ASCENT, str);
  339.   
  340.   /* draw dividing line */
  341.   XSetForeground(theDisp,    theGC, infofg);
  342.   XDrawLine(theDisp, dirW,   theGC, 0, dirMB.y-6, DIRWIDE, dirMB.y-6);
  343.   if (ctrlColor) {
  344.     XSetForeground(theDisp,  theGC, locol);
  345.     XDrawLine(theDisp, dirW, theGC, 0, dirMB.y-5, DIRWIDE, dirMB.y-5);
  346.     XSetForeground(theDisp,  theGC, hicol);
  347.   }
  348.   XDrawLine(theDisp, dirW,   theGC, 0, dirMB.y-4, DIRWIDE, dirMB.y-4);
  349.   
  350.   
  351.   
  352.   for (i=0; i<(savemode ? S_NBUTTS : S_LOAD_NBUTTS); i++) BTRedraw(&dbut[i]);
  353.   
  354.   MBRedraw(&dirMB);
  355.   MBRedraw(&fmtMB);
  356.   MBRedraw(&colMB);
  357.  
  358.   XSetForeground(theDisp, theGC, infofg);
  359.   XSetBackground(theDisp, theGC, infobg);
  360.  
  361.   txtw = StringWidth(FMTLABEL);
  362.   if (StringWidth(COLLABEL) > txtw) txtw = StringWidth(COLLABEL);
  363.  
  364.   if (!savemode) {
  365.     XCopyArea(theDisp, d_loadPix, dirW, theGC, 0,0,d_load_width,d_load_height, 
  366.           10, (dirMB.y-6)/2 - d_load_height/2);
  367.  
  368.     XSetFillStyle(theDisp, theGC, FillStippled);
  369.     XSetStipple(theDisp, theGC, dimStip);
  370.     DrawString(dirW, fmtMB.x-6-txtw, 5+3+ASCENT, FMTLABEL);
  371.     DrawString(dirW, fmtMB.x-6-txtw, 5+3+ASCENT + (LINEHIGH+6), COLLABEL);
  372.     XSetFillStyle(theDisp,theGC,FillSolid);
  373.  
  374.     CBRedraw(&browseCB);
  375.   }
  376.   else {
  377.     XCopyArea(theDisp, d_savePix, dirW, theGC, 0,0,d_save_width,d_save_height,
  378.           10, (dirMB.y-6)/2 - d_save_height/2);
  379.  
  380.     XSetForeground(theDisp, theGC, infofg);
  381.     DrawString(dirW, fmtMB.x-6-txtw, 5+3+ASCENT, FMTLABEL);
  382.     DrawString(dirW, fmtMB.x-6-txtw, 5+3+ASCENT + (LINEHIGH+6), COLLABEL);
  383.  
  384.     CBRedraw(&savenormCB);
  385.     CBRedraw(&saveselCB);
  386.   }
  387. }
  388.  
  389.  
  390. /***************************************************/
  391. int ClickDirW(x,y)
  392. int x,y;
  393. {
  394.   BUTT  *bp;
  395.   int    bnum,i,maxbut,v;
  396.   char   buf[1024];
  397.  
  398.   if (savemode) {                           /* check format/colors MBUTTS */
  399.     i = v = 0;
  400.     if      (MBClick(&fmtMB, x,y) && (v=MBTrack(&fmtMB))>=0) i=1;
  401.     else if (MBClick(&colMB, x,y) && (v=MBTrack(&colMB))>=0) i=2;
  402.     
  403.     if (i) {  /* changed one of them */
  404.       if (i==1) SetDirSaveMode(F_FORMAT, v);
  405.            else SetDirSaveMode(F_COLORS, v);
  406.       changeSuffix();
  407.     }
  408.   }
  409.   
  410.   
  411.   if (!savemode) {  /* LOAD */
  412.     if (CBClick(&browseCB,x,y)) CBTrack(&browseCB);
  413.   } 
  414.   else {            /* SAVE */
  415.     if      (CBClick(&savenormCB,x,y)) CBTrack(&savenormCB);
  416.     else if (CBClick(&saveselCB,x,y))  CBTrack(&saveselCB);
  417.   }
  418.  
  419.  
  420.   maxbut = (savemode) ? S_NBUTTS : S_LOAD_NBUTTS;
  421.  
  422.   for (bnum=0; bnum<maxbut; bnum++) {
  423.     bp = &dbut[bnum];
  424.     if (PTINRECT(x, y, bp->x, bp->y, bp->w, bp->h)) break;
  425.   }
  426.  
  427.   if (bnum<maxbut && BTTrack(bp)) {   /* found one */
  428.     if (bnum<S_BOLDSET) return bnum;  /* do Ok,Cancel,Rescan in xvevent.c */
  429.  
  430.     if (bnum == S_BOLDSET && savemode && haveoldinfo) {
  431.       MBSelect(&fmtMB, oldformat);
  432.       MBSelect(&colMB, oldcolors);
  433.       changeSuffix();
  434.     }
  435.  
  436.     else if (bnum == S_BOLDNAM && savemode && haveoldinfo) {
  437.       setFName(oldfname);
  438.     }
  439.  
  440.     else if (bnum == S_BLOADALL && !savemode) {
  441.       int j, oldnumnames;
  442.       char *dname;
  443.  
  444.       oldnumnames = numnames;
  445.  
  446.       for (i=0; i<numfnames && numnames<MAXNAMES; i++) {
  447.     if (fnames[i][0] == C_REG || fnames[i][0] == C_EXE) {
  448.       sprintf(buf,"%s%s", path, fnames[i]+1);
  449.  
  450.       /* check for dups.  Don't add it if it is. */
  451.       for (j=0; j<numnames && strcmp(buf,namelist[j]); j++);
  452.  
  453.       if (j==numnames) {  /* add to list */
  454.         namelist[numnames] = (char *) malloc(strlen(buf)+1);
  455.         if (!namelist[numnames]) FatalError("out of memory!\n");
  456.         strcpy(namelist[numnames],buf);
  457.  
  458.         dname = namelist[numnames];
  459.  
  460.         /* figure out how much of name can be shown */
  461.         if (StringWidth(dname) > (nList.w-10-16)) {   /* truncate */
  462.           char *tmp;
  463.           int   prelen = 0;
  464.  
  465.           tmp = dname;
  466.           while (1) {
  467.         tmp = (char *) index(tmp,'/'); /* find next '/' in buf */
  468.         if (!tmp) break;
  469.  
  470.         tmp++;                   /* move to char following the '/' */
  471.         prelen = tmp - dname;
  472.         if (StringWidth(tmp) <= (nList.w-10-16)) break; /* cool now */
  473.           }
  474.  
  475.           dispnames[numnames] = dname + prelen;
  476.         }
  477.         else dispnames[numnames] = dname;
  478.  
  479.         numnames++;
  480.       }
  481.     }
  482.       }
  483.  
  484.       if (oldnumnames != numnames) {  /* added some */
  485.     if (numnames>0) BTSetActive(&but[BDELETE],1); 
  486.     windowMB.dim[WMB_TEXTVIEW] = (numnames==0);
  487.  
  488.     LSNewData(&nList, dispnames, numnames);
  489.     nList.selected = oldnumnames;
  490.     curname = oldnumnames - 1;
  491.  
  492.     ActivePrevNext();
  493.  
  494.     ScrollToCurrent(&nList);
  495.     DrawCtrlNumFiles();
  496.  
  497.     if (!browseCB.val) DirBox(0);
  498.       }
  499.  
  500.     }
  501.   }
  502.  
  503.  
  504.  
  505.   if (MBClick(&dirMB, x, y)) {
  506.     i = MBTrack(&dirMB);
  507.     if (i >= 0) changedDirMB(i);
  508.   }
  509.  
  510.   return -1;
  511. }
  512.  
  513.  
  514. /***************************************************/
  515. void SelectDir(n)
  516. int n;
  517. {
  518.   /* called when entry #n in the dir list was selected/double-clicked */
  519.  
  520.   /* if n<0, nothing was double-clicked, but perhaps the selection
  521.      has changed.  Copy the selection to the filename if a) we're in
  522.      the 'load' box, and b) it's not a directory name */
  523.  
  524.   if (n<0) {
  525.     if (dList.selected>=0)
  526.       setFName(dList.str[dList.selected]+1);
  527.     return;
  528.   }
  529.  
  530.   /* can just pretend 'enter' was hit on a double click, as the original
  531.      click would've copied the string to filename */
  532.  
  533.   if (!DirCheckCD()) FakeButtonPress(&dbut[S_BOK]);
  534. }
  535.  
  536.  
  537.  
  538. /***************************************************/
  539. static void changedDirMB(sel)
  540.      int sel;
  541. {
  542.   if (sel != 0) {   /* changed directories */
  543.     char tmppath[MAXPATHLEN+1], *trunc_point;
  544.  
  545.     /* end 'path' by changing trailing '/' (of dir name) to a '\0' */
  546.     trunc_point = (dirs[(ndirs-1)-sel + 1] - 1);
  547.     *trunc_point = '\0';
  548.  
  549.     if (path[0] == '\0') {
  550.       /* special case:  if cd to '/', fix path (it's currently "") */
  551. #ifdef apollo    /*** Apollo DomainOS uses // as the network root ***/
  552.       strcpy(tmppath,"//");
  553. #else
  554.       strcpy(tmppath,"/");
  555. #endif
  556.     }
  557.     else strcpy(tmppath, path);
  558.  
  559. #ifdef VMS
  560.     /*
  561.      *  The VMS chdir always needs 2 components (device and directory),
  562.      *  so convert "/device" to "/device/000000" and convert
  563.      *  "/" to "/XV_Root_Device/000000" (XV_Root_Device will need to be
  564.      *  a special concealed device setup to provide a list of available
  565.      *  disks).
  566.      */
  567.     if ( ((ndirs-sel) == 2) && (strlen(tmppath) > 1) ) 
  568.       strcat ( tmppath, "/000000" ); /* add root dir for device */
  569.     else if  ((ndirs-sel) == 1 ) {
  570.       strcpy ( tmppath, "/XV_Root_Device/000000" );  /* fake top level */
  571.     }
  572. #endif
  573.  
  574.     if (chdir(tmppath)) {
  575.       char str[512];
  576.       sprintf(str,"Unable to cd to '%s'\n", tmppath);
  577.       *trunc_point = '/';  /* restore the path */
  578.       MBRedraw(&dirMB);
  579.       ErrPopUp(str, "\nWhatever");
  580.     }
  581.     else {
  582.       loadCWD();
  583.     }
  584.   }
  585. }
  586.  
  587.  
  588. /***************************************************/
  589. static void RedrawDList(delta, sptr)
  590.      int   delta;
  591.      SCRL *sptr;
  592. {
  593.   LSRedraw(&dList,delta);
  594. }
  595.  
  596.  
  597. /***************************************************/
  598. static void loadCWD()
  599. {
  600.   /* loads up current-working-directory into load/save list */
  601.  
  602.   xv_getwd(path, sizeof(path));
  603.   LoadCurrentDirectory();
  604. }
  605.   
  606.  
  607.  
  608. /***************************************************/
  609. void LoadCurrentDirectory()
  610. {
  611.   /* rescans current load/save directory */
  612.  
  613.   DIR           *dirp;
  614.   int            i, j, ftype, mode, changedDir;
  615.   struct stat    st;
  616.   char          *dbeg, *dend;
  617.   static char    oldpath[MAXPATHLEN + 2] = { '\0' };
  618.  
  619. #ifdef NODIRENT
  620.   struct direct *dp;
  621. #else
  622.   struct dirent *dp;
  623. #endif
  624.   
  625.  
  626.   /* get rid of previous file names */
  627.   for (i=0; i<numfnames; i++) free(fnames[i]);
  628.   numfnames = 0;
  629.  
  630.   /* get rid of old dirMBlist */
  631.   for (i=0; i<ndirs; i++) free(dirMBlist[i]);
  632.  
  633. #ifndef VMS
  634.   if (strlen(path) == 0) xv_getwd(path, sizeof(path));  /* no dir, use cwd */
  635. #else
  636.   xv_getwd(path, sizeof(path));
  637. #endif
  638.  
  639.   if (chdir(path)) {
  640.     ErrPopUp("Current load/save directory seems to have gone away!",
  641.          "\nYikes!");
  642. #ifdef apollo
  643.     strcpy(path,"//");
  644. #else
  645.     strcpy(path,"/");
  646. #endif
  647.     chdir(path);
  648.   }
  649.  
  650.   changedDir = strcmp(path, oldpath);
  651.   strcpy(oldpath, path);
  652.  
  653.   if ((strlen(path) > (size_t) 1) && path[strlen(path)-1] != '/')
  654.     strcat(path,"/");   /* tack on a trailing '/' to make path consistent */
  655.  
  656.   /* path will be something like: "/u3/bradley/src/weiner/whatever/" */
  657.   /* parse path into individual directory names */
  658.   dbeg = dend = path;
  659.   for (i=0; i<MAXDEEP && dend; i++) {
  660.     dend = (char *) index(dbeg,'/');  /* find next '/' char */
  661.  
  662. #ifdef apollo
  663.     /** On apollos the path will be something like //machine/users/foo/ **/
  664.     /** handle the initial // **/
  665.     if ((dend == dbeg ) && (dbeg[0] == '/') && (dbeg[1] == '/')) dend += 1;
  666. #endif
  667.  
  668.     dirs[i] = dbeg;
  669.     dbeg = dend+1;
  670.   }
  671.   ndirs = i-1;
  672.  
  673.  
  674.   /* build dirMBlist */
  675.   for (i=ndirs-1,j=0; i>=0; i--,j++) {
  676.     size_t stlen = (i<(ndirs-1)) ? dirs[i+1] - dirs[i] : strlen(dirs[i]);
  677.     dirMBlist[j] = (char *) malloc(stlen+1);
  678.     if (!dirMBlist[j]) FatalError("unable to malloc dirMBlist[]");
  679.  
  680.     strncpy(dirMBlist[j], dirs[i], stlen);
  681.     dirMBlist[j][stlen] = '\0';
  682.   }
  683.     
  684.  
  685.   lastdir = dirs[ndirs-1];
  686.   dirMB.list = dirMBlist;
  687.   dirMB.nlist = ndirs;
  688.   XClearArea(theDisp, dirMB.win, dirMB.x, dirMB.y, 
  689.          (u_int) dirMB.w+3, (u_int) dirMB.h+3, False);
  690.   i = StringWidth(dirMBlist[0]) + 10;
  691.   dirMB.x = dirMB.x + dirMB.w/2 - i/2;
  692.   dirMB.w = i;
  693.   MBRedraw(&dirMB);
  694.  
  695.  
  696.   dirp = opendir(".");
  697.   if (!dirp) {
  698.     LSNewData(&dList, fnames, 0);
  699.     RedrawDirW(0,0,DIRWIDE,DIRHIGH);
  700.     return;
  701.   }
  702.  
  703.   WaitCursor();
  704.  
  705.   i=0;
  706.   while ( (dp = readdir(dirp)) != NULL) {
  707.     if (strcmp(dp->d_name, ".")==0   || 
  708.     (strcmp(dp->d_name, "..")==0 && 
  709.      (strcmp(path,"/")==0 || strcmp(path,"//")==0)) ||
  710.     strcmp(dp->d_name, THUMBDIR)==0) {
  711.       /* skip over '.' and '..' and THUMBDIR */
  712.     }
  713.     else {
  714.  
  715.       if (i == MAXNAMES) {
  716.     fprintf(stderr,
  717.         "%s: too many directory entries.  Only using first %d.\n",
  718.         cmd, MAXNAMES);
  719.     break;
  720.       }
  721.  
  722.       if ((i&31)==0) WaitCursor();
  723.  
  724.       fnames[i] = (char *) malloc(strlen(dp->d_name)+2); /* +2=ftype + '\0' */
  725.  
  726.       if (!fnames[i]) FatalError("malloc error while reading directory");
  727.       strcpy(fnames[i]+1, dp->d_name);
  728.  
  729.       /* figure out what type of file the beastie is */
  730.       fnames[i][0] = C_REG;   /* default to normal file, if stat fails */
  731.  
  732. #ifdef VMS
  733.       /* For VMS we will default all files EXCEPT directories to avoid
  734.      the high cost of the VAX C implementation of the stat function.
  735.      Suggested by Kevin Oberman (OBERMAN@icdc.llnl.gov) */
  736.  
  737.       if (xv_strstr (fnames[i]+1, ".DIR") != NULL) fnames[i][0] = C_DIR;
  738.       if (xv_strstr (fnames[i]+1, ".EXE") != NULL) fnames[i][0] = C_EXE;
  739.       if (xv_strstr (fnames[i]+1, ".OBJ") != NULL) fnames[i][0] = C_BLK;
  740. #else
  741.       if (!nostat && (stat(fnames[i]+1, &st)==0)) {
  742.     mode  = st.st_mode & 0777;     /* rwx modes */
  743.  
  744.         ftype = st.st_mode;
  745.         if      (S_ISDIR(ftype))  fnames[i][0] = C_DIR;
  746.         else if (S_ISCHR(ftype))  fnames[i][0] = C_CHR;
  747.         else if (S_ISBLK(ftype))  fnames[i][0] = C_BLK;
  748.     else if (S_ISLINK(ftype)) fnames[i][0] = C_LNK;
  749.     else if (S_ISFIFO(ftype)) fnames[i][0] = C_FIFO;
  750.     else if (S_ISSOCK(ftype)) fnames[i][0] = C_SOCK;
  751.         else if (fnames[i][0] == C_REG && (mode&0111)) fnames[i][0] = C_EXE;
  752.       }
  753.       else {
  754.     /* fprintf(stderr,"problems 'stat-ing' files\n");*/
  755.     fnames[i][0] = C_REG;
  756.       }
  757. #endif /* VMS */
  758.  
  759.       i++;
  760.     }
  761.   }
  762.  
  763.   closedir(dirp);
  764.  
  765.   numfnames = i;
  766.  
  767.   qsort((char *) fnames, (size_t) numfnames, sizeof(char *), dnamcmp);
  768.  
  769.   if (changedDir) LSNewData(&dList, fnames, numfnames);
  770.              else LSChangeData(&dList, fnames, numfnames);
  771.   RedrawDirW(0,0,DIRWIDE,DIRHIGH);
  772.   SetCursors(-1);
  773. }
  774.  
  775.  
  776. /***************************************************/
  777. void GetDirPath(buf)
  778.      char *buf;
  779. {
  780.   /* returns current 'dirW' path.  buf should be MAXPATHLEN long */
  781.  
  782.   strcpy(buf, path);
  783. }
  784.  
  785.  
  786. /***************************************************/
  787. static int cd_able(str)
  788. char *str;
  789. {
  790.   return ((str[0] == C_DIR || str[0] == C_LNK));
  791. }
  792.  
  793.  
  794. /***************************************************/
  795. static int dnamcmp(p1,p2)
  796.      const void *p1, *p2;
  797. {
  798.   char **s1, **s2;
  799.  
  800.   s1 = (char **) p1;
  801.   s2 = (char **) p2;
  802.  
  803. #ifdef FOO
  804.   /* sort so that directories are at beginning of list */
  805.  
  806.   /* if both dir/lnk or both NOT dir/lnk, sort on name */
  807.  
  808.   if ( ( cd_able(*s1) &&  cd_able(*s2)) ||
  809.        (!cd_able(*s1) && !cd_able(*s2)))
  810.     return (strcmp((*s1)+1, (*s2)+1));
  811.  
  812.   else if (cd_able(*s1)) return -1;  /* s1 is first */
  813.   else return 1;                     /* s2 is first */
  814. #else
  815.   /* sort in pure alpha order */
  816.   return(strcmp((*s1)+1, (*s2)+1));
  817. #endif
  818. }
  819.  
  820.  
  821.  
  822.  
  823.  
  824. /***************************************************/
  825. int DirKey(c)
  826.      int c;
  827. {
  828.   /* got keypress in dirW.  stick on end of filename */
  829.   int len;
  830.  
  831.   len = strlen(filename);
  832.   
  833.   if (c>=' ' && c<'\177') {             /* printable characters */
  834.     /* note: only allow 'piped commands' in savemode... */
  835.  
  836.     /* only allow spaces in 'piped commands', not filenames */
  837.     if (c==' ' && (!ISPIPE(filename[0]) || curPos==0)) return (-1);
  838.  
  839.     /* only allow vertbars in 'piped commands', not filenames */
  840.     if (c=='|' && curPos!=0 && !ISPIPE(filename[0])) return(-1);
  841.  
  842.     if (len >= MAXFNLEN-1) return(-1);  /* max length of string */
  843.     xvbcopy(&filename[curPos], &filename[curPos+1], (size_t) (len-curPos+1));
  844.     filename[curPos]=c;  curPos++;
  845.  
  846.     scrollToFileName();
  847.   }
  848.  
  849.   else if (c=='\010' || c=='\177') {    /* BS or DEL */
  850.     if (curPos==0) return(-1);          /* at beginning of str */
  851.     xvbcopy(&filename[curPos], &filename[curPos-1], (size_t) (len-curPos+1));
  852.     curPos--;
  853.  
  854.     if (strlen(filename) > (size_t) 0) scrollToFileName();
  855.   }
  856.  
  857.   else if (c=='\025') {                 /* ^U: clear entire line */
  858.     filename[0] = '\0';
  859.     curPos = 0;
  860.   }
  861.  
  862.   else if (c=='\013') {                 /* ^K: clear to end of line */
  863.     filename[curPos] = '\0';
  864.   }
  865.  
  866.   else if (c=='\001') {                 /* ^A: move to beginning */
  867.     curPos = 0;
  868.   }
  869.  
  870.   else if (c=='\005') {                 /* ^E: move to end */
  871.     curPos = len;
  872.   }
  873.  
  874.   else if (c=='\004') {                 /* ^D: delete character at curPos */
  875.     if (curPos==len) return(-1);
  876.     xvbcopy(&filename[curPos+1], &filename[curPos], (size_t) (len-curPos));
  877.   }
  878.  
  879.   else if (c=='\002') {                 /* ^B: move backwards char */
  880.     if (curPos==0) return(-1);
  881.     curPos--;
  882.   }
  883.  
  884.   else if (c=='\006') {                 /* ^F: move forwards char */
  885.     if (curPos==len) return(-1);
  886.     curPos++;
  887.   }
  888.  
  889.   else if (c=='\012' || c=='\015') {    /* CR or LF */
  890.     if (!DirCheckCD()) FakeButtonPress(&dbut[S_BOK]);
  891.   }
  892.  
  893.   else if (c=='\033') {                  /* ESC = Cancel */
  894.     FakeButtonPress(&dbut[S_BCANC]);
  895.   }
  896.  
  897.   else if (c=='\011') {                  /* tab = filename expansion */
  898.     if (!autoComplete()) XBell(theDisp, 0);
  899.     else {
  900.       curPos = strlen(filename);
  901.       scrollToFileName();
  902.     }
  903.   }
  904.  
  905.   else return(-1);                      /* unhandled character */
  906.  
  907.   showFName();
  908.  
  909.   /* if we cleared out filename, clear out deffname as well */
  910.   if (!filename[0]) deffname[0] = '\0';
  911.  
  912.   return(0);
  913. }
  914.  
  915.  
  916. /***************************************************/
  917. static int autoComplete()
  918. {
  919.   /* called to 'auto complete' a filename being entered.  If the name that
  920.      has been entered so far is anything but a simple filename (ie, has
  921.      spaces, pipe char, '/', etc) fails.  If it is a simple filename, 
  922.      looks through the name list to find something that matches what's already
  923.      been typed.  If nothing matches, it fails.  If more than one thing
  924.      matches, it sets the name to the longest string that the multiple
  925.      matches have in common, and succeeds (and beeps).  
  926.      If only one matches, sets the string to the match and succeeds.
  927.      
  928.      returns zero on failure, non-zero on success */
  929.   
  930.   int i, firstmatch, slen, nummatch, cnt;
  931.  
  932.   /* is filename a simple filename? */
  933.   if (strlen(filename)==0  || 
  934.       ISPIPE(filename[0])  ||
  935.       index(filename, '/') ||
  936.       filename[0]=='~'   ) return 0;
  937.  
  938.   slen = strlen(filename);
  939.   for (i=0; i<dList.nstr; i++) {
  940.     if (strncmp(filename, dList.str[i]+1, (size_t) slen) <= 0) break;
  941.   }
  942.   if (i==dList.nstr) return 0;
  943.   if (strncmp(filename, dList.str[i]+1, (size_t) slen) < 0) return 0;
  944.  
  945.   /* there's a match of some sort... */
  946.   firstmatch = i;
  947.  
  948.   /* count # of matches */
  949.   for (i=firstmatch, nummatch=0; 
  950.        i<dList.nstr && strncmp(filename, dList.str[i]+1, (size_t) slen)==0;
  951.        i++, nummatch++);
  952.  
  953.   if (nummatch == 1) {       /* only one match */
  954.     strcpy(filename, dList.str[firstmatch]+1);
  955.     return 1;
  956.   }
  957.  
  958.  
  959.   /* compute longest common prefix among the matches */
  960.   while (dList.str[firstmatch][slen+1]!='\0') {
  961.     filename[slen] = dList.str[firstmatch][slen+1];
  962.     slen++;  filename[slen] = '\0';
  963.     
  964.     for (i=firstmatch, cnt=0;
  965.      i<dList.nstr && strncmp(filename, dList.str[i]+1, (size_t) slen)==0;
  966.      i++, cnt++);
  967.  
  968.     if (cnt != nummatch) {  slen--;  filename[slen] = '\0';  break; }
  969.   }  
  970.   
  971.   XBell(theDisp, 0);
  972.  
  973.   return 1;
  974. }
  975.  
  976. /***************************************************/
  977. static void scrollToFileName()
  978. {
  979.   int i, hi, lo, pos, cmp;
  980.  
  981.   /* called when 'fname' changes.  Tries to scroll the directory list
  982.      so that fname would be centered in it */
  983.  
  984.   /* nothing to do if scrlbar not enabled ( <= NLINES names in list) */
  985.   if (dList.scrl.max <= 0) return;
  986.  
  987.   /* find the position in the namelist that the current name should be at
  988.      (binary search) */
  989.  
  990.   pos = 0;  lo = 0;  hi = dList.nstr-1;
  991.   i = strlen(filename);
  992.   if (!i) { SCSetVal(&dList.scrl, 0); return; }
  993.  
  994.   while ((hi-lo)>=0) {
  995.     pos = lo + (hi-lo)/2;
  996.     cmp = strcmp(filename, dList.str[pos]+1);
  997.     if      (cmp<0) hi = pos-1;
  998.     else if (cmp>0) lo = pos+1;
  999.     else break;      /* found it! */
  1000.   }
  1001.  
  1002.   /* set scroll position so that 'pos' will be centered in the list */
  1003.   i = pos - (NLINES/2);
  1004.   SCSetVal(&dList.scrl, i);
  1005. }
  1006.   
  1007.  
  1008. /***************************************************/
  1009. void RedrawDNamW()
  1010. {
  1011.   int cpos;
  1012.  
  1013.   /* draw substring filename[stPos:enPos] and cursor */
  1014.  
  1015.   Draw3dRect(dnamW, 0, 0, (u_int) DNAMWIDE+5, (u_int) LINEHIGH+4, R3D_IN, 2, 
  1016.          hicol, locol, infobg);
  1017.  
  1018.   XSetForeground(theDisp, theGC, infofg);
  1019.  
  1020.   if (stPos>0) {  /* draw a "there's more over here" doowah */
  1021.     XDrawLine(theDisp, dnamW, theGC, 0,0,0,LINEHIGH+5);
  1022.     XDrawLine(theDisp, dnamW, theGC, 1,0,1,LINEHIGH+5);
  1023.     XDrawLine(theDisp, dnamW, theGC, 2,0,2,LINEHIGH+5);
  1024.   }
  1025.  
  1026.   if ((size_t) enPos < strlen(filename)) { 
  1027.     /* draw a "there's more over here" doowah */
  1028.     XDrawLine(theDisp, dnamW, theGC, DNAMWIDE+5,0,DNAMWIDE+5,LINEHIGH+5);
  1029.     XDrawLine(theDisp, dnamW, theGC, DNAMWIDE+4,0,DNAMWIDE+4,LINEHIGH+5);
  1030.     XDrawLine(theDisp, dnamW, theGC, DNAMWIDE+3,0,DNAMWIDE+3,LINEHIGH+5);
  1031.   }
  1032.  
  1033.   XDrawString(theDisp, dnamW, theGC,3,ASCENT+3,filename+stPos, enPos-stPos);
  1034.  
  1035.   cpos = XTextWidth(mfinfo, &filename[stPos], curPos-stPos);
  1036.   XDrawLine(theDisp, dnamW, theGC, 3+cpos, 2, 3+cpos, 2+CHIGH+1);
  1037.   XDrawLine(theDisp, dnamW, theGC, 3+cpos, 2+CHIGH+1, 5+cpos, 2+CHIGH+3);
  1038.   XDrawLine(theDisp, dnamW, theGC, 3+cpos, 2+CHIGH+1, 1+cpos, 2+CHIGH+3);
  1039. }
  1040.  
  1041.  
  1042. /***************************************************/
  1043. int DoSave()
  1044. {
  1045.   FILE *fp;
  1046.   byte *thepic, *rp, *gp, *bp;
  1047.   int   i, w, h, rv, fmt, col, nc, ptype, pfree;
  1048.   char *fullname;
  1049.  
  1050.   /* opens file, does appropriate color pre-processing, calls save routine
  1051.      based on chosen format.  Returns '0' if successful */
  1052.  
  1053.   dbut[S_BOK].lit = 1;  BTRedraw(&dbut[S_BOK]);
  1054.  
  1055.   fullname = GetDirFullName();
  1056.  
  1057.   fmt = MBWhich(&fmtMB);
  1058.   col = MBWhich(&colMB);
  1059.  
  1060.   if (fmt<0 || col<0) 
  1061.     FatalError("xv: no 'checked' format or color.  shouldn't happen!\n");
  1062.  
  1063.  
  1064.   if (fmt == F_FILELIST) {       /* write filename list */
  1065.     fp = OpenOutFile(fullname);
  1066.     if (!fp) {
  1067.       SetCursors(-1);
  1068.       dbut[S_BOK].lit = 0;  BTRedraw(&dbut[S_BOK]);
  1069.       return -1;
  1070.     }
  1071.     
  1072.     for (i=0; i<numnames; i++) {
  1073.       if ((i&0x3f)==0) WaitCursor();
  1074.       if (namelist[i][0] != '/') fprintf(fp, "%s/%s\n", initdir, namelist[i]);
  1075.                             else fprintf(fp, "%s\n", namelist[i]);
  1076.     }
  1077.     
  1078.     i = (ferror(fp)) ? 1 : 0;
  1079.     if (CloseOutFile(fp, fullname, i) == 0) {
  1080.       DirBox(0);
  1081.       XVCreatedFile(fullname);
  1082.     }
  1083.     
  1084.     SetCursors(-1);
  1085.     dbut[S_BOK].lit = 0;  BTRedraw(&dbut[S_BOK]);
  1086.     return i;
  1087.   }  /* FILELIST */
  1088.  
  1089.  
  1090.   /* handle formats that pop up 'how do you want to save this' boxes */
  1091.  
  1092.  
  1093.   if (fmt == F_PS) {   /* PostScript */
  1094.     PSSaveParams(fullname, col);
  1095.     PSDialog(1);                   /* open PSDialog box */
  1096.     dbut[S_BOK].lit = 0;  BTRedraw(&dbut[S_BOK]);
  1097.     return 0;                      /* always 'succeeds' */
  1098.   }
  1099.  
  1100. #ifdef HAVE_JPEG
  1101.   else if (fmt == F_JPEG) {   /* JPEG */
  1102.     JPEGSaveParams(fullname, col);
  1103.     JPEGDialog(1);                   /* open JPEGDialog box */
  1104.     dbut[S_BOK].lit = 0;  BTRedraw(&dbut[S_BOK]);
  1105.     return 0;                      /* always 'succeeds' */
  1106.   }
  1107. #endif
  1108.  
  1109. #ifdef HAVE_TIFF
  1110.   else if (fmt == F_TIFF) {   /* TIFF */
  1111.     TIFFSaveParams(fullname, col);
  1112.     TIFFDialog(1);                   /* open TIFF Dialog box */
  1113.     dbut[S_BOK].lit = 0;  BTRedraw(&dbut[S_BOK]);
  1114.     return 0;                      /* always 'succeeds' */
  1115.   }
  1116. #endif
  1117.  
  1118.  
  1119.  
  1120.  
  1121.   WaitCursor();
  1122.  
  1123.   thepic = GenSavePic(&ptype, &w, &h, &pfree, &nc, &rp, &gp, &bp);
  1124.  
  1125.   fp = OpenOutFile(fullname);
  1126.   if (!fp) {
  1127.     if (pfree) free(thepic);
  1128.     SetCursors(-1);
  1129.     dbut[S_BOK].lit = 0;  BTRedraw(&dbut[S_BOK]);
  1130.     return -1;
  1131.   }
  1132.  
  1133.  
  1134.   if (col == F_REDUCED) col = F_FULLCOLOR;
  1135.   rv = 0;
  1136.  
  1137.   switch (fmt) {
  1138.   case F_GIF:
  1139.     rv = WriteGIF   (fp, thepic, ptype, w, h, rp,gp,bp, nc,col,picComments);
  1140.     break;
  1141.  
  1142.   case F_PM:
  1143.     rv = WritePM    (fp, thepic, ptype, w, h, rp,gp,bp, nc,col,picComments);
  1144.     break;
  1145.  
  1146.   case F_PBMRAW:
  1147.     rv = WritePBM   (fp, thepic, ptype, w, h, rp,gp,bp, nc,col,1,picComments);
  1148.     break;
  1149.  
  1150.   case F_PBMASCII: 
  1151.     rv = WritePBM   (fp, thepic, ptype, w, h, rp,gp,bp, nc,col,0,picComments);
  1152.     break;
  1153.  
  1154.   case F_XBM:
  1155.     rv = WriteXBM   (fp, thepic, w, h, rp, gp, bp, fullname);          break;
  1156.  
  1157.   case F_SUNRAS:
  1158.     rv = WriteSunRas(fp, thepic, ptype, w, h, rp, gp, bp, nc, col,0);  break;
  1159.  
  1160.   case F_BMP:
  1161.     rv = WriteBMP   (fp, thepic, ptype, w, h, rp, gp, bp, nc, col);    break;
  1162.  
  1163.   case F_IRIS:
  1164.     rv = WriteIRIS  (fp, thepic, ptype, w, h, rp, gp, bp, nc, col);    break;
  1165.     
  1166.   case F_TARGA:
  1167.     rv = WriteTarga (fp, thepic, ptype, w, h, rp, gp, bp, nc, col);    break;
  1168.     
  1169.   case F_XPM:
  1170.     rv = WriteXPM   (fp, thepic, ptype, w, h, rp, gp, bp, nc, col, 
  1171.              fullname, picComments);    
  1172.   case F_FITS:
  1173.     rv = WriteFITS  (fp, thepic, ptype, w, h, rp, gp, bp, nc, col, 
  1174.              picComments);    
  1175.     break;
  1176.   }
  1177.   
  1178.  
  1179.   if (CloseOutFile(fp, fullname, rv) == 0) {
  1180.     DirBox(0);
  1181.     if (!dopipe) {
  1182.       XVCreatedFile(fullname);
  1183.       StickInCtrlList(0);
  1184.     }
  1185.   }
  1186.  
  1187.   
  1188.   if (pfree) free(thepic);
  1189.   
  1190.   SetCursors(-1);
  1191.   dbut[S_BOK].lit = 0;  BTRedraw(&dbut[S_BOK]);
  1192.   
  1193.   return rv;
  1194. }
  1195.  
  1196.  
  1197.  
  1198. /***************************************************/
  1199. void SetDirFName(st)
  1200.      char *st;
  1201. {
  1202.   strncpy(deffname, st, (size_t) MAXFNLEN-1);
  1203.   setFName(st);
  1204. }
  1205.  
  1206.  
  1207. /***************************************************/
  1208. static void setFName(st)
  1209.      char *st;
  1210. {
  1211.   strncpy(filename, st, (size_t) MAXFNLEN-1);
  1212.   filename[MAXFNLEN-1] = '\0';  /* make sure it's terminated */
  1213.   curPos = strlen(st);
  1214.   stPos = 0;  enPos = curPos;
  1215.   
  1216.   showFName();
  1217. }
  1218.  
  1219.  
  1220. /***************************************************/
  1221. static void showFName()
  1222. {
  1223.   int len;
  1224.   
  1225.   len = strlen(filename);
  1226.   
  1227.   if (curPos<stPos) stPos = curPos;
  1228.   if (curPos>enPos) enPos = curPos;
  1229.   
  1230.   if (stPos>len) stPos = (len>0) ? len-1 : 0;
  1231.   if (enPos>len) enPos = (len>0) ? len-1 : 0;
  1232.   
  1233.   /* while substring is shorter than window, inc enPos */
  1234.   
  1235.   while (XTextWidth(mfinfo, &filename[stPos], enPos-stPos) < DNAMWIDE
  1236.      && enPos<len) { enPos++; }
  1237.  
  1238.   /* while substring is longer than window, dec enpos, unless enpos==curpos,
  1239.      in which case, inc stpos */
  1240.  
  1241.   while (XTextWidth(mfinfo, &filename[stPos], enPos-stPos) > DNAMWIDE) {
  1242.     if (enPos != curPos) enPos--;
  1243.     else stPos++;
  1244.   }
  1245.  
  1246.  
  1247.   if (ctrlColor) XClearArea(theDisp, dnamW, 2,2, (u_int) DNAMWIDE+5-3, 
  1248.                 (u_int) LINEHIGH+4-3, False);
  1249.   else XClearWindow(theDisp, dnamW);
  1250.  
  1251.   RedrawDNamW();
  1252.   BTSetActive(&dbut[S_BOK], strlen(filename)!=0);
  1253. }
  1254.  
  1255.  
  1256. /***************************************************/
  1257. char *GetDirFName()
  1258. {
  1259.   return (filename);
  1260. }
  1261.  
  1262.  
  1263. /***************************************************/
  1264. char *GetDirFullName()
  1265. {
  1266.   static char globname[MAXFNLEN+100];   /* the +100 is for ~ expansion */
  1267.   static char fullname[MAXPATHLEN+2];
  1268.  
  1269.   if (ISPIPE(filename[0])) strcpy(fullname, filename);
  1270.   else {
  1271.     strcpy(globname, filename);
  1272.     if (globname[0] == '~') Globify(globname);
  1273.     
  1274.     if (globname[0] != '/') sprintf(fullname, "%s%s", path, globname);
  1275.     else strcpy(fullname, globname);
  1276.   }
  1277.  
  1278.   return (fullname);
  1279. }
  1280.  
  1281.  
  1282. /***************************************************/
  1283. void SetDirSaveMode(group, bnum)
  1284.      int group, bnum;
  1285. {
  1286.   if (group == F_COLORS) {
  1287.     if (picType == PIC24) {   /* disable REDUCED COLOR */
  1288.       colMB.dim[F_REDUCED] = 1;
  1289.       if (MBWhich(&colMB) == F_REDUCED) MBSelect(&colMB, F_FULLCOLOR);
  1290.     }
  1291.     else {  /* PIC8 - turn on REDUCED COLOR, if not XBM */
  1292.       if (MBWhich(&fmtMB) != F_XBM) {
  1293.     colMB.dim[F_REDUCED] = 0;
  1294.     MBRedraw(&fmtMB);
  1295.       }
  1296.     }
  1297.     
  1298.     if (bnum>=0) MBSelect(&colMB, bnum);
  1299.   }
  1300.   
  1301.  
  1302.   else if (group == F_FORMAT) {
  1303.     MBSelect(&fmtMB, bnum);
  1304.     if (MBWhich(&fmtMB) == F_XBM) { /* turn off all but B/W */
  1305.       colMB.dim[F_FULLCOLOR] = 1;
  1306.       colMB.dim[F_GREYSCALE] = 1;
  1307.       colMB.dim[F_BWDITHER]  = 0;
  1308.       colMB.dim[F_REDUCED]   = 1;
  1309.       MBSelect(&colMB, F_BWDITHER);
  1310.     }
  1311.  
  1312.     else if (MBWhich(&fmtMB) == F_FITS) { /* turn off 'color' modes */
  1313.       colMB.dim[F_FULLCOLOR] = 1;
  1314.       colMB.dim[F_GREYSCALE] = 0;
  1315.       colMB.dim[F_BWDITHER]  = 0;
  1316.       colMB.dim[F_REDUCED]   = 1;
  1317.       MBSelect(&colMB, F_GREYSCALE);
  1318.     }
  1319.  
  1320.     else {                       /* turn on all */
  1321.       colMB.dim[F_FULLCOLOR] = 0;
  1322.       colMB.dim[F_GREYSCALE] = 0;
  1323.       colMB.dim[F_BWDITHER]  = 0;
  1324.       colMB.dim[F_REDUCED]   = (picType==PIC8) ? 0 : 1;
  1325.       if (picType!=PIC8 && MBWhich(&colMB)==F_REDUCED) 
  1326.     MBSelect(&colMB, F_FULLCOLOR);
  1327.     }
  1328.     
  1329.     if (MBWhich(&fmtMB) == F_FILELIST) {
  1330.       MBSetActive(&colMB,      0);
  1331.       CBSetActive(&savenormCB, 0);
  1332.     }
  1333.     else {
  1334.       MBSetActive(&colMB,      1);
  1335.       CBSetActive(&savenormCB, 1);
  1336.     }
  1337.   }
  1338. }
  1339.  
  1340.   
  1341.  
  1342. /***************************************/
  1343. static void changeSuffix()
  1344. {
  1345.   /* see if there's a common suffix at the end of the filename.  
  1346.      if there is, remember what case it was (all caps or all lower), lop
  1347.      it off, and replace it with a new appropriate suffix, in the
  1348.      same case */
  1349.  
  1350.   int allcaps;
  1351.   char *suffix, *sp, *dp, lowsuf[512];
  1352.  
  1353.   /* find the last '.' in the filename */
  1354.   suffix = (char *) rindex(filename, '.');
  1355.   if (!suffix) return;
  1356.   suffix++;  /* point to first letter of the suffix */
  1357.  
  1358.   /* check for all-caposity */
  1359.   for (sp = suffix, allcaps=1; *sp; sp++) 
  1360.     if (islower(*sp)) allcaps = 0;
  1361.  
  1362.   /* copy the suffix into an all-lower-case buffer */
  1363.   for (sp=suffix, dp=lowsuf; *sp; sp++, dp++) {
  1364.     *dp = (isupper(*sp)) ? tolower(*sp) : *sp;
  1365.   }
  1366.   *dp = '\0';
  1367.  
  1368.   /* compare for common suffixes */
  1369.   if ((strcmp(lowsuf,"gif" )==0) ||
  1370.       (strcmp(lowsuf,"pm"  )==0) ||
  1371.       (strcmp(lowsuf,"pbm" )==0) ||
  1372.       (strcmp(lowsuf,"pgm" )==0) ||
  1373.       (strcmp(lowsuf,"ppm" )==0) ||
  1374.       (strcmp(lowsuf,"pnm" )==0) ||
  1375.       (strcmp(lowsuf,"bm"  )==0) ||
  1376.       (strcmp(lowsuf,"xbm" )==0) ||
  1377.       (strcmp(lowsuf,"ras" )==0) ||
  1378.       (strcmp(lowsuf,"bmp" )==0) ||
  1379.       (strcmp(lowsuf,"ps"  )==0) ||
  1380.       (strcmp(lowsuf,"eps" )==0) ||
  1381.       (strcmp(lowsuf,"rgb" )==0) ||
  1382.       (strcmp(lowsuf,"tga" )==0) ||
  1383.       (strcmp(lowsuf,"xpm" )==0) ||
  1384.       (strcmp(lowsuf,"fits")==0) ||
  1385.       (strcmp(lowsuf,"fts" )==0) ||
  1386.       (strcmp(lowsuf,"jpg" )==0) ||
  1387.       (strcmp(lowsuf,"jpeg")==0) ||
  1388.       (strcmp(lowsuf,"jfif")==0) ||
  1389.       (strcmp(lowsuf,"tif" )==0) ||
  1390.       (strcmp(lowsuf,"tiff")==0)) {
  1391.  
  1392.     /* found one.  set lowsuf = to the new suffix, and tack on to filename */
  1393.  
  1394.     int fmt, col;
  1395.     fmt = MBWhich(&fmtMB);
  1396.     col = MBWhich(&colMB);
  1397.  
  1398.     if (fmt<0 || col<0) return;    /* shouldn't happen */
  1399.  
  1400.     switch (fmt) {
  1401.     case F_GIF:      strcpy(lowsuf,"gif");  break;
  1402.     case F_PM:       strcpy(lowsuf,"pm");   break;
  1403.     case F_PBMRAW:
  1404.     case F_PBMASCII: if (col == F_FULLCOLOR || col == F_REDUCED) 
  1405.                                                   strcpy(lowsuf,"ppm");
  1406.                      else if (col == F_GREYSCALE) strcpy(lowsuf,"pgm");
  1407.                      else if (col == F_BWDITHER)  strcpy(lowsuf,"pbm");
  1408.                      break;
  1409.  
  1410.     case F_XBM:      strcpy(lowsuf,"xbm");  break;
  1411.     case F_SUNRAS:   strcpy(lowsuf,"ras");  break;
  1412.     case F_BMP:      strcpy(lowsuf,"bmp");  break;
  1413.     case F_PS:       strcpy(lowsuf,"ps");   break;
  1414.     case F_IRIS:     strcpy(lowsuf,"rgb");  break;
  1415.     case F_TARGA:    strcpy(lowsuf,"tga");  break;
  1416.     case F_XPM:      strcpy(lowsuf,"xpm");  break;
  1417.     case F_FITS:     strcpy(lowsuf,"fts");  break;
  1418.  
  1419. #ifdef HAVE_JPEG
  1420.     case F_JPEG:     strcpy(lowsuf,"jpg");  break;
  1421. #endif
  1422.  
  1423. #ifdef HAVE_TIFF
  1424.     case F_TIFF:     strcpy(lowsuf,"tif");  break;
  1425. #endif
  1426.     }
  1427.  
  1428.     if (allcaps) {  /* upper-caseify lowsuf */
  1429.       for (sp=lowsuf; *sp; sp++) 
  1430.     *sp = (islower(*sp)) ? toupper(*sp) : *sp;
  1431.     }
  1432.     
  1433.     /* one other case:  if the original suffix started with a single
  1434.        capital letter, make the new suffix start with a single cap */
  1435.     if (isupper(suffix[0])) lowsuf[0] = toupper(lowsuf[0]);
  1436.  
  1437.     strcpy(suffix, lowsuf);   /* tack onto filename */
  1438.     SetDirFName(filename);
  1439.   }
  1440.  
  1441. }
  1442.   
  1443.  
  1444. /***************************************************/
  1445. int DirCheckCD()
  1446. {
  1447.   /* checks if the current filename is a directory.  If so,
  1448.      cd's there, resets the filename to 'deffname', and returns '1'
  1449.  
  1450.      otherwise, does nothing and returns '0' */
  1451.  
  1452.   if (FNameCdable()) {
  1453.     setFName(deffname);
  1454.     return 1;
  1455.   }
  1456.  
  1457.   return 0;
  1458. }
  1459.  
  1460.  
  1461. /***************************************************/
  1462. static int FNameCdable()
  1463. {
  1464.   /* returns '1' if filename is a directory, and goes there */
  1465.   
  1466.   char newpath[1024];
  1467.   struct stat st;
  1468.   int retval = 0;
  1469.  
  1470.   newpath[0] = '\0';   /* start out empty */
  1471.  
  1472.   if (ISPIPE(filename[0]) || strlen(filename)==0) return 0;
  1473.  
  1474.   if (filename[0] == '/' || filename[0] == '~') {  /* absolute path */
  1475.     strcpy(newpath, filename);
  1476.   }
  1477.   else {  /* not an absolute pathname */
  1478.     strcpy(newpath,path);
  1479.     strcat(newpath,filename);
  1480.   }
  1481.  
  1482.   if (newpath[0]=='~') {    /* handle globbing */
  1483.     Globify(newpath);
  1484.   }
  1485.  
  1486. #ifdef VMS
  1487.   /* Convert names of form "/device.dir" to "/device/000000.DIR"  */
  1488.   if ( rindex ( newpath, '/' ) == newpath ) {
  1489.     strcpy ( rindex ( newpath, '.' ), "/000000.DIR" );
  1490.   }
  1491. #endif
  1492.  
  1493.   if (stat(newpath, &st)==0) {
  1494.     int isdir;
  1495.  
  1496.     isdir = S_ISDIR(st.st_mode);
  1497.  
  1498.     if (isdir) {
  1499. #ifdef VMS
  1500.       /* remove .DIR from the path so that false 000000 directories work */
  1501.       char *dirext;
  1502.       dirext = rindex ( newpath, '/' );
  1503.       if ( dirext == NULL ) dirext = newpath; else dirext++;
  1504.       dirext = xv_strstr ( dirext, "." );
  1505.       *dirext = '\0';
  1506. #endif
  1507.  
  1508.       if (chdir(newpath)==0) {
  1509.     loadCWD();  /* success! */
  1510.       }
  1511.  
  1512.       else {
  1513.     char str[512];
  1514.  
  1515.     sprintf(str,"Can't chdir to '%s'.\n\n  %s.",filename, ERRSTR(errno));
  1516.     ErrPopUp(str, "\nPity");
  1517.       }
  1518.       retval = 1;
  1519.     }
  1520.   }
  1521.   
  1522.   return retval;
  1523. }
  1524.  
  1525.  
  1526. /**************************************************************************/
  1527. int Globify(fname)
  1528.   char *fname;
  1529. {
  1530.   /* expands ~s in file names.  Returns the name inplace 'name'.
  1531.      returns 0 if okay, 1 if error occurred (user name not found) */
  1532.  
  1533.   struct passwd *entry;
  1534.   char *cp, *sp, *up, uname[64], tmp[MAXFNLEN+100];
  1535.  
  1536. #ifdef VMS
  1537.   return 1;
  1538. #else
  1539.   if (*fname != '~') return 0; /* doesn't start with a tilde, don't expand */
  1540.  
  1541.   /* look for the first '/' after the tilde */
  1542.   sp = (char *) index(fname,'/');
  1543.   if (sp == 0) {               /* no '/' after the tilde */
  1544.     sp = fname+strlen(fname);  /* sp = end of string */
  1545.   }
  1546.  
  1547.   /* uname equals the string between the ~ and the / */
  1548.   for (cp=fname+1,up=uname; cp<sp; *up++ = *cp++);
  1549.   *up='\0';
  1550.  
  1551.   if (*uname=='\0') { /* no name.  substitute ~ with $HOME */
  1552.     char *homedir;
  1553.     homedir = (char *) getenv("HOME");  
  1554.     if (homedir == NULL) homedir = ".";
  1555.     strcpy(tmp,homedir);
  1556.     strcat(tmp,sp);
  1557.   }
  1558.  
  1559.   else {              /* get password entry for uname */
  1560.     entry = getpwnam(uname);
  1561.     if (entry==0) return 1;       /* name not found */
  1562.     strcpy(tmp,entry->pw_dir);
  1563.     strcat(tmp,sp);
  1564.     endpwent();
  1565.   }
  1566.  
  1567.   strcpy(fname,tmp);  /* return expanded file name */
  1568.   return 0;
  1569. #endif  /* !VMS */
  1570. }
  1571.  
  1572.  
  1573.  
  1574.  
  1575. /***************************************/
  1576. FILE *OpenOutFile(filename)
  1577.      char *filename;
  1578. {
  1579.   /* opens file for output.  does various error handling bits.  Returns
  1580.      an open file pointer if success, NULL if failure */
  1581.  
  1582.   FILE *fp;
  1583.   struct stat st;
  1584.  
  1585.   if (!filename || filename[0] == '\0') return NULL;
  1586.   strcpy(outFName, filename);
  1587.   dopipe = 0;
  1588.  
  1589.   /* make sure we're in the correct directory */
  1590.   if (strlen(path)) chdir(path);
  1591.  
  1592.   if (ISPIPE(filename[0])) {   /* do piping */
  1593.     /* make up some bogus temp file to put this in */
  1594. #ifndef VMS
  1595.     sprintf(outFName, "%s/xvXXXXXX", tmpdir);
  1596. #else
  1597.     strcpy(outFName, "[]xvXXXXXX.lis");
  1598. #endif
  1599.     mktemp(outFName);
  1600.     dopipe = 1;
  1601.   }
  1602.  
  1603.  
  1604. #ifndef VMS
  1605.   /*
  1606.   ** On OpenVMS, unless a user has set a version limit within a directory
  1607.   ** writing a new file will not overwrite the existing file.  Don't put out
  1608.   ** the warning.
  1609.   ** If a version limit is set, well that person should be shot :-)
  1610.   ** BJM
  1611.   */
  1612.  
  1613.   /* see if file exists (ie, we're overwriting) */
  1614.   if (stat(outFName, &st)==0) {   /* stat succeeded, file must exist */
  1615.     static char *foo[] = { "\nOk", "\033Cancel" };
  1616.     char str[512];
  1617.  
  1618.     sprintf(str,"Overwrite existing file '%s'?", outFName);
  1619.     if (PopUp(str, foo, 2)) return NULL;
  1620.   }
  1621. #endif    
  1622.  
  1623.   /* Open file */
  1624.   fp = fopen(outFName, "w");
  1625.   if (!fp) {
  1626.     char  str[512];
  1627.     sprintf(str,"Can't write file '%s'\n\n  %s.",outFName, ERRSTR(errno));
  1628.     ErrPopUp(str, "\nBummer");
  1629.     return NULL;
  1630.   }
  1631.  
  1632.   return fp;
  1633. }
  1634.   
  1635.  
  1636. /***************************************/
  1637. int CloseOutFile(fp, filename, failed)
  1638.      FILE *fp;
  1639.      char *filename;
  1640.      int   failed;
  1641. {
  1642.   char buf[64];
  1643.  
  1644.   /* close output file, and if piping, deal... Returns '0' if everything OK */
  1645.  
  1646.   if (failed) {    /* failure during format-specific output routine */
  1647.     char  str[512];
  1648.     sprintf(str,"Couldn't write file '%s'.", outFName);
  1649.     ErrPopUp(str, "\nBummer!");
  1650.     unlink(outFName);   /* couldn't properly write file:  delete it */
  1651.     return 1;
  1652.   }
  1653.  
  1654.     
  1655.   if (fclose(fp) == EOF) {
  1656.     static char *foo[] = { "\nWeird!" };
  1657.     char  str[512];
  1658.     sprintf(str,"Can't close file '%s'\n\n  %s.",outFName, ERRSTR(errno));
  1659.     ErrPopUp(str, "\nWeird!");
  1660.     return 1;
  1661.   }
  1662.  
  1663.   buf[0]= '\0';  /* empty buffer */
  1664.   { /* compute size of written file */
  1665.     FILE *fp;
  1666.     long  filesize;
  1667.     fp = fopen(outFName,"r");
  1668.     if (fp) {
  1669.       fseek(fp, 0L, 2);
  1670.       filesize = ftell(fp);
  1671.       fclose(fp);
  1672.  
  1673.       sprintf(buf,"  (%ld bytes)", filesize);
  1674.     }
  1675.   }
  1676.  
  1677.   SetISTR(ISTR_INFO,"Successfully wrote '%s'%s", outFName, buf);
  1678.   
  1679.   if (dopipe) {
  1680.     char cmd[512], str[1024];
  1681.     int  i;
  1682.  
  1683. #ifndef VMS
  1684.     sprintf(cmd, "cat %s |%s", outFName, filename+1);  /* lose pipe char */
  1685. #else
  1686.     sprintf(cmd, "Print /Queue = XV_Queue /Delete %s", outFName);
  1687. #endif
  1688.     sprintf(str,"Doing command: '%s'", cmd);
  1689.     OpenAlert(str);
  1690.     i = system(cmd);
  1691.  
  1692. #ifdef VMS
  1693.     i = !i;
  1694. #endif
  1695.  
  1696.     if (i) {
  1697.       sprintf(str, "Unable to complete command:\n  %s", cmd);
  1698.       CloseAlert();
  1699.       ErrPopUp(str, "\nThat Sucks!");
  1700.       unlink(outFName);
  1701.       return 1;
  1702.     }
  1703.     else {
  1704.       CloseAlert();
  1705.       SetISTR(ISTR_INFO,"Successfully completed command.");
  1706. #ifndef VMS
  1707.       unlink(outFName);
  1708. #endif
  1709.     }
  1710.   }
  1711.  
  1712.   /* save old info */
  1713.   haveoldinfo = 1;
  1714.   oldformat = MBWhich(&fmtMB);
  1715.   oldcolors = MBWhich(&colMB);
  1716.   strcpy(oldfname, filename);
  1717.  
  1718.   return 0;
  1719. }
  1720.  
  1721.       
  1722.  
  1723.  
  1724. static byte rBW[2], gBW[2], bBW[2];
  1725. static byte gray[256];
  1726.  
  1727. /***************************************/
  1728. static byte *handleBWandReduced(pic, ptype, pw, ph, color, nc, rpp, gpp, bpp)
  1729.      byte  *pic;
  1730.      int    ptype, pw, ph, color, *nc;
  1731.      byte **rpp, **gpp, **bpp;
  1732. {
  1733.   /* given 'color mode' (F_FULLCOLOR, etc.), we may have to dither
  1734.      and/or use different colormaps.  Returns 'nc', rpp, gpp, bpp (the
  1735.      colormap to use).  Also, if the function returns non-NULL, it generated
  1736.      a new (dithered) image to use. */
  1737.  
  1738.   int   i;
  1739.   byte *bwpic;
  1740.  
  1741.   bwpic = (byte *) NULL;
  1742.   *nc = numcols;  *rpp = rMap;  *gpp = gMap;  *bpp = bMap;
  1743.  
  1744.   /* quick check:  if we're saving a 24-bit image, then none of this 
  1745.      complicated 'reduced'/dithered/smoothed business comes into play.
  1746.      'reduced' is disabled, for semi-obvious reasons, in 24-bit mode,
  1747.      as is 'dithered'.  If 'smoothed', and we're saving at current
  1748.      size, no problem.  Otherwise, if we're saving at original size,
  1749.      smoothing should have no effect, so there's no reason to smooth
  1750.      the original pic...
  1751.  
  1752.      In any event:  in 24-bit mode, all we have to do here is determine
  1753.      if we're saving B/W DITHERED, and deal accordingly */
  1754.  
  1755.  
  1756.   if (ptype == PIC24) {  
  1757.     if (color != F_BWDITHER) return NULL;
  1758.     else {                                /* generate a bw-dithered version */
  1759.       byte *p24, *thepic;
  1760.       
  1761.       thepic = pic;
  1762.       p24 = GammifyPic24(thepic, pw, ph);
  1763.       if (p24) thepic = p24;
  1764.       
  1765.       /* generate a FSDithered 1-byte per pixel image */
  1766.       bwpic = FSDither(thepic, PIC24, pw, ph, NULL,NULL,NULL, 0, 1);
  1767.       if (!bwpic) FatalError("unable to malloc dithered picture (DoSave)");
  1768.       
  1769.       if (p24) free(p24);  /* won't need it any more */
  1770.       
  1771.       /* build a BW colormap */
  1772.       rBW[0] = gBW[0] = bBW[0] = 0;
  1773.       rBW[1] = gBW[1] = bBW[1] = 255;
  1774.       
  1775.       *rpp = rBW;  *gpp = gBW;  *bpp = bBW;
  1776.       *nc = 2;
  1777.       
  1778.       return bwpic;
  1779.     }
  1780.   }
  1781.     
  1782.  
  1783.  
  1784.   /* ptype == PIC8 ... */
  1785.  
  1786.   *nc = numcols;  *rpp = rMap;  *gpp = gMap;  *bpp = bMap;
  1787.   if (color==F_REDUCED) { *rpp = rdisp;  *gpp = gdisp;  *bpp = bdisp; }
  1788.  
  1789.   /* if DITHER or SMOOTH, and color==FULLCOLOR or GREY, 
  1790.      make color=REDUCED, so it will be written with the correct colortable  */
  1791.  
  1792.   if ((epicMode == EM_DITH || epicMode == EM_SMOOTH) && color != F_REDUCED) {
  1793.     if (color == F_FULLCOLOR) {
  1794.       *rpp = rdisp;  *gpp = gdisp;  *bpp = bdisp;
  1795.     }
  1796.     else if (color == F_GREYSCALE) {
  1797.       for (i=0; i<256; i++) gray[i] = MONO(rdisp[i], gdisp[i], bdisp[i]);
  1798.       *rpp = gray;  *gpp = gray;  *bpp = gray;
  1799.     }
  1800.   }
  1801.   
  1802.  
  1803.  
  1804.  
  1805.   if (color==F_BWDITHER || (ncols==0 && color==F_REDUCED) ) {
  1806.     /* if we're saving as 'dithered', or we're viewing as dithered
  1807.        and we're saving 'reduced' then generate a dithered image */
  1808.  
  1809.     if (numcols==2) return NULL;     /* already dithered */
  1810.  
  1811.     /* generate a dithered 1-byte per pixel image */
  1812.     bwpic = FSDither(pic, PIC8, pw, ph, rMap,gMap,bMap, 0, 1);
  1813.     if (!bwpic) FatalError("unable to malloc dithered picture (DoSave)");
  1814.  
  1815.     /* put a BW colormap */
  1816.     rBW[0] = (blkRGB>>16)&0xff;     rBW[1] = (whtRGB>>16)&0xff;
  1817.     gBW[0] = (blkRGB>>8)&0xff;      gBW[1] = (whtRGB>>8)&0xff;
  1818.     bBW[0] = blkRGB&0xff;           bBW[1] =  whtRGB&0xff;
  1819.     *rpp = rBW;  *gpp = gBW;  *bpp = bBW;
  1820.     *nc = 2;
  1821.   }
  1822.  
  1823.   return bwpic;
  1824. }
  1825.  
  1826.  
  1827. /***************************************/
  1828. static byte *handleNormSel(pptype, pwide, phigh, pfree)
  1829.      int *pptype, *pwide, *phigh, *pfree;
  1830. {
  1831.   /* called to return a pointer to a 'pic', its type, its width & height,
  1832.    * and whether or not it should be freed when we're done with it.  The 'pic'
  1833.    * returned is the desired portion of 'cpic' or 'epic' if there is a
  1834.    * selection, and the saveselCB is enabled, or alternately, it's the
  1835.    * whole cpic or epic.  
  1836.    * 
  1837.    * if selection does not intersect cpic/epic, returns cpic/epic
  1838.    * NEVER RETURNS NULL
  1839.    */
  1840.  
  1841.   byte *thepic;
  1842.   int   pw, ph, slx, sly, slw, slh;
  1843.  
  1844.   *pfree = 0;  *pptype = picType;
  1845.  
  1846.   if (savenormCB.val) { thepic = cpic;  pw = cWIDE;  ph = cHIGH; }
  1847.                  else { thepic = epic;  pw = eWIDE;  ph = eHIGH; }
  1848.  
  1849.   *pwide = pw;  *phigh = ph;  
  1850.  
  1851.  
  1852.   if (saveselCB.active && saveselCB.val && HaveSelection()) {
  1853.     GetSelRCoords(&slx, &sly, &slw, &slh);    /* in 'pic' coords */
  1854.  
  1855.     if (savenormCB.val) {
  1856.       CropRect2Rect(&slx, &sly, &slw, &slh, 0,0,pWIDE,pHIGH);
  1857.  
  1858.       if (slw<1 || slh<1) { slx = sly = 0;  slw=pWIDE;  slh=pHIGH; }
  1859.  
  1860.       if (slx==0 && sly==0 && slw==pWIDE && slh==pHIGH) thepic = pic;
  1861.       else {
  1862.     thepic = XVGetSubImage(pic, *pptype, pWIDE,pHIGH, slx, sly, slw, slh);
  1863.     *pfree = 1;
  1864.       }
  1865.     }
  1866.     else {                                    /* convert sel -> epic coords */
  1867.       int x1,x2,y1,y2;
  1868.       x1 = slx;        y1 = sly;
  1869.       x2 = slx + slw;  y2 = sly + slh;
  1870.       CoordP2E(x1,y1, &x1, &y1);
  1871.       CoordP2E(x2,y2, &x2, &y2);
  1872.       slx = x1;  sly = y1;  slw = x2-x1;  slh = y2-y1;
  1873.       CropRect2Rect(&slx, &sly, &slw, &slh, 0,0,pw,ph);
  1874.       
  1875.       if (slw<1 || slh<1) { slx = sly = 0;  slw=pw;  slh=ph; }
  1876.  
  1877.       if (slx!=0 || sly!=0 || slw!=pw || slh!=ph) {
  1878.     thepic = XVGetSubImage(thepic, *pptype, pw, ph, slx, sly, slw, slh);
  1879.     *pfree = 1;
  1880.       }
  1881.     }
  1882.  
  1883.     *pwide = slw;  *phigh = slh;  
  1884.   }
  1885.  
  1886.   return thepic;
  1887. }
  1888.  
  1889.  
  1890. /***************************************/
  1891. byte *GenSavePic(ptypeP, wP, hP, freeP, ncP, rmapP, gmapP, bmapP)
  1892.      int  *ptypeP, *wP, *hP, *freeP, *ncP;
  1893.      byte **rmapP, **gmapP, **bmapP;
  1894. {
  1895.   /* handles the whole ugly mess of the various save options.
  1896.    * returns an image, of type 'ptypeP', size 'wP,hP'.  
  1897.    * if (*ptypeP == PIC8), also returns numcols 'ncP', and the r,g,b map
  1898.    * to use rmapP, gmapP, bmapP.
  1899.    *
  1900.    * if freeP is set, image can safely be freed after it is saved 
  1901.    */
  1902.  
  1903.   byte *pic1, *pic2;
  1904.   int   ptype, w, h, pfree;
  1905.  
  1906.   pic1 = handleNormSel(&ptype, &w, &h, &pfree);
  1907.  
  1908.   pic2 = handleBWandReduced(pic1, ptype, w,h, MBWhich(&colMB), 
  1909.                   ncP, rmapP, gmapP, bmapP);
  1910.   if (pic2) { 
  1911.     if (pfree) free(pic1);
  1912.     pic1  = pic2;
  1913.     pfree = 1;
  1914.     ptype = PIC8;
  1915.   }
  1916.  
  1917.  
  1918.   if (ptype == PIC24) {       
  1919.     pic2 = GammifyPic24(pic1, w, h);
  1920.     if (pic2) {
  1921.       if (pfree) free(pic1);
  1922.       pic1  = pic2;
  1923.       pfree = 1;
  1924.     }
  1925.   }
  1926.  
  1927.   *ptypeP = ptype;  *wP = w;  *hP = h;  *freeP = pfree;
  1928.  
  1929.   return pic1;
  1930. }
  1931.  
  1932.      
  1933. /***************************************/
  1934. void GetSaveSize(wP, hP)
  1935.      int *wP, *hP;
  1936. {
  1937.   /* returns the size (in pixels) of the save image.  Takes 'normal size'
  1938.      and 'save selection' checkboxes into account */
  1939.  
  1940.   int slx,sly,slw,slh;
  1941.  
  1942.   if (savenormCB.val) { slw = cWIDE;  slh = cHIGH; }
  1943.                  else { slw = eWIDE;  slh = eHIGH; }
  1944.  
  1945.   if (saveselCB.active && saveselCB.val && HaveSelection()) {
  1946.     GetSelRCoords(&slx, &sly, &slw, &slh);                 /* pic coord     */
  1947.     if (savenormCB.val) {
  1948.       CropRect2Rect(&slx, &sly, &slw, &slh, 0,0,pWIDE,pHIGH);
  1949.       if (slw<1 || slh<1) { slx = sly = 0;  slw=pWIDE;  slh=pHIGH; }
  1950.     }
  1951.     else {                                                 /* -> epic coord */
  1952.       int x1,x2,y1,y2;
  1953.       x1 = slx;        y1 = sly;
  1954.       x2 = slx + slw;  y2 = sly + slh;
  1955.       CoordP2E(x1,y1, &x1, &y1);
  1956.       CoordP2E(x2,y2, &x2, &y2);
  1957.       slx = x1;  sly = y1;  slw = x2-x1;  slh = y2-y1;
  1958.       CropRect2Rect(&slx, &sly, &slw, &slh, 0,0,eWIDE,eHIGH);
  1959.       
  1960.       if (slw<1 || slh<1) { slx = sly = 0;  slw=eWIDE;  slh=eHIGH; }
  1961.     }
  1962.   }
  1963.  
  1964.   *wP = slw;  *hP = slh;
  1965. }
  1966.  
  1967.  
  1968.  
  1969.  
  1970. /*************************************************************/
  1971. /*       POLLING ROUTINES                                    */
  1972. /*************************************************************/
  1973.  
  1974.  
  1975. static struct stat origStat, lastStat;
  1976. static int haveStat = 0, haveLastStat = 0;
  1977. static time_t lastchgtime;
  1978.  
  1979. /****************************/
  1980. void InitPoll()
  1981. {
  1982.   /* called whenever a file is initially loaded.  stat's the file and puts
  1983.      the results in origStat */
  1984.  
  1985.   haveStat = haveLastStat = 0;
  1986.   lastchgtime = (time_t) 0;
  1987.  
  1988.   /* only do stat() if curname is a valid index, and it's not '<stdin>' */
  1989.   if (curname>=0 && curname<numnames &&
  1990.       (strcmp(namelist[curname], STDINSTR)!=0)) {
  1991.  
  1992.     if (stat(namelist[curname], &origStat)==0) {
  1993.       haveStat = 1;
  1994.       if (DEBUG) fprintf(stderr," origStat.size=%ld,  origStat.mtime=%d\n", 
  1995.              origStat.st_size, origStat.st_mtime);
  1996.     }
  1997.   }
  1998. }
  1999.  
  2000.  
  2001. /****************************/
  2002. int CheckPoll(del)
  2003.      int del;
  2004. {
  2005.   /* returns '1' if the file has been modified, and either 
  2006.       A) the file has stabilized (st = lastStat), or
  2007.       B) 'del' seconds have gone by since the file last changed size
  2008.    */
  2009.  
  2010.   struct stat st;
  2011.   time_t nowT;
  2012.  
  2013.   time(&nowT);
  2014.  
  2015.   if (haveStat && curname>=0 && curname<numnames &&
  2016.       (strcmp(namelist[curname], STDINSTR)!=0)) {
  2017.  
  2018.     if (stat(namelist[curname], &st)==0) {
  2019.       if (DEBUG) fprintf(stderr," st.size=%ld,  st.mtime=%d\n", 
  2020.              st.st_size, st.st_mtime);
  2021.  
  2022.       if ((st.st_size  == origStat.st_size) &&
  2023.       (st.st_mtime == origStat.st_mtime)) return 0;  /* no change */
  2024.  
  2025.       /* if it's changed since last looked ... */
  2026.       if (!haveLastStat || 
  2027.       st.st_size  != lastStat.st_size  ||
  2028.       st.st_mtime != lastStat.st_mtime)   {
  2029.     xvbcopy((char *) &st, (char *) &lastStat, sizeof(struct stat));
  2030.     haveLastStat = 1;
  2031.     lastchgtime = nowT;
  2032.     return 0;
  2033.       }
  2034.  
  2035.       /* if it hasn't changed in a while... */
  2036.       if (haveLastStat && st.st_size > 0 && (nowT - lastchgtime) > del) {
  2037.     xvbcopy((char *) &st, (char *) &origStat, sizeof(struct stat));
  2038.     haveLastStat = 0;  lastchgtime = 0;
  2039.     return 1;
  2040.       }
  2041.     }
  2042.   }
  2043.   
  2044.   return 0;
  2045. }
  2046.  
  2047.  
  2048. /***************************************************************/
  2049. void DIRDeletedFile(name)
  2050.      char *name;
  2051. {
  2052.   /* called when file 'name' has been deleted.  If any of the browsers
  2053.      were showing the directory that the file was in, does a rescan() */
  2054.   
  2055.   int  i;
  2056.   char buf[MAXPATHLEN + 2], *tmp;
  2057.  
  2058.   strcpy(buf, name);
  2059.   tmp = BaseName(buf);
  2060.   *tmp = '\0';     /* truncate after last '/' */
  2061.   
  2062.   if (strcmp(path, buf)==0) LoadCurrentDirectory();
  2063. }
  2064.  
  2065.  
  2066. /***************************************************************/
  2067. void DIRCreatedFile(name)
  2068.      char *name;
  2069. {
  2070.   DIRDeletedFile(name);
  2071. }
  2072.  
  2073.  
  2074.