Skip to content

Commit 0e6d428

Browse files
authored
Merge pull request #1941 from codefori/fix/CCSID297UpperCase
Replace all toUpperCase whith IBMi.upperCaseName where relevant
2 parents 20044bb + b8e6362 commit 0e6d428

File tree

12 files changed

+156
-101
lines changed

12 files changed

+156
-101
lines changed

src/api/IBMi.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,7 @@ export default class IBMi {
541541
// We need to check if our remote programs are installed.
542542
remoteApps.push(
543543
{
544-
path: `/QSYS.lib/${this.config.tempLibrary.toUpperCase()}.lib/`,
544+
path: `/QSYS.lib/${this.upperCaseName(this.config.tempLibrary)}.lib/`,
545545
names: [`GETNEWLIBL.PGM`],
546546
specific: `GE*.PGM`
547547
}

src/api/IBMiContent.ts

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -515,14 +515,14 @@ export default class IBMiContent {
515515
* @returns an array of IBMiObject
516516
*/
517517
async getObjectList(filters: { library: string; object?: string; types?: string[]; filterType?: FilterType }, sortOrder?: SortOrder): Promise<IBMiObject[]> {
518-
const library = filters.library.toUpperCase();
518+
const library = this.ibmi.upperCaseName(filters.library);
519519
if (!await this.checkObject({ library: "QSYS", name: library, type: "*LIB" })) {
520520
throw new Error(`Library ${library} does not exist.`);
521521
}
522522

523523
const singleEntry = filters.filterType !== 'regex' ? singleGenericName(filters.object) : undefined;
524524
const nameFilter = parseFilter(filters.object, filters.filterType);
525-
const object = filters.object && (nameFilter.noFilter || singleEntry) && filters.object !== `*` ? filters.object.toUpperCase() : `*ALL`;
525+
const object = filters.object && (nameFilter.noFilter || singleEntry) && filters.object !== `*` ? this.ibmi.upperCaseName(filters.object) : `*ALL`;
526526

527527
const typeFilter = filters.types && filters.types.length > 1 ? (t: string) => filters.types?.includes(t) : undefined;
528528
const type = filters.types && filters.types.length === 1 && filters.types[0] !== '*' ? filters.types[0] : '*ALL';
@@ -638,14 +638,14 @@ export default class IBMiContent {
638638
*/
639639
async getMemberList(filter: { library: string, sourceFile: string, members?: string, extensions?: string, sort?: SortOptions, filterType?: FilterType }): Promise<IBMiMember[]> {
640640
const sort = filter.sort || { order: 'name' };
641-
const library = filter.library.toUpperCase();
642-
const sourceFile = filter.sourceFile.toUpperCase();
641+
const library = this.ibmi.upperCaseName(filter.library);
642+
const sourceFile = this.ibmi.upperCaseName(filter.sourceFile);
643643

644644
const memberFilter = parseFilter(filter.members, filter.filterType);
645-
const singleMember = memberFilter.noFilter && filter.members && !filter.members.includes(",") ? filter.members.toLocaleUpperCase().replace(/[*]/g, `%`) : undefined;
645+
const singleMember = memberFilter.noFilter && filter.members && !filter.members.includes(",") ? this.ibmi.upperCaseName(filter.members).replace(/[*]/g, `%`) : undefined;
646646

647647
const memberExtensionFilter = parseFilter(filter.extensions, filter.filterType);
648-
const singleMemberExtension = memberExtensionFilter.noFilter && filter.extensions && !filter.extensions.includes(",") ? filter.extensions.toLocaleUpperCase().replace(/[*]/g, `%`) : undefined;
648+
const singleMemberExtension = memberExtensionFilter.noFilter && filter.extensions && !filter.extensions.includes(",") ? this.ibmi.upperCaseName(filter.extensions).replace(/[*]/g, `%`) : undefined;
649649

650650
const statement =
651651
`With MEMBERS As (
@@ -779,20 +779,21 @@ export default class IBMiContent {
779779

780780
async memberResolve(member: string, files: QsysPath[]): Promise<IBMiMember | undefined> {
781781
// Escape names for shell
782-
const pathList = files
783-
.map(file => {
784-
const asp = file.asp || this.config.sourceASP;
785-
if (asp && asp.length > 0) {
786-
return [
787-
Tools.qualifyPath(file.library, file.name, member, asp, true),
788-
Tools.qualifyPath(file.library, file.name, member, undefined, true)
789-
].join(` `);
790-
} else {
791-
return Tools.qualifyPath(file.library, file.name, member, undefined, true);
792-
}
793-
})
794-
.join(` `)
795-
.toUpperCase();
782+
const pathList = this.ibmi.upperCaseName(
783+
files
784+
.map(file => {
785+
const asp = file.asp || this.config.sourceASP;
786+
if (asp && asp.length > 0) {
787+
return [
788+
Tools.qualifyPath(file.library, file.name, member, asp, true),
789+
Tools.qualifyPath(file.library, file.name, member, undefined, true)
790+
].join(` `);
791+
} else {
792+
return Tools.qualifyPath(file.library, file.name, member, undefined, true);
793+
}
794+
})
795+
.join(` `)
796+
);
796797

797798
const command = `for f in ${pathList}; do if [ -f $f ]; then echo $f; break; fi; done`;
798799
const result = await this.ibmi.sendCommand({
@@ -819,7 +820,7 @@ export default class IBMiContent {
819820
}
820821

821822
async objectResolve(object: string, libraries: string[]): Promise<string | undefined> {
822-
const command = `for f in ${libraries.map(lib => `/QSYS.LIB/${lib.toUpperCase()}.LIB/${object.toUpperCase()}.*`).join(` `)}; do if [ -f $f ] || [ -d $f ]; then echo $f; break; fi; done`;
823+
const command = `for f in ${libraries.map(lib => `/QSYS.LIB/${this.ibmi.upperCaseName(lib)}.LIB/${this.ibmi.upperCaseName(object)}.*`).join(` `)}; do if [ -f $f ] || [ -d $f ]; then echo $f; break; fi; done`;
823824

824825
const result = await this.ibmi.sendCommand({
825826
command,
@@ -891,8 +892,8 @@ export default class IBMiContent {
891892

892893
async checkObject(object: { library: string, name: string, type: string, member?: string }, authorities: Authority[] = [`*NONE`]) {
893894
return (await this.ibmi.runCommand({
894-
command: IBMiContent.toCl(`CHKOBJ`, {
895-
obj: `${object.library.toLocaleUpperCase()}/${object.name.toLocaleUpperCase()}`,
895+
command: this.toCl(`CHKOBJ`, {
896+
obj: `${this.ibmi.upperCaseName(object.library)}/${this.ibmi.upperCaseName(object.name)}`,
896897
objtype: object.type.toLocaleUpperCase(),
897898
aut: authorities.join(" "),
898899
mbr: object.member
@@ -911,7 +912,7 @@ export default class IBMiContent {
911912
}
912913
else { //QSYS path
913914
const qsysObject = Tools.parseQSysPath(path);
914-
return this.config.protectedPaths.includes(qsysObject.library.toLocaleUpperCase());
915+
return this.config.protectedPaths.includes(this.ibmi.upperCaseName(qsysObject.library));
915916
}
916917
}
917918

@@ -921,15 +922,15 @@ export default class IBMiContent {
921922
* @param parameters A key/value object of parameters
922923
* @returns Formatted CL string
923924
*/
924-
static toCl(command: string, parameters: { [parameter: string]: string | number | undefined }) {
925+
toCl(command: string, parameters: { [parameter: string]: string | number | undefined }) {
925926
let cl = command;
926927

927928
for (const [key, value] of Object.entries(parameters)) {
928929
let parmValue;
929930

930931
if (value !== undefined) {
931932
if (typeof value === 'string') {
932-
if (value === value.toLocaleUpperCase()) {
933+
if (value === this.ibmi.upperCaseName(value)) {
933934
parmValue = value;
934935
} else {
935936
parmValue = value.replace(/'/g, `''`);

src/api/debug/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ export async function initialize(context: ExtensionContext) {
141141

142142
if (qualifiedPath.object) {
143143
// Remove .pgm ending potentially
144-
qualifiedPath.object = qualifiedPath.object.toUpperCase();
144+
qualifiedPath.object = connection.upperCaseName(qualifiedPath.object);
145145
if (qualifiedPath.object.endsWith(`.PGM`))
146146
qualifiedPath.object = qualifiedPath.object.substring(0, qualifiedPath.object.length - 4);
147147
}
@@ -523,8 +523,8 @@ export async function startDebug(instance: Instance, options: DebugOptions) {
523523
"port": port,
524524
"secure": secure, // Enforce secure mode
525525
"ignoreCertificateErrors": true,
526-
"library": options.library.toUpperCase(),
527-
"program": options.object.toUpperCase(),
526+
"library": connection!.upperCaseName(options.library),
527+
"program": connection!.upperCaseName(options.object),
528528
"startBatchJobCommand": `SBMJOB CMD(${currentCommand}) INLLIBL(${options.libraries.libraryList.join(` `)}) CURLIB(${options.libraries.currentLibrary}) JOBQ(QSYSNOMAX) MSGQ(*USRPRF)`,
529529
"updateProductionFiles": updateProductionFiles,
530530
"trace": enableDebugTracing,

src/filesystems/qsys/QSysFs.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,13 @@ export class QSysFS implements vscode.FileSystemProvider {
7777
}
7878

7979
stat(uri: vscode.Uri): vscode.FileStat {
80+
let type = uri.path.split(`/`).length > 3 ? vscode.FileType.File : vscode.FileType.Directory;
81+
8082
return {
8183
ctime: 0,
8284
mtime: 0,
8385
size: 0,
84-
type: vscode.FileType.File,
86+
type,
8587
permissions: getFilePermission(uri)
8688
}
8789
}

src/filesystems/qsys/extendedContent.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ export class ExtendedIBMiContent {
3030
async downloadMemberContentWithDates(asp: string | undefined, lib: string, spf: string, mbr: string) {
3131
const content = instance.getContent();
3232
const config = instance.getConfig();
33+
const connection = instance.getConnection();
34+
if (connection && config && content) {
35+
lib = connection.upperCaseName(lib);
36+
spf = connection.upperCaseName(spf);
37+
mbr = connection.upperCaseName(mbr);
3338

34-
lib = lib.toUpperCase();
35-
spf = spf.toUpperCase();
36-
mbr = mbr.toUpperCase();
37-
38-
if (config && content) {
3939
const sourceColourSupport = GlobalConfiguration.get<boolean>(`showSeuColors`);
4040
const tempLib = config.tempLibrary;
4141
const alias = getAliasName(lib, spf, mbr);

src/instantiate.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ export async function loadAllofExtension(context: vscode.ExtensionContext) {
196196
const connection = instance.getConnection();
197197
let starRemoved: boolean = false;
198198

199-
if (!storage && !content) return;
199+
if (!storage && !content && !connection) return;
200200
let list: string[] = [];
201201

202202
// Get recently opened files - cut if limit has been reduced.
@@ -265,14 +265,14 @@ export async function loadAllofExtension(context: vscode.ExtensionContext) {
265265
);
266266
filteredItems = [];
267267
} else {
268-
if (!starRemoved && !list.includes(quickPick.value.toUpperCase())) {
269-
quickPick.items = [quickPick.value.toUpperCase(), ...list].map(label => ({ label }));
268+
if (!starRemoved && !list.includes(connection!.upperCaseName(quickPick.value))) {
269+
quickPick.items = [connection!.upperCaseName(quickPick.value), ...list].map(label => ({ label }));
270270
}
271271
}
272272

273273
// autosuggest
274274
if (config && config.enableSQL && (!quickPick.value.startsWith(`/`)) && quickPick.value.endsWith(`*`)) {
275-
const selectionSplit = quickPick.value.toUpperCase().split('/');
275+
const selectionSplit = connection!.upperCaseName(quickPick.value).split('/');
276276
const lastPart = selectionSplit[selectionSplit.length - 1];
277277
let filterText = lastPart.substring(0, lastPart.indexOf(`*`));
278278

@@ -427,7 +427,7 @@ export async function loadAllofExtension(context: vscode.ExtensionContext) {
427427
);
428428
vscode.window.showInformationMessage(`Cleared cached files.`);
429429
} else {
430-
const selectionSplit = selection.toUpperCase().split('/')
430+
const selectionSplit = connection!.upperCaseName(selection).split('/')
431431
if (selectionSplit.length === 3 || selection.startsWith(`/`)) {
432432
if (config && config.enableSQL && !selection.startsWith(`/`)) {
433433
const lib = `${connection!.sysNameInAmerican(selectionSplit[0])}`;
@@ -463,12 +463,12 @@ export async function loadAllofExtension(context: vscode.ExtensionContext) {
463463
vscode.window.showWarningMessage(`${selection} does not exist or is not a file.`);
464464
return;
465465
}
466-
selection = selection.toUpperCase() === quickPick.value.toUpperCase() ? quickPick.value : selection;
466+
selection = connection!.upperCaseName(selection) === connection!.upperCaseName(quickPick.value) ? quickPick.value : selection;
467467
}
468468
vscode.commands.executeCommand(`code-for-ibmi.openEditable`, selection, { readonly });
469469
quickPick.hide();
470470
} else {
471-
quickPick.value = selection.toUpperCase() + '/'
471+
quickPick.value = connection!.upperCaseName(selection) + '/'
472472
}
473473
}
474474
}

src/testing/connection.ts

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -151,13 +151,13 @@ export const ConnectionSuite: TestSuite = {
151151
name: `Test runCommand (ILE)`, test: async () => {
152152
const connection = instance.getConnection();
153153

154-
const result = await connection?.runCommand({
155-
command: `DSPLIBL`,
154+
const result = await connection!.runCommand({
155+
command: `DSPJOB OPTION(*DFNA)`,
156156
environment: `ile`
157157
});
158158

159159
assert.strictEqual(result?.code, 0);
160-
assert.strictEqual(result.stdout.includes(`Library List`), true);
160+
assert.strictEqual(["JOBPTY", "OUTPTY", "ENDSEV", "DDMCNV", "BRKMSG", "STSMSG", "DEVRCYACN", "TSEPOOL", "PRTKEYFMT", "SRTSEQ"].every(attribute => result.stdout.includes(attribute)), true);
161161
}
162162
},
163163

@@ -275,33 +275,59 @@ export const ConnectionSuite: TestSuite = {
275275
name: `Test withTempDirectory`, test: async () => {
276276
const connection = instance.getConnection()!;
277277
let temp;
278-
const countFiles = async(dir:string) => {
279-
const countResult = await connection.sendCommand({command: `find ${dir} -type f | wc -l`});
278+
const countFiles = async (dir: string) => {
279+
const countResult = await connection.sendCommand({ command: `find ${dir} -type f | wc -l` });
280280
assert.strictEqual(countResult.code, 0);
281281
return Number(countResult.stdout);
282282
};
283283

284284
await connection.withTempDirectory(async tempDir => {
285285
temp = tempDir;
286286
//Directory must exist
287-
assert.strictEqual((await connection.sendCommand({command: `[ -d ${tempDir} ]`})).code, 0);
287+
assert.strictEqual((await connection.sendCommand({ command: `[ -d ${tempDir} ]` })).code, 0);
288288

289289
//Directory must be empty
290290
assert.strictEqual(await countFiles(tempDir), 0);
291291

292292
const toCreate = 10;
293-
for(let i = 0; i < toCreate; i++){
294-
assert.strictEqual((await connection.sendCommand({command: `echo "Test ${i}" >> ${tempDir}/file${i}`})).code, 0);
293+
for (let i = 0; i < toCreate; i++) {
294+
assert.strictEqual((await connection.sendCommand({ command: `echo "Test ${i}" >> ${tempDir}/file${i}` })).code, 0);
295295
}
296296

297-
const newCountResult = await connection.sendCommand({command: `ls -l ${tempDir} | wc -l`});
297+
const newCountResult = await connection.sendCommand({ command: `ls -l ${tempDir} | wc -l` });
298298
assert.strictEqual(newCountResult.code, 0);
299299
assert.strictEqual(await countFiles(tempDir), toCreate);
300300
});
301301

302-
if(temp){
302+
if (temp) {
303303
//Directory must be gone
304-
assert.strictEqual((await connection.sendCommand({command: `[ -d ${temp} ]`})).code, 1);
304+
assert.strictEqual((await connection.sendCommand({ command: `[ -d ${temp} ]` })).code, 1);
305+
}
306+
}
307+
},
308+
{
309+
name: `Test upperCaseName`, test: async () => {
310+
const connection = instance.getConnection()!;
311+
const variantsBackup = connection.variantChars.local;
312+
313+
try{
314+
const checkVariants = () => connection.variantChars.local !== connection.variantChars.local.toLocaleUpperCase();
315+
//CCSID 297 variants
316+
connection.variantChars.local = '£à$';
317+
connection.dangerousVariants = checkVariants();
318+
assert.strictEqual(connection.dangerousVariants, true);
319+
assert.strictEqual(connection.upperCaseName("àTesT£ye$"), "àTEST£YE$");
320+
assert.strictEqual(connection.upperCaseName("test_cAsE"), "TEST_CASE");
321+
322+
//CCSID 37 variants
323+
connection.variantChars.local = '#@$';
324+
connection.dangerousVariants = checkVariants();
325+
assert.strictEqual(connection.dangerousVariants, false);
326+
assert.strictEqual(connection.upperCaseName("@TesT#ye$"), "@TEST#YE$");
327+
assert.strictEqual(connection.upperCaseName("test_cAsE"), "TEST_CASE");
328+
}
329+
finally{
330+
connection.variantChars.local = variantsBackup;
305331
}
306332
}
307333
}

src/testing/content.ts

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import assert from "assert";
22
import tmp from 'tmp';
3-
import util from 'util';
3+
import util, { TextDecoder } from 'util';
44
import { Uri, workspace } from "vscode";
55
import { TestSuite } from ".";
6-
import IBMiContent from "../api/IBMiContent";
76
import { Tools } from "../api/Tools";
87
import { instance } from "../instantiate";
98
import { CommandResult } from "../typings";
9+
import { getMemberUri } from "../filesystems/qsys/QSysFs";
1010

1111
export const ContentSuite: TestSuite = {
1212
name: `Content API tests`,
@@ -66,20 +66,6 @@ export const ContentSuite: TestSuite = {
6666
}
6767
},
6868

69-
{
70-
name: `Test memberResolve with bad name`, test: async () => {
71-
const content = instance.getContent();
72-
73-
const member = await content?.memberResolve(`BOOOP`, [
74-
{ library: `QSYSINC`, name: `MIH` }, // Doesn't exist here
75-
{ library: `NOEXIST`, name: `SUP` }, // Doesn't exist here
76-
{ library: `QSYSINC`, name: `H` } // Doesn't exist here
77-
]);
78-
79-
assert.deepStrictEqual(member, undefined);
80-
}
81-
},
82-
8369
{
8470
name: `Test objectResolve .FILE`, test: async () => {
8571
const content = instance.getContent();
@@ -555,7 +541,7 @@ export const ContentSuite: TestSuite = {
555541
},
556542
{
557543
name: `To CL`, test: async () => {
558-
const command = IBMiContent.toCl("TEST", {
544+
const command = instance.getContent()!.toCl("TEST", {
559545
ZERO: 0,
560546
NONE:'*NONE',
561547
EMPTY: `''`,
@@ -609,5 +595,37 @@ export const ContentSuite: TestSuite = {
609595
assert.deepStrictEqual(resultB.OBJTEXT, "Code for i test");
610596
}
611597
},
598+
{
599+
name: `Write tab to member using SQL`, test: async () => {
600+
const lines = [
601+
`if (a) {`,
602+
`\tcoolstuff();\t`,
603+
`}`
604+
].join(`\n`);
605+
606+
const connection = instance.getConnection();
607+
const config = instance.getConfig()!;
608+
609+
assert.ok(config.enableSourceDates, `Source dates must be enabled for this test.`);
610+
611+
const tempLib = config!.tempLibrary;
612+
613+
await connection!.runCommand({ command: `CRTSRCPF FILE(${tempLib}/TABTEST) RCDLEN(112)`, noLibList: true });
614+
await connection!.runCommand({ command: `ADDPFM FILE(${tempLib}/TABTEST) MBR(THEBADONE) SRCTYPE(HELLO)` });
615+
616+
const theBadOneUri = getMemberUri({library: tempLib, file: `TABTEST`, name: `THEBADONE`, extension: `HELLO`});
617+
618+
// We have to read it first to create the alias!
619+
await workspace.fs.readFile(theBadOneUri);
620+
621+
await workspace.fs.writeFile(theBadOneUri, Buffer.from(lines, `utf8`));
622+
623+
const memberContentBuf = await workspace.fs.readFile(theBadOneUri);
624+
const fileContent = new TextDecoder().decode(memberContentBuf)
625+
626+
assert.strictEqual(fileContent, lines);
627+
628+
}
629+
}
612630
]
613631
};

0 commit comments

Comments
 (0)