1 //******************************************************************************
2 // Copyright 1996-2005,2021 by Thomas E. Dickey                                *
3 // All Rights Reserved.                                                        *
4 //                                                                             *
5 // Permission to use, copy, modify, and distribute this software and its       *
6 // documentation for any purpose and without fee is hereby granted, provided   *
7 // that the above copyright notice appear in all copies and that both that     *
8 // copyright notice and this permission notice appear in supporting            *
9 // documentation, and that the name of the above listed copyright holder(s)    *
10 // not be used in advertising or publicity pertaining to distribution of the   *
11 // software without specific, written prior permission. THE ABOVE LISTED       *
12 // COPYRIGHT HOLDER(S) DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,   *
13 // INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO      *
14 // EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY SPECIAL, *
15 // INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM  *
16 // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE  *
17 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR   *
18 // PERFORMANCE OF THIS SOFTWARE.                                               *
19 //******************************************************************************
20 // $Id: tabs.cpp,v 1.26 2021/01/08 23:32:37 tom Exp $
21 // Tab conversion & first-pass scanning
22 
23 #include <ctype.h>
24 #include <string.h>            // strlen(), strstr(), strchr(), strcpy(), strcmp()
25 
26 #include "bcpp.h"
27 
28 // ----------------------------------------------------------------------------
29 // Function takes a unsigned char and converts it to a C type string that
30 // contains the char's value, but in octal (i.e "\000" = null char).
31 //
32 // Parameters:
33 // value     : The value that wishes to be converted
34 //
35 // Return Values:
36 // char*     : Returns a pointer to the string that was converted.
37 // Memory is allocated via the new command, and once string has been used,
38 // memory should be returned to the system.
39 //
ConvertCharToOctal(unsigned char value)40 static char* ConvertCharToOctal (unsigned char value)
41 {
42     const char octalVals[] = "01234567";
43 
44     char* pOctalValue = new char[5]; // \000 digits plus null terminator
45 
46     if (pOctalValue != NULL)
47     {
48         int last = 1;
49         switch (value)
50         {
51             case '\a':  pOctalValue[1] = 'a';   break;
52             case '\b':  pOctalValue[1] = 'b';   break;
53             case '\f':  pOctalValue[1] = 'f';   break;
54             case '\n':  pOctalValue[1] = 'n';   break;
55             case '\r':  pOctalValue[1] = 'r';   break;
56             case '\t':  pOctalValue[1] = 't';   break;
57             default:
58                 last = 3;
59                 for (int pos = last; pos >= 1; pos--)
60                 {
61                     pOctalValue[pos] = octalVals[(value & 7)];
62                     value >>= 3; // left shift to next three bits
63                 }
64         }
65         pOctalValue[0] = ESCAPE;
66         pOctalValue[last+1] = NULLC;
67     }
68 
69     return pOctalValue;
70 }
71 
72 // ----------------------------------------------------------------------------
73 // Compute the number of characters in an escape
skipEscape(char * String)74 static int skipEscape(char *String)
75 {
76     int it = 1;
77     int n = 1;
78 
79     if (isdigit(String[n]))
80     {
81         while (n <= 3)
82         {
83             if (isdigit(String[n++]))
84                 it = n-1;
85             else
86                 break;
87         }
88     }
89     else if (String[n] == 'x')
90     {
91         while (n <= 3)
92         {
93             if (isxdigit(String[++n]))
94                 it = n-1;
95             else
96                 break;
97         }
98     }
99     return it;
100 }
101 
102 // ----------------------------------------------------------------------------
103 #ifdef DEBUG
showCharState(CharState theState)104 static const char *showCharState(CharState theState)
105 {
106     const char *it;
107     switch (theState)
108     {
109         default:
110         case Blank:     it = "Blank";   break;
111         case PreProc:   it = "PreProc"; break;
112         case Normal:    it = "Normal";  break;
113         case Comment:   it = "Comment"; break;
114         case Ignore:    it = "Ignore";  break;
115         case DQuoted:   it = "DQuoted"; break;
116         case SQuoted:   it = "SQuoted"; break;
117     }
118     return it;
119 }
120 #endif
121 
122 // ----------------------------------------------------------------------------
123 // Compute the state after this character is processed
nextCharState(char * & String,CharState & theState,int & skip)124 static void nextCharState(char * &String, CharState &theState, int &skip)
125 {
126     if (skip-- <= 0)
127     {
128         skip = 0;
129         if (theState == Blank)
130         {
131             if (!isspace(String[0]))
132                 theState = Normal;
133         }
134         if (theState == Normal)
135         {
136             switch (String[0])
137             {
138                 case ESCAPE:
139                     skip = skipEscape(String);
140                     break;
141                 case DQUOTE:
142                     theState = DQuoted;
143                     break;
144                 case SQUOTE:
145                     theState = SQuoted;
146                     break;
147                 case '/':
148                     switch (String[1])
149                     {
150                     case '*':
151                         theState = Comment;
152                         skip = 1;
153                         break;
154                     case '/':
155                         theState = Ignore;
156                         break;
157                     default:
158                         break;
159                     }
160                     break;
161                 case SPACE:
162                 case TAB:
163                     theState = Blank;
164                     break;
165                 default:
166                     break;
167             }
168         }
169         else if (theState == Comment)
170         {
171             if (String[0] == '*'
172              && String[1] == '/')
173             {
174                 theState = Normal;
175                 skip = 1;
176             }
177         }
178         else if (theState == SQuoted)
179         {
180             switch (String[0])
181             {
182                 case SQUOTE:
183                     theState = Normal;
184                     break;
185                 case ESCAPE:
186                     skip = skipEscape(String);
187                     break;
188                 default:
189                     break;
190             }
191         }
192         else if (theState == DQuoted)
193         {
194             switch (String[0])
195             {
196                 case DQUOTE:
197                     theState = Normal;
198                     break;
199                 case ESCAPE:
200                     skip = skipEscape(String);
201                     break;
202                 default:
203                     break;
204             }
205         }
206     }
207 }
208 
209 // ----------------------------------------------------------------------------
210 // Check for non-printable characters, which we'll either quote or remove,
211 // depending on whether they're in strings or not.
NonPrintable(char c,int mode)212 static int NonPrintable(char c, int mode)
213 {
214     int it = false;
215     unsigned char check = static_cast<unsigned char>(c);
216 
217     // remove chars below a space, but not if char is a TAB.
218     if (check < SPACE && check != TAB) {
219         it = true;
220     } else if (mode == 1) {
221         it = (check >= 127);                    /* non 7-bit ASCII? */
222     } else if (mode == 3) {
223         it = (check >= 127) && (check < 160);   /* ISO C1 character? */
224     }
225     return it;
226 }
227 
228 // ----------------------------------------------------------------------------
229 // Function expands tabs to spaces; the number of spaces to expand to is
230 // dependent upon the tabSpaceSize parameter within user settings, and
231 // tab column positions.
232 //
233 // Parameters:
234 //      pString     : Pointer to the string to process !
235 //      tabLen      : How much a tab is worth in spaces.
236 //      deleteChars : mode to select non-printing characters for removal/quoting
237 //      quoteChars  : quote non-printing characters
238 //      curState    : character-state at beginning (end) of string
239 //      lineState   : character-states within string
240 //
241 //      curState and lineState are set as side-effects
242 //
ExpandTabs(char * & pString,int tabLen,int deleteChars,bool quoteChars,CharState & curState,char * & lineState,bool & codeOnLine)243 void ExpandTabs (char* &pString,
244     int tabLen,
245     int deleteChars,
246     bool quoteChars,
247     CharState &curState, char * &lineState, bool &codeOnLine)
248 {
249     int   col = 0;
250     int   skip = 0;
251     size_t last = 0;
252     char* pSTab = pString;
253     bool  expand = true;
254     bool  my_pString = false;
255     bool  had_print = false;
256     CharState oldState = curState;
257 
258     lineState = new char[strlen (pString) + 1];
259     if (lineState == 0)
260         return;
261 
262     lineState[0] = NullC;
263 
264     //TRACE((" ExpandTabs(%s)%s\n", pString, codeOnLine ? " code" : ""))
265     while (*pSTab != NULLC)
266     {
267         col++;
268 
269         if (isgraph(*pSTab))
270             had_print = true;
271 
272         if (skip || !isspace(*pSTab))
273             last = col + skip;
274 
275         if (*pSTab == TAB                       // calculate tab positions !
276          && expand
277          && skip == 0
278          && !(had_print && (curState == Ignore || curState == Comment))
279          && curState != SQuoted
280          && curState != DQuoted)
281         {
282             int tabAmount = 0;
283 
284             // tab is first character !!!!
285             if (col == 1)
286                 tabAmount = tabLen;
287             else
288                 tabAmount = ((((col+tabLen-1) / tabLen)) * tabLen) - col + 1;
289 
290             //TRACE(("amount:%d, col:%d, state:%s (%d)\n", tabAmount, col, showCharState(curState), had_print))
291             if (tabAmount > 0)
292             {
293                 // create newString, remove tab !
294                 char* pNewString = new char[strlen (pString) + tabAmount + 1];
295                 char* pNewStates = new char[strlen (pString) + tabAmount + 1];
296 
297                 if (pNewString == NULL
298                  || pNewStates == NULL)
299                 {
300                     if (my_pString)
301                     {
302                         delete[] pString;
303                         pString = 0;
304                     }
305                     delete[] lineState;
306                     return;
307                 }
308                 my_pString = true;
309 
310                 strcpy (pNewStates, lineState);
311                 delete[] lineState;
312                 lineState = pNewStates;
313 
314                 // copy first part
315                 strcpy (pNewString, pString);
316 
317                 // add spaces
318                 char *pAddSpc = pNewString + col - 1;
319                 while (tabAmount-- > 0)
320                     *pAddSpc++ = SPACE;
321 
322                 // add original trailing spaces
323                 strcpy (pAddSpc, pSTab+1);
324                 delete[] pString;               // remove old string from memory
325                 pString = pNewString;
326                 pSTab   = pString + col - 1;    // point to the first blank
327                 //TRACE(("...%d:%s\n", col, pString))
328             }
329             else
330                 *pSTab = SPACE;
331 
332         }
333         // SCCS ID contains a tab that we don't want to touch
334         else if (*pSTab == '@' && !strncmp(pSTab+1, "(#)", 3))
335         {
336             expand = false;
337         }
338         else if (NonPrintable(*pSTab, deleteChars))
339         {
340             if (quoteChars
341              && (curState == SQuoted
342               || curState == DQuoted)) {
343                 char* pOctal = ConvertCharToOctal(*pSTab);
344                 char* pTemp = new char[strlen(pString)+strlen(pOctal)+1];
345                 if (pOctal == 0 || pTemp == 0)
346                 {
347                     delete[] pOctal;
348                     delete[] pTemp;
349                     return;
350                 }
351                 *pSTab = NULLC;
352                 strcpy(pTemp, pString);
353                 strcat(pTemp, pOctal);
354                 strcat(pTemp, pSTab+1);
355                 pSTab   = pTemp + (pSTab - pString);
356 
357                 delete[] pString;
358                 pString = pTemp;
359 
360                 pTemp = new char[strlen(pString)+strlen(pOctal)+1];
361                 if (pTemp == 0)
362                 {
363                     delete[] pOctal;
364                     delete[] pTemp;
365                     delete[] pString;
366                     pString = NULL;
367                     return;
368                 }
369                 strcpy(pTemp, lineState);
370 
371                 delete[] lineState;
372                 lineState = pTemp;
373 
374                 delete[] pOctal;
375             }
376             else    // simply remove the character
377             {
378                 int n = 0;
379                 while ((pSTab[n] = pSTab[n+1]) != NULLC)
380                     n++;
381             }
382             col--;
383             //TRACE(("re-interpret col %d\n", col))
384             continue;   // re-interpret character
385         }
386 
387         if (skip == 0)
388             oldState = curState;
389         nextCharState(pSTab, curState, skip);
390 
391         // Set the saved-state based on whether we're transitioning from
392         // something that's got quotes (which are part of it):
393         lineState[col-1] = (curState == Normal)
394                 && ((oldState == DQuoted)
395                  || (oldState == SQuoted)
396                  || (oldState == Comment))
397                    ? oldState
398                    : curState;
399 
400         // Override the first '#' on a non-continued line to mark a
401         // preprocessor-control.
402         if (*pSTab == POUNDC
403          && !codeOnLine
404          && ispunct(curState))
405         {
406             lineState[col-1] = PreProc;
407         }
408         else if (ispunct(lineState[col-1]))
409         {
410             codeOnLine = true;
411         }
412 
413         lineState[col] = NullC;
414 
415         pSTab++;
416     }
417 
418     // Set up for the next time through this procedure
419     if (curState == Ignore)
420         curState = Normal;
421     if (col == 0
422      || pString[col-1] != ESCAPE)
423     {
424         codeOnLine = false;
425     }
426 
427     if (skip == 0
428      && (curState == DQuoted
429       || curState == SQuoted))
430         curState = Normal;    // recover from syntax error
431 
432     if (last < strlen(pString))
433     {
434         pString[last] = NULLC;      // trim trailing blanks
435         lineState[last] = NullC;
436     }
437 
438     TRACE((" Expanded  (%s)\n", pString));
439     TRACE((" lineState (%s)\n", lineState));
440     TRACE(("%s %d/%d %s\n", last > strlen(pString)+1 ? "FIXME" : "", last, strlen(pString), showCharState(curState)));
441 }
442 
443 // ----------------------------------------------------------------------------
444 // This function is used to allocate memory for indentation within function
445 // OutputToOutFile(). Once the memory needed is allocated, it fills the memory
446 // with spaces, or tabs depending upon the fill mode.
447 //
448 // Parameters:
449 // Mode         : Defines the fill mode of the memory that it allocate
450 //             1 = tabs only
451 //             2 = spaces only
452 //             3 = both
453 // len       : Number of bytes needed to be allocated
454 // spaceIndent:Number of memory locations a tab character take up
455 //
456 // Return Values:
457 // char*     : Returns a pointer to the memory/string that was allocated
458 //
TabSpacing(int mode,int col,int len,int spaceIndent)459 char* TabSpacing (int mode, int col, int len, int spaceIndent)
460 {
461     char* pOutTab = NULL;
462     char* pOutSpc = NULL;
463 
464     if ((mode & 1) == 1)
465     {
466         int numOfTabs = 0;
467 
468         // bypass exception error
469         if (spaceIndent > 0)
470         {
471            numOfTabs = ((len+col) / spaceIndent) - (col / spaceIndent);
472            if (len != 0)
473                len = (len + col) % spaceIndent;
474         }
475 
476         pOutTab = new char[numOfTabs + 1];
477         if (pOutTab != NULL)
478         {
479             for (int fillTabs = 0; fillTabs < numOfTabs; fillTabs++)
480                     pOutTab[fillTabs] = TAB;
481             pOutTab[numOfTabs] = NULLC;
482         }
483         else
484             return NULL; // memory allocation failed
485 
486         // If not in both tab, and space concatenation.
487         if ((mode & 2) == 0)
488               return pOutTab;
489     }//bit 0 set !
490 
491     if ((mode & 2) == 2)
492     {
493         if (pOutTab == NULL) //##### normal space allocation !
494         {
495             pOutSpc = new char[len+1];
496             if (pOutSpc != NULL)
497             {
498                 for (int fillSpcs = 0; fillSpcs < len; fillSpcs++)
499                     pOutSpc[fillSpcs] = SPACE;
500                 pOutSpc[len] = NULLC;
501                 return pOutSpc;   //##### return end product
502             }
503             else
504                 return NULL;    // memory allocation failed
505         }
506         else  // else a mix of spaces & tabs
507         {
508             int numOfSpcs = 0;
509 
510             if (spaceIndent > 0)
511                numOfSpcs = len % spaceIndent;
512 
513             pOutSpc = new char[numOfSpcs+1];
514             if (pOutSpc != NULL)
515             {
516                 for (int fillSpcs = 0; fillSpcs < numOfSpcs; fillSpcs++)
517                     pOutSpc[fillSpcs] = SPACE;
518                 pOutSpc[numOfSpcs] = NULLC;
519             }
520             else
521                 return NULL; // memory allocation failed
522         }
523     }// bit 1 set
524 
525     //##### Concatenate tabs & spaces
526     if ( ((mode & 1) == 1) && ((mode & 2) == 2) )
527     {
528         char* pConCat = new char[(strlen (pOutTab) + strlen (pOutSpc) + 1)];
529         // #### Check memory allocation
530         if (pConCat == NULL)
531         {
532             delete[] pOutTab;
533             delete[] pOutSpc;
534             return NULL;
535         }
536         strcpy (pConCat, pOutTab);
537         strcpy (pConCat + strlen (pConCat), pOutSpc);
538         delete[] pOutTab;
539         delete[] pOutSpc;
540         return pConCat;
541     }
542 
543     return NULL; //##### illegal mode passed !
544 }
545