home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1995 August / NEBULA.mdf / Apps / DevTools / Compiler2.0 / Source / Controller.m < prev    next >
Encoding:
Text File  |  1993-01-25  |  12.7 KB  |  533 lines

  1. //
  2. // This is the Compiler's Companion a program to integrate make's and Edit
  3. // more cleanly. Version 2.0
  4. //
  5. // Copyright(C) 1992 Daryll Strauss
  6. //
  7. //    This program is shareware; you can redistribute it and/or modify
  8. //    it under the terms of the GNU General Public License as published by
  9. //    the Free Software Foundation; either version 1, or (at your option)
  10. //    any later version as long as you leave all the references to the fact
  11. //    that it is shareware and covered by the GNU General Public License in
  12. //    tact.
  13. //
  14. //    This program is distributed in the hope that it will be useful,
  15. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17. //    GNU General Public License for more details.
  18. //
  19. //    You should have received a copy of the GNU General Public License
  20. //    along with this program; if not, write to the Free Software
  21. //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22. //
  23. //    Please see the README.rtf file for more details on the shareware
  24. //    distribution and the GNU General Public License.
  25. //
  26.  
  27. #import "Controller.h"
  28. #import <stdio.h>
  29. #import <stdlib.h>
  30. #import <strings.h>
  31. #import <mach_init.h>
  32. #import <mach_interface.h>
  33. #import <streams/streams.h>
  34. #import <sys/file.h>
  35. #import <dpsclient/dpsclient.h>
  36. #import <appkit/nextstd.h>
  37. #import <appkit/ScrollView.h>
  38. #import <appkit/Text.h>
  39. #import <appkit/Application.h>
  40. #import <appkit/Listener.h>
  41. #import <appkit/Speaker.h>
  42. #import <appkit/Panel.h>
  43. #import <appkit/Form.h>
  44. #import <appkit/Pasteboard.h>
  45. #import <appkit/Panel.h>
  46. #import <appkit/OpenPanel.h>
  47. #import "Defaults.h"
  48.  
  49. #define DISPLAY_ERROR(msg) [self sysError:msg :__FILE__ :__LINE__]
  50.  
  51. @implementation Controller
  52.  
  53. static char *directory;
  54. static char *command;
  55. static char *good_cmd;
  56. static char *bad_cmd;
  57. static BOOL pref_services;
  58. static BOOL pref_pref_dir;
  59. static BOOL hide_on_success;
  60. static char *file;
  61. static BOOL prompt_User=TRUE;
  62. static BOOL Aux_Loaded=FALSE;
  63. static int end_pos;
  64. static BOOL Make_In_Progress=FALSE;
  65.  
  66. Full_Defaults_Vector CompilerDefaults[] = {
  67.     {default_string, "directory", "", &directory},
  68.     {default_const_string, "command", "make -k", &command},
  69.     {default_bool, "services", "Yes", &pref_services},
  70.     {default_bool, "usePrefDir", "No", &pref_pref_dir},
  71.     {default_bool, "hideOnSuccess", "No", &hide_on_success},
  72.     {default_string, "executeGood", "sndplay ~/Apps/Compiler.app/good.snd &", 
  73.         &good_cmd},
  74.     {default_string, "executeBad", "sndplay ~/Apps/Compiler.app/bad.snd &",
  75.         &bad_cmd},
  76.     {default_end, 0, 0, 0}
  77. };
  78.  
  79. //
  80. // Basic support functions
  81. //
  82.  
  83. void set_directory(char *new_dir)
  84. {
  85.     if (directory) free(directory);
  86.     directory=malloc(strlen(new_dir)+1);
  87.     strcpy(directory, new_dir);
  88. }
  89.  
  90. void set_file(char *new_file)
  91. {
  92.     if (file) free(file);
  93.     file=malloc(strlen(new_file)+1);
  94.     strcpy(file, new_file);
  95. }
  96.  
  97. int Find_Newline(NXStream *data, int direction)
  98. {
  99.     int pos;
  100.     
  101.     if (direction==1) {
  102.         while (!NXAtEOS(data))
  103.             if (NXGetc(data)=='\n') return(1);
  104.         return(0);
  105.     } else {
  106.         pos=NXTell(data);
  107.         while (pos>0) {
  108.             NXSeek(data, pos, NX_FROMSTART);
  109.             if (NXGetc(data)=='\n') return(1);
  110.             pos--;
  111.         }
  112.         return(0);
  113.     }
  114. }
  115.  
  116. int parse_error(NXStream *data, char **file, int *line)
  117. {
  118.     int file_pos, line_pos, i;
  119.     char *filename, c=0;
  120.     
  121.     file_pos=NXTell(data);
  122.     while (!NXAtEOS(data)) {
  123.         c=NXGetc(data);
  124.         if (c=='\n') return(0);
  125.         if (c==' ') return(0);
  126.         if (c==':') break;
  127.     }
  128.     if (c!=':') return(0);
  129.     line_pos=NXTell(data);
  130.     filename=*file=malloc(line_pos-file_pos);
  131.     NXSeek(data, file_pos, NX_FROMSTART);
  132.     for (i=0; i<line_pos-file_pos-1; i++) {
  133.         *filename=NXGetc(data);
  134.         filename++;
  135.     }
  136.     *filename=0;
  137.     NXGetc(data);
  138.     *line=(-1);
  139.     while (!NXAtEOS(data)) {
  140.         c=NXGetc(data);
  141.         if (c==':' || c=='\n') break;
  142.         if (c<'0' || c>'9') {
  143.             free(*file);
  144.             Find_Newline(data, 1);
  145.             return(0);
  146.         } else {
  147.             if (*line==-1) *line=0;
  148.             *line=10*(*line)+c-'0';
  149.         }
  150.     }
  151.     if ((c=='\n') || (*line==-1)) {
  152.         free(*file);
  153.         return(0);
  154.     }
  155.     return(1);    
  156. }
  157.  
  158. //
  159. // Object support functions
  160. //
  161.  
  162. - (void)sysError:(char *)msg :(char *)file :(int)line
  163. {
  164.     NXRunAlertPanel("System Error", "A system error occurred.\nThis should never happen.\nPlease include this in your bug report:\nVersion %s -- %s at %s:%d", "Sorry",
  165.     0, 0, __DATE__, msg, file, line);
  166. }
  167.  
  168. - (const char *)Find_Lost_File:(const char *)file;
  169. {
  170.     OpenPanel *op;
  171.     
  172.     op = [[OpenPanel new] allowMultipleFiles:FALSE];
  173.     [op setAccessoryView:locate];
  174.     if ([op runModal]) return([op filename]);
  175.     return(0);
  176. }
  177.  
  178. - (void)positionOnFile:(const char *)filename Line:(int)line
  179. {
  180.     port_t edit_port;
  181.     int completed;
  182.     char position[100];
  183.     
  184.     if ((edit_port=NXPortFromName("Edit", 0))==PORT_NULL) {
  185.         DISPLAY_ERROR("Finding port");
  186.         return;
  187.     }
  188.     if (access(filename, R_OK)<0) { /* We can't find or open the given file */
  189.         filename=[self Find_Lost_File:filename];
  190.         if (!filename) {
  191.             NXRunAlertPanel(0, "Couldn't find the file from the error message",
  192.                 0, 0, 0);
  193.             return;
  194.         }
  195.     }
  196.     [[NXApp appSpeaker] setSendPort:edit_port];
  197.     if (![[NXApp appSpeaker] sendOpenFileMsg:filename ok:&completed
  198.         andDeactivateSelf:TRUE]) {
  199.         if (!completed) {
  200.             NXRunAlertPanel(0, "Edit couldn't open file %s\n", 0, 0, 0,
  201.                 filename);
  202.             return;
  203.         }
  204.     } else {
  205.         DISPLAY_ERROR("Sending open");
  206.         return;
  207.     }
  208.     sprintf(position, "%d:%d", line, line);
  209.     if (![[NXApp appSpeaker] msgSetPosition:position posType:NX_LINENUMPOSTYPE
  210.         andSelect:TRUE ok:&completed]) {
  211.         if (!completed) {
  212.             NXRunAlertPanel(0, "Edit couldn't position the file at line %d\n",
  213.                 0, 0, 0, line);
  214.             return;
  215.         }
  216.     } else {
  217.         DISPLAY_ERROR("Sending position");
  218.         return;
  219.     }
  220. }
  221.  
  222. - (void)loadAux
  223. {
  224.     if (!Aux_Loaded) {
  225.         [NXApp loadNibSection:"Auxillary.nib" owner:self];
  226.         Aux_Loaded=TRUE;
  227.     }
  228. }
  229.  
  230. - appDidInit:sender
  231. {
  232.     Register_Full_Defaults("Compiler", CompilerDefaults);
  233.     directory=0;
  234.     Get_Full_Defaults("Compiler", CompilerDefaults);
  235.     [cmd_form setStringValue:directory at:0];
  236.     [cmd_form setStringValue:command at:1];
  237.     [[NXApp appListener] setServicesDelegate:self];
  238.     NXSetServicesMenuItemEnabled("Compiler/Make", pref_services);
  239.     NXSetServicesMenuItemEnabled("Compiler/Show Error", pref_services);
  240.     return(self);
  241. }
  242.  
  243. //
  244. // Services activated functions
  245. //
  246.  
  247. - doMake:(id)pasteboard userData:(const char *)user error:(char **)error
  248. {
  249.     char *data, *ptr;
  250.     int len;
  251.     const NXAtom *types;
  252.     
  253.     types=[pasteboard types];
  254.     while (types)
  255.         if (*types==NXFilenamePboardType) break;
  256.     if (!*types)
  257.         DISPLAY_ERROR("Wrong pasteboard type");
  258.     if ([pasteboard readType:NXFilenamePboardType data:(void*)&data
  259.             length:&len]) {
  260.             if (pref_pref_dir) 
  261.                 Get_Default_By_Name("Compiler", "directory", CompilerDefaults);
  262.             else {
  263.                 ptr=data+strlen(data);
  264.                 while (*ptr!='/') ptr--;
  265.                 set_file(ptr+1);
  266.                 *ptr=0;
  267.                 if (strcmp(data, directory)) {
  268.                     set_directory(data);
  269.                     prompt_User=TRUE;
  270.                 }
  271.             }
  272.             vm_deallocate(task_self(), (vm_address_t)data, len);
  273.     }
  274.     [self doMake:self];
  275.     return(self);
  276. }    
  277.  
  278. - nextError:(id)pasteboard userData:(const char *)user error:(char **)error
  279. {
  280.     [self nextError:self];
  281.     return(self);
  282. }    
  283.  
  284. //
  285. // Delegate methods for the subprocess object
  286. //
  287.  
  288. - subprocessOutput:(char *)text
  289. {
  290.     int cnt;
  291.     NXSelPt start, end;
  292.  
  293.     cnt=strlen(text);
  294.     if (cnt) {
  295.         [[textfield docView] setAutodisplay:FALSE];
  296.         [[textfield docView] getSel:&start :&end];
  297.         [[textfield docView] setSel:end_pos :end_pos];
  298.         [[textfield docView] replaceSel:text];
  299.         [[textfield docView] setSel:start.cp :end.cp];
  300.         [[textfield docView] display];
  301.         [[textfield docView] setAutodisplay:TRUE];
  302.         end_pos+=cnt;
  303.     }
  304.     return(self);
  305. }    
  306.  
  307. - subprocessDone:(int)status
  308. {
  309.     NXStream *data;
  310.     char buf[10], c;
  311.     BOOL result=TRUE, more;
  312.     int pos;
  313.     
  314.     [process free];
  315.     process=0;
  316.     data=[[textfield docView] stream];
  317.     NXSeek(data, 0, NX_FROMEND);
  318.     Find_Newline(data, 0);
  319.     do {
  320.         pos=NXTell(data)-2;
  321.         NXRead(data, buf, 9);
  322.         buf[9]=0;
  323.         if (!strcmp(buf, "*** Exit ")) {
  324.             do {
  325.                 c=NXGetc(data);
  326.             } while ((c>='0') && (c<='9'));
  327.             if (c=='\n') {
  328.                 result=FALSE;
  329.                 break;
  330.             }
  331.         }
  332.         NXSeek(data, pos, NX_FROMSTART);
  333.         more=Find_Newline(data, 0);
  334.     } while (more);
  335.     if (result && !status) {
  336.         [[textfield window] setTitle:"Compile Done -- SUCCESS"];
  337.         if (good_cmd && *good_cmd) system(good_cmd);
  338.         if (hide_on_success) [NXApp hide:self];
  339.     } else {
  340.         [[textfield window] setTitle:"Compile Done -- FAILURE"];
  341.         if (bad_cmd && *bad_cmd) system(bad_cmd);
  342.     }
  343.     Make_In_Progress=FALSE;
  344.     return(self);
  345. }
  346.  
  347. - subprocessError:(char *)msg
  348. {
  349.     DISPLAY_ERROR(msg);
  350.     return(self);
  351. }
  352.  
  353. //
  354. // User interface activated functions
  355. //
  356.  
  357. -doMake:(id)sender
  358. {
  359.     char *shell, *ptr;
  360.     
  361.     if (Make_In_Progress) {
  362.          NXRunAlertPanel("Busy", "Compilation in progress. Please start your compilation when the current one finishes", "OK", 0, 0);
  363.         return(self);
  364.     }
  365.     if (prompt_User) {
  366.         [[cmd_form window] makeKeyAndOrderFront:self];
  367.         [cmd_form setStringValue:directory at:0];
  368.         [cmd_form setStringValue:command at:1];
  369.         return(self);
  370.     }
  371.     Make_In_Progress=TRUE;
  372.     [[textfield window] makeKeyAndOrderFront:self];
  373.     [[textfield docView] setSel:0 :0];
  374.     shell=getenv("SHELL");
  375.     if (!shell) shell="/bin/csh";
  376.     process=[[Subprocess alloc] init:shell withDelegate:self
  377.         andPtySupport:FALSE andStdErr:TRUE];
  378.     if (!process) {
  379.         DISPLAY_ERROR("Failed to create child process");
  380.         Make_In_Progress=FALSE;
  381.         return(self);
  382.     }
  383.     if (file) {
  384.         [process send:"set file=" withNewline:FALSE];
  385.         [process send:file withNewline:TRUE];
  386.         ptr=file+strlen(file);
  387.         while (--ptr>=file)
  388.             if (*ptr=='.') {
  389.                 *ptr=0;
  390.                 break;
  391.             }
  392.         [process send:"set stripped=" withNewline:FALSE];
  393.         [process send:file withNewline:TRUE];
  394.         if (ptr>=file) *ptr='.';
  395.     }
  396.     if (directory) {
  397.         [process send:"set dir=" withNewline:FALSE];
  398.         [process send:directory withNewline:TRUE];
  399.     }
  400.     [process send:"echo cd " withNewline:FALSE];
  401.     [process send:directory withNewline:TRUE];
  402.     [process send:"cd " withNewline:FALSE];
  403.     [process send:directory withNewline:TRUE];
  404.     [process send:"echo " withNewline:FALSE];
  405.     [process send:command withNewline:TRUE];
  406.     [process send:"exec " withNewline:FALSE];
  407.     [process send:command withNewline:TRUE];
  408.     [process terminateInput];
  409.     [[textfield window] setTitle:"Compile…Running"];
  410.     end_pos=0;
  411.     [[textfield docView] setText:""];
  412.     [[textfield docView] setSel:0 :0];
  413.     return(self);
  414. }
  415.  
  416. - nextError:(id)sender
  417. {
  418.     NXSelPt start, end;
  419.     NXStream *data;
  420.     char *file, *path;
  421.     int line;
  422.     
  423.     [[textfield docView] getSel:&start :&end];
  424.     data=[[textfield docView] stream];
  425.     if (start.cp<0) start.cp=0;
  426.     NXSeek(data, end.cp, NX_FROMSTART);
  427.     Find_Newline(data, 0);
  428.     while (!NXAtEOS(data)) {
  429.         start.cp=NXTell(data);
  430.         if (parse_error(data, &file, &line)) {
  431.             Find_Newline(data, 1);
  432.             end.cp=NXTell(data);
  433.             [[textfield docView] setSel:start.cp :end.cp];
  434.             [[textfield docView] scrollSelToVisible];
  435.             path=malloc(strlen(directory)+strlen(file)+2);
  436.             sprintf(path, "%s/%s", directory, file);
  437.             [self positionOnFile:path Line:line];
  438.             free(path);
  439.             free(file);
  440.             break;
  441.         }
  442.     }
  443.     return(self);
  444. }
  445.  
  446. - stopMake:(id)sender
  447. {
  448.     if (Make_In_Progress) {
  449.         [process terminate:self];
  450.         [self subprocessOutput:"TERMINATED\n"];
  451.     }
  452.     return(self);
  453. }
  454.  
  455. - resetPrefs:sender
  456. {
  457.     char *hold_directory, *hold_command;
  458.     
  459.     hold_directory=directory;
  460.     hold_command=command;
  461.     directory=0;
  462.     command=0;
  463.     Reset_Defaults(CompilerDefaults);
  464.     [self show_prefs:self];
  465.     set_directory(hold_directory);
  466.     command=hold_command;
  467.     return(self);
  468. }
  469.  
  470. - setPrefs:sender
  471. {
  472.     set_directory((char *)[pref_form stringValueAt:0]);
  473.     command=(char *)[pref_form stringValueAt:1];
  474.     good_cmd=(char *)[pref_form stringValueAt:2];
  475.     bad_cmd=(char *)[pref_form stringValueAt:3];
  476.     pref_services=[service_switch intValue];
  477.     pref_pref_dir=[pref_dir_switch intValue];
  478.     hide_on_success=[hide_switch intValue];
  479.     Put_Full_Defaults("Compiler", CompilerDefaults);
  480.     NXSetServicesMenuItemEnabled("Compiler/Make", pref_services);
  481.     NXSetServicesMenuItemEnabled("Compiler/Show Error", pref_services);
  482.     prompt_User=FALSE;
  483.     [[pref_form window] orderOut:self];
  484.     return(self);
  485. }
  486.  
  487. - setValues:sender
  488. {
  489.     prompt_User=FALSE;
  490.     set_directory((char *)[cmd_form stringValueAt:0]);
  491.     command=(char *)[cmd_form stringValueAt:1];
  492.     [[cmd_form window] orderOut:self];
  493.     [self doMake:self];
  494.     return(self);
  495. }    
  496.  
  497. - show_help:sender
  498. {
  499.     [self loadAux];
  500.     [NXGetNamedObject("Help", self) makeKeyAndOrderFront:self];
  501.     return(self);
  502. }
  503.  
  504. - show_info:sender
  505. {
  506.     [self loadAux];
  507.     [NXGetNamedObject("Info", self) makeKeyAndOrderFront:self];
  508.     return(self);
  509. }
  510.  
  511. - show_license:sender
  512. {
  513.     [self loadAux];
  514.     [NXGetNamedObject("License", self) makeKeyAndOrderFront:self];
  515.     return(self);
  516. }
  517.  
  518. - show_prefs:sender
  519. {
  520.     [self loadAux];
  521.     [pref_form setStringValue:directory at:0];
  522.     [pref_form setStringValue:command at:1];
  523.     [pref_form setStringValue:good_cmd at:2];
  524.     [pref_form setStringValue:bad_cmd at:3];
  525.     [service_switch setIntValue:pref_services];
  526.     [pref_dir_switch setIntValue:pref_pref_dir];
  527.     [hide_switch setIntValue:hide_on_success];
  528.     [NXGetNamedObject("Preferences", self) makeKeyAndOrderFront:self];
  529.     return(self);
  530. }
  531.  
  532. @end
  533.