home *** CD-ROM | disk | FTP | other *** search
/ Oakland CPM Archive / oakcpm.iso / cpm / cpm68k / utils.lbr / INDENT.CQ / INDENT.C
Encoding:
C/C++ Source or Header  |  1986-05-22  |  32.9 KB  |  1,215 lines

  1. /* -*-c,save-*-
  2.  
  3.               Copyright (C) 1976
  4.                 by the
  5.               Board of Trustees
  6.                 of the
  7.             University of Illinois
  8.  
  9.              All rights reserved
  10.  
  11.  
  12. NAME:
  13.     indent main program
  14.  
  15. FUNCTION:
  16.     This is the main program of the indent program.  Indent will take a C
  17.     program source and reformat it into a semi-reasonable form.
  18.  
  19. ALGORITHM:
  20.     The routine lexi scans tokens and passes them back one at a time to the
  21.     main routine.  The subroutine parse takes care of much of the work of
  22.     figuring indentation level.  
  23.  
  24.     1) Call lexi
  25.     2) Enter a monster switch statement on the code returned by lexi.  If 
  26.        the indentation level for the line yet to be printed should be 
  27.        changed, set the variable ind_level.  If the indentation level for
  28.        the following line should be changed, set the variable i_l_follow.
  29.  
  30. PARAMETERS:
  31.     None
  32.  
  33. RETURNS:
  34.     Nothing
  35.  
  36. GLOBALS:
  37.     be_save =
  38.     break_comma
  39.     bp_save =
  40.     btype_2 =
  41.     code_lines
  42.     com_ind =
  43.     com_lines
  44.     dec_nest =
  45.     decl_com_ind =
  46.     decl_on_line =
  47.     i_l_follow =
  48.     in_decl =
  49.     ind_level =
  50.     ind_size =
  51.     ind_stmt =
  52.     last_u_d =
  53.     leave_comma =
  54.     line_no =
  55.     ljust_decl =
  56.     max_col =
  57.     out_coms
  58.     out_lines
  59.     p_l_follow =
  60.     paren_level =
  61.     pcase =
  62.     sc_end =
  63.     unindent_displace =
  64.     use_ff =
  65.     verbose =
  66.  
  67. CALLS:
  68.     atoi (lib)
  69.     cmp
  70.     creat (lib)
  71.     unlink (lib)
  72.     dump_line
  73.     eqin
  74.     fill_buffer
  75.     lexi
  76.     open (lib)
  77.     parse
  78.     pr_comment
  79.     printf (lib)
  80.     seek (lib)
  81.     time (lib)
  82.  
  83. CALLED BY:
  84.     No one (main routine)
  85.  
  86. HISTORY:
  87.     November 1976    D A Willcox of CAC    Initial coding
  88.     12/9/76        D A Willcox of CAC    Fixed defaults for decl_com_ind
  89.                         to be 8 less than com_ind if 
  90.                         left justifying declarations
  91.     12/9/76        D A Willcox of CAC    Fixed processing of nested
  92.                         <c>?<s>:<s> constructs
  93.     1/7/77        D A Willcox of CAC    Added check for overwrite of
  94.                         input file
  95.                         Added code to handle -br and -bl
  96.                         parameters
  97.     10/30/83    William H. Nugent    Change unlink to delete for
  98.                         VAX/VMS and commented out
  99.                         checking for input & output
  100.                         filename checking to protect
  101.                         from overwrite (not a worry in
  102.                         VMS land.  And changed the
  103.                         indent.pro to handle VMS (i.e.
  104.                         SYS$LOGIN:INDENT.PRO).  Backup
  105.                         modified to take advantage of
  106.                         VAX/VMS version numbers.
  107.  
  108.     8/17/85        Robert Heller        Change delete back to unlink
  109.                         for CP/M-68k and changed the
  110.                         indent.pro to be compatable
  111.                         with CP/M-68k.  also added
  112.                         calls to perror to give
  113.                         usefull error messages on
  114.                         system I/O errors.  Also
  115.                         re-enabled filename checking
  116.                         to prevent overwrite (CP/M-68K
  117.                         does not have version numbers).
  118.  
  119. */
  120. #include "indntglo.h";
  121. #include "indntcod.h";
  122.  
  123. /* #define dolog 1    /* if this define is removed, then the code to
  124.                produce a log file will be removed */
  125.  
  126. struct templ {               /* this is a template for the list of
  127.                       command line args */
  128.     char   *str;           /* pointer to string which is a valid
  129.                       command line arg */
  130.     int     code;           /* code to be used in switch for processing
  131.                       this arg */
  132. };
  133.  
  134.  
  135. struct templ    options[] =
  136. {                   /* warning - because of the way that this
  137.                       table is scanned, if one entry is an
  138.                       initial substring of another, then the
  139.                       longer entry should occur first */
  140.     "-cd", 4,
  141.     "-c", 2,
  142.     "-l", 1,
  143.     "-i", 3,
  144.     "-v", 5,
  145.     "-nv", 6,
  146.     "-dj", 7,
  147.     "-d", 13,               /* unindented comment placement */
  148.     "-ndj", 8,
  149.     "-bc", 10,               /* break after command in decl */
  150.     "-nbc", 9,               /* don't break after comma */
  151.     "-br", 14,               /* put brace on right of stmt */
  152.     "-bl", 15,               /* put brace on left by itself */
  153.     "-st", 16,               /* use the standard input and output
  154.                   files */
  155.     0, 0
  156. };
  157.  
  158.  
  159. char   *in_name = "Standard Input";
  160.                    /* will always point to name of input file 
  161.                    */
  162. char   *out_name = "Standard Output";
  163.                    /* will always point to name of output file
  164.                       */
  165. char    bakfile[32] = "";
  166.  
  167. main (argc, argv)
  168. int     argc;
  169. char  **argv;
  170. {
  171.  
  172.     int     dec_ind;           /* current indentation for declarations */
  173.     int     di_stack[20];      /* a stack of structure indentation levels 
  174.                    */
  175.     int     flushed_nl;           /* used when buffering up comments to
  176.                       remember that a newline was passed over 
  177.                    */
  178.     int     force_nl;           /* when true, code must be broken */
  179.     int     hd_type;           /* used to store type of stmt for if (...),
  180.                       for (...), etc */
  181.     register int    i;           /* local loop counter */
  182.     int     in_or_st;           /* Will be true iff there has been a
  183.                       declarator (e.g. int or char) and no
  184.                       left paren since the last semicolon.
  185.                       When true, a { is starting a structure
  186.                       definition or an initialization list */
  187.     register int    j;           /* local loop counter */
  188.     int     scase;           /* set to true when we see a case, so we
  189.                       will know what to do with the following
  190.                       colon */
  191.     int     sp_sw;           /* when true, we are in the expressin of
  192.                       if(...), while(...), etc. */
  193.     int     squest;           /* when this is positive, we have seen a ?
  194.                       without the matching : in a <c>?<s>:<s>
  195.                       construct */
  196.     register char  *t_ptr;     /* used for copying tokens */
  197.     int     type_code;           /* the type of token, returned by lexi */
  198.     int     want_blank;           /* set to true when the following token
  199.                       should be prefixed by a blank. (Said
  200.                       prefixing is ignored in some cases.) */
  201.  
  202. #ifdef dolog               /* include declarations needed for log */
  203.     int     log_fid;           /* fid of log file */
  204.  
  205.     struct logtmpl {           /* structure of a log entry */
  206.     int     tvec[2];       /* time of execution */
  207.     char    inp;           /* input fid */
  208.     char    outp;           /* output fid */
  209.     int     nout;           /* # output lines */
  210.     int     ncom;           /* # comments */
  211.     int     wcom;           /* # lines w/ comments */
  212.     int     wcode;           /* # lines w/code */
  213.     char    mc;           /* max line size */
  214.     char    ci;           /* comment indentation */
  215.     char    inds;           /* indent size */
  216.     char    dci;           /* decl comment indentation */
  217.     char    verb;           /* verbose */
  218.     char    ljus;           /* left just */
  219.     char    lvcom;           /* leave commas */
  220.     char    unin;           /* unindented comment indentation */
  221.     char    uid;           /* the user id */
  222.     char    bropt;           /* btype_2 */
  223.     int     reserved[2];
  224.     };
  225.  
  226.     struct logtmpl  logent;
  227. #endif
  228.  
  229. /*-----------------------------------------------*\
  230. |    INITIALIZATION
  231. \*-----------------------------------------------*/
  232.  
  233.  
  234.     combuf[0] = codebuf[0] = labbuf[0] = ' ';
  235.  /* set up code, label, and comment buffers */
  236.     combuf[1] = codebuf[1] = labbuf[1] = '\0';
  237.     s_lab = e_lab = labbuf + 1;
  238.     s_code = e_code = codebuf + 1;
  239.     s_com = e_com = combuf + 1;
  240.  
  241.     buf_ptr = buf_end = in_buffer;
  242.     line_no = 1;
  243.     had_eof = in_decl = decl_on_line = break_comma = false;
  244.     sp_sw = force_nl = false;
  245.     in_or_st = false;
  246.     bl_line = true;
  247.     dec_ind = 0;
  248.     di_stack[dec_nest = 0] = 0;
  249.     want_blank = in_stmt = ind_stmt = false;
  250.  
  251.  
  252.     scase = pcase = false;
  253.     squest = 0;
  254.     sc_end = 0;
  255.     bp_save = 0;
  256.     be_save = 0;
  257.  
  258.     input = -1;
  259.     output = -1;
  260.     ljust_decl = d_ljust;
  261.  
  262.  
  263.  
  264. /*--------------------------------------------------*\
  265. |   COMMAND LINE SCAN
  266. \*--------------------------------------------------*/
  267.  
  268.     max_col = d_max_col;       /* set up some default values */
  269.     com_ind = d_com_ind;
  270.     ind_size = d_ind_size;
  271.     verbose = d_verbose;
  272.     decl_com_ind = 0;           /* if this is not set to some positive
  273.                       value by an arg, we will set this equal
  274.                       to com_ind */
  275.     btype_2 = d_btype_2;
  276.     unindent_displace = d_unindent;
  277.     leave_comma = d_leave_comma;
  278.  
  279.     set_profile ();
  280.  
  281.     for (i = 1; i < argc; ++i) {
  282.     /* look thru args (if any) for changes to defaults */
  283.     if (argv[i][0] != '-') {/* no flag on parameter */
  284.         if (input < 0) {   /* we must have the input file */
  285.         in_name = argv[i];    /* remember name of input
  286.                        file */
  287.         input = open (in_name, 0);
  288.         if (input < 0) {    /* check for open error */
  289.             perror("indent");
  290.             printf ("Can't open %s\n", argv[i]);
  291.             exit ();
  292.         }
  293.         continue;
  294.         }
  295.         else
  296.         if (output < 0) {    /* we have the output file */
  297.             out_name = argv[i];    /* remember name of output file */
  298.             if (cmp (in_name, out_name) == 0) {      /* attempt to
  299.                        overwright the file */
  300.             printf ("Input and output files must be different\n");
  301.             exit ();
  302.             }
  303.  
  304.             output = creat (out_name, 0644);
  305.             if (output < 0) {   /* check for create error */
  306.             perror("indent");
  307.             printf ("Can't create %s\n", argv[i]);
  308.             exit ();
  309.             }
  310.             continue;
  311.         }
  312.  
  313.         printf ("Unknown parameter: %s\n", argv[i]);
  314.         exit ();
  315.     }
  316.     else
  317.         set_option (argv[i]);
  318.  
  319.     }                   /* end of for */
  320.  
  321.     if (input < 0) {
  322.     printf ("Usage: indent file [ outfile ] [ options ]\n");
  323.     exit ();
  324.     }
  325.     if (output < 0) {
  326.     out_name = in_name;
  327.     bakcopy ();
  328.     }
  329.  
  330.     if (com_ind <= 1)
  331.     com_ind = 2;           /* don't put normal comments before column
  332.                       2 */
  333.  
  334.     if (decl_com_ind <= 0)     /* if not specified by user, set this */
  335.     decl_com_ind = ljust_decl ? (com_ind <= 10 ? 2 : com_ind - 8) : com_ind;
  336.  
  337.     fill_buffer ();           /* get first batch of stuff into input
  338.                       buffer */
  339.  
  340.     parse (semicolon);
  341. /*-----------------------------------------------------
  342. |   START OF MAIN LOOP
  343. \*----------------------------------------------------*/
  344.  
  345.     while (1) {               /* this is the main loop.  it will go until
  346.                       we reach eof */
  347.     type_code = lexi ();   /* lexi reads one token.  The actual
  348.                       characters read are stored in "token".
  349.                       lexi returns a code indicating the type
  350.                       of token */
  351.  
  352.     /* 
  353.      * The following code moves everything following an if (), while (),
  354.      * else, etc. up to the start of the following stmt to a buffer.  This
  355.      * allows proper handling of both kinds of brace placement.
  356.      */
  357.  
  358.     flushed_nl = false;
  359.     while (search_brace) { /* if we scanned an if(), while(), etc., we
  360.                       might need to copy stuff into a buffer 
  361.     *//* we must loop, copying stuff into save_com, until we find the
  362.        start of the stmt which follows the if, or whatever */
  363.         switch (type_code) {
  364.         case newline: 
  365.             ++line_no;
  366.             flushed_nl = true;
  367.         case form_feed: 
  368.             break;     /* form feeds and newlines found here will
  369.                       be ignored */
  370.  
  371.         case lbrace:   /* this is a brace that starts the compound
  372.                       stmt */
  373.             if (sc_end == 0) {
  374.             /* ignore buffering if a comment wasn't stored up */
  375.             search_brace = false;
  376.             goto check_type;
  377.             }
  378.  
  379.             if (btype_2) {
  380.             save_com[0] = '{';
  381.             /* we either want to put the brace right after the if 
  382.             */
  383.             goto sw_buffer;
  384.             /* go to common code to get out of this loop */
  385.             }
  386.  
  387.         default:       /* it is the start of a normal statment */
  388.             if (flushed_nl)
  389.                    /* if we flushed a newline, make sure it is
  390.                       put back */
  391.             force_nl = true;
  392.  
  393.             if (sc_end == 0) {
  394.             /* ignore buffering if comment wasn't saved up */
  395.             search_brace = false;
  396.             goto check_type;
  397.             }
  398.  
  399.             if (force_nl) {
  400.             /* if we should insert a nl here, put it into the
  401.                buffer */
  402.             force_nl = false;
  403.             --line_no;
  404.             /* this will be re-increased when the nl is read from
  405.                the buffer */
  406.             *sc_end++ = '\n';
  407.             *sc_end++ = ' ';
  408.             if (verbose && !flushed_nl)
  409.                    /* print error msg if the line was not
  410.                       already broken */
  411.                 printf ("%d: Line broken\n", line_no);
  412.             flushed_nl = false;
  413.             }
  414.  
  415.             for (t_ptr = token; *t_ptr; ++t_ptr)
  416.             *sc_end++ = *t_ptr;
  417.         /* copy token into temp buffer */
  418.  
  419.         sw_buffer: 
  420.             search_brace = false;
  421.         /* stop looking for start of stmt */
  422.             bp_save = buf_ptr;
  423.         /* save current input buffer */
  424.             be_save = buf_end;
  425.             buf_ptr = save_com;
  426.         /* fix so that subsequent calls to lexi will take tokens
  427.            out of save_com */
  428.             *sc_end++ = ' ';
  429.         /* add trailing blank, just in case */
  430.             buf_end = sc_end;
  431.             sc_end = 0;
  432.             break;
  433.  
  434.         case comment:  /* we have a comment, so we must copy it
  435.                       into the buffer */
  436.             if (sc_end == 0) {
  437.             /* if this is the first comment, we must set up the
  438.                buffer */
  439.             save_com[0] = save_com[1] = ' ';
  440.             sc_end = &(save_com[2]);
  441.             }
  442.             else {
  443.             *sc_end++ = '\n';
  444.             /* add newline between comments */
  445.             *sc_end++ = ' ';
  446.             --line_no;
  447.             }
  448.  
  449.             *sc_end++ = '/';
  450.         /* copy in start of comment */
  451.             *sc_end++ = '*';
  452.  
  453.             for (;;) { /* loop until we get to the end of the
  454.                       comment */
  455.             *sc_end = *buf_ptr++;
  456.             if (buf_ptr >= buf_end)
  457.                 fill_buffer ();
  458.  
  459.             if (*sc_end++ == '*' && *buf_ptr == '/')
  460.                 break;
  461.             /* we are at end of comment */
  462.  
  463.             if (sc_end >= &(save_com[sc_size])) {
  464.             /* check for temp buffer overflow */
  465.                 printf ("%d: Internal buffer overflow.\n",
  466.                     line_no);
  467.                 printf ("Move big comment from right after if,\
  468.  while, or whatever.\n");
  469.                 exit ();
  470.             }
  471.             }
  472.  
  473.             *sc_end++ = '/';
  474.         /* add ending slash */
  475.             if (++buf_ptr >= buf_end)/* get past / in buffer */
  476.             fill_buffer ();
  477.             break;
  478.         }               /* end of switch */
  479.  
  480.         if (type_code != 0)/* we must make this check, just in case
  481.                       there was an unexpected EOF */
  482.         type_code = lexi ();
  483.     /* read another token */
  484.     }               /* end of while (serach_brace) */
  485. check_type: 
  486.  
  487.     if (type_code == 0) {  /* we got eof */
  488.         if (s_lab != e_lab || s_code != e_code
  489.             || s_com != e_com)/* must dump end of line */
  490.         dump_line ();
  491.         if (i_l_follow != 0)/* check for balanced braces */
  492.         printf ("%d too few }'s\n", i_l_follow);
  493.  
  494. #ifdef dolog               /* only include this stuff if we want to
  495.                       keep a log */
  496.         log_fid = open ("/mnt/net/willcox/indent/indent_log", 1);
  497.     /* open the log file */
  498.         if (log_fid >= 0) {
  499.         seek (log_fid, 0, 2);
  500.         /* point to end of log */
  501.         time (logent.tvec);
  502.         /* get current time */
  503.         logent.inp = input;
  504.         /* set up the log entry */
  505.         logent.outp = output;
  506.         logent.nout = out_lines;
  507.         logent.ncom = out_coms;
  508.         logent.wcom = com_lines;
  509.         logent.wcode = code_lines;
  510.         logent.mc = max_col;
  511.         logent.ci = com_ind;
  512.         logent.inds = ind_size;
  513.         logent.dci = decl_com_ind;
  514.         logent.verb = verbose;
  515.         logent.ljus = ljust_decl;
  516.         logent.lvcom = leave_comma;
  517.         logent.unin = unindent_displace;
  518.         logent.uid = getuid ();
  519.         logent.bropt = btype_2;
  520.         write (log_fid, &logent, sizeof logent);
  521.         }
  522. #endif
  523.         if (verbose) {
  524.         printf ("There were %d output lines and %d comments\n",
  525.             out_lines, out_coms);
  526.         printf ("(Lines with comments)/(Lines with code): %6.3f\n",
  527.             (1.0 * com_lines) / code_lines);
  528.         }
  529.  
  530.         exit ();
  531.     }
  532.  
  533.     if (
  534.         (type_code != comment) &&
  535.         (type_code != newline) &&
  536.         (type_code != preesc) &&
  537.         (type_code != form_feed)) {
  538.         if (
  539.             force_nl
  540.             &&
  541.             (type_code != semicolon) &&
  542.             (
  543.             type_code != lbrace
  544.             ||
  545.             !btype_2
  546.             )) {       /* we should force a broken line here */
  547.         if (verbose && !flushed_nl)
  548.             printf ("%d: Line broken\n", line_no);
  549.         flushed_nl = false;
  550.         dump_line ();
  551.         want_blank = false;
  552.         /* don't insert blank at line start */
  553.         force_nl = false;
  554.         }
  555.  
  556.         in_stmt = true;    /* turn on flag which causes an extra level
  557.                       of indentation. this is turned off by a
  558.                       ; or } */
  559.         if (s_com != e_com) {
  560.         /* the turkey has embedded a comment in a line. fix it */
  561.         *e_code++ = ' ';
  562.         for (t_ptr = s_com; *t_ptr; ++t_ptr)
  563.             *e_code++ = *t_ptr;
  564.         *e_code++ = ' ';
  565.         *e_code = '\0';/* null terminate code sect */
  566.         want_blank = false;
  567.         e_com = s_com;
  568.         }
  569.     }
  570.     else
  571.         if (type_code != comment)
  572.                    /* preserve force_nl thru a comment */
  573.         force_nl = false;
  574.     /* cancel forced newline after newline, form feed, etc */
  575.  
  576.  
  577.  
  578.     /*----------------------------------------------------*\
  579.     |   do switch on type of token scanned
  580.     \*----------------------------------------------------*/
  581.     switch (type_code) {   /* now, decide what to do with the token */
  582.  
  583.         case form_feed:    /* found a form feed in line */
  584.         use_ff = true; /* a form feed is treated much like a
  585.                       newline */
  586.         dump_line ();
  587.         want_blank = false;
  588.         break;
  589.  
  590.         case newline: 
  591.         dump_line ();
  592.         ++line_no;     /* keep track of input line number */
  593.         want_blank = false;
  594.         break;
  595.  
  596.         case lparen:       /* got a ( or [ */
  597.         ++p_l_follow;  /* count parens to make Healy happy */
  598.         if (want_blank && *token != '[')
  599.                    /* don't put space in front of square
  600.                       bracket */
  601.             *e_code++ = ' ';
  602.  
  603.         if (in_decl)
  604.             while ((e_code - s_code) < dec_ind)
  605.             *e_code++ = ' ';
  606.  
  607.         *e_code++ = token[0];
  608.         want_blank = false;
  609.         if (in_or_st && *token == '(') {
  610.         /* this is a kluge to make sure that declarations will be
  611.            aaigned right if proc decl has an explicit type on it,
  612.            i.e. "int a(x) {..." */
  613.             parse (semicolon);
  614.         /* I said this was a kluge... */
  615.             in_or_st = false;
  616.         /* turn off flag for structure decl or initialization */
  617.         }
  618.  
  619.         break;
  620.  
  621.         case rparen:       /* got a ) or ] */
  622.         if (--p_l_follow < 0) {
  623.             p_l_follow = 0;
  624.             printf ("%d: Extra %c\n", line_no, *token);
  625.         }
  626.  
  627.         if (e_code == s_code)/* if the paren starts the line */
  628.             paren_level = p_l_follow;
  629.         /*    then indent it */
  630.  
  631.         *e_code++ = token[0];
  632.         want_blank = true;
  633.  
  634.         if (sp_sw && (p_l_follow == 0)) {
  635.         /* check for end of if (...), or some such */
  636.             sp_sw = false;
  637.             force_nl = true;
  638.         /* must force newline after if */
  639.             last_u_d = true;
  640.         /* inform lexi that a following operator is unary */
  641.             in_stmt = false;
  642.         /* don't use stmt continuation indentation */
  643.  
  644.             parse (hd_type);
  645.         /* let parser worry about if, or whatever */
  646.         }
  647.  
  648.         search_brace = btype_2;
  649.         /* this should insure that constructs such as main(){... and
  650.            int[]{... have their braces put in the right place */
  651.         break;
  652.  
  653.         case unary_op:     /* this could be any unary operation */
  654.         if (want_blank)
  655.             *e_code++ = ' ';
  656.  
  657.         if (in_decl) { /* if this is a unary op in a *//*
  658.                       declaration, we should indent this token
  659.                       */
  660.             for (i = 0; token[i]; ++i);
  661.         /* find length of token */
  662.             while ((e_code - s_code) < (dec_ind - i))
  663.             *e_code++ = ' ';
  664.         /* pad it */
  665.         }
  666.  
  667.         for (t_ptr = token; *t_ptr; ++t_ptr)
  668.             *e_code++ = *t_ptr;
  669.         /* move the token to buffer */
  670.         want_blank = false;
  671.         break;
  672.  
  673.         case binary_op:    /* any binary operation */
  674.     do_binary: 
  675.         if (want_blank)
  676.             *e_code++ = ' ';
  677.         for (t_ptr = token; *t_ptr; ++t_ptr)
  678.             *e_code++ = *t_ptr;
  679.         /* move the operator */
  680.         want_blank = true;
  681.         break;
  682.  
  683.         case postop:       /* got a trailing ++ or -- */
  684.         *e_code++ = token[0];
  685.         *e_code++ = token[1];
  686.         want_blank = true;
  687.         break;
  688.  
  689.         case question:     /* got a ? */
  690.         squest++;      /* this will be used when a later colon
  691.                       appears so we can distinguish the
  692.                       <c>?<n>:<n> construct */
  693.         if (want_blank)
  694.             *e_code++ = ' ';
  695.         *e_code++ = '?';
  696.         want_blank = true;
  697.         break;
  698.  
  699.         case casestmt:     /* got word 'case' or 'default' */
  700.         scase = true;  /* so we can process the later colon
  701.                       properly */
  702.         if (want_blank)
  703.             *e_code++ = ' ';
  704.         for (t_ptr = token; *t_ptr; ++t_ptr)
  705.             *e_code++ = *t_ptr;
  706.         want_blank = true;
  707.         break;
  708.  
  709.         case colon:        /* got a ':' */
  710.         if (squest > 0) {
  711.         /* it is part of the <c>?<n>: <n> construct */
  712.             --squest;
  713.             if (want_blank)
  714.             *e_code++ = ' ';
  715.             *e_code++ = ':';
  716.             want_blank = true;
  717.             break;
  718.         }
  719.  
  720.         in_stmt = false;
  721.         /* seeing a label does not imply we are in a stmt */
  722.         for (t_ptr = s_code; *t_ptr; ++t_ptr)
  723.             *e_lab++ = *t_ptr;
  724.         /* turn everything so far into a label */
  725.         e_code = s_code;
  726.         *e_lab++ = ':';
  727.         *e_lab++ = ' ';
  728.         *e_lab = '\0';
  729.  
  730.         force_nl = pcase = scase;
  731.         /* pcase will be used by dump_line to decide how to indent the
  732.            label. force_nl will force a case n: to be on a line by
  733.            itself */
  734.         scase = false;
  735.         want_blank = false;
  736.         break;
  737.  
  738.         case semicolon:    /* got a ';' */
  739.         in_or_st = false;
  740.         /* we are not in an initialization or structure declaration */
  741.         scase = false; /* these will only need resetting in a
  742.                       error */
  743.         squest = 0;
  744.  
  745.         if (in_decl && s_code == e_code)
  746.                    /* align this in a declaration */
  747.             while ((e_code - s_code) < (dec_ind - 1))
  748.             *e_code++ = ' ';
  749.  
  750.         in_decl = (dec_nest > 0);
  751.         /* if we were in a first level structure declaration, we
  752.            aren't any more */
  753.  
  754.         if ((!sp_sw || hd_type != forstmt) && p_l_follow > 0) {
  755.         /* This should be true iff there were unbalanced parens in
  756.            the stmt.  It is a bit complicated, because the
  757.            semicolon might be in a for stmt */
  758.             printf ("%d: Unbalanced parens\n", line_no);
  759.             p_l_follow = 0;
  760.             if (sp_sw) {
  761.             /* this is a check for a if, while, etc. with
  762.                unbalanced parens */
  763.             sp_sw = false;
  764.             parse (hd_type);
  765.             /* don't lose the if, or whatever */
  766.             }
  767.         }
  768.  
  769.         *e_code++ = ';';
  770.         want_blank = true;
  771.         in_stmt = (p_l_follow > 0);
  772.         /* we are no longer in the middle of a stmt */
  773.  
  774.         if (!sp_sw) {  /* if not if for (;;) */
  775.             parse (semicolon);
  776.         /* let parser know about end of stmt */
  777.             force_nl = true;
  778.         /* force newline after a end of stmt */
  779.         }
  780.  
  781.         break;
  782.  
  783.         case lbrace:       /* got a { */
  784.         in_stmt = false;
  785.         /* don't indent the { */
  786.         force_nl = true;
  787.         /* force other stuff on same line as { onto new line */
  788.  
  789.         if (s_code != e_code && !btype_2) {
  790.         /* bracket is not alone on line */
  791.             if (verbose)
  792.             printf ("%d: Line broken\n", line_no);
  793.             dump_line ();
  794.             want_blank = false;
  795.         }
  796.  
  797.         if (p_l_follow > 0) {
  798.         /* check for preceeding unbalanced parens */
  799.             printf ("%d: Unbalanced parens\n", line_no);
  800.             p_l_follow = 0;
  801.             if (sp_sw) {
  802.             /* check for unclosed if, for, etc. */
  803.             sp_sw = false;
  804.             parse (hd_type);
  805.             ind_level = i_l_follow;
  806.             }
  807.         }
  808.  
  809.         if (s_code == e_code)
  810.             ind_stmt = false;
  811.         /* don't put extra indentation on line with '{' */
  812.         if (in_decl && in_or_st) {
  813.         /* this is either a structure declaration or an init */
  814.             di_stack[dec_nest++] = dec_ind;
  815.             dec_ind = 0;
  816.         }
  817.         else
  818.             decl_on_line = false;
  819.         /* we can't be in the middle of a declaration, so don't do
  820.            special indentation of comments */
  821.  
  822.         parse (lbrace);/* let parser know about this */
  823.         if (want_blank)/* put a blank before { if { is not at
  824.                       start of line */
  825.             *e_code++ = ' ';
  826.         want_blank = false;
  827.         *e_code++ = '{';
  828.         break;
  829.  
  830.         case rbrace:       /* got a } */
  831.         if (p_l_follow) {
  832.         /* check for unclosed if, for, else. */
  833.             printf ("%d: Unbalanced parens\n", line_no);
  834.             p_l_follow = 0;
  835.             sp_sw = false;
  836.         }
  837.  
  838.         if (s_code != e_code) {
  839.         /* } must be first on line */
  840.             if (verbose)
  841.             printf ("%d: Line broken\n", line_no);
  842.             dump_line ();
  843.         }
  844.  
  845.         *e_code++ = '}';
  846.         want_blank = true;
  847.         in_stmt = ind_stmt = false;
  848.  
  849.         if (dec_nest > 0) {
  850.         /* we are in multi-level structure declaration */
  851.             dec_ind = di_stack[--dec_nest];
  852.             in_decl = true;
  853.         }
  854.  
  855.         parse (rbrace);/*   let parser know about this */
  856.         break;
  857.  
  858.         case swstmt:       /* got keyword "switch" */
  859.         sp_sw = true;
  860.         hd_type = swstmt;
  861.         /* keep this for when we have seen the expression */
  862.         goto copy_id;  /* go move the token into buffer */
  863.  
  864.         case sp_paren:     /* token is if, while, for */
  865.         sp_sw = true;  /* the interesting stuff is done after the
  866.                       expression is scanned */
  867.         hd_type = (*token == 'i' ? ifstmt :
  868.             (*token == 'w' ? whilestmt : forstmt));
  869.         /* remember the type of header for later use by parser */
  870.         goto copy_id;  /* copy the token into line */
  871.  
  872.         case sp_nparen:    /* got else, do */
  873.         in_stmt = false;
  874.         if (e_code != s_code) {
  875.         /* make sure this starts a line */
  876.             if (verbose)
  877.             printf ("%d: Line broken\n", line_no);
  878.             dump_line ();
  879.             want_blank = false;
  880.         }
  881.  
  882.         force_nl = true;
  883.         /* also, following stuff must go onto new line */
  884.         parse (*token == 'e' ? elselit : dolit);
  885.         /* pass token on to parser */
  886.         goto copy_id;  /* move the token into line */
  887.  
  888.         case decl:            /* we have a declaration type (int,
  889.                       register, etc.) */
  890.         parse (decl);  /* let parser worry about indentation */
  891.         in_or_st = true;
  892.         /* this might be a structure or initialization declaration */
  893.         in_decl = decl_on_line = true;
  894.         for (i = 0; token[i++];);
  895.         /* get length of token */
  896.  
  897.         if (i <= 3)
  898.             i = 4;
  899.  
  900.         dec_ind = ((e_code - s_code + i) / ind_size + 1) * ind_size;
  901.         /* this will tell us how far to indent subsequent identifiers 
  902.         */
  903.         goto copy_id;
  904.  
  905.         case ident:        /* got an identifier or constant */
  906.         if (in_decl) { /* if we are in a declaration, we must
  907.                       indent identifier */
  908.             if (want_blank)
  909.             *e_code++ = ' ';
  910.             want_blank = false;
  911.  
  912.             while ((e_code - s_code) < dec_ind)
  913.             *e_code++ = ' ';
  914.         }
  915.         else
  916.             if (sp_sw && p_l_follow == 0) {
  917.             /* check for if expr w/o parens *//* this will make
  918.                JRM's obsurd "for ever" statements work */
  919.             sp_sw = false;
  920.             force_nl = true;
  921.             last_u_d = true;
  922.             in_stmt = false;
  923.             parse (hd_type);
  924.             }
  925.  
  926.     copy_id: 
  927.         if (want_blank)
  928.             *e_code++ = ' ';
  929.         for (t_ptr = token; *t_ptr; ++t_ptr)
  930.             *e_code++ = *t_ptr;
  931.         want_blank = true;
  932.         break;
  933.  
  934.         case period:       /* treat a period kind of like a binary
  935.                       operation */
  936.         *e_code++ = '.';
  937.         /* move the period into line */
  938.         want_blank = false;
  939.         /* don't put a blank after a period */
  940.         break;
  941.  
  942.         case comma: 
  943.         want_blank = (s_code != e_code);
  944.         /* only put blank after comma if comma does not start the line
  945.            */
  946.         if (in_decl)   /* align these in a declaration */
  947.             while ((e_code - s_code) < (dec_ind - 1))
  948.             *e_code++ = ' ';
  949.  
  950.         *e_code++ = ',';
  951.  
  952.         if (break_comma && p_l_follow == 0 && !leave_comma)
  953.             force_nl = true;
  954.  
  955.         break;
  956.  
  957.         case preesc:       /* got the character '#' */
  958.         if (
  959.             (s_com != e_com) ||
  960.             (s_lab != e_lab) ||
  961.             (s_code != e_code)) {
  962.         /* true iff the '#' was not at start of the line */
  963.             printf ("%d: What is this # doing here?\n", line_no);
  964.             goto do_binary;
  965.         /* treat it as a binary operator */
  966.         }
  967.  
  968.         *e_lab++ = '#';/* move whole line to 'label' buffer */
  969.         while (*buf_ptr != '\n') {
  970.             *e_lab = *buf_ptr++;
  971.             if (buf_ptr >= buf_end)
  972.             fill_buffer ();
  973.  
  974.             if (*e_lab++ == '/' && *buf_ptr == '*') {
  975.             /* check for comment on preprocessor line */
  976.             e_lab -= 2;
  977.             /* skip back over slash */
  978.             while (*e_lab == '\t' || *e_lab == ' ')
  979.                 --e_lab;
  980.             /* strip off trailing blanks and tabs */
  981.             *(++e_lab) = '\0';
  982.             /* null terminate the line */
  983.             if (++buf_ptr >= buf_end)
  984.                    /* space past start of comment */
  985.                 fill_buffer ();
  986.             col_1 = false;
  987.             /* don't let pr_comment think that this comment starts
  988.                in column 1 */
  989.             decl_on_line = true;
  990.             /* treat this as a declaration for comment placement
  991.                purposes */
  992.             goto proc_comment;
  993.             /* go process the comment */
  994.             }
  995.         }
  996.  
  997.         *e_lab = '\0'; /* null terminate line */
  998.         pcase = false;
  999.         break;           /* subsequent processing of the newline
  1000.                       character will cause the line to be
  1001.                       printed */
  1002.  
  1003.         case comment:      /* we have gotten a /*  this is a biggie */
  1004.     proc_comment: 
  1005.         pr_comment ();
  1006.         break;
  1007.     }               /* end of big switch stmt */
  1008.  
  1009.     *e_code = '\0';           /* make sure code section is null
  1010.                       terminated */
  1011.  
  1012.     }                   /* end of main while (1) loop */
  1013. };
  1014.  
  1015. /*
  1016.  * copy input file to backup file
  1017.  * if in_name is /blah/blah/blah/file, then backup file
  1018.  * will be ".Bfile"
  1019.  * then make the backup file the input and original
  1020.  * input file the output
  1021.  */
  1022. bakcopy () {
  1023.     int     n,
  1024.             bakchn;
  1025.     char    buff[512];
  1026.     register char  *p;
  1027.  
  1028.  /* construct file name .Bfile */
  1029.  /* RPH - fix backup file name - .Bfile is not legal for CP/M.  will
  1030.     instead create a name.BAK file like ED. */
  1031.     strcpy(bakfile,in_name);    /* copy whole thing over first */
  1032.     /* skip to end of name or of string */
  1033.     for (p = bakfile; *p != '\0' && *p != '.' ; p++);
  1034.     if (*p == '\0') *p = '.';    /* add dot to typeless file name */
  1035.     p++;    /* skip over dot */
  1036.     strcpy(p,"BAK");    /* create file name */
  1037.  
  1038.  /* copy in_name to backup file */
  1039.     bakchn = creat (bakfile, 0600);
  1040.     if (bakchn < 0) {
  1041.     perror("indent");
  1042.     printf ("can't create backup file \"%s\"\n", bakfile);
  1043.     exit ();
  1044.     }
  1045.     while (n = read (input, buff, 512))
  1046.     write (bakchn, buff, n);
  1047.     close (bakchn);
  1048.     close (input);
  1049.  
  1050.  /* re-open backup file as the input file */
  1051.     input = open (bakfile, 0);
  1052.     if (input < 0) {
  1053.     perror("indent");
  1054.     printf ("can't re-open backup file\n");
  1055.     exit ();
  1056.     }
  1057.  
  1058.  /* now the original input file will be the output */
  1059.     output = creat (in_name, 0644);
  1060.     if (output < 0) {
  1061.     perror("indent");
  1062.     printf ("can't create %s\n", in_name);
  1063.     unlink (bakfile);
  1064.     exit ();
  1065.     }
  1066. }
  1067.  
  1068.  
  1069. set_option (arg)
  1070. char   *arg;
  1071. {
  1072.     register    j;
  1073.     for (j = 0; options[j].str != 0; ++j) {
  1074.                    /* look thru list of possible options */
  1075.     if (eqin (options[j].str, arg)) {
  1076.         set_var (j, arg);
  1077.         break;           /* get out of for loop */
  1078.     }
  1079.     }
  1080.  
  1081.     if (options[j].str == 0) { /* illegal arg given */
  1082.     printf ("Unknown parameter: %s\n", arg);
  1083.     exit ();
  1084.     }
  1085. }
  1086.  
  1087.  
  1088. set_var (j, arg)
  1089. char   *arg;
  1090. {
  1091.     switch (options[j].code) {
  1092.     case 1:            /* have -lnnn */
  1093.         max_col = atoi (&arg[2]);
  1094.         break;
  1095.     case 2:            /* have -cnnn */
  1096.         com_ind = atoi (&arg[2]);
  1097.         break;
  1098.     case 3:            /* have -innn */
  1099.         ind_size = atoi (&arg[2]);
  1100.         break;
  1101.     case 4:            /* have -cdnnn */
  1102.         decl_com_ind = atoi (&arg[3]);
  1103.         break;
  1104.     case 5:            /* have -v */
  1105.         verbose = true;
  1106.         break;
  1107.     case 6:            /* have -nv */
  1108.         verbose = false;
  1109.         break;
  1110.     case 7:            /* have -dj */
  1111.         ljust_decl = true;
  1112.         break;
  1113.     case 8:            /* have -ndj */
  1114.         ljust_decl = false;
  1115.         break;
  1116.     case 9:            /* -nbc */
  1117.         leave_comma = true;
  1118.         break;
  1119.     case 10:            /* -bc */
  1120.         leave_comma = false;
  1121.         break;
  1122.     case 13:            /* -dnnn */
  1123.         unindent_displace = atoi (&arg[2]);
  1124.         break;
  1125.     case 14:            /* -br */
  1126.         btype_2 = true;
  1127.         break;
  1128.     case 15:            /* -bl */
  1129.         btype_2 = false;
  1130.         break;
  1131.     case 16:
  1132.         if(input<0) input = 0;
  1133.         if(output<0) output = 1;
  1134.         break;
  1135.     }
  1136. }
  1137.  
  1138.  
  1139. /*
  1140.  * GETPRO - get profile file
  1141.  * profile file is max 127 characters
  1142.  */
  1143. getpro (name, buf)
  1144. char   *name,               /* profile file name, as in '.indent.pro' 
  1145.                    */
  1146.        *buf;               /* will receive contents of .pro file */
  1147. {
  1148.     register    chn,
  1149.                 n;
  1150.     char    file[64];
  1151.  
  1152.     file[0] = 0;
  1153. /*  WHN 10/30/83  VAX/VMS file name time (CP/M-68k too - RPH)
  1154.     strcat (file, getenv ("HOME"));
  1155.     strcat (file, "/");
  1156. */
  1157.     strcat (file, name);
  1158.     chn = open (file, 0);
  1159.     if (chn < 0)
  1160.     return (-1);
  1161.     n = read (chn, buf, 127);
  1162.     if (n < 0)
  1163.     return (-1);
  1164.     buf[n--] = 0;           /* null terminate line */
  1165.     if (buf[n] == '\n')
  1166.     buf[n] = 0;
  1167.     close (chn);
  1168.     return (0);
  1169. }
  1170.  
  1171.  
  1172. /*
  1173.  * strip off arguments in a string:
  1174.  * p is address of a character pointer
  1175.  * nextchr returns pointer to front of first arg
  1176.  * arg is null terminated.
  1177.  * p is reset to after arg for subsequent calls
  1178.  */
  1179. char   *nxtarg (p)
  1180. char  **p;
  1181. {
  1182.     register char  *f,
  1183.                    *b;
  1184.     f = b = *p;
  1185.     while (*f && (*f == ' ' || *f == '\t'))
  1186.     f++;
  1187.     while (*b && (*b != ' ' && *b != '\t'))
  1188.     b++;
  1189.     if (*b != 0)
  1190.     *b++ = 0;
  1191.     *p = b;
  1192.     return (f);
  1193. }
  1194.  
  1195.  
  1196. set_profile () {
  1197.     char    line[128],
  1198.            *b;
  1199.     register char  *f;
  1200.     extern char *nxtarg ();
  1201. /*  WHN 10/30/83 VAX/VMS filename
  1202.     if (getpro (".indent.pro", line) < 0)
  1203.     return;
  1204. */
  1205. /*  RPH 8/17/85 CP/M-68K filename */
  1206. /*  if (getpro ("SYS$LOGIN:INDENT.PRO", line) <0)*/  /* WHN 10/30/83 VAX/VMS */
  1207. /*    return;*/
  1208.     if (getpro("INDENT.PRO", line) < 0) return;    /* RPH 8/17/85 CP/M-68K */
  1209.  
  1210.     b = line;
  1211.     if(verbose) printf ("profile: %s\n", b);
  1212.     while (*(f = nxtarg (&b)))
  1213.     set_option (f);
  1214. }
  1215.