home *** CD-ROM | disk | FTP | other *** search
/ vsiftp.vmssoftware.com / VSIPUBLIC@vsiftp.vmssoftware.com.tar / FREEWARE / FREEWARE40.ZIP / pine / pico / osdep_vms.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-03-31  |  32.0 KB  |  1,466 lines

  1. /* OSDEP_VMS.C */
  2. #include     <stdio.h>
  3. #include    <errno.h>
  4. #include    <setjmp.h>
  5. #include    <time.h>
  6. #include    "osdep.h"
  7. #include        "pico.h"
  8. #include    "estruct.h"
  9. #include        "edef.h"
  10. #include        "efunc.h"
  11. #ifdef __ALPHA
  12. #include    <fcntl.h>
  13. #endif
  14. #include    <file.h>
  15. #include    <types.h>
  16. #include    <time.h>
  17.  
  18. #include <iodef.h>
  19. #include <ttdef.h>
  20. #include <tt2def.h>
  21. #include <lib$routines.h>
  22. #include <descrip.h>
  23.  
  24. /* Output channel and buffers */
  25. static short    TerminalChan;
  26. #define    MAXBUF    1024        /* Save up to 1024 characters before sending them */
  27. unsigned char    OutputBuffer[MAXBUF + 1];
  28. int    OutputBufferCounter;
  29.  
  30. /* For setting and resting the PASTHRU flag: */
  31. struct    {
  32.     unsigned char    class, type;
  33.     unsigned short    width;
  34.     unsigned long    BasicChar,
  35.             ExtendChar;
  36.     } TerminalChar;
  37.  
  38.  
  39. struct KBSTREE *kpadseqs = NULL;
  40.  
  41.  
  42. #define    SIGTYPE    void
  43.  
  44. struct DESC {        /* String descriptor */
  45.     short    length, type;
  46.     char    *address;
  47.     } ;
  48.  
  49.     int      kbseq();
  50.     SIGTYPE  do_hup_signal();
  51.     SIGTYPE  rtfrmshell();
  52. #ifdef    TIOCGWINSZ
  53.     SIGTYPE  winch_handler();
  54. #endif
  55.  
  56.  
  57. /*
  58.  * for alt_editor arg[] building
  59.  */
  60. #define    MAXARGS    10
  61. #ifdef HEBREW
  62. #include "hebrew.h"
  63.  
  64. unsigned char xlat_tab[]={
  65.   0xf9,  /* a - shin*/
  66.   0xf0,  /* b - nun */
  67.   0xe1,  /* c - bet */
  68.   0xe2,  /* d - gimel*/
  69.   0xf7,  /* e - kuf*/
  70.   0xeb,  /* f - chaf */
  71.   0xf2,  /* g - ayin */
  72.   0xe9,  /* h - yud */
  73.   0xef,  /* i - nun sofit */
  74.   0xe7,  /* j - het */
  75.   0xec,  /* k - lamed */
  76.   0xea,  /* l - chaf sofit */
  77.   0xf6,  /* m - zadik */
  78.   0xee,  /* n - mem */
  79.   0xed,  /* o - mem sofit */
  80.   0xf4,  /* p - pey */
  81.   0x2f,  /* q - / */ 
  82.   0xf8,  /* r - raish */
  83.   0xe3,  /* s - dalet */
  84.   0xe0,  /* t - aleph */
  85.   0xe5,  /* u - vav */
  86.   0xe4,  /* v - hey */
  87.   0x27,  /* w - ' */
  88.   0xf1,  /* x - samech */
  89.   0xe8,  /* y - tet */
  90.   0xe6,  /* z - zayin */
  91. };
  92. unsigned char xlat_str1[]="`;',./";
  93. unsigned char xlat_str2[]={
  94.   ';',   /* ` - ; */
  95.   0xf3,  /* ; - pey sofit */
  96.   ',',   /* ' - , */
  97.   0xfa,  /* , - tav */
  98.   0xf5,  /* . - zadik sofit */
  99.   '.',   /* / - . */
  100. };
  101. #endif
  102.  
  103. /*
  104.  * ttopen - this function is called once to set up the terminal device 
  105.  *          streams.  if called as pine composer, don't mess with
  106.  *          tty modes, but set signal handlers.
  107.  */
  108. ttopen()
  109. {
  110.     long    status;
  111.     struct DESC TerminalDesc;
  112.     char    TerminalName[] = "SYS$OUTPUT:";
  113.  
  114.     TerminalDesc.address = TerminalName; TerminalDesc.type = 0;
  115.     TerminalDesc.length = strlen(TerminalName);
  116.  
  117.     status = sys$assign(&TerminalDesc, &TerminalChan, (int)(0), (int)(0));
  118.     if((status & 0x1) == 0) {
  119.         printf("Can't assign channel to terminal\n");
  120.         exit(status);
  121.     }
  122.  
  123.     signal(SIGHUP,  do_hup_signal);    /* deal with SIGHUP */
  124.     signal(SIGTERM, do_hup_signal);    /* deal with SIGTERM */
  125. #ifdef    SIGTSTP
  126.     signal(SIGTSTP, SIG_DFL);
  127. #endif
  128.  
  129. /* Some default values since we do not have TERMCAP: */
  130.     eolexist = FALSE;
  131.     revexist = TRUE;
  132.     delchar = FALSE;
  133.     inschar = FALSE;
  134.       scrollexist = TRUE;
  135.  
  136.     if(gmode&MDFKEY)
  137.         HelpKeyNames = funckeynames;
  138.     else
  139.         HelpKeyNames = NULL;
  140.  
  141.     init_kpnames();
  142.  
  143.     OutputBufferCounter = 0;    /* Output buffer is empty */
  144.  
  145. /* Set the PASTHRU bit */
  146.     status = sys$qiow((int)(0), TerminalChan, (short)(IO$_SENSEMODE),
  147.             NULL, (int)(0), (int)(0),
  148.             &TerminalChar, sizeof(TerminalChar),
  149.             (int)(0), (int)(0), (int)(0), (int)(0));
  150.     if((status & 0x1) == 0) {
  151.         printf("Can't read for set terminal to /PASTHRU\n");
  152.         exit(status);
  153.     }
  154.  
  155.     TerminalChar.ExtendChar |= TT2$M_PASTHRU;
  156.     status = sys$qiow((int)(0), TerminalChan, (short)(IO$_SETMODE),
  157.             NULL, (int)(0), (int)(0),
  158.             &TerminalChar, sizeof(TerminalChar),
  159.             (int)(0), (int)(0), (int)(0), (int)(0));
  160.     if((status & 0x1) == 0) {
  161.         printf("Can't set terminal to /PASTHRU\n");
  162.         exit(status);
  163.     }
  164.  
  165.     ttgetwinsz();
  166.     return(1);
  167. }
  168.  
  169.  
  170.  
  171. /*
  172.  * ttclose - this function gets called just before we go back home to 
  173.  *           the command interpreter.  If called as pine composer, don't
  174.  *           worry about modes, but set signals to default, pine will 
  175.  *           rewire things as needed.
  176.  */
  177. ttclose()
  178. {
  179.     int    status;
  180.  
  181.     ttflush();
  182.  
  183. /* Clear the PASTHRU flag */
  184.     TerminalChar.ExtendChar &= ~TT2$M_PASTHRU;
  185.     status = sys$qiow((int)(0), TerminalChan, (short)(IO$_SETMODE),
  186.             NULL, (int)(0), (int)(0),
  187.             &TerminalChar, sizeof(TerminalChar),
  188.             (int)(0), (int)(0), (int)(0), (int)(0));
  189.     if((status & 0x1) == 0) {
  190.         printf("Can't set terminal to /NOPASTHRU\n");
  191.         exit(status);
  192.     }
  193.     sys$dassgn(TerminalChan);
  194.  
  195.     return(1);
  196. }
  197.  
  198.  
  199. /*
  200.  * ttspeed - return TRUE if tty line speed < 9600 else return FALSE
  201.  */
  202. ttisslow()
  203. {
  204.     return(TRUE);
  205. }
  206.  
  207.  
  208. /*
  209.  * ttgetwinsz - set global rows and columns values and return
  210.  */
  211. ttgetwinsz()
  212. {
  213. #define TT_STRING_SIZE 20
  214.        char string[TT_STRING_SIZE] ;   /* string for holding tt:'s dev name */
  215.        int rows, col ;                 /* row / column vars */
  216.        unsigned long status ;          /* status return var */
  217.        unsigned long terminal_item = 797 ; /* JPI$_TERMINAL */
  218.        unsigned long tt_page_item = 170 ;  /* DVI$_TT_PAGE */
  219.        unsigned long devbufsz_item = 8 ;   /* DVI$_DEVBUFSIZ */
  220.        struct dsc$descriptor_vs tt_sdesc =
  221.                                {TT_STRING_SIZE,/* max string size */
  222.                                DSC$K_DTYPE_VT, /* descriptor type */
  223.                                DSC$K_CLASS_VS, /* descriptor class = var str */
  224.                                string} ;       /* pointer to var string */
  225.  
  226.  
  227. /* Set the defaults */
  228.           term.t_nrow = 23;
  229.           term.t_ncol = 80;
  230.  
  231.                /* Get our terminal's device name */
  232.        status = lib$getjpi(&terminal_item,NULL,NULL,NULL,&tt_sdesc,NULL) ;
  233.        if ((status&1) == 1)
  234.           {
  235.                /* Get our terminal's page size */
  236.           status = lib$getdvi(&tt_page_item,NULL,&tt_sdesc,&rows,NULL,NULL) ;
  237.                /* Get our terminal's page width */
  238.       if((status & 0x1) == 1) {
  239.               status = lib$getdvi(&devbufsz_item,NULL,&tt_sdesc,&col,NULL,NULL) ;
  240.         if((status & 0x1) == 1) {
  241.                    /* Set the globals for rows and columns */
  242.                   term.t_nrow = rows-1 ;
  243.                   term.t_ncol = col ;
  244.             }
  245.         }
  246.           }
  247.        else
  248.           printf("Can't find process's terminal -- using defaults.\n") ;
  249. }
  250.  
  251. /*
  252.  | Physically write a string to the terminal.
  253.  */
  254. write_terminal(string, size)
  255. char    *string;
  256. int    size;
  257. {
  258.     long    status;
  259.     short    iosb[4];
  260.  
  261.     status = sys$qiow((int)(0), TerminalChan, (short)(IO$_WRITEVBLK),
  262.             iosb, (int)(0), (int)(0),
  263.             string, size, (int)(0), (int)(0), (int)(0), (int)(0));
  264.     if(((status & 0x1) == 0) || ((iosb[0] & 0x1) == 0)) {
  265.         printf("Can't write to terminal; status=%d, iosb=%d\n",
  266.             status, iosb[0]);
  267.         exit(status);
  268.     }
  269.     return 0;    /* Success */
  270. }
  271.  
  272.  
  273. /*
  274.  * __ttputc - Write a character to the display. Actually save it in our buffer
  275.  * and write it only if the buffer is becoming full.
  276.  * This function is called by TTPUTC().
  277.  */
  278. __ttputc(c)
  279. {
  280.     OutputBuffer[OutputBufferCounter++] = c;
  281.     if(OutputBufferCounter >= MAXBUF)
  282.         ttflush();
  283.     return c;
  284. }
  285.  
  286. /*
  287.  * ttputc - Write a character to the display. Actually save it in our buffer
  288.  * and write it only if the buffer is becoming full.
  289.  */
  290. #ifndef HEBREW
  291. ttputc(c)
  292. {
  293.     return(__ttputc(c));
  294. }
  295. #else
  296. ttputc(c)
  297. {
  298. extern int ttrow,ttcol;
  299. int ret;
  300.  
  301. if(compose_heb && hebmode){      
  302.     ret = __ttputc(c);
  303.     (*term.t_move)(ttrow, ttcol+1);
  304. }
  305. /* else ret=write_terminal(&c, 1); */
  306.     else ret = __ttputc(c);
  307.     return c;
  308. }
  309.  
  310. #endif
  311.  
  312.  
  313. /*
  314.  * ttflush - Flush our buffer to the terminal.
  315.  */
  316. ttflush()
  317. {
  318.     if(OutputBufferCounter > 0) {
  319.         write_terminal(OutputBuffer, OutputBufferCounter);
  320.         OutputBufferCounter = 0;
  321.     }
  322. }
  323.  
  324.  
  325. /*
  326.  * ttgetc - Read a character from the terminal, performing no editing 
  327.  *          and doing no echo at all.
  328.  */
  329. ttgetc()
  330. {
  331.     unsigned char c;
  332.     int i;
  333.     long    status;
  334.     unsigned long Terminator[4] = { -1, -1, -1, -1};
  335.     struct DESC    TerminatorDesc = { sizeof(Terminator), 0, Terminator };
  336.     short    iosb[4];
  337.  
  338.     status = sys$qiow((int)(0), TerminalChan,
  339.         (short)(IO$_READLBLK | IO$M_NOFORMAT | IO$M_NOFILTR | IO$M_NOECHO | IO$M_TRMNOECHO),
  340.             iosb, (int)(0), (int)(0),
  341.             &c, 1, (int)(0), &TerminatorDesc, (int)(0), (int)(0));
  342.     if(((status & 0x1) == 0) || ((iosb[0] & 0x1) == 0)) {
  343.         printf("Can't read from terminal; status=%d, iosb=%d\n",
  344.             status, iosb[0]);
  345.         exit(status);
  346.     }
  347.  
  348.     c &= 0xff;
  349.     if(c == 155) c = 27;    /* Convert 8th bit escape to 7bit one */
  350.     return((int)(c));
  351. }
  352.  
  353.  
  354. /*
  355.  * GetKey - Read in a key.
  356.  * Do the standard keyboard preprocessing. Convert the keys to the internal
  357.  * character set.  Resolves escape sequences and returns no-op if global
  358.  * timeout value exceeded.
  359.  */
  360. GetKey()
  361. {
  362.     int    c;
  363. #ifdef HEBREW
  364.     unsigned char *ptr;
  365. #endif
  366.  
  367. /*~~~ HUJI/VMS - No timeout is supported on VMS to make it simple... */
  368.  
  369.     if ((c = (*term.t_getchar)()) == METACH) { /* Apply M- prefix      */
  370.     int status;
  371.         
  372.     /*
  373.      * this code should intercept special keypad keys
  374.      */
  375.     switch(status = kbseq(&c)){
  376.       case 0 :     /* no dice */
  377.         return(c);
  378.       case  K_PAD_UP        :
  379.       case  K_PAD_DOWN        :
  380.       case  K_PAD_RIGHT        :
  381.       case  K_PAD_LEFT        :
  382.       case  K_PAD_PREVPAGE    :
  383.       case  K_PAD_NEXTPAGE    :
  384.       case  K_PAD_HOME        :
  385.         return(status);
  386.       case F1  :
  387.       case F2  :
  388.       case F3  :
  389.       case F4  :
  390.       case F5  :
  391.       case F6  :
  392.       case F7  :
  393.       case F8  :
  394.       case F9  :
  395.       case F10 :
  396.       case F11 :
  397.       case F12 :
  398.         return(status);
  399.       case BADESC :
  400.         if(c == '\033'){
  401.         c = (*term.t_getchar)();
  402.         if(islower(c))    /* canonicalize c */
  403.           c = toupper(c);
  404.  
  405.         return((isalpha(c) || c == '@' || (c >= '[' && c <= '_'))
  406.                ? (CTRL | c) : c);
  407.         }
  408.  
  409.       default :                /* punt the whole thing    */
  410.         (*term.t_beep)();
  411.         break;
  412.     }
  413.     }
  414.  
  415.     if (c>=0x00 && c<=0x1F)                 /* C0 control -> C-     */
  416.       c = CTRL | (c+'@');
  417. #ifdef HEBREW
  418.       if(compose_heb && (!message_mode || search_mode) && (hebmain!=eng_in_heb)){
  419.     if(c >= 'a' && c <= 'z')c = xlat_tab[c-'a'];
  420.     else{
  421.       ptr = (unsigned char *) strchr(xlat_str1, c);
  422.       if(ptr != NULL) c = xlat_str2[(int)(ptr - xlat_str1)];
  423.     }
  424.     }
  425. #endif
  426.     return (c);
  427.  
  428. }
  429.  
  430.  
  431.  
  432. /* 
  433.  * kbseq - looks at an escape sequence coming from the keyboard and 
  434.  *         compares it to a trie of known keyboard escape sequences, and
  435.  *         performs the function bound to the escape sequence.
  436.  * 
  437.  *         returns: BADESC, the escaped function, or 0 if not found.
  438.  */
  439. kbseq(c)
  440. int    *c;
  441. {
  442.     register char    b;
  443.     register int    first = 1;
  444.     register struct    KBSTREE    *current = kpadseqs;
  445. #ifdef HEBREW
  446.     register int        firsth = 1;
  447. #endif
  448.  
  449.     if(kpadseqs == NULL)            /* bag it */
  450.       return(BADESC);
  451.  
  452.     while(1){
  453.     *c = b = (*term.t_getchar)();
  454. #ifdef HEBREW    
  455.     if(firsth){
  456.       firsth=0;
  457.       if(strchr(escfuns,b&0xffdf)){
  458.           *c &= 0xffdf;
  459.           *c |= META;
  460.           return 0;
  461.         }
  462.     }
  463. #endif
  464.     while(current->value != b){
  465.         if(current->left == NULL){        /* NO MATCH */
  466.         if(first)
  467.           return(BADESC);
  468.         else
  469.           return(0);
  470.         }
  471.         current = current->left;
  472.     }
  473.  
  474.     if(current->down == NULL)        /* match!!!*/
  475.       return(current->func);
  476.     else
  477.       current = current->down;
  478.  
  479.     first = 0;
  480.     }
  481. }
  482.  
  483.  
  484.  
  485. /*
  486.  * alt_editor - Use normal SYSTEM() and not fork.
  487.  */
  488. alt_editor(f, n)
  489. {
  490.     char   eb[NLINE];                /* buf holding edit command */
  491.     char   *fn;                    /* tmp holder for file name */
  492.     char   *cp;
  493.     char   *args[MAXARGS];            /* ptrs into edit command */
  494.     long   l;
  495.     int    i, pid, done = 0;
  496.  
  497.     if(Pmaster == NULL)
  498.       return;
  499.  
  500.     if(gmode&MDSCUR){
  501.     emlwrite("Alternate editor not available in restricted mode", NULL);
  502.     return;
  503.     }
  504.  
  505.     if(Pmaster->alt_ed == NULL){
  506.     if(!(gmode&MDADVN)){
  507.         emlwrite("\007Unknown Command",NULL);
  508.         return;
  509.     }
  510.  
  511.     if(getenv("EDITOR"))
  512.       strcpy(eb, (char *)getenv("EDITOR"));
  513.     else
  514.       *eb = '\0';
  515.  
  516.     while(!done){
  517.         pid = mlreplyd("Which alternate editor ? ", eb, NLINE, QDEFLT);
  518.  
  519.         switch(pid){
  520.           case ABORT:
  521.         return(-1);
  522.           case HELPCH:
  523.         emlwrite("no alternate editor help yet", NULL);
  524.  
  525. /* take sleep and break out after there's help */
  526.         sleep(3);
  527.         break;
  528.           case (CTRL|'L'):
  529.         sgarbf = TRUE;
  530.         update();
  531.         break;
  532.           case TRUE:
  533.           case FALSE:            /* does editor exist ? */
  534.         if(*eb == '\0'){        /* leave silently? */
  535.             mlerase();
  536.             return(-1);
  537.         }
  538.  
  539.         done++;
  540.         break;
  541.           default:
  542.         break;
  543.         }
  544.     }
  545.     }
  546.     else
  547.       strcpy(eb, Pmaster->alt_ed);
  548.  
  549.     if((fn=writetmp(0, 1)) == NULL){        /* get temp file */
  550.     emlwrite("Problem writing temp file for alt editor", NULL);
  551.     return(-1);
  552.     }
  553.  
  554.     strcat(eb, " ");
  555.     strcat(eb, fn);
  556.  
  557.     cp = eb;
  558.     for(i=0; *cp != '\0';i++){            /* build args array */
  559.     if(i < MAXARGS){
  560.         args[i] = NULL;            /* in case we break out */
  561.     }
  562.     else{
  563.         emlwrite("Too many args for command!", NULL);
  564.         return(-1);
  565.     }
  566.  
  567.     while(isspace(*cp))
  568.       if(*cp != '\0')
  569.         cp++;
  570.       else
  571.         break;
  572.  
  573.     args[i] = cp;
  574.  
  575.     while(!isspace(*cp))
  576.       if(*cp != '\0')
  577.         cp++;
  578.       else
  579.         break;
  580.  
  581.     if(*cp != '\0')
  582.       *cp++ = '\0';
  583.     }
  584.  
  585.     args[i] = NULL;
  586.  
  587.     if(Pmaster)
  588.       (*Pmaster->raw_io)(0);            /* turn OFF raw mode */
  589.  
  590.     system(args);
  591.     if(Pmaster)
  592.       (*Pmaster->raw_io)(1);        /* turn ON raw mode */
  593.  
  594.     /*
  595.      * replace edited text with new text 
  596.      */
  597.     curbp->b_flag &= ~BFCHG;        /* make sure old text gets blasted */
  598.     readin(fn, 0);            /* read new text overwriting old */
  599.     unlink(fn);                /* blast temp file */
  600.     curbp->b_flag |= BFCHG;        /* mark dirty for packbuf() */
  601.     ttopen();                /* reset the signals */
  602.     refresh(0, 1);            /* redraw */
  603. }
  604.  
  605.  
  606.  
  607. /*
  608.  *  bktoshell - suspend and wait to be woken up
  609.  */
  610. bktoshell()        /* suspend MicroEMACS and wait to wake up */
  611. {
  612.     emlwrite("\007Unknown command: ^Z", NULL);
  613.     return;
  614. }
  615.  
  616.  
  617. /* 
  618.  * rtfrmshell - back from shell, fix modes and return
  619.  */
  620. SIGTYPE
  621. rtfrmshell()
  622. {
  623. }
  624.  
  625.  
  626. /*
  627.  * do_hup_signal - jump back in the stack to where we can handle this
  628.  */
  629. SIGTYPE
  630. do_hup_signal()
  631. {
  632.     if(Pmaster){
  633.     extern jmp_buf finstate;
  634.  
  635.     signal(SIGHUP, SIG_IGN);         /* don't bother us. */
  636.     signal(SIGTERM, SIG_IGN);
  637.     longjmp(finstate, COMP_GOTHUP);
  638.     }
  639.     else{
  640.     /*
  641.      * if we've been interrupted and the buffer is changed,
  642.      * save it...
  643.      */
  644.     if(anycb() == TRUE){            /* time to save */
  645.         if(curbp->b_fname[0] == '\0'){    /* name it */
  646.         strcpy(curbp->b_fname, "pico.save");
  647.         }
  648.         else{
  649.         strcat(curbp->b_fname, ".save");
  650.         }
  651.         writeout(curbp->b_fname);
  652.     }
  653.     vttidy();
  654.     exit(1);
  655.     }
  656. }
  657.  
  658.  
  659. unlink(fn)
  660. char    *fn;
  661. {
  662.     delete(fn);
  663. }
  664.  
  665. /*
  666.  * big bitmap of ASCII characters allowed in a file name
  667.  * (needs reworking for other char sets)
  668.  */
  669. unsigned char okinfname[32] = {
  670.       0,    0,             /* ^@ - ^G, ^H - ^O  */
  671.       0,    0,            /* ^P - ^W, ^X - ^_  */
  672.       0xff, 0xff,        /* SP - ' ,  ( - /   */
  673.       0xff, 0xff,        /*  0 - 7 ,  8 - ?   */
  674.       0xff, 0xff,        /*  @ - G ,  H - O   */
  675.       0xff, 0xff,        /*  P - W ,  X - _   */
  676.       0xff, 0xff,        /*  ` - g ,  h - o   */
  677.       0xff, 0xff,        /*  p - w ,  x - DEL */
  678.       0,    0,             /*  > DEL   */
  679.       0,    0,            /*  > DEL   */
  680.       0,    0,             /*  > DEL   */
  681.       0,    0,             /*  > DEL   */
  682.       0,    0             /*  > DEL   */
  683. };
  684.  
  685.  
  686. /*
  687.  * fallowc - returns TRUE if c is allowable in filenames, FALSE otw
  688.  */
  689. fallowc(c)
  690. char c;
  691. {
  692.     return(okinfname[c>>3] & 0x80>>(c&7));
  693. }
  694.  
  695.  
  696. /*
  697.  * fexist - returns TRUE if the file exists with mode passed in m, 
  698.  *          FALSE otherwise.  By side effect returns length of file in l
  699.  */
  700. fexist(file, m, l)
  701. char *file;
  702. char *m;                    /* files mode: r, w or rw */
  703. long *l;
  704. {
  705.     struct stat    sbuf;
  706.  
  707.     if(l)
  708.       *l = 0L;
  709.  
  710.     if(stat(file, &sbuf) < 0){
  711.     switch(errno){
  712.       case ENOENT :                /* File not found */
  713.         return(FIOFNF);
  714. #ifdef    ENAMETOOLONG
  715.       case ENAMETOOLONG :            /* Name is too long */
  716.         return(FIOLNG);
  717. #endif
  718.       default:                /* Some other error */
  719.         return(FIOERR);
  720.     }
  721.     }
  722.  
  723.     if(l)
  724.       *l = sbuf.st_size;
  725.  
  726.     if((sbuf.st_mode&S_IFMT) == S_IFDIR)
  727.       return(FIODIR);
  728.  
  729.     if(m[0] == 'r')                /* read access? */
  730.       return((S_IREAD&sbuf.st_mode) ? FIOSUC : FIONRD);
  731.     else if(m[0] == 'w')            /* write access? */
  732.       return((S_IWRITE&sbuf.st_mode) ? FIOSUC : FIONWT);
  733.     else if(m[0] == 'x')            /* execute access? */
  734.       return((S_IEXEC&sbuf.st_mode) ? FIOSUC : FIONEX);
  735.     return(FIOERR);                /* what? */
  736. }
  737.  
  738.  
  739. /*
  740.  * isdir - returns true if fn is a readable directory, false otherwise
  741.  *         silent on errors (we'll let someone else notice the problem;)).
  742.  */
  743. isdir(fn, l)
  744. char *fn;
  745. long *l;
  746. {
  747.     struct stat sbuf;
  748.  
  749.     if(l)
  750.       *l = 0;
  751.  
  752.     if(stat(fn, &sbuf) < 0)
  753.       return(0);
  754.  
  755.     if(l)
  756.       *l = sbuf.st_size;
  757.     return((sbuf.st_mode&S_IFMT) == S_IFDIR);
  758. }
  759.  
  760.  
  761.  
  762. /*
  763.  * gethomedir - returns the users home directory
  764.  *              Note: home is malloc'd for life of pico
  765.  */
  766. char *
  767. gethomedir(l)
  768. int *l;
  769. {
  770.     static char *home = NULL;
  771.     static short hlen = 0;
  772.  
  773.     if(home == NULL){
  774.     strcpy(s, "~");
  775.     fixpath(s, NLINE);        /* let fixpath do the work! */
  776.     hlen = strlen(s);
  777.     if((home=(char *)malloc((strlen(s) + 1) * sizeof(char))) == NULL){
  778.         emlwrite("Problem allocating space for home dir", NULL);
  779.         return(0);
  780.     }
  781.     strcpy(home, s);
  782.     }
  783.  
  784.     if(l)
  785.       *l = hlen;
  786.  
  787.     return(home);
  788. }
  789.  
  790.  
  791. /*
  792.  * homeless - returns true if given file does not reside in the current
  793.  *            user's home directory tree. 
  794.  */
  795. homeless(f)
  796. char *f;
  797. {
  798.     char *home;
  799.     int   len;
  800.  
  801.     home = gethomedir(&len);
  802.     return(strncmp(home, f, len));
  803. }
  804.  
  805.  
  806.  
  807. /*
  808.  * errstr - return system error string corresponding to given errno
  809.  *          Note: strerror() is not provided on all systems, so it's 
  810.  *          done here once and for all.
  811.  */
  812. char *
  813. errstr(err)
  814. int err;
  815. {
  816.     extern char *sys_errlist[];
  817.     extern int  sys_nerr;
  818.  
  819.     return((err >= 0 && err < sys_nerr) ? sys_errlist[err] : NULL);
  820. }
  821.  
  822.  
  823.  
  824. /*
  825.  * getfnames - return all file names in the given directory in a single 
  826.  *             malloc'd string.  n contains the number of names
  827.  */
  828. char *
  829. getfnames(dn, n)
  830. char *dn;
  831. int  *n;
  832. {
  833.     int    context = 0;    /* Used nu LIB$FIND_FILE */
  834.     struct    DESC    file_mask, file_name;
  835.     int    status;
  836.     char    *p, *OutputPointer;
  837.     char    *RetFiles = malloc(8192);    /* Hopefully it will be enough... */
  838.     char    FileName[256];    /* Temporary place */
  839.  
  840.     *n = 0;        /* Init - no files yet */
  841.     if(RetFiles == NULL) return;
  842.     *(OutputPointer = RetFiles) = '\0';
  843.  
  844.     file_mask.length = strlen(dn); file_mask.address = dn;
  845.     file_mask.type = 0;
  846.  
  847.     for(;;) {
  848.         file_name.address = FileName; file_name.length = sizeof(FileName) - 1;
  849.         file_name.type = 0;
  850.  
  851.         status = LIB$FIND_FILE(&file_mask, &file_name, &context);
  852.         if((status & 0x1) == 0) {
  853.             if(context != 0)
  854.                 LIB$FIND_FILE_END(&context);
  855.             context = 0;    /* Init for next search */
  856.             return RetFiles;
  857.         }
  858.  
  859. /* File found - Remove the leading directory name and the trailing spaces */
  860.         if((p = strchr(FileName, ' ')) != NULL) *p = '\0';
  861.         if((p = strrchr(FileName, ']')) != NULL) p++;
  862.         else                    p = FileName;
  863.         while((*OutputPointer++ = *p++) != '\0');
  864.         *n += 1;
  865.     }
  866. }
  867.  
  868.  
  869. /*
  870.  * fioperr - given the error number and file name, display error
  871.  */
  872. void
  873. fioperr(e, f)
  874. int  e;
  875. char *f;
  876. {
  877.     switch(e){
  878.       case FIOFNF:                /* File not found */
  879.     emlwrite("\007File \"%s\" not found", f);
  880.     break;
  881.       case FIOEOF:                /* end of file */
  882.     emlwrite("\007End of file \"%s\" reached", f);
  883.     break;
  884.       case FIOLNG:                /* name too long */
  885.     emlwrite("\007File name \"%s\" too long", f);
  886.     break;
  887.       case FIODIR:                /* file is a directory */
  888.     emlwrite("\007File \"%s\" is a directory", f);
  889.     break;
  890.       case FIONWT:
  891.     emlwrite("\007Write permission denied: %s", f);
  892.     break;
  893.       case FIONRD:
  894.     emlwrite("\007Read permission denied: %s", f);
  895.     break;
  896.       case FIONEX:
  897.     emlwrite("\007Execute permission denied: %s", f);
  898.     break;
  899.       default:
  900.     emlwrite("\007File I/O error: %s", f);
  901.     }
  902. }
  903.  
  904.  
  905.  
  906. /*
  907.  * pfnexpand - pico's function to expand the given file name if there is 
  908.  *           a leading '~'
  909.  */
  910. char *pfnexpand(fn, len)
  911. char *fn;
  912. int  len;
  913. {
  914.     char    *pw;
  915.     register char *x, *y, *z;
  916.     char name[256];
  917.     
  918.     if(*fn == '~') {
  919.         for(x = fn+1, y = name; *x != '/' && *x != '\0'; *y++ = *x++);
  920.         *y = '\0';
  921.         if(x == fn + 1) 
  922.           pw = getenv("HOME");
  923.         else
  924.           pw = NULL;
  925.         if(pw == NULL)
  926.           return(NULL);
  927.  
  928.     /* make room for expanded path */
  929.     for(z=x+strlen(x),y=fn+strlen(x)+strlen(pw);
  930.         z >= x;
  931.         *y-- = *z--);
  932.     /* and insert the expanded address */
  933.     for(x=fn,y=pw; *y != '\0'; *x++ = *y++);
  934.     }
  935.     return(fn);
  936. }
  937.  
  938.  
  939.  
  940. /*
  941.  * fixpath - do nothing...
  942.  */
  943. fixpath(name, len)
  944. char *name;
  945. int  len;
  946. {
  947. }
  948.  
  949.  
  950. /*
  951.  * compresspath - given a base path and an additional directory, collapse
  952.  *                ".." and "." elements and return absolute path (appending
  953.  *                base if necessary).  
  954.  *
  955.  *                returns  1 if OK, 
  956.  *                         0 if there's a problem
  957.  *                         new path, by side effect, if things went OK
  958.  */
  959. compresspath(base, path, len)
  960. char *base, *path;
  961. int  len;
  962. {
  963.     register int i;
  964.     int  depth = 0;
  965.     char *p;
  966.     char *stack[32];
  967.  
  968. #define PUSHD(X)  (stack[depth++] = X)
  969. #define POPD()    ((depth > 0) ? stack[--depth] : "")
  970.  
  971.     if(*path == '~'){
  972.     fixpath(path, len);
  973.     strcpy(s, path);
  974.     }
  975.     else if(*path != C_FILESEP)
  976.       sprintf(s, "%s%c%s", base, C_FILESEP, path);
  977.     else
  978.       strcpy(s, path);
  979.  
  980.     p = s;
  981.     for(i=0; s[i] != '\0'; i++){        /* pass thru path name */
  982.     if(s[i] == '/'){
  983.         if(p != s)
  984.           PUSHD(p);                /* push dir entry */
  985.         p = &s[i+1];            /* advance p */
  986.         s[i] = '\0';            /* cap old p off */
  987.         continue;
  988.     }
  989.  
  990.     if(s[i] == '.'){            /* special cases! */
  991.         if(s[i+1] == '.'            /* parent */
  992.            && (s[i+2] == '/' || s[i+2] == '\0')){
  993.         if(!strcmp(POPD(),""))        /* bad news! */
  994.           return(0);
  995.  
  996.         i += 2;
  997.         p = (s[i] == '\0') ? "" : &s[i+1];
  998.         }
  999.         else if(s[i+1] == '/' || s[i+1] == '\0'){        /* no op */
  1000.         i++;
  1001.         p = (s[i] == '\0') ? "" : &s[i+1];
  1002.         }
  1003.     }
  1004.     }
  1005.  
  1006.     if(*p != '\0')
  1007.       PUSHD(p);                    /* get last element */
  1008.  
  1009.     path[0] = '\0';
  1010.     for(i = 0; i < depth; i++){
  1011.     strcat(path, S_FILESEP);
  1012.     strcat(path, stack[i]);
  1013.     }
  1014.  
  1015.     return(1);                    /* everything's ok */
  1016. }
  1017.  
  1018.  
  1019. /*
  1020.  * tmpname - return a temporary file name in the given buffer
  1021.  */
  1022. void
  1023. tmpname(name)
  1024. char *name;
  1025. {
  1026.     sprintf(name, "/SYS$SCRATCH/pico.%d", getpid());    /* tmp file name */
  1027. }
  1028.  
  1029.  
  1030. /*
  1031.  * Take a file name, and from it
  1032.  * fabricate a buffer name. This routine knows
  1033.  * about the syntax of file names on the target system.
  1034.  * I suppose that this information could be put in
  1035.  * a better place than a line of code.
  1036.  */
  1037. void
  1038. makename(bname, fname)
  1039. char    bname[];
  1040. char    fname[];
  1041. {
  1042.     register char   *cp1;
  1043.     register char   *cp2;
  1044.  
  1045.     cp1 = &fname[0];
  1046.     while (*cp1 != 0)
  1047.       ++cp1;
  1048.  
  1049.     while (cp1!=&fname[0] && cp1[-1]!='/')
  1050.       --cp1;
  1051.  
  1052.     cp2 = &bname[0];
  1053.     while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=';')
  1054.       *cp2++ = *cp1++;
  1055.  
  1056.     *cp2 = 0;
  1057. }
  1058.  
  1059.  
  1060. /*
  1061.  * copy - copy contents of file 'a' into a file named 'b'.  Return error
  1062.  *        if either isn't accessible or is a directory
  1063.  */
  1064. copy(a, b)
  1065. char *a, *b;
  1066. {
  1067.     int    in, out, n, rv = 0;
  1068.     char   *cb;
  1069.     struct stat tsb, fsb;
  1070.     extern int  errno;
  1071.  
  1072.     if(stat(a, &fsb) < 0){        /* get source file info */
  1073.     emlwrite("Can't Copy: %s", errstr(errno));
  1074.     return(-1);
  1075.     }
  1076.  
  1077.     if(!(fsb.st_mode&S_IREAD)){        /* can we read it? */
  1078.     emlwrite("\007Read permission denied: %s", a);
  1079.     return(-1);
  1080.     }
  1081.  
  1082.     if((fsb.st_mode&S_IFMT) == S_IFDIR){ /* is it a directory? */
  1083.     emlwrite("\007Can't copy: %s is a directory", a);
  1084.     return(-1);
  1085.     }
  1086.  
  1087.     if(stat(b, &tsb) < 0){        /* get dest file's mode */
  1088.     switch(errno){
  1089.       case ENOENT:
  1090.         break;            /* these are OK */
  1091.       default:
  1092.         emlwrite("\007Can't Copy: %s", errstr(errno));
  1093.         return(-1);
  1094.     }
  1095.     }
  1096.     else{
  1097.     if(!(tsb.st_mode&S_IWRITE)){    /* can we write it? */
  1098.         emlwrite("\007Write permission denied: %s", b);
  1099.         return(-1);
  1100.     }
  1101.  
  1102.     if((tsb.st_mode&S_IFMT) == S_IFDIR){    /* is it directory? */
  1103.         emlwrite("\007Can't copy: %s is a directory", b);
  1104.         return(-1);
  1105.     }
  1106.  
  1107.     if(fsb.st_dev == tsb.st_dev && fsb.st_ino == tsb.st_ino){
  1108.         emlwrite("\007Identical files.  File not copied", NULL);
  1109.         return(-1);
  1110.     }
  1111.     }
  1112.  
  1113.     if((in = open(a, O_RDONLY)) < 0){
  1114.     emlwrite("Copy Failed: %s", errstr(errno));
  1115.     return(-1);
  1116.     }
  1117.  
  1118.     if((out=creat(b, fsb.st_mode&0xfff)) < 0){
  1119.     emlwrite("Can't Copy: %s", errstr(errno));
  1120.     close(in);
  1121.     return(-1);
  1122.     }
  1123.  
  1124.     if((cb = (char *)malloc(NLINE*sizeof(char))) == NULL){
  1125.     emlwrite("Can't allocate space for copy buffer!", NULL);
  1126.     close(in);
  1127.     close(out);
  1128.     return(-1);
  1129.     }
  1130.  
  1131.     while(1){                /* do the copy */
  1132.     if((n = read(in, cb, NLINE)) < 0){
  1133.         emlwrite("Can't Read Copy: %s", errstr(errno));
  1134.         rv = -1;
  1135.         break;            /* get out now */
  1136.     }
  1137.  
  1138.     if(n == 0)            /* done! */
  1139.       break;
  1140.  
  1141.     if(write(out, cb, n) != n){
  1142.         emlwrite("Can't Write Copy: %s", errstr(errno));
  1143.         rv = -1;
  1144.         break;
  1145.     }
  1146.     }
  1147.  
  1148.     free(cb);
  1149.     close(in);
  1150.     close(out);
  1151.     return(rv);
  1152. }
  1153.  
  1154.  
  1155. /*
  1156.  * Open a file for writing. Return TRUE if all is well, and FALSE on error
  1157.  * (cannot create).
  1158.  */
  1159. ffwopen(fn)
  1160. char    *fn;
  1161. {
  1162.     extern FILE *ffp;
  1163.  
  1164.     if ((ffp=fopen(fn, "w")) == NULL) {
  1165.         emlwrite("Cannot open file for writing", NULL);
  1166.         return (FIOERR);
  1167.     }
  1168.  
  1169.     return (FIOSUC);
  1170. }
  1171.  
  1172.  
  1173. /*
  1174.  * Close a file. Should look at the status in all systems.
  1175.  */
  1176. ffclose()
  1177. {
  1178.     extern FILE *ffp;
  1179.  
  1180.     if (fclose(ffp) != FALSE) {
  1181.         emlwrite("Error closing file", NULL);
  1182.         return(FIOERR);
  1183.     }
  1184.  
  1185.     return(FIOSUC);
  1186. }
  1187.  
  1188.  
  1189. /*
  1190.  * P_open - run the given command in a sub-shell returning a file pointer
  1191.  *        from which to read the output
  1192.  *
  1193.  * note:
  1194.  *    For OS's other than unix, you will have to rewrite this function.
  1195.  *    Hopefully it'll be easy to exec the command into a temporary file, 
  1196.  *    and return a file pointer to that opened file or something.
  1197.  */
  1198. FILE *P_open(s)
  1199. char *s;
  1200. {
  1201.     printf("Popen not supported on VMS yet.\n");
  1202.     return NULL;
  1203. }
  1204.  
  1205.  
  1206.  
  1207. /*
  1208.  * P_close - close the given descriptor
  1209.  *
  1210.  */
  1211. P_close(fp)
  1212. FILE *fp;
  1213. {
  1214.     return;
  1215. }
  1216.  
  1217.  
  1218.  
  1219. /*
  1220.  * worthit - generic sort of test to roughly gage usefulness of using 
  1221.  *           optimized scrolling.
  1222.  *
  1223.  * note:
  1224.  *    returns the line on the screen, l, that the dot is currently on
  1225.  */
  1226. worthit(l)
  1227. int *l;
  1228. {
  1229.     int i;            /* l is current line */
  1230.     unsigned below;        /* below is avg # of ch/line under . */
  1231.  
  1232.     *l = doton(&i, &below);
  1233.     below = (i > 0) ? below/(unsigned)i : 0;
  1234.  
  1235.     return(below > 3);
  1236. }
  1237.  
  1238.  
  1239.  
  1240. /*
  1241.  * pico_new_mail - just checks mtime and atime of mail file and notifies user 
  1242.  *               if it's possible that they have new mail.
  1243.  */
  1244. pico_new_mail()
  1245. {
  1246.     int ret = 0;
  1247.     static time_t lastchk = 0;
  1248.     struct stat sbuf;
  1249.     char   inbox[256], *p;
  1250.  
  1251.     sprintf(inbox,"SYS$LOGIN:MAIL.MAI");
  1252.  
  1253.     if(stat(inbox, &sbuf) == 0){
  1254.     ret = sbuf.st_atime <= sbuf.st_mtime &&
  1255.       (lastchk < sbuf.st_mtime && lastchk < sbuf.st_atime);
  1256.     lastchk = sbuf.st_mtime;
  1257.     return(ret);
  1258.     }
  1259.     else
  1260.       return(ret);
  1261. }
  1262.  
  1263.  
  1264.  
  1265. /*
  1266.  * time_to_check - checks the current time against the last time called 
  1267.  *                 and returns true if the elapsed time is > timeout
  1268.  */
  1269. time_to_check()
  1270. {
  1271.     static time_t lasttime = 0L;
  1272.  
  1273.     if(!timeout)
  1274.       return(FALSE);
  1275.  
  1276.     if(time((long *) 0) - lasttime > (time_t)timeout){
  1277.     lasttime = time((long *) 0);
  1278.     return(TRUE);
  1279.     }
  1280.     else
  1281.       return(FALSE);
  1282. }
  1283.  
  1284.  
  1285. /*
  1286.  * sstrcasecmp - compare two pointers to strings case independently
  1287.  */
  1288. sstrcasecmp(s1, s2)
  1289. QcompType *s1, *s2;
  1290. {
  1291.     register char *a, *b;
  1292.  
  1293.     a = *(char **)s1;
  1294.     b = *(char **)s2;
  1295.     while(toupper(*a) == toupper(*b++))
  1296.     if(*a++ == '\0')
  1297.       return(0);
  1298.  
  1299.     return(toupper(*a) - toupper(*--b));
  1300. }
  1301.  
  1302.  
  1303. #ifdef    TIOCGWINSZ
  1304. /*
  1305.  * winch_handler - handle window change signal
  1306.  */
  1307. SIGTYPE winch_handler()
  1308. {
  1309.     struct winsize win;
  1310.     extern int resize_pico();
  1311.  
  1312.     signal(SIGWINCH, winch_handler);
  1313.  
  1314.     if (ioctl(0, TIOCGWINSZ, &win) == 0) {
  1315.     if (win.ws_col && win.ws_row)
  1316.       resize_pico(win.ws_row - 1, win.ws_col);
  1317.     }
  1318. }
  1319. #endif    /* TIOCGWINSZ */
  1320.  
  1321.  
  1322. /* Copied from TCAP */
  1323. hide_cur()
  1324. {
  1325. write_terminal("\033[?25l", 6);
  1326. }
  1327. show_cur()
  1328. {
  1329. write_terminal("\033[?25h", 6);
  1330. }
  1331.  
  1332. o_insert()
  1333. {
  1334.     return 0;
  1335. }
  1336.  
  1337. o_delete()
  1338. {
  1339.     return 0;
  1340. }
  1341.  
  1342.  
  1343. init_kpnames()
  1344. {
  1345.     kpinsert("OP",F1);
  1346.     kpinsert("OQ",F2);
  1347.     kpinsert("OR",F3);
  1348.     kpinsert("OS",F4);
  1349.     kpinsert("Op",F5);
  1350.     kpinsert("Oq",F6);
  1351.     kpinsert("Or",F7);
  1352.     kpinsert("Os",F8);
  1353.     kpinsert("Ot",F9);
  1354.     kpinsert("Ou",F10);
  1355.     kpinsert("Ov",F11);
  1356.     kpinsert("Ow",F12);
  1357.  
  1358.     /*
  1359.      * special keypad functions
  1360.      */
  1361.     kpinsert("[4J",K_PAD_PREVPAGE);
  1362.     kpinsert("[3J",K_PAD_NEXTPAGE);
  1363.     kpinsert("[2J",K_PAD_HOME);
  1364.     kpinsert("[N",K_PAD_END);
  1365.  
  1366.     /* 
  1367.      * ANSI mode.
  1368.      */
  1369.     kpinsert("[=a",F1);
  1370.     kpinsert("[=b",F2);
  1371.     kpinsert("[=c",F3);
  1372.     kpinsert("[=d",F4);
  1373.     kpinsert("[=e",F5);
  1374.     kpinsert("[=f",F6);
  1375.     kpinsert("[=g",F7);
  1376.     kpinsert("[=h",F8);
  1377.     kpinsert("[=i",F9);
  1378.     kpinsert("[=j",F10);
  1379.     kpinsert("[=k",F11);
  1380.     kpinsert("[=l",F12);
  1381.  
  1382.     kpinsert("OA",K_PAD_UP);    /* DEC vt100, ANSI and cursor key mode. */
  1383.     kpinsert("OB",K_PAD_DOWN);
  1384.     kpinsert("OD",K_PAD_LEFT);
  1385.     kpinsert("OC",K_PAD_RIGHT);
  1386.  
  1387.     kpinsert("[A",K_PAD_UP);    /* DEC vt100, ANSI, cursor key mode reset. */
  1388.     kpinsert("[B",K_PAD_DOWN);
  1389.     kpinsert("[D",K_PAD_LEFT);
  1390.     kpinsert("[C",K_PAD_RIGHT);
  1391.  
  1392.     kpinsert("A",K_PAD_UP);    /* DEC vt52 mode. */
  1393.     kpinsert("B",K_PAD_DOWN);
  1394.     kpinsert("D",K_PAD_LEFT);
  1395.     kpinsert("C",K_PAD_RIGHT);
  1396.  
  1397.     kpinsert("[215z",K_PAD_UP); /* Sun Console sequences. */
  1398.     kpinsert("[221z",K_PAD_DOWN);
  1399.     kpinsert("[217z",K_PAD_LEFT);
  1400.     kpinsert("[219z",K_PAD_RIGHT);
  1401. }
  1402.  
  1403.  
  1404. #define    newnode()    (struct KBSTREE *)malloc(sizeof(struct KBSTREE))
  1405. /*
  1406.  * kbinsert - insert a keystroke escape sequence into the global search
  1407.  *          structure.
  1408.  */
  1409. kpinsert(kstr, kval)
  1410. char    *kstr;
  1411. int    kval;
  1412. {
  1413.     register    char    *buf;
  1414.     register    struct KBSTREE *temp;
  1415.     register    struct KBSTREE *trail;
  1416.  
  1417.     if(kstr == NULL)
  1418.       return;
  1419.  
  1420.     temp = trail = kpadseqs;
  1421.     if(kstr[0] == '\033')
  1422.       buf = kstr+1;            /* can the ^[ character */ 
  1423.     else
  1424.       buf = kstr;
  1425.  
  1426.     for(;;) {
  1427.     if(temp == NULL){
  1428.         temp = newnode();
  1429.         temp->value = *buf;
  1430.         temp->func = 0;
  1431.         temp->left = NULL;
  1432.         temp->down = NULL;
  1433.         if(kpadseqs == NULL)
  1434.           kpadseqs = temp;
  1435.         else
  1436.           trail->down = temp;
  1437.     }
  1438.     else{                /* first entry */
  1439.         while((temp != NULL) && (temp->value != *buf)){
  1440.         trail = temp;
  1441.         temp = temp->left;
  1442.         }
  1443.  
  1444.         if(temp == NULL){   /* add new val */
  1445.         temp = newnode();
  1446.         temp->value = *buf;
  1447.         temp->func = 0;
  1448.         temp->left = NULL;
  1449.         temp->down = NULL;
  1450.         trail->left = temp;
  1451.         }
  1452.     }
  1453.  
  1454.     if (*(++buf) == '\0'){
  1455.         break;
  1456.     }
  1457.     else{
  1458.         trail = temp;
  1459.         temp = temp->down;
  1460.     }
  1461.     }
  1462.     
  1463.     if(temp != NULL)
  1464.       temp->func = kval;
  1465. }