/* * $Id: os2 7281 2004-04-05 01:06:15Z amk $ * * Program: Operating system dependent routines - OS/2 Text mode * * * Michael Seibel * Networks and Distributed Computing * Computing and Communications * University of Washington * Administration Builiding, AG-44 * Seattle, Washington, 98195, USA * Internet: mikes@cac.washington.edu * * Please address all bugs and comments to "pine-bugs@cac.washington.edu" * * * Pine and Pico are registered trademarks of the University of Washington. * No commercial use of these trademarks may be made without prior written * permission of the University of Washington. * * Pine, Pico, and Pilot software and its included text are Copyright * 1989-2001 by the University of Washington. * * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this distribution. * * * Notes: * - mouse support added (mss, 921215) * * Portions of this code derived from MicroEMACS 3.10: * * OS2.C: Operating specific I/O and Spawning functions * under the OS/2 operating system * for MicroEMACS 3.10 * (C)opyright 1988 by Daniel M. Lawrence * */ #include /* * Internal functions... */ int checkmouse PROTO((unsigned *, int, int, int)); void invert_label PROTO((int, MENUITEM *)); int enhanced_keybrd PROTO((void)); int dont_interrupt PROTO((void)); int interrupt_ok PROTO((void)); int kbseq PROTO((int *)); int specialkey PROTO((unsigned int)); void do_alarm_signal PROTO((void)); void do_hup_signal PROTO((int sig)); char *pfnexpand PROTO((char *, size_t)); int ssleep PROTO((long)); void mouseon PROTO((void)); void mouseoff PROTO((void)); void turnmouseoff PROTO((void)); /* * Useful global def's */ int timeo = 0; int (*pcollator)(); static int oldbut; /* Previous state of mouse buttons */ static int enhncd; /* Enhanced keyboard */ static KBDINFO initialKbdInfo; #ifdef MOUSE /* * Useful definitions... */ static int mexist = 0; /* is the mouse driver installed? */ static int nbuttons; /* number of buttons on the mouse */ static unsigned mnoop; static unsigned char okinfname[32] = { 0, 0, /* ^@ - ^G, ^H - ^O */ 0, 0, /* ^P - ^W, ^X - ^_ */ 0, 0x17, /* SP - ' , ( - / */ 0xff, 0xe4, /* 0 - 7 , 8 - ? */ 0x7f, 0xff, /* @ - G , H - O */ 0xff, 0xe9, /* P - W , X - _ */ 0x7f, 0xff, /* ` - g , h - o */ 0xff, 0xf6, /* p - w , x - DEL */ 0, 0, /* > DEL */ 0, 0, /* > DEL */ 0, 0, /* > DEL */ 0, 0, /* > DEL */ 0, 0 /* > DEL */ }; #endif static void set_kbd(int state) { KBDINFO kbdInfo = initialKbdInfo; if (state) { kbdInfo.fsMask &= ~(0x0001|0x0008|0x0100); /* echo,cooked */ kbdInfo.fsMask |= (0x0002|0x0004|0x0100); /* noecho,raw,shift-rpt on */ } KbdSetStatus(&kbdInfo, 0); } /* * DISable ctrl-break interruption */ dont_interrupt() { signal (SIGINT, SIG_IGN); signal (SIGBREAK, SIG_IGN); set_kbd(1); } /* * re-enable ctrl-break interruption */ interrupt_ok() { set_kbd(0); signal (SIGINT, SIG_DFL); signal (SIGBREAK, SIG_DFL); } /* * return true if an enhanced keyboard is present */ enhanced_keybrd() { return(1); } /* * This function is called once to set up the terminal device streams. */ ttopen() { initialKbdInfo.cb = sizeof(initialKbdInfo); KbdGetStatus(&initialKbdInfo, 0); dont_interrupt(); /* don't allow interrupt */ enhncd = enhanced_keybrd(); /* check for extra keys */ #if MOUSE init_mouse(); #else /* !MOUSE */ mexist = 0; #endif /* MOUSE */ return(1); } #ifdef MOUSE HMOU Mouse_Handle =(HMOU)-1; #ifdef FASTVIO int mouse_state =0; #endif /* * init_mouse - check for and initialize mouse driver... */ init_mouse() { if (Mouse_Handle==(HMOU)-1) { int rc = MouOpen(NULL, &Mouse_Handle); mexist=0; if (rc) return(FALSE); mexist=1; nbuttons=2; MouFlushQue(Mouse_Handle); turnmouseoff(); } return(TRUE); } static int mouchk() { if (mexist) return (Mouse_Handle==(HMOU)-1) ? init_mouse() : TRUE; return FALSE; } deinit_mouse() { turnmouseoff(); } /* * mouseon - call made available for programs calling pico to turn ON the * mouse cursor. */ void mouseon() { /* Show Cursor */ if (mouchk() && !mouse_state) { #ifdef FASTVIO mouse_state=TRUE; vidUpdate(); #else MouDrawPtr(Mouse_Handle); #endif } } void turnmouseoff() { static NOPTRRECT r = { 0, 0, (USHORT)-1, (USHORT)-1 }; if (r.cRow == (USHORT)-1) { VIOMODEINFO mi; mi.cb = sizeof mi; VioGetMode(&mi, 0); r.cRow = mi.row-1; r.cCol = mi.col-1; } /* Hide Cursor */ MouRemovePtr(&r, Mouse_Handle); mouse_state=FALSE; } /* * mouseon - call made available for programs calling pico to turn OFF the * mouse cursor. */ void mouseoff() { #ifdef FASTVIO /* This is ignored if in FASTVIO mode */ #else if (mouchk()) { turnmouseoff(); } #endif } #endif /* * This function gets called just before we go back home to the command * interpreter. */ ttclose() { if(!Pmaster) interrupt_ok(); #ifdef MOUSE deinit_mouse(); #endif return(1); } /* * Flush terminal buffer. Does real work where the terminal output is buffered * up. A no-operation on systems where byte at a time terminal I/O is done. */ ttflush() { return(1); } /* * specialkey - return special key definition */ specialkey(kc) unsigned kc; { switch(kc){ case 0x3b00 : return(F1); case 0x3c00 : return(F2); case 0x3d00 : return(F3); case 0x3e00 : return(F4); case 0x3f00 : return(F5); case 0x4000 : return(F6); case 0x4100 : return(F7); case 0x4200 : return(F8); case 0x4300 : return(F9); case 0x4400 : return(F10); case 0x8500 : return(F11); case 0x8600 : return(F12); case 0x4800 : return(KEY_UP); case 0x5000 : return(KEY_DOWN); case 0x4b00 : return(KEY_LEFT); case 0x4d00 : return(KEY_RIGHT); case 0x4700 : return(KEY_HOME); case 0x4f00 : return(KEY_END); case 0x4900 : return(KEY_PGUP); case 0x5100 : return(KEY_PGDN); case 0x5300 : return(KEY_DEL); case 0x48e0 : return(KEY_UP); /* grey key version */ case 0x50e0 : return(KEY_DOWN); /* grey key version */ case 0x4be0 : return(KEY_LEFT); /* grey key version */ case 0x4de0 : return(KEY_RIGHT); /* grey key version */ case 0x47e0 : return(KEY_HOME); /* grey key version */ case 0x4fe0 : return(KEY_END); /* grey key version */ case 0x49e0 : return(KEY_PGUP); /* grey key version */ case 0x51e0 : return(KEY_PGDN); /* grey key version */ case 0x53e0 : return(KEY_DEL); /* grey key version */ default : return(NODATA); } } /* * Read a character from the terminal, performing no editing and doing no echo * at all. Also mouse events are forced into the input stream here. */ ttgetc(return_on_intr, recorder, bail_handler) int return_on_intr; int (*recorder)(); int (*bail_handler)(); { int key = kbd_getkey(); return key ? key : NODATA; } /* * ctrlkey - used to check if the key hit was a control key. */ ctrlkey() { return !!(kbd_shift() & KBDSTF_CONTROL); } /* * win_multiplex - give OS/2 a shot at the CPU */ win_multiplex() { DosSleep(32); } /* * Read in a key. * Do the standard keyboard preprocessing. Convert the keys to the internal * character set. Resolves escape sequences and returns no-op if global * timeout value exceeded. */ GetKey() { unsigned ch = 0, lch, intrupt = 0; long timein; vidUpdate(); if(mexist || timeo) { timein = time(0L); #ifdef MOUSE mouseon(); /* Show Cursor */ #endif while(!kbd_ready()) { #if MOUSE if(timeo && time(0L) >= timein+timeo){ mouseoff(); /* Hide Cursor */ return(NODATA); } if(checkmouse(&ch,0,0,0)){ /* something happen ?? */ mouseoff(); /* Hide Cursor */ curwp->w_flag |= WFHARD; return(ch); } #else if(time(0L) >= timein+timeo) { mouseoff(); /* Hide Cursor */ return(NODATA); } #endif /* MOUSE */ /* * Surrender the CPU... */ win_multiplex(); } #ifdef MOUSE mouseoff(); /* Hide Cursor */ #endif /* MOUSE */ } ch = (*term.t_getchar)(0, NULL, NULL); lch = (ch&0xff); if(lch & 0x80 && Pmaster && Pmaster->hibit_entered) *Pmaster->hibit_entered = 1; return((lch && (lch != 0xe0 || !(ch & 0xff00))) ? (lch < ' ') ? (CTRL|(lch + '@')) : (lch == ' ' && ctrlkey()) ? (CTRL|'@') : lch : specialkey(ch)); } #if MOUSE /* * checkmouse - look for mouse events in key menu and return * appropriate value. */ int checkmouse(ch, down, xxx, yyy) unsigned *ch; int down, xxx, yyy; { MOUQUEINFO qi; if(!mouchk()) return(FALSE); if (MouGetNumQueEl(&qi, Mouse_Handle)==0 && qi.cEvents) { MOUEVENTINFO m; USHORT w = MOU_NOWAIT; /* check to see if any mouse buttons are different */ if (MouReadEventQue(&m, &w, Mouse_Handle)==0) { int k; int rv = 0; int button = M_BUTTON_LEFT; int newbut = ((m.fs & 4)?1:0) | ((m.fs & 16)?2:0) | ((m.fs & 64)?4:0); /* only notice button changes */ if (oldbut == newbut) return(FALSE); for (k=1; k != (1 << nbuttons); k <<= 1) { /* For each button on the mouse */ if ((oldbut&k) != (newbut&k)) { if(k == 1){ static int oindex; int i = 0; MENUITEM *mp; if(newbut&k) /* button down */ oindex = -1; for(mp = mfunc; mp; mp = mp->next) if(mp->action && M_ACTIVE(m.row, m.col, mp)) break; if(mp){ unsigned long r; r = (*mp->action)((newbut&k) ? M_EVENT_DOWN : M_EVENT_UP, m.row, m.col, button, 0); if(r & 0xffff){ *ch = (unsigned)((r>>16)&0xffff); rv = TRUE; } } else{ while(1){ /* see if we understand event */ if(i >= 12){ i = -1; break; } if(M_ACTIVE(m.row, m.col, &menuitems[i])) break; i++; } if(newbut&k){ /* button down */ oindex = i; /* remember where */ if(i != -1) { /* invert label */ invert_label(1, &menuitems[i]); } } else{ /* button up */ if(oindex != -1){ if(i == oindex){ *ch = menuitems[i].val; rv = 1; } } } } if(!(newbut&k) && oindex != -1) { invert_label(0, &menuitems[oindex]); /* restore label */ } } oldbut = newbut; return(rv); } ++button; } } } return(FALSE); } /* * invert_label - highlight the label of the given menu item. */ void invert_label(state, m) int state; MENUITEM *m; { USHORT r, c; VIOCURSORINFO oldInfo, newInfo; int i, j, p; char *lp; int old_state = getrevstate(); if(m->val == mnoop) return; VioGetCurPos(&r, &c, 0); VioGetCurType(&oldInfo, 0); newInfo = oldInfo; newInfo.attr = (USHORT)-1; VioSetCurType(&newInfo, 0); /* Hide Cursor */ (*term.t_move)(m->tl.r, m->tl.c); (*term.t_rev)(state); for(i = m->tl.r; i <= m->br.r; i++) for(j = m->tl.c; j <= m->br.c; j++) if(i == m->lbl.r && j == m->lbl.c){ /* show label?? */ lp = m->label; while(*lp && j++ < m->br.c) (*term.t_putchar)(*lp++); continue; } else (*term.t_putchar)(' '); (*term.t_rev)(old_state); VioSetCurPos(r, c, 0); /* restore old position */ VioSetCurType(&oldInfo, 0); /* Show Cursor */ vidUpdate(); } #endif /* MOUSE */ /* * alt_editor - fork off an alternate editor for mail message composition */ #define MAXARGS 10 alt_editor(f, n) int f, n; { char *fn; /* tmp holder for file name */ char *cp; int child, rc, i, done = 0; long l; int stat; FILE *p; char *args[MAXARGS]; /* ptrs into edit command */ char eb[NLINE]; if(Pmaster == NULL) return(-1); if(gmode&MDSCUR){ emlwrite("Alternate editor not available in restricted mode", NULL); return(-1); } if(Pmaster->alt_ed == NULL){ if (!(gmode&MDADVN)) { emlwrite("\007Unknown Command",NULL); return(-1); } if((cp=getenv("VISUAL"))!=0 || (cp=getenv("EDITOR"))!=0) strcpy(eb, (char *)getenv("EDITOR")); else *eb = '\0'; while(!done) { rc = mlreplyd("Which alternate editor ? ",eb,NLINE,QDEFLT,NULL); switch(rc) { case ABORT: return(-1); case HELPCH: emlwrite("no alternate editor help yet", NULL); /* take sleep and break out after there's help */ sleep(3); break; case (CTRL|'L'): sgarbf = TRUE; update(); break; case TRUE: case FALSE: /* does editor exist ? */ if(*eb == '\0'){ /* leave silently? */ mlerase(); curwp->w_flag |= WFMODE; return(-1); } done++; break; default: break; } } } else strcpy(eb, Pmaster->alt_ed); if((fn=writetmp(1, NULL)) == NULL){ /* get temp file */ emlwrite("Problem writing temp file for alt editor", NULL); return(-1); } strcat(eb, " "); strcat(eb, fn); cp = eb; for(i=0; *cp != '\0';i++) { /* build args array */ if(i < MAXARGS) { args[i] = NULL; /* in case we break out */ } else{ emlwrite("Too many args for command!", NULL); return(-1); } while(isspace((unsigned char)(*cp))) if(*cp != '\0') cp++; else break; args[i] = cp; while(!isspace((unsigned char)(*cp))) if(*cp != '\0') cp++; else break; if(*cp != '\0') *cp++ = '\0'; } args[i] = NULL; (*Pmaster->tty_fix)(0); emlwrite("Invoking alternate editor...", NULL); { void (*ohup)() = signal(SIGHUP, SIG_IGN); /* ignore signals for now */ void (*oint)() = signal(SIGINT, SIG_IGN); cp=args[0]; rc = spawnvp(P_WAIT, cp, args); signal(SIGHUP, ohup); /* restore signals */ signal(SIGINT, oint); } (*Pmaster->tty_fix)(1); /* * Editor may have set a hibit, we don't know. Assume it did. */ if(!f && Pmaster && Pmaster->hibit_entered) *Pmaster->hibit_entered = 1; dont_interrupt(); if (rc==-1) { /* Can't run it */ emlwrite("error attempting to run alt editor", NULL); } /* * replace edited text with new text */ else{ rc = 0; curbp->b_flag &= ~BFCHG; /* make sure old text gets blasted */ readin(fn, 0, 0); /* read new text overwriting old */ curbp->b_flag |= BFCHG; /* mark dirty for packbuf() */ } unlink(fn); /* blast temp file */ ttopen(); /* reset the signals */ pico_refresh(0, 1); /* redraw */ return(rc); } /* * bktoshell - suspend and wait to be woken up */ int bktoshell() /* suspend MicroEMACS and wait to wake up */ { int i; static char * shell = NULL; if (shell == NULL) { char *p; shell=getenv("SHELL"); if (!shell && !(shell=getenv("COMSPEC"))) shell="CMD.EXE"; if ((p = strdup(shell)) > 0) { for (shell = p; (p = strchr(shell, '/')) != 0; ) *p = '\\'; } } (*term.t_move)(term.t_nrow, 0); (*term.t_eeol)(); exit_text_mode(NULL); interrupt_ok(); if (system(shell) == -1) emlwrite("Error loading %s", shell); else pico_refresh(0, 1); /* redraw */ enter_text_mode(NULL); dont_interrupt(); return(1); } /* * P_open - run the given command in a sub-shell returning a file pointer * from which to read the output * * note: * For OS's other than unix, you will have to rewrite this function. * Hopefully it'll be easy to exec the command into a temporary file, * and return a file pointer to that opened file or something. */ FILE *P_open(c) char *c; { return(popen(c,"r")); } /* * P_close - close the given descriptor * */ void P_close(fp) FILE *fp; { (void)pclose(fp); } /* * A replacement for fflush * relies on #define fflush os2_fflush */ #undef fflush int os2_fflush (FILE *f) { if (f == stdout) { vidUpdate(); } else fflush (f); } /* * ttresize - recompute the screen dimensions if necessary, and then * adjust pico's internal buffers accordingly */ void ttresize () { return; } /* * picosigs - Install any handlers for the signals we're interested * in catching. */ void picosigs() { signal(SIGHUP, do_hup_signal); /* deal with SIGHUP */ signal(SIGTERM, do_hup_signal); /* deal with SIGTERM */ } /* * do_hup_signal - jump back in the stack to where we can handle this */ void do_hup_signal(int sig) { sig=sig; signal(SIGHUP, SIG_IGN); /* ignore further SIGHUP's */ signal(SIGTERM, SIG_IGN); /* ignore further SIGTERM's */ if(Pmaster){ extern jmp_buf finstate; longjmp(finstate, COMP_GOTHUP); } else{ /* * if we've been interrupted and the buffer is changed, * save it... */ if(anycb() == TRUE){ /* time to save */ if(curbp->b_fname[0] == '\0'){ /* name it */ strcpy(curbp->b_fname, "pico.sav"); } else{ strcat(curbp->b_fname, ".sav"); } writeout(curbp->b_fname, TRUE); } vttidy(); exit(1); } } #ifdef MOUSE /* * end_mouse - a no-op on DOS/Windows */ void end_mouse() { } /* * mouseexist - function to let outsiders know if mouse is turned on * or not. */ mouseexist() { return(mexist); } #endif /* MOUSE */ /* * fallowc - returns TRUE if c is allowable in filenames, FALSE otw */ fallowc(c) int c; { return(okinfname[c>>3] & 0x80>>(c&7)); } /* * fexist - returns TRUE if the file exists, FALSE otherwise */ fexist(file, m, l) char *file, *m; off_t *l; { struct stat sbuf; if(l != NULL) *l = (off_t)0; if(stat(file, &sbuf) < 0){ if(ENOENT) /* File not found */ return(FIOFNF); else return(FIOERR); } if(l != NULL) *l = (off_t)sbuf.st_size; if(sbuf.st_mode & S_IFDIR) return(FIODIR); if(m[0] == 'r') /* read access? */ return((S_IREAD & sbuf.st_mode) ? FIOSUC : FIONRD); else if(m[0] == 'w') /* write access? */ return((S_IWRITE & sbuf.st_mode) ? FIOSUC : FIONWT); else if(m[0] == 'x') /* execute access? */ return((S_IEXEC & sbuf.st_mode) ? FIOSUC : FIONEX); return(FIOERR); /* what? */ } /* * isdir - returns true if fn is a readable directory, false otherwise * silent on errors (we'll let someone else notice the problem;)). */ isdir(fn, l, d) char *fn; long *l; time_t *d; { struct stat sbuf; if(l) *l = 0; if(stat(fn, &sbuf) < 0) return(0); if(l) *l = sbuf.st_size; if(d) *d = sbuf.st_mtime; return(sbuf.st_mode & S_IFDIR); } /* * gethomedir - returns the users home directory * Note: home is malloc'd for life of pico */ char *gethomedir(l) int *l; { static char *home = NULL; static short hlen = 0; if(home == NULL){ char *p, buf[NLINE]; if (home=getenv("PINEHOME")) /* Overrides all others */ strcpy(buf, home); else if (home=getenv("HOME")) /* Convenient group placement */ strcpy(buf, home); else if (home=getenv("ETC")) /* IBM TCPIP */ strcpy(buf, home); else sprintf(buf, "%c:\\", _getdrive()); hlen = strlen(buf); if ((home=(char *)malloc(hlen + 1)) == NULL) { emlwrite("Problem allocating space for home dir", NULL); return(0); } strcpy(home, buf); while ((p=strchr(home,'/')) != NULL) /* Normalise, just in case */ *p = '\\'; } if(l) *l = hlen; return(home); } /* * homeless - returns true if given file does not reside in the current * user's home directory tree. */ homeless(f) char *f; { char *home; int len; home = gethomedir(&len); return(strncmp(home, f, len)); } /* * errstr - return system error string corresponding to given errno * Note: strerror() is not provided on all systems, so it's * done here once and for all. */ char *errstr(err) int err; { return((err >= 0 && err < sys_nerr) ? (char*)sys_errlist[err] : NULL); } /* * getfnames - return all file names in the given directory in a single * malloc'd string. n contains the number of names */ char *getfnames(dn, pat, n, e) char *dn, *pat, *e; int *n; { int status; long l; size_t avail, alloced, incr = 1024; char *names, *np, *p; char buf[NLINE]; struct stat sbuf; ULONG count=1; FILEFINDBUF3 findbuf; HDIR hdir=HDIR_CREATE; *n = 0; while ((p = strchr(dn, '/')) != NULL) *p = '\\'; if(stat(dn, &sbuf) < 0){ if(e) sprintf(e, "\007Dir \"%s\": %s", dn, strerror(errno)); return(NULL); } else{ #define MAX(x,y) ((x) > (y) ? (x) : (y)) avail = alloced = MAX(sbuf.st_size, incr); if(!(sbuf.st_mode & S_IFDIR)){ if(e) sprintf(e, "\007Not a directory: \"%s\"", dn); return(NULL); } } if((names=(char *)malloc(alloced * sizeof(char))) == NULL){ if(e) sprintf(e, "\007Can't malloc space for file names"); return(NULL); } np = names; strcpy(buf, dn); if (*buf && buf[strlen(buf)-1] != '\\') strcat(buf, "\\"); if (pat && *pat) strcat(buf, pat); if (!pat || !*pat || strchr(pat, '.')==NULL) strcat(buf, "*"); if (DosFindFirst(buf, &hdir, FILE_NORMAL|FILE_DIRECTORY, &findbuf, sizeof findbuf, &count, FIL_STANDARD) != 0) { if(e) sprintf(e, "Can't find first file in \"%s\"", dn); free((char *) names); return(NULL); } do{ (*n)++; p = findbuf.achName; l = strlen(p); while(avail < l+1){ char *oldnames; alloced += incr; avail += incr; oldnames = names; if((names = (char *)realloc((void *)names, alloced * sizeof(char))) == NULL){ if(e) sprintf(e, "\007Can't malloc enough space for file names"); return(NULL); } np = names + (np-oldnames); } avail -= (l+1); while((*np++ = *p++) != '\0') ; } while(DosFindNext(hdir, &findbuf, sizeof findbuf, &count) == 0); return(names); } /* * fioperr - given the error number and file name, display error */ void fioperr(e, f) int e; char *f; { switch(e){ case FIOFNF: /* File not found */ emlwrite("\007File \"%s\" not found", f); break; case FIOEOF: /* end of file */ emlwrite("\007End of file \"%s\" reached", f); break; case FIOLNG: /* name too long */ emlwrite("\007File name \"%s\" too long", f); break; case FIODIR: /* file is a directory */ emlwrite("\007File \"%s\" is a directory", f); break; case FIONWT: emlwrite("\007Write permission denied: %s", f); break; case FIONRD: emlwrite("\007Read permission denied: %s", f); break; case FIONEX: emlwrite("\007Execute permission denied: %s", f); break; default: emlwrite("\007File I/O error: %s", f); } } /* * pfnexpand - pico's function to expand the given file name if there is * a leading '~' */ char *pfnexpand(fn, len) char *fn; size_t len; { register char *x, *y, *z; char *home = gethomedir(NULL); char name[20]; if(*fn == '~') { for(x = fn+1, y = name; *x != '/' && *x != '\\' && *x != '\0' && y-name < sizeof(name)-1; *y++ = *x++); *y = '\0'; if(strlen(home) + strlen(fn) >= len) { return(NULL); } /* make room for expanded path */ for(z=x+strlen(x),y=fn+strlen(x)+strlen(home); z >= x; *y-- = *z--); /* and insert the expanded address */ for(x=fn,y=home; *y != '\0'; *x++ = *y++); } return(fn); } getcurdir(drv, buf) int drv; char *buf; { LONG ml = _MAX_PATH; drv = drv ? toupper(drv) : _getdrive(); buf[0] = (char)drv; buf[1] = ':'; buf[2] = '\\'; return DosQueryCurrentDir(drv-'A'+1, buf + 3, &ml) == 0; } /* * fixpath - make the given pathname into an absolute path */ void fixpath(name, len) char *name; size_t len; { char *p; char file[_MAX_PATH]; int dr; if(!len) return; /* normalize: xlate any '/' into '\\' */ while ((p=strchr(name,'/'))!=NULL) *p='\\'; /* return the full path of given file */ if(isalpha((unsigned char)name[0]) && name[1] == ':'){ /* have drive spec? */ if(name[2] == '\\') /* including path? */ return; if (!getcurdir(name[0], file)) return; name += 2; } else if(name[0] == '.' && (!name[1] || name[1] == '\\')) { getcurdir(0, file); name += (1 + name[1] == '\\'); } else if(name[0] == '\\') { /* no drive spec! */ file[0] = (char)_getdrive(); file[1] = ':'; file[2] = '\0'; } else getcurdir(0, file); /* no qualification */ if(*name) { /* if name, append it */ p = NULL; if (name[0] == '.' && name[1] == '.' && (!name[2] || name[2] == '\\')) { if ((p = strrchr(name,'\\'))!=NULL) { *p++ = '\0'; if (!*p && (p=strrchr(name,'\\'))!=NULL) *p = '\0'; } name += (2 & name[2] == '\\'); } if (*name) { if (name[0] == '.' && (!name[1] || name[1] == '\\')) name += (1 + name[1] == '\\'); if (*name) { if (*file && file[strlen(file)-1] != '\\' && *name != '\\') strcat(file, "\\"); strcat(file, name); } } } strncpy(name, file, len-1); /* copy back to real buffer */ name[len-1] = '\0'; /* tie off just in case */ } /* * compresspath - given a base path and an additional directory, collapse * ".." and "." elements and return absolute path (appending * base if necessary). * * returns 1 if OK, * 0 if there's a problem * new path, by side effect, if things went OK */ compresspath(base, path, len) char *base, *path; int len; { register int i; int depth = 0; char *p; char *stack[32]; char pathbuf[NLINE]; #define PUSHD(X) (stack[depth++] = X) #define POPD() ((depth > 0) ? stack[--depth] : "") strcpy(pathbuf, path); fixpath(pathbuf, len); p = pathbuf; for(i=0; pathbuf[i] != '\0'; i++){ /* pass thru path name */ if(pathbuf[i] == C_FILESEP){ if(p != pathbuf) PUSHD(p); /* push dir entry */ p = &pathbuf[i+1]; /* advance p */ pathbuf[i] = '\0'; /* cap old p off */ continue; } if(pathbuf[i] == '.'){ /* special cases! */ if(pathbuf[i+1] == '.' /* parent */ && (pathbuf[i+2] == C_FILESEP || pathbuf[i+2] == '\0')){ if(!strcmp(POPD(),"")) /* bad news! */ return(0); i += 2; p = (pathbuf[i] == '\0') ? "" : &pathbuf[i+1]; } else if(pathbuf[i+1] == C_FILESEP || pathbuf[i+1] == '\0'){ i++; p = (pathbuf[i] == '\0') ? "" : &pathbuf[i+1]; } } } if(*p != '\0') PUSHD(p); /* get last element */ path[0] = '\0'; for(i = 0; i < depth; i++){ strcat(path, S_FILESEP); strcat(path, stack[i]); } return(1); /* everything's ok */ } /* * tmpname - return a temporary file name in the given buffer */ void tmpname(dir, name) char *dir; char *name; { static int counter = 0; static char *tmpdir = NULL; char * p; if(dir && *dir) tmpdir = dir; if(!tmpdir && (tmpdir=getenv("TEMP"))==NULL && (tmpdir = getenv("TMP"))==NULL) tmpdir="."; p = strchr(strcpy(name, tmpdir), '/'); while (p != NULL) { *p++ = '\\'; p = strchr(p, '/'); } p = strrchr(name, '\\'); if (p == 0) p = name + strlen(name); else if (*++p != 0) { p = name + strlen(name); *p++ = '\\'; } sprintf(p, "pn%d%d", getpid(), ++counter); if(dir && *dir) tmpdir = NULL; } /* * Take a file name, and from it * fabricate a buffer name. This routine knows * about the syntax of file names on the target system. * I suppose that this information could be put in * a better place than a line of code. */ void makename(bname, fname) char bname[]; char fname[]; { register char *cp1; register char *cp2; cp1 = &fname[0]; while (*cp1 != 0) ++cp1; while (cp1!=&fname[0] && cp1[-1]!='\\') --cp1; cp2 = &bname[0]; while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=';') *cp2++ = *cp1++; *cp2 = 0; } /* * copy - copy contents of file 'a' into a file named 'b'. Return error * if either isn't accessible or is a directory */ copy(a, b) char *a, *b; { int n, rv = 0; struct stat tsb, fsb; if(stat(a, &fsb) < 0){ /* get source file info */ emlwrite("Can't Copy: %s", errstr(errno)); return(-1); } if(!(fsb.st_mode&S_IREAD)){ /* can we read it? */ emlwrite("\007Read permission denied: %s", a); return(-1); } if((fsb.st_mode&S_IFMT) == S_IFDIR){ /* is it a directory? */ emlwrite("\007Can't copy: %s is a directory", a); return(-1); } if(stat(b, &tsb) < 0){ /* get dest file's mode */ switch(errno){ case ENOENT: break; /* these are OK */ default: emlwrite("\007Can't Copy: %s", errstr(errno)); return(-1); } } else{ if(!(tsb.st_mode&S_IWRITE)){ /* can we write it? */ emlwrite("\007Write permission denied: %s", b); return(-1); } if((tsb.st_mode&S_IFMT) == S_IFDIR){ /* is it directory? */ emlwrite("\007Can't copy: %s is a directory", b); return(-1); } if(fsb.st_dev == tsb.st_dev && fsb.st_ino == tsb.st_ino){ emlwrite("\007Identical files. File not copied", NULL); return(-1); } } rv=DosCopy(a, b, DCPY_EXISTING); if(rv != 0 < 0){ emlwrite("Copy Failed: DosCopy() error %d", rv); return(-1); } return(rv); } /* * Open a file for writing. Return TRUE if all is well, and FALSE on error * (cannot create). */ ffwopen(fn, readonly) char *fn; int readonly; { extern FIOINFO g_pico_fio; g_pico_fio.flags = FIOINFO_WRITE; if ((g_pico_fio.fp = fopen(gf_pico_fio.name = fn, "w")) == NULL) { emlwrite("Cannot open file for writing", NULL); return (FIOERR); } if(readonly) chmod(fn, MODE_READONLY); /* fix access rights */ return (FIOSUC); } /* * Close a file. Should look at the status in all systems. */ ffclose() { extern FIOINFO g_pico_fio; if (fclose(g_pico_fio.fp) != FALSE) { emlwrite("Error closing file", NULL); return(FIOERR); } return(FIOSUC); } /* * ffelbowroom - make sure the destination's got enough room to receive * what we're about to write... */ ffelbowroom(fn, readonly) char *fn; int readonly; { return(TRUE); } /* * worthit - generic sort of test to roughly gage usefulness of using * optimized scrolling. * * note: * returns the line on the screen, l, that the dot is currently on */ worthit(l) int *l; { int i; /* l is current line */ unsigned below; /* below is avg # of ch/line under . */ *l = doton(&i, &below); below = (i > 0) ? below/(unsigned)i : 0; return(below > 3); } /* * o_insert - optimize screen insert of char c */ o_insert(c) char c; { return(0); } /* * o_delete - optimized character deletion */ o_delete() { return(0); } /* * pico_new_mail - just checks mtime and atime of mail file and notifies user * if it's possible that they have new mail. */ pico_new_mail() { return(0); } /* * time_to_check - checks the current time against the last time called * and returns true if the elapsed time is > timeout */ time_to_check() { static time_t lasttime = 0L; if(!timeo) return(FALSE); if(time((long *) 0) - lasttime > (time_t)timeo){ lasttime = time((long *) 0); return(TRUE); } else return(FALSE); } /* * sstrcasecmp - compare two pointers to strings case independently */ sstrcasecmp(s1, s2) QcompType *s1, *s2; { return((*pcollator)(*(char **)s1, *(char **)s2)); } int strucmp(o, r) char *o, *r; { return(o ? (r ? stricmp(o, r) : 1) : (r ? -1 : 0)); } int struncmp(o, r, n) char *o, *r; int n; { return(o ? (r ? strnicmp(o, r, n) : 1) : (r ? -1 : 0)); } /* * sleep the given number of microseconds */ ssleep(s) clock_t s; { s += clock(); while(s > clock()) ; } /* * chkptinit -- initialize anything we need to support composer * checkpointing */ void chkptinit(file, n) char *file; int n; { if(!file[0]){ long gmode_save = gmode; if(gmode&MDCURDIR) gmode &= ~MDCURDIR; /* so fixpath will use home dir */ strcpy(file, "#picoTM0.txt"); fixpath(file, NLINE); gmode = gmode_save; } else{ int l = strlen(file); if(file[l-1] != '\\'){ file[l++] = '\\'; file[l] = '\0'; } strcpy(file + l, "#picoTM0.txt"); } if(fexist(file, "r", (off_t *)NULL) == FIOSUC){ /* does file exist? */ char copy[NLINE]; strcpy(copy, "#picoTM1.txt"); fixpath(copy, NLINE); rename(file, copy); /* save so we don't overwrite it */ } unlink(file); } void set_collation(collation, ctype) int collation; int ctype; { extern int collator(); /* strcoll isn't declared on all systems */ #ifdef LC_COLLATE char *status = NULL; #endif pcollator = strucmp; #ifdef LC_COLLATE if(collation){ /* * This may not have the desired effect, if collator is not * defined to be strcoll in os.h and strcmp and friends * don't know about locales. If your system does have strcoll * but we haven't defined collator to be strcoll in os.h, let us know. */ status = setlocale(LC_COLLATE, ""); /* * If there is an error or if the locale is the "C" locale, then we * don't want to use strcoll because in the default "C" locale strcoll * uses strcmp ordering and we want strucmp ordering. * * The test for "C" isn't really correct, since status does not have to * be "C" even if we're in the "C" locale. But this works on some systems. */ if(status && !(status[0] == 'C' && status[1] == '\0')) pcollator = collator; } #endif #ifdef LC_CTYPE if(ctype){ (void)setlocale(LC_CTYPE, ""); } #endif } static USHORT myShift = 0; int kbd_ready() { KBDKEYINFO ki; if (KbdPeek(&ki, 0)==0) { if (ki.fbStatus & 0x40) { int key = (USHORT)((ki.chScan << 8) | ki.chChar); myShift = ki.fsState; if (!key && ki.fbStatus & 0x01) { KbdCharIn(&ki, IO_WAIT, 0); } return key; } } return 0; } int kbd_getkey() { KBDKEYINFO ki; while (KbdCharIn(&ki, IO_WAIT, 0)==0) { if (ki.fbStatus & 0x40) { myShift = ki.fsState; return (USHORT)((ki.chScan << 8) | ki.chChar); } if (ki.fbStatus & 0x01) { myShift = ki.fsState; } } return NODATA; } void kbd_flush() { KbdFlushBuffer(0); } int kbd_shift() { return myShift; }