/********************************************************************************************* | |
* ibm1130_fmt.c : interpret tabs in 1130 Assembler or Fortran source | |
* Bob Flanders | |
* ------------------------------------------------------------------------------------------- | |
* | |
* These routines are used by ibm1130_cr.c when the user has indicated | |
* that the input text is formatted with tabs. Input lines are edited | |
* into the appropriate column format. Three edit modes are recognized: | |
* | |
* Assembler mode: | |
* Input lines of the form | |
* | |
* [label]<whitespace>[opcode]<tab>[tag][L]<tab>[argument] | |
* | |
* are rearranged so that the input fields are placed in the appropriate columns | |
* | |
* The label must start on the first character of the line. If there is no label, | |
* the first character(s) before the opcode must be whitespace. Following the opcode, there | |
* MUST be a tab character, followed by the format and tag. Following the format and tag | |
* may be exactly one whitespace character, and then starts the argument. | |
* | |
* Input lines with * in column 1 and blank lines are turned into Assembler comments, | |
* with the * in the Opcode field. | |
* | |
* Assembler directive lines at the beginning of the deck must be preceded by | |
* ! to indicate that they are not comments. For example, | |
* | |
* !*LIST | |
* * This is a comment | |
* | |
* Fortran mode: | |
* Input lines of the form | |
* | |
* [label]<tab>statement | |
* | |
* or | |
* | |
* [label]<tab>Xcontinuation | |
* | |
* where X is a non alphabetic contination character are rearranged in the | |
* appropriate manner: | |
* | |
* 1 2 | |
* 12345678901234567890... | |
* ------------------------ | |
* label statement | |
* labelXcontinuation | |
* | |
* However, you must take care that you don't end up with statement text after column 72. | |
* | |
* Input lines with * or C in column 1 are left alone (comments and directives) | |
* | |
* (The ! escape is not used before Fortran directives as before Assembler directives) | |
* | |
* Tab mode: | |
* Tabs are replaced with spaces. Tab settings are assumed to be eight characters wide, | |
* as is standard for vi, notepad, etc. | |
*********************************************************************************************/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <memory.h> | |
#include <ctype.h> | |
#include <string.h> | |
#include "ibm1130_fmt.h" | |
#define MAXLINE 81 /* maximum output line size */ | |
#define WORKSZ 256 /* size for tab work area */ | |
#define TAGOFFSET 12 /* offset for tag field */ | |
#define FMTOFFSET 11 /* offset for format field */ | |
#define MIN(a,b) ((a < b) ? a : b) | |
#define AMSG " with Assembler Reformat" | |
#define FMSG " with FORTRAN Reformat" | |
#define WMSG " with tab replacement" | |
#define AFORMAT "%20.20s%-60.60s"," " | |
#define ACOMMENTFMT "%20.20s%-60.60s"," " | |
#define ABLANKLINE "%20.20s*"," " | |
#define FFORMAT "%-5.5s %-74.74s" | |
#define FCONTFMT "%-5.5s%-75.75s" | |
static char gszLabel[6]; /* work area for label */ | |
static char gszArg[MAXLINE]; /* .. argument */ | |
static char gszOutput[MAXLINE]; /* .. output */ | |
static short gaiAsmTabs[] = {7,12,15,20,25,30,35,40,45,52,0};/* tab stops for assembler */ | |
static short gaiPlainTabs[42]; /* tab stops for plain tabs. Settings will be made later. Max # positions when tabs are every 2 positions */ | |
static int giPlainTabWidth = 0; | |
/* | |
* helper routines | |
*/ | |
/************************************************* | |
* ExpandTabs: Expand tabs to spaces | |
*/ | |
char* ExpandTabs(char* p_szInbuf, /* expand tabs .. input buffer */ | |
char* p_szOutbuf, /* .. output buffer */ | |
short* p_aiTabs) /* .. array of tab stops (1 based) -- 0 end of array */ | |
{ | |
short iI, /* input position */ | |
iO, /* output position */ | |
iT; /* next tab stop */ | |
char cX; /* character to test */ | |
iI = 0; /* init input position */ | |
iO = 0; /* init output position */ | |
iT = 0; /* init tab stop */ | |
while ((cX = *(p_szInbuf + iI)) != 0) /* while there are characters */ | |
{ | |
if (cX == '\t') /* q. tab character? */ | |
{ /* a. yes .. */ | |
while ((p_aiTabs[iT] <= iO + 1) /* search for next valid stop .. */ | |
&& (p_aiTabs[iT] != 0)) /* .. or end of table */ | |
iT++; /* .. go to next tab */ | |
if (p_aiTabs[iT] != 0) /* q. end of tab array? */ | |
{ /* a. no .. */ | |
while (iO < (p_aiTabs[iT] - 1)) /* fill to tab with blanks */ | |
*(p_szOutbuf + iO++) = ' '; /* .. put in a blank */ | |
} | |
else /* Otherwise ... */ | |
*(p_szOutbuf + iO++) = ' '; /* .. Translate to blank */ | |
} | |
else /* Otherwise .. not tab */ | |
*(p_szOutbuf + iO++) = cX; /* .. save the input char */ | |
iI++; /* next input character */ | |
} | |
*(p_szOutbuf + iO) = 0; /* end the string.. */ | |
return p_szOutbuf; /* .. return output area addr */ | |
} | |
/************************************************* | |
* extract next token, modify pointer | |
*/ | |
char* GetToken(char* p_szOut, /* output location */ | |
int p_iLen, /* max output length */ | |
char**p_pszToken) /* pointer to input token */ | |
{ | |
int iI; /* work integer */ | |
char* pszX; /* work pointer */ | |
pszX = *p_pszToken; /* get pointer to token */ | |
for (iI = 0; *(pszX + iI) && (!isspace(*(pszX + iI)));) /* while not whitespace & not end */ | |
iI++; /* .. count token length */ | |
memset(p_szOut, 0, p_iLen); /* zero out output area */ | |
if (iI > 0) /* q. any chars? */ | |
strncpy(p_szOut, *p_pszToken, MIN(iI, p_iLen-1)); /* a. yes.. copy max of p_iLen-1 */ | |
*p_pszToken += iI; /* point beyond token */ | |
return p_szOut; /* .. return token pointer */ | |
} | |
/************************************************* | |
* EditToAsm - convert tab-formatted text line to 1130 Assembler format | |
*/ | |
const char *EditToAsm (char* p_pszEdit, int width) /* convert line to 1130 assembler */ | |
{ | |
char pszLine[MAXLINE]; /* source line */ | |
char pszWork[WORKSZ]; /* work buffer */ | |
char acTFWrk[2]; /* tag/format work area */ | |
size_t iI; /* work integer */ | |
if (p_pszEdit == NULL) /* q. null request? */ | |
return AMSG; /* a. yes .. return display message */ | |
if (*p_pszEdit == '!') /* leave lines starting with ! alone */ | |
return EditToWhitespace(p_pszEdit+1, width); | |
if (*p_pszEdit == '*') /* q. comment line? */ | |
{ /* a. yes.. */ | |
strncpy(pszWork, EditToWhitespace(p_pszEdit, width), MAXLINE); /* .. convert any tabs */ | |
sprintf(gszOutput, ACOMMENTFMT, pszWork); /* .. put the comment out there in the opcode column */ | |
return gszOutput; /* .. and return it */ | |
} | |
strncpy(pszLine, p_pszEdit, MAXLINE-1); /* copy the line local */ | |
ExpandTabs(pszLine, pszWork, gaiAsmTabs); /* expand the tabs */ | |
strncpy(pszLine, pszWork, MAXLINE-1); /* copy the line back */ | |
for (iI = strlen(pszLine); iI--;) /* trim trailing whitespace */ | |
{ | |
if (*(pszLine + iI) <= ' ') /* q. space or less? */ | |
*(pszLine + iI) = 0; /* a. yes .. remove it */ | |
else /* otherwise */ | |
break; /* .. done. Leave loop. */ | |
} | |
if (strlen(pszLine) == 0) /* q. blank line? */ | |
{ /* a. yes .. Assembler abhors these so */ | |
sprintf(gszOutput, ABLANKLINE); /* format as comment statement */ | |
return gszOutput; /* .. and return it */ | |
} | |
/* TODO: Add code to process a strip switch | |
* comment? | |
*/ | |
if (strlen(pszLine) > (TAGOFFSET + 1)) /* q. line long enough? */ | |
{ /* a. yes.. reorder tag/format */ | |
memcpy(acTFWrk, pszLine + FMTOFFSET, 2); /* get tag/format */ | |
memset((pszLine + FMTOFFSET), ' ', 2); /* .. blank 'em out */ | |
for (iI = 0; iI < 2; iI ++) | |
if (isalpha(acTFWrk[iI])) /* q. alpha char? */ | |
*(pszLine + FMTOFFSET) = acTFWrk[iI]; /* a. yes .. make it format */ | |
else if (isdigit(acTFWrk[iI])) /* q. digit? */ | |
*(pszLine + TAGOFFSET) = acTFWrk[iI]; /* a. yes .. make it the tag */ | |
} | |
sprintf(gszOutput, AFORMAT, pszLine); /* format the line */ | |
return gszOutput; /* return formatted line */ | |
} | |
/************************************************* | |
* EditToFortran - convert tab-formatted input text line to FORTRAN format | |
* (a la DEC Fortran) | |
*/ | |
const char *EditToFortran(char* p_pszEdit, int width) /* convert line to 1130 assembler */ | |
{ | |
char pszLine[MAXLINE]; /* source line */ | |
char* pszWork; /* work pointer */ | |
size_t iI; /* work integer */ | |
int bContinue; /* true if continue */ | |
if (p_pszEdit == NULL) /* q. null request? */ | |
return FMSG; /* a. yes .. return display message */ | |
if (strchr(p_pszEdit, '\t') == NULL) /* q. no tab in the line? */ | |
return p_pszEdit; /* a. nope, return line as is, assume it's formatted correctly */ | |
if (*p_pszEdit == 'C' || *p_pszEdit == '*' || *p_pszEdit == '\0') /* q. comment or directive or blank line? */ | |
{ /* a. yes.. don't restructure */ | |
return EditToWhitespace(p_pszEdit, width); | |
} | |
strncpy(pszLine, p_pszEdit, MAXLINE-1); /* copy the line local */ | |
for (iI = strlen(pszLine); iI--;) /* trim trailing whitespace */ | |
{ | |
if (*(pszLine + iI) <= ' ') /* q. space or less? */ | |
*(pszLine + iI) = 0; /* a. yes .. remove it */ | |
else /* otherwise */ | |
break; /* .. done. Leave loop. */ | |
} | |
/* | |
* TODO: Add code to process a strip switch | |
* comment? | |
*/ | |
pszWork = (char*) pszLine; /* set pointer to line */ | |
GetToken(gszLabel, 6, &pszWork); /* get the line, if any. */ | |
pszWork++; /* skip tab/whitespace */ | |
/* continuation... */ | |
bContinue = ((isdigit(*pszWork) && (*pszWork != '0')) /* if first char non-zero digit */ | |
|| (!isspace(*pszWork) && !isalpha(*pszWork))); /* .. or non-alpha non-blank */ | |
memset(gszArg, 0, MAXLINE); /* .. and arguments */ | |
strncpy(gszArg, pszWork, 75); /* copy rest to argument */ | |
sprintf(gszOutput, (bContinue) ? FCONTFMT : FFORMAT, /* format the line */ | |
gszLabel, /* .. statement # */ | |
gszArg); /* .. code */ | |
return gszOutput; /* return formatted line */ | |
} | |
/************************************************* | |
* EditToWhitespace - expand tabs at n space intervals. | |
*/ | |
const char* EditToWhitespace(char *p_pszEdit, int width) | |
{ | |
int iI; /* work integer */ | |
int iPos; /* work integer for settings tab stops */ | |
char pszLine[MAXLINE]; /* source line */ | |
char pszWork[WORKSZ]; /* work buffer */ | |
if (p_pszEdit == NULL) /* q. null request? */ | |
return WMSG; /* a. yes .. return display message */ | |
strncpy(pszLine, p_pszEdit, MAXLINE-1); /* copy the line local */ | |
if (width == 0) width = 8; /* default */ | |
if ((width != giPlainTabWidth) && (width > 1) && (width < 30)) { | |
giPlainTabWidth = width; /* if width is different, and valid, rebuild tabstop array */ | |
iI = 0; /* output index */ | |
iPos = width + 1; /* first tab position */ | |
while (iPos < 80) { /* fill array up to but not including position 80 */ | |
gaiPlainTabs[iI++] = iPos; | |
iPos += width; | |
} | |
gaiPlainTabs[iI] = 0; /* mark end of array */ | |
} | |
ExpandTabs(pszLine, pszWork, gaiPlainTabs); /* expand the tabs */ | |
strncpy(gszOutput, pszWork, MAXLINE-1); /* copy the line back */ | |
for (iI = strlen(gszOutput); iI--;) /* look at each character */ | |
{ | |
if (*(gszOutput + iI) <= ' ') /* q. space or less? */ | |
*(gszOutput + iI) = 0; /* a. yes .. remove it */ | |
else /* otherwise */ | |
break; /* .. done. Leave loop. */ | |
} | |
return gszOutput; /* ... return buffer */ | |
} |