home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / developm / source / suntar1.cpt / gc / my ANSI / my console.c next >
Encoding:
C/C++ Source or Header  |  1991-06-13  |  38.0 KB  |  2,257 lines

  1.  
  2. /*
  3.  *  console.c
  4.  *
  5.  *  Copyright (c) 1989 Symantec Corporation.  All rights reserved.
  6.  *
  7.  
  8.  This file has some additions by G. Speranza, clearly identified by comments
  9.  It is fully compatible with the original version, nothing was lost, but there
  10.  are a few new entry points and a few "hooks" by which you may exploit some
  11.  potentialities which existed but where not intended to be exploited by user 
  12.  programs
  13.  */
  14.  
  15. #include <MacHeaders>
  16. #include <PrintMgr.h>
  17. extern Boolean WWExist : 0x8F2;
  18.  
  19. /*  remove this line to work on ALL systems  */
  20. #include <PrintTraps.h>        /*  requires System 4.1  */
  21.  
  22. #include "stdio.h"
  23. #include "stddef.h"
  24. #include "stdlib.h"
  25. #include "string.h"
  26. #include "signal.h"
  27. #include "errno.h"
  28. #include "console.h"
  29. #include "ansi_private.h"
  30.  
  31.  
  32. /********* aggiunta di G. Speranza **********/
  33.  
  34. int isConsoleWindow(WindowPeek);
  35.  
  36. void (*my_add_about)(MenuHandle) =NULL;    /* aggiunta entry al men¥ mela */
  37.                 /* you may assign to it a function which adds items 
  38.                 to the apple menu */
  39. void (*my_add_commands)(MenuHandle) =NULL; /* aggiunta al men¥ file */
  40.                 /* add items to the File menu */
  41. void (*my_add_menus)() =NULL;        /* aggiunta altri men¥ dopo il men¥ edit */
  42.                 /* add menus after the Edit menu */
  43. void (*my_handle_menus)(long)=NULL;        /* gestione nuove entry in qualunque men¥ */
  44.                 /* handle selection of any item introduced by previous
  45.                 routines */
  46. void (*my_event_filter)(EventRecord*)=NULL;    /* permette di "filtrare" un evento, dopo che
  47.                 ProcessEvent ha chiamato GetNextEvent e prima che lo
  48.                 gestisca, basta settare il campo what a nullEvent per 
  49.                 sopprimere la gestione standard*/
  50.                 /* filter events, that is handle (and possibly modify)
  51.                 them before the standard actions take place */
  52.  
  53. long my_current_size=0;        /* permette di settare le dimensioni iniziali
  54.                 di una finestra a un valore diverso dalle dimensioni
  55.                 massime (quelle oltre cui poi si rifiuta di crescere),
  56.                 meglio usarlo attraverso le funzioni in my_window */
  57. long my_current_A5;        /* da usare attraverso le funzioni in my_window */
  58. static short quit_selection;
  59. static short n_about_entries;
  60.  
  61. /********* isConsoleWindow: returns true if the window belongs to the ANSI library
  62. (FindWindow returns inSysWindow, as if they belonged to desk accessories! ) */
  63.  
  64. int isConsoleWindow(wp)
  65. WindowPeek wp;
  66. {
  67. register FILE *fp;
  68. int n;
  69. for (fp = &__file[0], n = FOPEN_MAX ; n--; fp++)
  70.     if (fp->refnum && (fp->window==wp) ) return 1;
  71. return 0;
  72. }
  73.  
  74. /********* fine aggiunta ***************/
  75.  
  76.  
  77.  
  78. struct __copt console_options = { 50, 10, "\pconsole", 8, 4, 9, 0, 25, 80, 1 };
  79. char __log_stdout;
  80.  
  81. /*commentato
  82. static 
  83. *****/
  84.  void InitConsole(void);
  85. /****** commentato 
  86. static
  87. *****/
  88.  void ProcessEvent(void);
  89. static WindowPeek new_console(void);
  90. static void kill_console(void);
  91. static void closeecho(void);
  92. static WindowPeek cflush(FILE *);
  93.  
  94. static void console_exit(void);
  95. static int consoleio(FILE *, int);
  96. static pascal void myStdText(int, char *, Point, Point);
  97.  
  98. static void cleos(int);
  99. static void cleol(void);
  100. static void output(unsigned char *, int);
  101. static void overwrite(unsigned char *, long);
  102. static void delete(int, int);
  103. static void insert(char, int);
  104. static void pad(char, long, long);
  105. static void paste(int, int);
  106. static int setcursor(int);
  107. static TEPtr deactivate(void);
  108. static void strip(void);
  109. static char *copy(void);
  110. static void endcopy(void);
  111. static void newline(void);
  112. static void resize(void);
  113. static void setup(WindowPeek, struct save *);
  114. static void restore(struct save *);
  115. static void use(WindowPeek);
  116.  
  117. static int open_console_driver(void);
  118. static int doClose(void);
  119. static int doControl(void);
  120. static void doCursor(void);
  121. static void doClick(EventRecord *);
  122. static void doZoom(Point, int);
  123. static void doGrow(Point);
  124. static void doSelect(EventRecord *);
  125. static int doCmdKey(int);
  126. static void doKey(int);
  127. static void doCut(void);
  128. static void doCopy(void);
  129. static void doPaste(void);
  130.  
  131. static void print_console(void);
  132. static void print(void);
  133.  
  134. static pascal Handle NewHandle(long);
  135. static pascal void DisposHandle(void *);
  136.  
  137. static struct console {
  138.     WindowPeek            wp;
  139.     short                height;
  140.     short                width;
  141.     short                nrows;
  142.     short                ncols;
  143.     Point                cursor;
  144.     short                tabs;
  145.     TEHandle            hTE;
  146.     Point                limit;
  147.     Handle                pasteH;
  148.     long                pasteOfs;
  149.     long                pasteLen;
  150.     FILE                *echo2fp;
  151.     unsigned            raw : 1;
  152.     unsigned            cbreak : 1;
  153.     unsigned             edit : 1;
  154.     unsigned             reading : 1;
  155.     unsigned            inverse : 1;
  156.     unsigned            spool : 1;
  157. } c;
  158.  
  159. static char console_environment, noPrint, interrupted;
  160. static short console_refnum;
  161. static MenuHandle appleMenu;
  162. static WindowPeek theConsole;
  163.  
  164. struct save {
  165.     WindowPeek            console;
  166.     GrafPtr                port;
  167. };
  168.  
  169. static struct {
  170.     unsigned char    *buf;
  171.     unsigned char    *ptr;
  172.     size_t            cnt;
  173.     int                min;
  174.     int                max;
  175. } in;
  176.  
  177. struct vector {
  178.     short            vJMP;
  179.     int                (*vCode)();
  180. };
  181.  
  182. #define OFS(x)        offsetof(struct drvr, x)
  183.  
  184. static struct drvr {
  185.     short            drvrFlags, drvrDelay, drvrEMask, drvrMenu;
  186.     short            drvrOpen, drvrPrime, drvrCtl, drvrStatus, drvrClose;
  187.     char            drvrName[10];
  188.     short            drvrRTS;
  189.     struct vector    vCtl, vClose;
  190. } drvr = {
  191.     0x0760, 0, 0x016A, 0,
  192.     OFS(drvrRTS), OFS(drvrRTS), OFS(vCtl), OFS(drvrRTS), OFS(vClose),
  193.     "\p.console",
  194.     0x4E75,
  195.     { 0x4EF9 }, { 0x4EF9 }
  196. }, **drvrH;
  197.  
  198. #define hiword(x)        (((short *) &(x))[0])
  199. #define loword(x)        (((short *) &(x))[1])
  200.  
  201. /* ---------- public entry points ---------- */
  202.  
  203.  
  204. /*
  205.  *  fopenc - open a stream on a new console
  206.  *
  207.  */
  208.  
  209. FILE *
  210. fopenc()
  211. {
  212.     return(freopenc(NULL, __getfile()));
  213. }
  214.  
  215.  
  216. /*
  217.  *  freopenc - reopen a stream on a new or existing console
  218.  *
  219.  *  "fp" is closed, if necessary, then opened as a console.  If "fp2"
  220.  *  is NULL, a new console is created; otherwise "fp2" must refer to
  221.  *  a console, and "fp" is made to refer to the same console.
  222.  *
  223.  */
  224.  
  225. FILE *
  226. freopenc(fp2, fp)
  227. register FILE *fp2, *fp;
  228. {
  229.     if (fp == NULL)
  230.         return(NULL);
  231.     if (WWExist)
  232.         InitConsole();
  233.     fclose(fp);
  234.     fp->refnum = -1;
  235.     fp->window = fp2 ? fp2->window : new_console();
  236.     setvbuf(fp, NULL, _IOLBF, BUFSIZ);
  237.     fp->proc = consoleio;
  238.     __atexit_console(console_exit);
  239.     return(fp);
  240. }
  241.  
  242.  
  243. /*
  244.  *  cgotoxy - position cursor at <x,y>
  245.  *
  246.  *  The position of the upper left corner is <1,1>.
  247.  *
  248.  *  This routine does NOT check its arguments.  Don't place the
  249.  *  cursor off-screen!
  250.  *
  251.  */
  252.  
  253. void
  254. cgotoxy(x, y, fp)
  255. int x, y;
  256. FILE *fp;
  257. {
  258.     struct save save;
  259.     
  260.     setup(cflush(fp), &save);
  261.     c.cursor.h = x - 1;
  262.     c.cursor.v = y - 1;
  263.     restore(&save);
  264. }
  265.  
  266.  
  267. /*
  268.  *  cgetxy - report the current cursor position
  269.  *
  270.  *  The position of the upper left corner is <1,1>.
  271.  *
  272.  */
  273.  
  274. void
  275. cgetxy(x, y, fp)
  276. int *x, *y;
  277. FILE *fp;
  278. {
  279.     struct save save;
  280.     
  281.     setup(cflush(fp), &save);
  282.     *x = c.cursor.h + 1;
  283.     *y = c.cursor.v + 1;
  284.     restore(&save);
  285. }
  286.  
  287.  
  288. /*
  289.  *  ccleos - clear from cursor to end of screen
  290.  *
  291.  *  The line containing the cursor, and all following lines, are erased.
  292.  *  The cursor is left at the start of the first line erased.
  293.  *
  294.  */
  295.  
  296. void
  297. ccleos(fp)
  298. FILE *fp;
  299. {
  300.     struct save save;
  301.     
  302.     setup(cflush(fp), &save);
  303.     cleos(c.cursor.v);
  304.     restore(&save);
  305. }
  306.  
  307.  
  308. /*
  309.  *  ccleol - clear from cursor to end of line
  310.  *
  311.  */
  312.  
  313. void
  314. ccleol(fp)
  315. FILE *fp;
  316. {
  317.     struct save save;
  318.     
  319.     setup(cflush(fp), &save);
  320.     cleol();
  321.     restore(&save);
  322. }
  323.  
  324.  
  325. /*
  326.  *  csettabs - set tab stops
  327.  *
  328.  */
  329.  
  330. void
  331. csettabs(tabs, fp)
  332. int tabs;
  333. FILE *fp;
  334. {
  335.     struct save save;
  336.     
  337.     setup(cflush(fp), &save);
  338.     if (tabs < 1 || tabs > c.ncols)
  339.         tabs = 1;
  340.     c.tabs = tabs;
  341.     restore(&save);
  342. }
  343.  
  344.  
  345. /*
  346.  *  csetmode - set console mode
  347.  *
  348.  */
  349.  
  350. void
  351. csetmode(mode, fp)
  352. int mode;
  353. FILE *fp;
  354. {
  355.     struct save save;
  356.  
  357.     setup(cflush(fp), &save);
  358.     c.raw = c.cbreak = c.edit = 0;
  359.     switch (mode) {
  360.         case C_RAW:
  361.             c.raw = 1;
  362.             break;
  363.         case C_CBREAK:
  364.             c.cbreak = 1;
  365.             break;
  366.         case C_NOECHO:
  367.             break;
  368.         case C_ECHO:
  369.             c.edit = 1;
  370.             break;
  371.     }
  372.     restore(&save);
  373. }
  374.  
  375.  
  376. /*
  377.  *  cinverse - set inverse video mode
  378.  *
  379.  *  As a side effect, the entire screen is erased.  The cursor is moved
  380.  *  to the start of its line.
  381.  *
  382.  */
  383.  
  384. void
  385. cinverse(on, fp)
  386. int on;
  387. FILE *fp;
  388. {
  389.     register WindowPeek wp = cflush(fp);
  390.     struct save save;
  391.  
  392.     setup(wp, &save);
  393.     if (on) {
  394.         if (!wp->port.grafProcs) {
  395.             wp->port.grafProcs = malloc(sizeof(QDProcs));
  396.             SetStdProcs(wp->port.grafProcs);
  397.             wp->port.grafProcs->textProc = (Ptr) myStdText;
  398.         }
  399.     }
  400.     else {
  401.         if (wp->port.grafProcs) {
  402.             free(wp->port.grafProcs);
  403.             wp->port.grafProcs = 0;
  404.         }
  405.     }
  406.     cleos(0);
  407.     restore(&save);
  408. }
  409.  
  410.  
  411. /*
  412.  *  cshow - show a console window
  413.  *
  414.  *  All pending output to the window is forced to appear.
  415.  *
  416.  */
  417.  
  418. void
  419. cshow(fp)
  420. register FILE *fp;
  421. {
  422.     WindowPeek wp = cflush(fp);
  423.  
  424.     SelectWindow(wp);
  425.     ShowWindow(wp);
  426. }
  427.  
  428.  
  429. /*
  430.  *  chide - hide a console window
  431.  *
  432.  */
  433.  
  434. void
  435. chide(fp)
  436. FILE *fp;
  437. {
  438.     HideWindow(cflush(fp));
  439. }
  440.  
  441.  
  442. /*
  443.  *  cecho2file - echo console display to file
  444.  *
  445.  */
  446.  
  447. void
  448. cecho2file(s, append, fp)
  449. char *s;
  450. int append;
  451. FILE *fp;
  452. {
  453.     struct save save;
  454.     
  455.     setup(cflush(fp), &save);
  456.     closeecho();
  457.     c.echo2fp = fopen(s, append ? "a" : "w");
  458.     c.spool = 0;
  459.     restore(&save);
  460. }
  461.  
  462.  
  463. /*
  464.  *  cecho2printer - echo console display to printer
  465.  *
  466.  */
  467.  
  468. void
  469. cecho2printer(fp)
  470. FILE *fp;
  471. {
  472.     struct save save;
  473.     
  474.     setup(cflush(fp), &save);
  475.     closeecho();
  476.     c.echo2fp = tmpfile();
  477.     c.spool = 1;
  478.     restore(&save);
  479. }
  480.  
  481.  
  482. /* ---------- console management ---------- */
  483.  
  484.  
  485. /*
  486.  *  __open_std - open the std streams
  487.  *
  488.  *  This is called automatically (by "__checkfile") whenever an
  489.  *  unopened std stream is referenced.
  490.  *
  491.  */
  492.  
  493. void
  494. __open_std()
  495. {
  496.     FILE *fp = NULL;
  497.     char buf[40];
  498.     
  499.     if (stdin->std)
  500.         fp = freopenc(fp, stdin);
  501.     if (stdout->std)
  502.         fp = freopenc(fp, stdout);
  503.     if (stderr->std)
  504.         fp = freopenc(fp, stderr);
  505.     if (__log_stdout) {
  506.         sprintf(buf, "%#s.log", CurApName);
  507.         cecho2file(buf, 1, stdout);
  508.         console_options.pause_atexit = 0;
  509.     }
  510. }
  511.  
  512.  
  513. /*
  514.  *  InitConsole - initialize the console environment
  515.  *
  516.  */
  517.  
  518. /******* commentato
  519. static
  520. *****/
  521.  void
  522. InitConsole()
  523. {
  524.     MenuHandle menu;
  525.  
  526.     /***** aggiunta *****/
  527.     static _inited=0;
  528.  
  529.     if(_inited)return;
  530.     _inited=1;
  531.     /******** fine aggiunta *******/
  532.  
  533.         /*  initialize the Memory Manager  */
  534.  
  535.     asm {
  536.         moveq    #9,d1
  537.         tst.w    ROM85
  538.         bmi.s    @1
  539.         _MaxApplZone
  540. @1        _MoreMasters
  541.         dbra    d1,@1
  542.     }
  543.  
  544.         /*  initialize Quickdraw  */
  545.  
  546.     asm {
  547.         move.l    #206,d0
  548.         _NewPtr
  549.         pea        202(a0)
  550.         /************** aggiunta ***********/
  551.         move.l (SP),my_current_A5
  552.         /********* fine *********/
  553.         _InitGraf
  554.     }
  555.  
  556.         /*  initialize the Toolbox  */
  557.  
  558.     InitFonts();
  559.     InitWindows();
  560.     TEInit();
  561.     InitDialogs(0);
  562.     InitMenus();
  563.  
  564.         /*  create menus  */
  565.  
  566.     InsertMenu(appleMenu = NewMenu(1, "\p\024"), 0);
  567.  
  568.     /******** aggiunta di G. Speranza ****/
  569.     n_about_entries=0;
  570.     if(my_add_about!=NULL){
  571.         (*my_add_about)(appleMenu);
  572.         n_about_entries=CountMItems(appleMenu);
  573.         AppendMenu(appleMenu, "\p(-");
  574.         }
  575.     /** fine aggiunta **********/
  576.  
  577.     AddResMenu(appleMenu, 'DRVR');
  578.     InsertMenu(menu = NewMenu(2, "\pFile"), 0);
  579.  
  580.     /****** altra aggiunta *********/
  581.     if(my_add_commands!=NULL){
  582.         (*my_add_commands)(menu);
  583.         AppendMenu(menu, "\p(-");
  584.         }
  585.     quit_selection=CountMItems(menu)+1;
  586.     /****** fine aggiunta ********/
  587.  
  588.     AppendMenu(menu, "\pQuit/Q");
  589.  
  590.     InsertMenu(menu = NewMenu(3, "\pEdit"), 0);
  591.     AppendMenu(menu, "\pUndo/Z;(-;Cut/X;Copy/C;Paste/V;Clear");
  592.  
  593.     /******* altra aggiunta ******/
  594.     if(my_add_menus!=NULL)
  595.         (*my_add_menus)();
  596.     /****** fine aggiunta ********/
  597.  
  598.     DrawMenuBar();
  599.     
  600.         /*  ready to receive events  */
  601.         
  602.     asm {
  603.         moveq    #0,d0
  604.         subq.w    #1,d0
  605.         _FlushEvents
  606.     }
  607.     InitCursor();
  608.     console_environment = 1;
  609. }
  610.  
  611.  
  612. /*
  613.  *  ProcessEvent - handle one event
  614.  *
  615.  */
  616.  
  617. /************* commentato
  618. static 
  619. */
  620. void
  621. ProcessEvent()
  622. {
  623.     int key;
  624.     EventRecord event;
  625.     WindowPeek wp;
  626.     long choice;
  627.     Str255 buf;
  628.     
  629.         /*  process key from paste buffer  */
  630.  
  631.     if (c.pasteH) {
  632.         key = (unsigned char) (*c.pasteH)[c.pasteOfs++];
  633.         if (c.pasteOfs == c.pasteLen) {
  634.             DisposHandle(c.pasteH);
  635.             c.pasteH = 0;
  636.         }
  637.         if (c.inverse)
  638.             key &= 0x7F;
  639.         if (key == '\t')
  640.             key = ' ';
  641.         doKey(key);
  642.         return;
  643.     }
  644.     
  645.         /*  check for an event  */
  646.         
  647.     SystemTask();
  648.     SEvtEnb = false;
  649.     if (GetNextEvent(everyEvent, &event)) {
  650. /*********** aggiunta ************/
  651.         if(my_event_filter!=NULL) (*my_event_filter)(&event);
  652. /********* fine aggiunta *********/
  653.         if (!SystemEvent(&event))
  654.             goto doEvent;
  655.     }
  656.     else if (event.what == nullEvent) {
  657.         if (FrontWindow() == 0)
  658.             InitCursor();
  659.     }
  660.     return;
  661.     
  662.         /*  handle event  */
  663.  
  664. doEvent:
  665.     if (event.what == mouseDown) {
  666.         switch (FindWindow(event.where, &wp)) {
  667.             case inMenuBar:
  668.                 InitCursor();
  669.                 choice = MenuSelect(event.where);
  670.                 goto doMenu;
  671.             case inSysWindow:
  672.                 SystemClick(&event, wp);
  673.                 break;
  674.         }
  675.     }
  676.     return;
  677.  
  678.         /*  handle menu choice  */
  679.  
  680. doMenu:    
  681.     switch (hiword(choice)) {
  682.         case 1:        /*  Apple  */
  683.  
  684.             /****** aggiunta ******/
  685.             if(loword(choice)<=n_about_entries){
  686.                 (*my_handle_menus)(choice);
  687.                 break;
  688.                 }
  689.             /**** fine aggiunta **/
  690.  
  691.             GetItem(appleMenu, loword(choice), buf);
  692.             OpenDeskAcc(buf);
  693.             break;
  694.         case 2:        /*  File  */
  695.  
  696.             /****** aggiunta *********/
  697.             if(loword(choice)!=quit_selection){
  698.                 (*my_handle_menus)(choice);
  699.                 break;
  700.                 }
  701.             /**** fine aggiunta **/
  702.  
  703.             console_options.pause_atexit = 0;
  704.             exit(0);
  705.             /* no return */
  706.         case 3:        /*  Edit  */
  707.             SystemEdit(loword(choice) - 1);
  708.             break;
  709.  
  710.         /**** aggiunta *****/
  711.         case 0: /* no selection */
  712.             break;
  713.         default:
  714.             (*my_handle_menus)(choice);
  715.         /*** fine aggiunta ****/
  716.  
  717.     }
  718.     HiliteMenu(0);
  719. }
  720.  
  721.  
  722. /*
  723.  *  new_console - create a new console window
  724.  *
  725.  */
  726.  
  727. static WindowPeek
  728. new_console()
  729. {
  730.     GrafPtr savePort;
  731.     struct console **cH;
  732.     static Rect wbox = { 100, 100, 200, 300 };
  733.     register WindowPeek wp;
  734.     Rect bounds;
  735.     FontInfo fontInfo;
  736.     register WStateData *p;
  737.     
  738.     GetPort(&savePort);
  739.     use(0);
  740.     
  741.         /*  create the window  */
  742.         
  743.     wp = (WindowPeek) NewWindow(0, &wbox, console_options.title, 0, console_options.procID, -1, 0, 0);
  744.     MoveWindow(wp, console_options.left, console_options.top, 0);
  745.     SetPort(c.wp = wp);
  746.     
  747.         /*  set up the font characteristics  */
  748.         
  749.     TextFont(console_options.txFont);
  750.     TextSize(console_options.txSize);
  751.     TextFace(console_options.txFace);
  752.     GetFontInfo(&fontInfo);
  753.     c.height = fontInfo.ascent + fontInfo.descent + fontInfo.leading;
  754.     c.width = fontInfo.widMax;
  755.     
  756.         /*  set console defaults  */
  757.         
  758.     c.tabs = 8;
  759.     c.raw = c.cbreak = c.reading = c.inverse = 0;
  760.     c.edit = 1;
  761.     c.pasteH = 0;
  762.     c.echo2fp = 0;
  763.     
  764.         /*  set initial window size  */
  765.     
  766.     bounds.top = bounds.left = 0;
  767.     bounds.bottom = (c.nrows = console_options.nrows) * c.height + 8;
  768.     bounds.right = (c.ncols = console_options.ncols) * c.width + 8;
  769.  
  770.     /************** aggiunta ***********/
  771.     if(my_current_size!=0){
  772.         SizeWindow(wp, hiword(my_current_size) * c.width + 8,
  773.             loword(my_current_size) * c.height + 8, 0);
  774.         my_current_size=0;
  775.         }
  776.     else
  777.     /*************** fine aggiunta ********/
  778.     SizeWindow(wp, bounds.right, bounds.bottom, 0);
  779.     
  780.         /*  create TE record  */
  781.         
  782.     c.hTE = TENew(&bounds, &bounds);
  783.     (**c.hTE).crOnly = -1;
  784.     c.cursor.v = c.nrows - 1;
  785.     cleos(0);
  786.     
  787.         /*  set up grow/zoom parameters  */
  788.     
  789.     c.limit = botRight(bounds);
  790.     ++c.limit.v, ++c.limit.h;
  791.     LocalToGlobal(&topLeft(bounds));
  792.     LocalToGlobal(&botRight(bounds));
  793.     p = * (WStateData **) wp->dataHandle;
  794.     p->userState = p->stdState = bounds;
  795.  
  796.         /*  associate window with console  */
  797.  
  798.     asm {
  799.         lea        c,a0
  800.         move.l    #sizeof(c),d0
  801.         _PtrToHand
  802.         move.l    a0,offsetof(WindowRecord,refCon)(wp)
  803.     }
  804.     if (!console_refnum)
  805.         console_refnum = open_console_driver();
  806.     wp->windowKind = console_refnum;
  807.  
  808.         /*  done  */
  809.  
  810.     resize();
  811.     ShowWindow(wp);
  812.     SetPort(savePort);
  813.     return(wp);
  814. }
  815.  
  816.  
  817. /*
  818.  *  kill_console - close a console window
  819.  *
  820.  *  The console is not closed if more than one stream shares it.
  821.  *
  822.  */
  823.  
  824. static void
  825. kill_console()
  826. {
  827.     register FILE *fp;
  828.     int i = 0, n;
  829.     
  830.         /*  see if more than one stream shares this console  */
  831.         
  832.     for (fp = &__file[0], n = FOPEN_MAX; n--; fp++) {
  833.         if (fp->window == c.wp && i++)
  834.             return;
  835.     }
  836.     
  837.         /*  close echo file (if any)  */
  838.  
  839.     closeecho();
  840.     
  841.         /*  discard console data structures  */
  842.         
  843.     if (c.pasteH)
  844.         DisposHandle(c.pasteH);
  845.     DisposHandle((Handle) c.wp->refCon);
  846.     TEDispose(c.hTE);
  847.     DisposeWindow(c.wp);
  848.     c.wp = 0;
  849. }
  850.  
  851.  
  852. /*
  853.  *  closeecho - close echo file (if any)
  854.  *
  855.  */
  856.  
  857. static void
  858. closeecho()
  859. {
  860.     if (c.echo2fp) {
  861.         if (c.spool)
  862.             print_console();
  863.         fclose(c.echo2fp);
  864.     }
  865. }
  866.  
  867.  
  868. /*
  869.  *  cflush - flush all pending output to a console
  870.  *
  871.  */
  872.  
  873. static WindowPeek
  874. cflush(fp)
  875. register FILE *fp;
  876. {
  877.     WindowPeek wp = __checkfile(fp)->window;
  878.     int n;
  879.     
  880.     for (fp = &__file[0], n = FOPEN_MAX; n--; fp++) {
  881.         if (fp->dirty && fp->window == wp)
  882.             fflush(fp);
  883.     }
  884.     return(wp);
  885. }
  886.  
  887.  
  888. /* ---------- vectored entry points ---------- */
  889.  
  890.  
  891. /*
  892.  *  console_exit - console shutdown routine
  893.  *
  894.  */
  895.  
  896. static void
  897. console_exit()
  898. {
  899.     register FILE *fp;
  900.     int n;
  901.     
  902.         /*  complete pending output  */
  903.         
  904.     for (fp = &__file[0], n = FOPEN_MAX; n--; fp++) {
  905.         if (fp->dirty && fp->window)
  906.             fflush(fp);
  907.     }
  908.     
  909.         /*  pause for user acknowledgement  */
  910.     
  911.     if (console_environment && console_options.pause_atexit) {
  912.         for (fp = &__file[0], n = FOPEN_MAX; n--; fp++) {
  913.             if (fp->window) {
  914.                 SetWTitle(fp->window, "\ppress ╟return╚ to exit");
  915.                 c.raw = c.cbreak = c.edit = 0;
  916.                 setbuf(fp, NULL);
  917.                 fgetc(fp);
  918.                 break;
  919.             }
  920.         }
  921.     }
  922.  
  923.         /*  close consoles  */
  924.  
  925.     for (fp = &__file[0], n = FOPEN_MAX; n--; fp++) {
  926.         if (fp->window)
  927.             fclose(fp);
  928.     }
  929. }
  930.  
  931.  
  932. /*
  933.  *  consoleio - I/O handler proc for console windows
  934.  *
  935.  */
  936.  
  937. static int
  938. consoleio(fp, i)
  939. register FILE *fp;
  940. int i;
  941. {
  942.     struct save save;
  943.     int result = 0;
  944.     
  945.     if (_abnormal_exit)
  946.         return(0);
  947.     setup(fp->window, &save);
  948.     switch (i) {
  949.     
  950.                 /*  read  */
  951.             
  952.         case 0:
  953.             in.buf = in.ptr = fp->ptr;
  954.             if (console_environment) {
  955.                 cshow(fp);
  956.                 c.reading = 1;
  957.                 in.cnt = fp->cnt;
  958.                 if (c.edit && c.cursor.h + in.cnt > c.ncols)
  959.                     in.cnt = c.ncols - c.cursor.h + 1;
  960.                 in.min = in.max = c.raw ? 0 : setcursor(0);
  961.                 fp->eof = 0;
  962.                 do {
  963.                     ProcessEvent();
  964.                 } while (in.cnt && !c.raw);
  965.                 c.reading = 0;
  966.             }
  967.             if ((fp->cnt = in.ptr - in.buf) == 0) {
  968.                 fp->eof = 1;
  969.                 result = EOF;
  970.             }
  971.             break;
  972.             
  973.                 /*  write  */
  974.  
  975.         case 1:
  976.             output(fp->ptr, fp->cnt);
  977.             break;
  978.             
  979.                 /*  close  */
  980.  
  981.         case 2:
  982.             kill_console();
  983.             if (fp->window == save.console)
  984.                 save.console = 0;
  985.             break;
  986.     }
  987.     
  988.         /*  check for interrupt  */
  989.         
  990.     if (interrupted) {
  991.         interrupted = 0;
  992.         asm {
  993.             moveq    #keyDownMask,d0
  994.             _FlushEvents
  995.         }
  996.         fp->cnt = 0;
  997.         raise(SIGINT);
  998.         errno = EINTR;
  999.         result = EOF;
  1000.     }
  1001.  
  1002.         /*  done  */
  1003.     
  1004.     restore(&save);
  1005.     return(result);
  1006. }
  1007.  
  1008.  
  1009. /*
  1010.  *  myStdText - inverse video handler
  1011.  *
  1012.  */
  1013.  
  1014. static pascal void
  1015. myStdText(n, s, numer, denom)
  1016. register int n;
  1017. register char *s;
  1018. Point numer, denom;
  1019. {
  1020.     register char *t;
  1021.     char buf;
  1022.     
  1023.     while (n--) {
  1024.         t = s;
  1025.         asm {
  1026. @1            tst.b    (s)+
  1027. @2            dbmi    n,@1
  1028.             bpl.s    @3
  1029.             subq.l    #1,s
  1030. @3        }
  1031.         if (s > t)
  1032.             StdText(s - t, t, numer, denom);
  1033.         if (n < 0)
  1034.             break;
  1035.         buf = *s++ & 0x7F;
  1036.         TextMode(notSrcCopy);
  1037.         StdText(1, &buf, numer, denom);
  1038.         TextMode(srcCopy);
  1039.     }
  1040. }
  1041.  
  1042.  
  1043. /* ---------- I/O primitives ---------- */
  1044.  
  1045.  
  1046. /*
  1047.  *  cleos - clear to end of screen
  1048.  *
  1049.  *  The given line, and all following lines, are erased.  The cursor is
  1050.  *  moved to the start of its line.
  1051.  *
  1052.  */
  1053.  
  1054. static void
  1055. cleos(i)
  1056. int i;
  1057. {
  1058.     pad('\r', 0, c.nrows - i);
  1059.     paste((**c.hTE).lineStarts[i], (**c.hTE).teLength);
  1060.     c.cursor.h = 0;
  1061. }
  1062.  
  1063.  
  1064. /*
  1065.  *  cleol - clear to end of line
  1066.  *
  1067.  */
  1068.  
  1069. static void
  1070. cleol()
  1071. {
  1072.     register TEPtr pTE = deactivate();
  1073.     register int *line = &pTE->lineStarts[c.cursor.v];
  1074.     register int selStart = line[0] + c.cursor.h;
  1075.     register int selEnd = line[1] - 1;
  1076.  
  1077.     if (selStart < selEnd) {
  1078.         pTE->selStart = selStart;
  1079.         pTE->selEnd = selEnd;
  1080.         TEDelete(c.hTE);
  1081.     }
  1082. }
  1083.  
  1084.  
  1085. /*
  1086.  *  output - write characters to the current console
  1087.  *
  1088.  */
  1089.  
  1090. static void
  1091. output(s, n)
  1092. register unsigned char *s;
  1093. register int n;
  1094. {
  1095.     unsigned char *t;
  1096.     register EvQElPtr q;
  1097.     
  1098.     while (n--) {
  1099.         t = s;
  1100.         asm {
  1101.             moveq    #' ',d0
  1102. @1            cmp.b    (s)+,d0
  1103. @2            dbhi    n,@1
  1104.             bls.s    @3
  1105.             subq.l    #1,s
  1106. @3        }
  1107.         if (s > t)
  1108.             overwrite(t, s - t);
  1109.         if (n < 0)
  1110.             break;
  1111.         if (!c.raw) {
  1112.             for (q = (EvQElPtr) EventQueue.qHead; q; q = (EvQElPtr) q->qLink) {
  1113.                 if (q->evtQWhat == keyDown && (char) q->evtQMessage == '.') {
  1114.                     if (q->evtQModifiers & cmdKey) {
  1115.                         interrupted = 1;
  1116.                         return;
  1117.                     }
  1118.                 }
  1119.             }
  1120.         }
  1121.         switch (*s++) {
  1122.             case '\a':
  1123.                 SysBeep(4);
  1124.                 break;
  1125.             case '\b':
  1126.                 deactivate();
  1127.                 if (c.cursor.h)
  1128.                     --c.cursor.h;
  1129.                 break;
  1130.             case '\f':
  1131.                 c.cursor.v = 0;
  1132.                 cleos(0);
  1133.                 break;
  1134.             case '\n':
  1135.                 newline();
  1136.                 break;
  1137.             case '\v':
  1138.                 if (++c.cursor.v == c.nrows)
  1139.                     --c.cursor.v;
  1140.                 break;
  1141.             case '\r':
  1142.                 c.cursor.h = 0;
  1143.                 break;
  1144.             case '\t':
  1145.                 do {
  1146.                     ++c.cursor.h;
  1147.                 } while (c.cursor.h % c.tabs);
  1148.                 if (c.cursor.h > c.ncols)
  1149.                     c.cursor.h = c.ncols;
  1150.                 break;
  1151.         }
  1152.     }
  1153. }
  1154.  
  1155.  
  1156. /*
  1157.  *  overwrite - place new text on a line
  1158.  *
  1159.  *  The text must not contain any control characters.  Text is drawn
  1160.  *  starting at the current cursor, overwriting any existing characters.
  1161.  *  The cursor is left at the end of the new text.
  1162.  *
  1163.  */
  1164.  
  1165. static void
  1166. overwrite(s, n)
  1167. unsigned char *s;
  1168. register long n;
  1169. {
  1170.     register long m;
  1171.     register int *line, selStart, selEnd;
  1172.     
  1173.         /*  wrap output at "ncols"  */
  1174.  
  1175. more:    
  1176.     if (c.cursor.h + (m = n) > c.ncols)
  1177.         m = c.ncols - c.cursor.h;
  1178.     
  1179.         /*  set replacement range  */
  1180.         
  1181.     line = &(**c.hTE).lineStarts[c.cursor.v];
  1182.     selStart = line[0] + c.cursor.h;
  1183.     selEnd = line[1] - 1;
  1184.     if (selStart > selEnd) {    /*  pad line with blanks  */
  1185.         pad(' ', 0, selStart - selEnd);
  1186.         paste(selEnd, selEnd);
  1187.         selEnd = selStart;
  1188.     }
  1189.     else if (selEnd > selStart + m)
  1190.         selEnd = selStart + m;
  1191.     
  1192.         /*  paste in new text  */
  1193.         
  1194.     asm {
  1195.         movea.l    s,a0
  1196.         move.l    m,d0
  1197.         move.w    d0,TEScrpLength
  1198.         movea.l    TEScrpHandle,a1
  1199.         _PtrToXHand
  1200.     }
  1201.     paste(selStart, selEnd);
  1202.     
  1203.         /*  wrap to next line if necessary  */
  1204.         
  1205.     if (m < n) {
  1206.         newline();
  1207.         s += m;
  1208.         n -= m;
  1209.         goto more;
  1210.     }
  1211.     c.cursor.h += m;
  1212. }
  1213.  
  1214.  
  1215. /*
  1216.  *  delete - erase the indicated portion of the input field
  1217.  *
  1218.  */
  1219.  
  1220. static void
  1221. delete(selStart, selEnd)
  1222. int selStart, selEnd;
  1223. {
  1224.     register TEPtr pTE = deactivate();
  1225.     
  1226.     if (in.max + 1 == pTE->lineStarts[c.cursor.v + 1]) {
  1227.         pTE->selStart = selStart;
  1228.         pTE->selEnd = selEnd;
  1229.         TEDelete(c.hTE);
  1230.     }
  1231.     else {
  1232.         pTE->selStart = selEnd;
  1233.         pTE->selEnd = in.max;
  1234.         TECopy(c.hTE);
  1235.         pad(' ', in.max - selEnd, in.max - selStart);
  1236.         paste(selStart, in.max);
  1237.     }
  1238.     in.max -= selEnd - selStart;
  1239. }
  1240.  
  1241.  
  1242. /*
  1243.  *  insert - insert a character at the indicated point
  1244.  *
  1245.  */
  1246.  
  1247. static void
  1248. insert(ch, selStart)
  1249. char ch;
  1250. int selStart;
  1251. {
  1252.     register TEPtr pTE = deactivate();
  1253.     
  1254.     pTE->selStart = selStart;
  1255.     if (in.max + 1 == pTE->lineStarts[c.cursor.v + 1]) {
  1256.         pTE->selEnd = selStart;
  1257.         TEKey(ch, c.hTE);
  1258.     }
  1259.     else {
  1260.         pTE->selEnd = in.max;
  1261.         TECopy(c.hTE);
  1262.         Munger(TEScrpHandle, 0, 0, 0, &ch, 1);
  1263.         ++TEScrpLength;
  1264.         paste(selStart, in.max + 1);
  1265.     }
  1266.     in.max++;
  1267. }
  1268.  
  1269.  
  1270. /*
  1271.  *  pad - pad the TE scrap to a desired size
  1272.  *
  1273.  */
  1274.  
  1275. static void
  1276. pad(ch, ofs, len)
  1277. register char ch;
  1278. register long ofs, len;
  1279. {
  1280.         /*  set scrap size  */
  1281.         
  1282.     asm {
  1283.         movea.l    TEScrpHandle,a0
  1284.         move.l    len,d0
  1285.         move.w    d0,TEScrpLength
  1286.         _SetHandleSize
  1287.     }
  1288.     
  1289.         /*  fill scrap  */
  1290.         
  1291.     asm {
  1292.         movea.l    (a0),a0
  1293.         adda.l    ofs,a0
  1294.         sub.l    ofs,len
  1295.         bra.s    @2
  1296. @1        move.b    ch,(a0)+
  1297. @2        dbra    len,@1
  1298.     }
  1299. }
  1300.  
  1301.  
  1302. /*
  1303.  *  paste - replace range with contents of TEScrap
  1304.  *
  1305.  */
  1306.  
  1307. static void
  1308. paste(selStart, selEnd)
  1309. int selStart, selEnd;
  1310. {
  1311.     register TEPtr pTE = deactivate();
  1312.     
  1313.     pTE->selStart = selStart;
  1314.     pTE->selEnd = selEnd;
  1315.     TEPaste(c.hTE);
  1316. }
  1317.  
  1318.  
  1319. /*
  1320.  *  setcursor - activate flashing caret at cursor
  1321.  *
  1322.  */
  1323.  
  1324. static int
  1325. setcursor(sel)
  1326. register int sel;
  1327. {
  1328.     register TEPtr pTE = deactivate();
  1329.     register int *line = &pTE->lineStarts[c.cursor.v];
  1330.     register int end = line[1] - 1;
  1331.     
  1332.     sel += line[0] + c.cursor.h;
  1333.     if (sel > end) {        /*  pad line with blanks  */
  1334.         pad(' ', 0, sel - end);
  1335.         paste(end, end);
  1336.         pTE = *c.hTE;
  1337.     }
  1338.     pTE->selStart = pTE->selEnd = sel;
  1339.     pTE->clikStuff = 255;    /* per TN127 */
  1340.     TEActivate(c.hTE);
  1341.     return(sel);
  1342. }
  1343.  
  1344.  
  1345. /*
  1346.  *  deactivate - make sure TE record is inactive
  1347.  *
  1348.  *  The dereferenced pointer to the TE record is returned.
  1349.  *
  1350.  */
  1351.  
  1352. static TEPtr
  1353. deactivate()
  1354. {
  1355.     if ((**c.hTE).active)
  1356.         TEDeactivate(c.hTE);
  1357.     return(*c.hTE);
  1358. }
  1359.  
  1360.  
  1361. /*
  1362.  *  strip - strip trailing blanks from all lines
  1363.  *
  1364.  *  Note that we don't strip blanks that are part of the current
  1365.  *  input field.
  1366.  *
  1367.  */
  1368.  
  1369. static void
  1370. strip()
  1371. {
  1372.     register int i = c.nrows, j, k;
  1373.     register TEPtr pTE = *c.hTE;
  1374.     register char *p;
  1375.     
  1376.     while (i) {
  1377.         j = k = pTE->lineStarts[i--] - 1;
  1378.         for (p = *pTE->hText + j; j && *--p == ' '; j--)
  1379.             ;
  1380.         if (c.reading && !c.raw && i == c.cursor.v && j < in.max)
  1381.             j = in.max;
  1382.         if (k -= j) {
  1383.             Munger(pTE->hText, j, 0, k, "", 0);
  1384.             pTE = *c.hTE;
  1385.             if (c.reading) {
  1386.                 if (in.min > j)
  1387.                     in.min -= k;
  1388.                 if (in.max > j)
  1389.                     in.max -= k;
  1390.             }
  1391.             if (pTE->selStart > j)
  1392.                 pTE->selStart -= k;
  1393.             if (pTE->selEnd > j)
  1394.                 pTE->selEnd -= k;
  1395.         }
  1396.     }
  1397.     TECalText(c.hTE);
  1398. }
  1399.  
  1400.  
  1401. /*
  1402.  *  resize - respond to new window size
  1403.  *
  1404.  *  If the window is too small to display the entire console, the lower
  1405.  *  left portion of the console is displayed.
  1406.  *
  1407.  */
  1408.  
  1409. static void
  1410. resize()
  1411. {
  1412.     Rect bounds;
  1413.     
  1414.     bounds = c.wp->port.portRect;
  1415.     InvalRect(&bounds);
  1416.     InsetRect(&bounds, 4, 4);
  1417.     (**c.hTE).viewRect = bounds;
  1418.     bounds.top = bounds.bottom - c.height * c.nrows;
  1419.     (**c.hTE).destRect = bounds;
  1420. }
  1421.  
  1422.  
  1423. /*
  1424.  *  setup - focus on the appropriate console
  1425.  *
  1426.  *  The current console and the current grafport are set appropriately,
  1427.  *  and the previous values are saved.  Any pending update is performed.
  1428.  *
  1429.  */
  1430.  
  1431. static void
  1432. setup(wp, save)
  1433. register WindowPeek wp;
  1434. struct save *save;
  1435. {
  1436.     Rect bounds;
  1437.     
  1438.     GetPort(&save->port);
  1439.     save->console = theConsole;
  1440.     if (wp && wp->windowKind == console_refnum) {
  1441.         use(wp);
  1442.         SetPort(wp);
  1443.         if (!EmptyRgn(wp->updateRgn)) {
  1444.             bounds = wp->port.portRect;
  1445.             BeginUpdate(wp);
  1446.             EraseRect(&bounds);
  1447.             TEUpdate(&bounds, c.hTE);
  1448.             EndUpdate(wp);
  1449.         }
  1450.         theConsole = wp;
  1451.     }
  1452. }
  1453.  
  1454.  
  1455. /*
  1456.  *  restore - done with the current console
  1457.  *
  1458.  *  The console and grafport saved by a previous "setup" call are restored.
  1459.  *
  1460.  */
  1461.  
  1462. static void
  1463. restore(save)
  1464. register struct save *save;
  1465. {
  1466.     if (theConsole = save->console)
  1467.         use(save->console);
  1468.     SetPort(save->port);
  1469. }
  1470.  
  1471.  
  1472. /*
  1473.  *  use - load global "c" from window's refCon
  1474.  *
  1475.  */
  1476.  
  1477. static void
  1478. use(wp)
  1479. WindowPeek wp;
  1480. {
  1481.     if (wp != c.wp) {
  1482.         if (c.wp)
  1483.             ** (struct console **) c.wp->refCon = c;
  1484.         if (wp)
  1485.             c = ** (struct console **) wp->refCon;
  1486.     }
  1487. }
  1488.  
  1489.  
  1490. /*
  1491.  *  copy - get a pointer to the text in the TE scrap
  1492.  *
  1493.  *  If inverse video is in use, the high bit is stripped from each byte.
  1494.  *  This call must be balanced by a call to "endcopy".
  1495.  *
  1496.  */
  1497.  
  1498. static char *
  1499. copy()
  1500. {
  1501.     asm {
  1502.         movea.l    TEScrpHandle,a0
  1503.         _HLock
  1504.         move.l    (a0),d0
  1505.     }
  1506.     if (c.inverse) asm {
  1507.         movea.l    d0,a1
  1508.         move.w    TEScrpLength,d1
  1509.         bra.s    @2
  1510. @1        tst.b    (a1)+
  1511.         bpl.s    @2
  1512.         bclr    #7,-1(a1)
  1513. @2        dbra    d1,@1
  1514.     }
  1515. }
  1516.  
  1517.  
  1518. /*
  1519.  *  endcopy - done using text obtained by "copy"
  1520.  *
  1521.  */
  1522.  
  1523. static void
  1524. endcopy()
  1525. {
  1526.     asm {
  1527.         movea.l    TEScrpHandle,a0
  1528.         _HUnlock
  1529.     }
  1530. }
  1531.  
  1532.  
  1533. /*
  1534.  *  newline - print a newline character
  1535.  *
  1536.  */
  1537.  
  1538. static void
  1539. newline()
  1540. {
  1541.     register TEPtr pTE = deactivate();
  1542.     register int *line, delta, len;
  1543.     Rect bounds;
  1544.     RgnHandle rgn;
  1545.     Handle hText;
  1546.     EventRecord event;
  1547.     
  1548.     if (c.reading && !c.edit && !c.cbreak)
  1549.         return;
  1550.  
  1551.         /*  wait while mouse is down  */
  1552.  
  1553.     asm {
  1554.         lea        event,a0
  1555.         moveq    #mDownMask,d0
  1556.         _GetOSEvent
  1557.         bne.s    @2
  1558. @1        moveq    #mUpMask,d0
  1559.         _GetOSEvent
  1560.         bne.s    @1
  1561. @2    }
  1562.     
  1563.         /*  copy current line to echo-file  */
  1564.         
  1565.     if (c.echo2fp) {
  1566.         line = &pTE->lineStarts[c.cursor.v];
  1567.         pTE->selStart = line[0];
  1568.         pTE->selEnd = line[1];
  1569.         TECopy(c.hTE);
  1570.         fwrite(copy(), 1, TEScrpLength, c.echo2fp);
  1571.         endcopy();
  1572.     }
  1573.  
  1574.         /*  advance cursor, scrolling if necessary  */
  1575.  
  1576.     if (++c.cursor.v == c.nrows) {
  1577.         pTE = *c.hTE;
  1578.         hText = pTE->hText;
  1579.         len = pTE->teLength -= delta = pTE->lineStarts[1];
  1580.         ++pTE->teLength;
  1581.         bounds = pTE->destRect;
  1582.         ScrollRect(&bounds, 0, -c.height, rgn = NewRgn());
  1583.         DisposeRgn(rgn);
  1584.         Munger(hText, 0, 0, delta, "", 0);
  1585.         Munger(hText, len, 0, 0, "\r", 1);
  1586.         TECalText(c.hTE);
  1587.         --c.cursor.v;
  1588.     }
  1589.     c.cursor.h = 0;
  1590. }
  1591.  
  1592.  
  1593. /* ---------- console driver ---------- */
  1594.  
  1595.  
  1596. /*
  1597.  *  open_console_driver - install and initialize the console driver
  1598.  *
  1599.  */
  1600.  
  1601. static int
  1602. open_console_driver()
  1603. {
  1604.     int i;
  1605.     DCtlHandle dceH;
  1606.     register DCtlPtr dce;
  1607.     
  1608.         /*  create driver on heap  */
  1609.     
  1610.     if (!drvrH) {
  1611.         drvr.vCtl.vCode = doControl;
  1612.         drvr.vClose.vCode = doClose;
  1613.         asm {
  1614.             lea        drvr,a0
  1615.             move.l    #sizeof(drvr),d0
  1616.             _PtrToHand
  1617.             move.l    a0,drvrH
  1618.         }
  1619.     }
  1620.     
  1621.         /*  find available slot in unit table  */
  1622.         
  1623.     for (i = 27; dceH = UTableBase[i]; i++) {
  1624.         if (!((**dceH).dCtlFlags & dOpened))
  1625.             break;
  1626.     }
  1627.     i = ~i;
  1628.     
  1629.         /*  create DCE  */
  1630.         
  1631.     asm {
  1632.         move.w    i,d0
  1633.         dc.w    0xA13D            ;  _DrvrInstall
  1634.         movea.l    (a0),dce
  1635.     }
  1636.     dce->dCtlDriver = (Ptr) drvrH;
  1637.     dce->dCtlFlags = drvr.drvrFlags;
  1638.     dce->dCtlEMask = drvr.drvrEMask;
  1639.     return(i);
  1640. }
  1641.  
  1642.  
  1643. /*
  1644.  *  doClose - handle _PBClose call
  1645.  *
  1646.  *  Normally, we shouldn't get a close call, because a console window has
  1647.  *  no go-away box.  If we do reach here, we can keep from crashing (except
  1648.  *  on 64K ROMs) by refusing to close.
  1649.  *
  1650.  */
  1651.  
  1652. static int
  1653. doClose()
  1654. {
  1655.     return(closeErr);
  1656. }
  1657.  
  1658.  
  1659. /*
  1660.  *  doControl - handle _PBControl call
  1661.  *
  1662.  */
  1663.  
  1664. static int
  1665. doControl()
  1666. {
  1667.     register cntrlParam *pb;
  1668.     DCtlPtr dce;
  1669.     struct save save, save2;
  1670.     register EventRecord *event;
  1671.     int key;
  1672.     
  1673.     SetUpA5();
  1674.     asm {
  1675.         move.l    a0,pb
  1676.         move.l    a1,dce
  1677.     }
  1678.     setup((WindowPeek) FrontWindow(), &save);
  1679.     switch (pb->csCode) {
  1680.         case accCursor:
  1681.             doCursor();
  1682.             break;
  1683.         case accCut:
  1684.             doCut();
  1685.             break;
  1686.         case accCopy:
  1687.             doCopy();
  1688.             break;
  1689.         case accPaste:
  1690.             doPaste();
  1691.             break;
  1692.         case accClear:
  1693.             doKey('\33');
  1694.             break;
  1695.         case accEvent:
  1696.             event = * (EventRecord **) pb->csParam;
  1697.             switch (event->what) {
  1698.                 case updateEvt:
  1699.                     setup((WindowPeek) event->message, &save2);
  1700.                     break;
  1701.                 case mouseDown:
  1702.                     doClick(event);
  1703.                     break;
  1704.                 case keyDown:
  1705.                 case autoKey:
  1706.                     key = (unsigned char) event->message;
  1707.                     if (event->modifiers & cmdKey) {
  1708.                         if (event->what == autoKey)
  1709.                             break;
  1710.                         key = doCmdKey(key);
  1711.                     }
  1712.                     if (key)
  1713.                         doKey(key);
  1714.                     break;
  1715.             }
  1716.             break;
  1717.     }
  1718.     asm {
  1719.         movea.l    drvrH,a0
  1720.         _HUnlock                    ;  unlock driver
  1721.         movea.l    dce,a0
  1722.         _RecoverHandle  SYS
  1723.         _HUnlock                    ;  unlock DCE
  1724.     }
  1725.     restore(&save);
  1726.     RestoreA5();
  1727.     return(0);
  1728. }
  1729.  
  1730.  
  1731. /*
  1732.  *  doCursor - cursor maintenance
  1733.  *
  1734.  */
  1735.  
  1736. static void
  1737. doCursor()
  1738. {
  1739.     Point where;
  1740.     
  1741.     TEIdle(c.hTE);
  1742.     GetMouse(&where);
  1743.     if (PtInRect(where, &c.wp->port.portRect))
  1744.         SetCursor(*GetCursor(iBeamCursor));
  1745.     else asm {
  1746.         movea.l    (a5),a0
  1747.         pea        -108(a0)            ;  arrow
  1748.         _SetCursor
  1749.     }
  1750. }
  1751.  
  1752.  
  1753. /*
  1754.  *  doClick - handle mouse-down event
  1755.  *
  1756.  *  Since the click was passed to the console driver, the front window
  1757.  *  must be a console window, and the click can only be in its zoom box
  1758.  *  or content region (including the grow region).
  1759.  *
  1760.  */
  1761.  
  1762. static void
  1763. doClick(event)
  1764. register EventRecord *event;
  1765. {
  1766.     int part;
  1767.     
  1768.     c.wp->windowKind = userKind;
  1769.     part = FindWindow(event->where, &c.wp);
  1770.     c.wp->windowKind = console_refnum;
  1771.     switch (part) {
  1772.         case inZoomIn:
  1773.         case inZoomOut:
  1774.             doZoom(event->where, part);
  1775.             break;
  1776.         case inGrow:
  1777.             if (!(event->modifiers & (cmdKey|optionKey))) {
  1778.                 doGrow(event->where);
  1779.                 break;
  1780.             }
  1781.             /* ... */
  1782.         case inContent:
  1783.             doSelect(event);
  1784.             break;
  1785.     }
  1786. }
  1787.  
  1788.  
  1789. /*
  1790.  *  doZoom - handle click in zoom box
  1791.  *
  1792.  *  The "zoomed" size is that needed to display the entire console.
  1793.  *
  1794.  */
  1795.  
  1796. static void
  1797. doZoom(where, part)
  1798. Point where;
  1799. int part;
  1800. {
  1801.     register WindowPeek wp = c.wp;
  1802.  
  1803.     InitCursor();
  1804.     if (TrackBox(wp, where, part)) {
  1805.         EraseRect(&wp->port.portRect);
  1806.         ZoomWindow(wp, part, 0);
  1807.         resize();
  1808.     }
  1809. }
  1810.  
  1811.  
  1812. /*
  1813.  *  doGrow - handle click in grow region
  1814.  *
  1815.  *  No grow box is actually visible; nonetheless, clicking in the lower
  1816.  *  right corner has the same effect.  Hold down the command or option
  1817.  *  key to defeat this behavior (e.g. to select corner text).
  1818.  *
  1819.  *  The maximum window size is that needed to display the entire console.
  1820.  *
  1821.  */
  1822.  
  1823. static void
  1824. doGrow(where)
  1825. Point where;
  1826. {
  1827.     register WindowPeek wp = c.wp;
  1828.     long size;
  1829.     static Rect limit = { 100, 120, 9999, 9999 };
  1830.  
  1831.     InitCursor();
  1832.     botRight(limit) = c.limit;
  1833.     if (size = GrowWindow(wp, where, &limit)) {
  1834.         EraseRect(&wp->port.portRect);
  1835.         SizeWindow(wp, loword(size), hiword(size), 0);
  1836.         resize();
  1837.     }
  1838. }
  1839.  
  1840.  
  1841. /*
  1842.  *  doSelect - handle click in content region, initiating selection
  1843.  *
  1844.  */
  1845.  
  1846. static void
  1847. doSelect(event)
  1848. register EventRecord *event;
  1849. {
  1850.     int extend = 0;
  1851.     register TEPtr pTE;
  1852.     
  1853.     if (!(**c.hTE).active)
  1854.         setcursor(0);
  1855.     else if (event->modifiers & shiftKey)
  1856.         extend = 1;
  1857.     strip();
  1858.     
  1859.         /*  let TE do the work  */
  1860.         
  1861.     GlobalToLocal(&event->where);
  1862.     TEClick(event->where, extend, c.hTE);
  1863.     pTE = *c.hTE;
  1864.     
  1865.         /*  fix selection  */
  1866.         
  1867.     if (pTE->selStart == pTE->selEnd) {
  1868.         pTE->clikStuff = 255;    /* per TN127 */
  1869.         if (!c.reading || c.raw)
  1870.             TEDeactivate(c.hTE);
  1871.         else if (pTE->selStart < in.min)
  1872.             TESetSelect(in.min, in.min, c.hTE);
  1873.         else if (pTE->selEnd > in.max)
  1874.             TESetSelect(in.max, in.max, c.hTE);
  1875.     }
  1876. }
  1877.  
  1878.  
  1879. /*
  1880.  *  doCmdKey - handle command key
  1881.  *
  1882.  */
  1883.  
  1884. static int
  1885. doCmdKey(key)
  1886. int key;
  1887. {
  1888.     if (c.raw)
  1889.         return(key & 0x1F);        /*  controlify  */
  1890.     switch (key) {
  1891.         case 'x': case 'X':
  1892.             doCut();
  1893.             break;
  1894.         case 'c': case 'C':
  1895.             doCopy();
  1896.             break;
  1897.         case 'v': case 'V':
  1898.             doPaste();
  1899.             break;
  1900.         case '.':
  1901.             if (console_environment)
  1902.                 interrupted = 1;
  1903.             /* ... */
  1904.         case 'd': case 'D':
  1905.             return('\4');        /*  ctrl-D  */
  1906.         case 'u': case 'U':
  1907.         case 'z': case 'Z':
  1908.             return('\25');        /*  ctrl-U  */
  1909.         case 'q': case 'Q':
  1910.             if (console_environment) {
  1911.                 console_options.pause_atexit = 0;
  1912.                 exit(0);
  1913.             }
  1914.             break;
  1915.     }
  1916.     return(0);
  1917. }
  1918.  
  1919.  
  1920. /*
  1921.  *  doKey - handle keypress
  1922.  *
  1923.  */
  1924.  
  1925. static void
  1926. doKey(key)
  1927. register int key;
  1928. {
  1929.     register TEPtr pTE = *c.hTE;
  1930.     register int selStart = pTE->selStart;
  1931.     register int selEnd = pTE->selEnd;
  1932.     register int len;
  1933.     register char *p;
  1934.  
  1935.     if (!c.reading || c.inverse && key > 0x7F)
  1936.         goto beep;
  1937.     if (c.raw) {
  1938.         *in.ptr++ = key;
  1939.         in.cnt = 0;
  1940.         return;
  1941.     }
  1942.     
  1943.         /*  process control characters  */
  1944.         
  1945.     if (key < ' ') {
  1946.         switch (key) {
  1947.             case '\25':                        /*  ^U  */
  1948.             case '\33':                        /*  escape, clear  */
  1949.                 in.cnt += in.ptr - in.buf;
  1950.                 in.ptr = in.buf;
  1951.                 selStart = in.min;
  1952.                 selEnd = in.max;
  1953.                 goto delete;
  1954.             case '\b':                        /*  backspace  */
  1955.                 if (c.edit)
  1956.                     goto delete;
  1957.                 if (c.cbreak)
  1958.                     goto buffer;
  1959.                 if (in.ptr == in.buf)
  1960.                     goto beep;
  1961.                 --in.ptr;
  1962.                 ++in.cnt;
  1963.                 goto cursor;
  1964.             case '\34':                        /*  left arrow  */
  1965.                 if (selStart == selEnd)
  1966.                     --selStart;
  1967.                 goto cursor;
  1968.             case '\35':                        /*  right arrow  */
  1969.                 if (selStart == selEnd)
  1970.                     ++selEnd;
  1971.                 selStart = selEnd;
  1972.                 goto cursor;
  1973.             case '\36':                        /*  up arrow  */
  1974.                 selStart = in.min;
  1975.                 goto cursor;
  1976.             case '\37':                        /*  down arrow  */
  1977.             case '\t':                        /*  tab  */
  1978.                 selStart = in.max;
  1979.                 goto cursor;
  1980.             case '\4':                        /*  ^D  */
  1981.             case '\r':                        /*  return  */
  1982.             case '\3':                        /*  enter  */
  1983.                 if (len = in.max - in.min) {
  1984.                     p = *pTE->hText + in.min;
  1985.                     asm {
  1986.                         movea.l    in.ptr,a0
  1987.                         bra.s    @2
  1988. @1                        move.b    (p)+,(a0)+
  1989. @2                        dbra    len,@1
  1990.                         move.l    a0,in.ptr
  1991.                     }
  1992.                 }
  1993.                 if (key != '\4')
  1994.                     *in.ptr++ = '\n';
  1995.                 newline();
  1996.                 in.cnt = 0;
  1997.                 break;
  1998.         }
  1999.         return;
  2000.     }
  2001.     
  2002.         /*  delete any existing selection  */
  2003.  
  2004. delete:
  2005.     if (c.edit) {
  2006.         if (selStart == selEnd) {
  2007.             if (key != '\b')
  2008.                 goto insert;
  2009.             --selStart;
  2010.         }
  2011.         if (selStart < in.min || selEnd > in.max)
  2012.             goto beep;
  2013.         delete(selStart, selEnd);
  2014.     }
  2015.  
  2016.         /*  insert the key  */
  2017.         
  2018. insert:
  2019.     if (key >= ' ') {
  2020.         if (in.max - in.min == in.cnt - 1)
  2021.             SysBeep(2);
  2022.         else if (c.edit)
  2023.             insert(key, selStart++);
  2024.         else {
  2025. buffer:        *in.ptr++ = key;
  2026.             if (c.cbreak) {
  2027.                 output(in.ptr - 1, 1);
  2028.                 in.cnt = 0;
  2029.                 return;
  2030.             }
  2031.             --in.cnt;
  2032.         }
  2033.     }
  2034.     
  2035.         /*  update the cursor  */
  2036.  
  2037. cursor:
  2038.     if (selStart > in.max)
  2039.         selStart = in.max;
  2040.     else if (selStart < in.min)
  2041.         selStart = in.min;
  2042.     setcursor(selStart - in.min);
  2043.     return;
  2044.     
  2045.         /*  beep only  */
  2046.  
  2047. beep:
  2048.     SysBeep(2);
  2049. }
  2050.  
  2051.  
  2052. /*
  2053.  *  doCut - handle "cut" command
  2054.  *
  2055.  *  Selected text (if any) is placed in the desk scrap, then deleted.
  2056.  *
  2057.  */
  2058.  
  2059. static void
  2060. doCut()
  2061. {
  2062.     register TEPtr pTE = *c.hTE;
  2063.     
  2064.     if (pTE->active && pTE->selStart < pTE->selEnd) {
  2065.         if (!c.reading || pTE->selStart < in.min || pTE->selEnd > in.max)
  2066.             SysBeep(2);
  2067.         else {
  2068.             doCopy();
  2069.             doKey('\b');
  2070.         }
  2071.     }
  2072. }
  2073.  
  2074.  
  2075. /*
  2076.  *  doCopy - handle "copy" command
  2077.  *
  2078.  *  Selected text (if any) is placed in the desk scrap.
  2079.  *
  2080.  */
  2081.  
  2082. static void
  2083. doCopy()
  2084. {
  2085.     register TEPtr pTE = *c.hTE;
  2086.     
  2087.     if (pTE->active && pTE->selStart < pTE->selEnd) {
  2088.         TECopy(c.hTE);
  2089.         ZeroScrap();
  2090.         PutScrap(TEScrpLength, 'TEXT', copy());
  2091.         endcopy();
  2092.     }
  2093. }
  2094.  
  2095.  
  2096. /*
  2097.  *  doPaste - handle "paste" command
  2098.  *
  2099.  *  Pasted text is obtained from the desk scrap and stored in the paste
  2100.  *  buffer, "c.pasteH".  ProcessEvent will take characters from the buffer
  2101.  *  in preference to the keyboard.
  2102.  *
  2103.  */
  2104.  
  2105. static void
  2106. doPaste()
  2107. {
  2108.     if (!c.reading || (**c.hTE).selStart < in.min || (**c.hTE).selEnd > in.max) {
  2109.         SysBeep(2);
  2110.         return;
  2111.     }
  2112.     if ((c.pasteLen = GetScrap(TEScrpHandle, 'TEXT', &c.pasteOfs)) > 0) {
  2113.         c.pasteH = TEScrpHandle;
  2114.         TEScrpHandle = NewHandle(0);
  2115.         c.pasteOfs = 0;
  2116.     }
  2117.     TEScrpLength = 0;
  2118. }
  2119.  
  2120.  
  2121. /* ---------- printing ---------- */
  2122.  
  2123.  
  2124. /*
  2125.  *  print_console - print the echo file
  2126.  *
  2127.  */
  2128.  
  2129. static void
  2130. print_console()
  2131. {
  2132.         /*  check for availability of printing  */
  2133.  
  2134. #ifdef _PrintTraps_
  2135.     asm {
  2136.         move.w    #0xA89F,d0
  2137.         _GetTrapAddress
  2138.         move.l    a0,a1
  2139.         move.w    #0xA8FD,d0
  2140.         _GetTrapAddress
  2141.         cmpa.l    a0,a1
  2142.         bne.s    @ok
  2143.     }
  2144.     c.echo2fp->delete = 0;
  2145.     return;
  2146. #endif
  2147.     
  2148.         /*  print away  */
  2149.         
  2150. ok:    if (!noPrint) {
  2151.         PrOpen();
  2152.         if (!PrError()) {
  2153.             print();
  2154.             PrClose();
  2155.         }
  2156.     }
  2157. }
  2158.  
  2159.  
  2160. /*
  2161.  *  print - spool file to printer
  2162.  *
  2163.  */
  2164.  
  2165. static void
  2166. print()
  2167. {
  2168.     static THPrint hPrint;
  2169.     THPrint h;
  2170.     GrafPtr savePort;
  2171.     TPPrPort port;
  2172.     register TEPtr pTE;
  2173.     int ascent, nlines, i;
  2174.     Rect pageRect;
  2175.     Point where;
  2176.     char buf[BUFSIZ];
  2177.     TPrStatus status;
  2178.     
  2179.         /*  set up job record  */
  2180.     
  2181.     h = (THPrint) NewHandle(sizeof(TPrint));
  2182.     PrintDefault(h);
  2183.     if (hPrint) {
  2184.         PrJobMerge(hPrint, h);
  2185.         DisposHandle(hPrint);
  2186.     }
  2187.     else {
  2188.         InitCursor();
  2189.         if (!PrJobDialog(h)) {
  2190.             noPrint = 1;
  2191.             return;
  2192.         }
  2193.     }
  2194.     hPrint = h;
  2195.     
  2196.         /*  set up printing port  */
  2197.     
  2198.     GetPort(&savePort);
  2199.     port = PrOpenDoc(h, NULL, NULL);
  2200.     pTE = *c.hTE;
  2201.     TextFont(pTE->txFont);
  2202.     TextSize(pTE->txSize);
  2203.     TextFace(pTE->txFace);
  2204.     ascent = pTE->fontAscent;
  2205.     pageRect = (**h).prInfo.rPage;
  2206.     nlines = (pageRect.bottom - pageRect.top) / c.height;
  2207.     where.h = pageRect.left + 36;    /* leave 1/2 inch margin */
  2208.  
  2209.         /*  print each page  */
  2210.  
  2211.     rewind(c.echo2fp);
  2212.     c.echo2fp->binary = 0;
  2213.     do {
  2214.         PrOpenPage(port, NULL);
  2215.         where.v = pageRect.top + ascent;
  2216.         for (i = 0; i < nlines && fgets(buf, sizeof buf, c.echo2fp); i++) {
  2217.             MoveTo(where.h, where.v);
  2218.             DrawText(buf, 0, strlen(buf) - 1);
  2219.             where.v += c.height;
  2220.         }
  2221.         PrClosePage(port);
  2222.     } while (!PrError() && !feof(c.echo2fp));
  2223.     
  2224.         /*  done printing  */
  2225.     
  2226.     PrCloseDoc(port);
  2227.     SetPort(savePort);
  2228.     if ((**h).prJob.bJDocLoop == bSpoolLoop && !PrError())
  2229.         PrPicFile(h, NULL, NULL, NULL, &status);
  2230. }
  2231.  
  2232.  
  2233. /* ---------- memory manager glue ---------- */
  2234.  
  2235.  
  2236. static pascal Handle
  2237. NewHandle(size)
  2238. long size;
  2239. {
  2240.     asm {
  2241.         move.l    size,d0
  2242.         _NewHandle
  2243.         move.l    a0,d0
  2244.     }
  2245. }
  2246.  
  2247.  
  2248. static pascal void
  2249. DisposHandle(h)
  2250. void *h;
  2251. {
  2252.     asm {
  2253.         movea.l    h,a0
  2254.         _DisposHandle
  2255.     }
  2256. }
  2257.