From 9896ab6cee802f6c07e38aee130319de370d0812 Mon Sep 17 00:00:00 2001 From: simosathan9 Date: Wed, 11 Sep 2024 17:06:44 +0300 Subject: [PATCH] Add blocks for mv, rm and touch commands --- public/blocks/mvBlock.js | 40 ++++++- public/blocks/rmBlock.js | 62 +++++++++-- public/blocks/touchBlock.js | 57 +++++++++- public/js/el.js | 35 +++++- public/js/en.js | 29 ++++- public/main.js | 211 ++++++++++++++++++++++++++++++++++++ 6 files changed, 411 insertions(+), 23 deletions(-) diff --git a/public/blocks/mvBlock.js b/public/blocks/mvBlock.js index f584916..ce6e8b6 100644 --- a/public/blocks/mvBlock.js +++ b/public/blocks/mvBlock.js @@ -1,10 +1,13 @@ var mvBlock = { type: 'mv', category: 'File and Directory Operations', - message0: 'mv %1 %2', + message0: 'Move %1 to %2', // Correctly references %1 (SOURCE) and %2 (DEST) unix_description: [ { - command: 'mv %SOURCE %DEST' + not_prompt_confirmation: '-f', + prompt_confirmation: '-i', + verbose: '-v', + not_overwrite: '-n' } ], args0: [ @@ -19,6 +22,38 @@ var mvBlock = { text: 'dest' // default destination } ], + message1: '%{BKY_MV_NOT_PROMPT_CONFIRMATION}', + args1: [ + { + type: 'field_checkbox', + name: 'not_prompt_confirmation', + checked: false + } + ], + message2: '%{BKY_MV_PROMPT_CONFIRMATION}', + args2: [ + { + type: 'field_checkbox', + name: 'prompt_confirmation', + checked: false + } + ], + message3: '%{BKY_MV_VERBOSE}', + args3: [ + { + type: 'field_checkbox', + name: 'verbose', + checked: false + } + ], + message4: '%{BKY_MV_NOT_OVERWRITE}', + args4: [ + { + type: 'field_checkbox', + name: 'not_overwrite', + checked: false + } + ], style: 'File and Directory Operations', previousStatement: 'Action', nextStatement: 'Action', @@ -27,4 +62,3 @@ var mvBlock = { }; Blockly.defineBlocksWithJsonArray([mvBlock]); -// diff --git a/public/blocks/rmBlock.js b/public/blocks/rmBlock.js index 62d6f5c..14ea91d 100644 --- a/public/blocks/rmBlock.js +++ b/public/blocks/rmBlock.js @@ -1,34 +1,72 @@ var rmBlock = { type: 'rm', + message0: '%{BKY_RM}', category: 'File and Directory Operations', - message1: 'remove %1', unix_description: [ { - command: 'rm %FILE', - request_confirmation: '-i' + force: '-f', + request_confirmation: '-i', + remove_directory: '-d', + recursive: '-R', + verbose: '-v', + undelete: '-W', + no_cross_mount: '-x' } ], - message0: '%{BKY_RM_REQUEST_CONFIRMATION}', - args0: [ + message1: '%{BKY_RM_FORCE}', + args1: [ + { + type: 'field_checkbox', + name: 'force', + checked: false + } + ], + message2: '%{BKY_RM_REQUEST_CONFIRMATION}', + args2: [ { type: 'field_checkbox', name: 'request_confirmation', - checked: false // by default it's disabled + checked: false } ], - args1: [ + message3: '%{BKY_RM_REMOVE_DIRECTORIES}', + args3: [ + { + type: 'field_checkbox', + name: 'remove_directory', + checked: false + } + ], + message4: '%{BKY_RM_RECURSIVE}', + args4: [ { - type: 'field_input', - name: 'FILE', - text: 'file' // default file + type: 'field_checkbox', + name: 'recursive', + checked: false + } + ], + message5: '%{BKY_RM_VERBOSE}', + args5: [ + { + type: 'field_checkbox', + name: 'verbose', + checked: false + } + ], + message6: '%{BKY_RM_NO_CROSS_MOUNT}', + args6: [ + { + type: 'field_checkbox', + name: 'no_cross_mount', + checked: false } ], + extensions: [], style: 'File and Directory Operations', previousStatement: 'Action', nextStatement: 'Action', - tooltip: 'Διαγράφει αρχεία και καταλόγους.', + tooltip: '%{BKY_RM_TOOLTIP}', helpUrl: 'https://linux.die.net/man/1/rm' }; Blockly.defineBlocksWithJsonArray([rmBlock]); -// diff --git a/public/blocks/touchBlock.js b/public/blocks/touchBlock.js index eb6ffab..f11fa7d 100644 --- a/public/blocks/touchBlock.js +++ b/public/blocks/touchBlock.js @@ -1,8 +1,61 @@ var touchBlock = { type: 'touch', - category: 'File and Directory Operations', - unix_description: [{}], message0: '%{BKY_TOUCH}', + category: 'File and Directory Operations', + unix_description: [ + { + not_create_file: '-c', + change_time_t: '-t str', + change_time_d: '-d str', + access_time: '-a', + modification_time: '-r' + } + ], + message1: '%{BKY_TOUCH_NOT_CREATE_FILE}', + args1: [ + { + type: 'field_checkbox', + name: 'not_create_file', + checked: false + } + ], + message2: '%{BKY_TOUCH_CHANGE_ACCESS_TIME}', + args2: [ + { + type: 'field_checkbox', + name: 'access_time', + checked: false + } + ], + message3: '%{BKY_TOUCH_CHANGE_MODIFICATION_TIME}', + args3: [ + { + type: 'field_checkbox', + name: 'modification_time', + checked: false + } + ], + + message4: '%{BKY_TOUCH_SPECIFY_TIME_FORMAT_T}', + args4: [ + { + type: 'field_input', + name: 'change_time_t', + text: '', + check: 'String' + } + ], + message5: '%{BKY_TOUCH_PROPOSE_OTHER_FORMAT}', + message6: '%{BKY_TOUCH_SPECIFY_TIME_FORMAT_D}', + args6: [ + { + type: 'field_input', + name: 'change_time_d', + text: '', + check: 'String' + } + ], + extensions: ['validate_touch_time_t', 'validate_touch_time_d'], style: 'File and Directory Operations', previousStatement: 'Action', nextStatement: 'Action', diff --git a/public/js/el.js b/public/js/el.js index eaf1dc7..9280a00 100644 --- a/public/js/el.js +++ b/public/js/el.js @@ -136,6 +136,12 @@ Blockly.Msg['MKDIR_WRITE_DIRECTORY'] = 'Γράψτε όνομα καταλόγο Blockly.Msg['MKDIR_TOOLTIP'] = 'Δημιουργία καταλόγου'; Blockly.Msg['MKDIR_HELPURL'] = 'https://www.google.com/'; +Blockly.Msg['MV_NOT_PROMPT_CONFIRMATION'] = 'Μην ζητήσεις επιβεβαίωση %1'; +Blockly.Msg['MV_PROMPT_CONFIRMATION'] = + 'Ζήτα επιβεβαίωση προτού εκτελέστεί η εντολή %1'; +Blockly.Msg['MV_VERBOSE'] = 'Λεπτομερής αναφορά %1'; +Blockly.Msg['MV_NOT_OVERWRITE'] = 'Μη αντικατάσταση αρχείου %1'; + Blockly.Msg['RECORD_NUMBER'] = 'Τρέχων εγγραφή'; Blockly.Msg['RECORD_NUMBER_TOOLTIP'] = 'Αντιπροσωπεύει τον τρέχοντα αριθμό εγγραφής (γραμμής)'; @@ -592,7 +598,18 @@ Blockly.Msg['REMOVE_COMMENT'] = 'Αφαίρεση σχολίου'; Blockly.Msg['RENAME_VARIABLE'] = 'Μετονομασία μεταβλητής...'; Blockly.Msg['RENAME_VARIABLE_TITLE'] = "Μετονομασία όλων των μεταβλητών '%1' σε:"; -Blockly.Msg['RM_REQUEST_CONFIRMATION'] = 'Αίτημα επιβεβαίωσης για αφαίρεση %1'; +Blockly.Msg['RM'] = 'Αφαίρεση αρχείου(ων)'; +Blockly.Msg['RM_FORCE'] = 'Επιβολή αφαίρεσης χωρίς επιβεβαίωση %1'; +Blockly.Msg['RM_REQUEST_CONFIRMATION'] = + 'Ζητήστε επιβεβαίωση πριν την αφαίρεση αρχείου(ων) %1'; +Blockly.Msg['RM_REMOVE_DIRECTORIES'] = 'Αφαίρεση καταλόγων %1'; +Blockly.Msg['RM_RECURSIVE'] = 'Αφαίρεση αναδρομικά %1'; +Blockly.Msg['RM_VERBOSE'] = + 'Λειτουργία αναλυτικών πληροφοριών (εμφάνιση αφαιρεθέντων αρχείων) %1'; +Blockly.Msg['RM_NO_CROSS_MOUNT'] = 'Μην διασχίσετε σημεία σύνδεσης %1'; +Blockly.Msg['RM_TOOLTIP'] = + 'Αφαιρεί αρχεία και καταλόγους με βάση τις καθορισμένες επιλογές.'; + Blockly.Msg['TEXT_APPEND_HELPURL'] = 'https://github.com/google/blockly/wiki/Text#text-modification'; Blockly.Msg['TEXT_APPEND_TITLE'] = 'στο %1 προσθήκη κειμένου %2'; @@ -850,8 +867,20 @@ Blockly.Msg['TEE_TOOLTIP'] = 'Το εργαλείο tee αντιγράφει την τυπική είσοδο στην τυπική έξοδο, κάνοντας ένα αντίγραφο σε ένα ή περισσότερα αρχεία.'; Blockly.Msg['TEE_HELPURL'] = 'https://www.google.com/'; -Blockly.Msg['TOUCH'] = 'Δημιουργία αρχείου'; -Blockly.Msg['TOUCH_TOOLTIP'] = 'δημιουργία καταλόγου'; +Blockly.Msg['TOUCH'] = 'Τροποποίηση χρονικών στιγμών αρχείου'; +Blockly.Msg['TOUCH_NOT_CREATE_FILE'] = + 'Μη δημιουργία αρχείου αν δεν υπάρχει %1'; +Blockly.Msg['TOUCH_CHANGE_ACCESS_TIME'] = + 'Αλλαγή χρόνου τελευταίας πρόσβασης %1'; +Blockly.Msg['TOUCH_CHANGE_MODIFICATION_TIME'] = + 'Αλλαγή χρόνου τελευταίας τροποποίησης %1'; +Blockly.Msg['TOUCH_SPECIFY_TIME_FORMAT_T'] = + 'με προσθήκη ημερομηνίας στη μορφή [[CC]YY]MMDDhhmm[.SS] %1'; +Blockly.Msg['TOUCH_PROPOSE_OTHER_FORMAT'] = 'ή'; +Blockly.Msg['TOUCH_SPECIFY_TIME_FORMAT_D'] = + 'με προσθήκη ημερομηνίας στη μορφή YYYY-MM-DDThh:mm:SS[.frac][Z (για UTC)] %1'; +Blockly.Msg['TOUCH_TOOLTIP'] = + 'Όρισε την ώρα πρόσβασης και τροποποίησης ενός αρχείου. Αν το αρχείο δεν υπάρχει, δημιούργησε ένα κενό αρχείο.'; Blockly.Msg['TOUCH_HELPURL'] = 'https://www.google.com/'; Blockly.Msg['UNIQ'] = 'Αφαίρεση διπλότυπων γραμμών στο αρχείο\n'; diff --git a/public/js/en.js b/public/js/en.js index 1348f09..7c227df 100644 --- a/public/js/en.js +++ b/public/js/en.js @@ -133,6 +133,11 @@ Blockly.Msg['MKDIR_WRITE_DIRECTORY'] = 'Write directory or path %1'; Blockly.Msg['MKDIR_TOOLTIP'] = 'Create directory'; Blockly.Msg['MKDIR_HELPURL'] = 'https://www.google.com/'; +Blockly.Msg['MV_NOT_PROMPT_CONFIRMATION'] = 'Do not prompt for confirmation %1'; +Blockly.Msg['MV_PROMPT_CONFIRMATION'] = 'Prompt for confirmation %1'; +Blockly.Msg['MV_VERBOSE'] = 'Verbose mode %1'; +Blockly.Msg['MV_NOT_OVERWRITE'] = 'Do not overwrite %1'; + Blockly.Msg['RECORD_NUMBER'] = 'Current record'; Blockly.Msg['RECORD_NUMBER_TOOLTIP'] = 'Represents the current record or (line) number'; @@ -580,8 +585,16 @@ Blockly.Msg['REGOR_CREATE_WITH'] = 'multiple patterns'; Blockly.Msg['REMOVE_COMMENT'] = 'Remove Comment'; Blockly.Msg['RENAME_VARIABLE'] = 'Rename variable...'; Blockly.Msg['RENAME_VARIABLE_TITLE'] = "Rename all '%1' variables to:"; +Blockly.Msg['RM'] = 'Remove file(s)'; +Blockly.Msg['RM_FORCE'] = 'Force removal without prompt %1'; Blockly.Msg['RM_REQUEST_CONFIRMATION'] = - 'Request confirmation before deleting %1'; + 'Request confirmation before removing file(s) %1'; +Blockly.Msg['RM_REMOVE_DIRECTORIES'] = 'Remove directories %1'; +Blockly.Msg['RM_RECURSIVE'] = 'Remove recursively %1'; +Blockly.Msg['RM_VERBOSE'] = 'Verbose mode (show removed files) %1'; +Blockly.Msg['RM_NO_CROSS_MOUNT'] = 'Do not cross mount points %1'; +Blockly.Msg['RM_TOOLTIP'] = + 'Removes files and directories based on specified options.'; Blockly.Msg['TEXT_APPEND_HELPURL'] = 'https://github.com/google/blockly/wiki/Text#text-modification'; Blockly.Msg['TEXT_APPEND_TITLE'] = 'to %1 append text %2'; @@ -827,8 +840,18 @@ Blockly.Msg['TEE_TOOLTIP'] = 'The tee utility copies standard input to standard output, making a copy in zero or more files.'; Blockly.Msg['TEE_HELPURL'] = 'https://www.google.com/'; -Blockly.Msg['TOUCH'] = 'Create a file'; -Blockly.Msg['TOUCH_TOOLTIP'] = 'create directory'; +Blockly.Msg['TOUCH'] = 'Modify file timestamps'; +Blockly.Msg['TOUCH_NOT_CREATE_FILE'] = + 'Do not create file if it does not exist %1'; +Blockly.Msg['TOUCH_CHANGE_ACCESS_TIME'] = 'Change access time %1'; +Blockly.Msg['TOUCH_CHANGE_MODIFICATION_TIME'] = 'Change modification time %1'; +Blockly.Msg['TOUCH_SPECIFY_TIME_FORMAT_T'] = + 'with date in [[CC]YY]MMDDhhmm[.SS] %1'; +Blockly.Msg['TOUCH_PROPOSE_OTHER_FORMAT'] = 'or'; +Blockly.Msg['TOUCH_SPECIFY_TIME_FORMAT_D'] = + 'with date in YYYY-MM-DDThh:mm:SS[.frac][Z (UTC)] %1'; +Blockly.Msg['TOUCH_TOOLTIP'] = + 'Set the modification and access times of files. If any file does not exist, it is created with default permissions. '; Blockly.Msg['TOUCH_HELPURL'] = 'https://www.google.com/'; Blockly.Msg['UNIQ'] = 'Remove duplicate lines in file\n'; diff --git a/public/main.js b/public/main.js index b07d7d8..ced23f7 100644 --- a/public/main.js +++ b/public/main.js @@ -1513,6 +1513,217 @@ Blockly.Extensions.register('cut_validation', function () { }); }); +// Define validation extension for the touch -t flag +Blockly.Extensions.register('validate_touch_time_t', function () { + this.getField('change_time_t').setValidator(function (input) { + // Define multiple regex patterns to match different formats + var patterns = [ + /^$/, // Empty string pattern + /^(\d{2})(\d{2})(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])(0[0-9]|1[0-9]|2[0-3])(0[0-9]|[1-5][0-9])(\.[0-5][0-9])?$/, // CCYYMMDDhhmm[.SS] + /^(\d{2})(\d{2})(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])(0[0-9]|1[0-9]|2[0-3])(0[0-9]|[1-5][0-9])$/, // CCYYMMDDhhmm + /^(\d{2})(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])(0[0-9]|1[0-9]|2[0-3])(0[0-9]|[1-5][0-9])(\.[0-5][0-9])?$/, // YYMMDDhhmm[.SS] + /^(\d{2})(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])(0[0-9]|1[0-9]|2[0-3])(0[0-9]|[1-5][0-9])$/, // YYMMDDhhmm + /^(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])(0[0-9]|1[0-9]|2[0-3])(0[0-9]|[1-5][0-9])(\.[0-5][0-9])?$/, // MMDDhhmm[.SS] + /^(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])(0[0-9]|1[0-9]|2[0-3])(0[0-9]|[1-5][0-9])$/ // MMDDhhmm + ]; + + // Allow empty input + if (input === '') { + return input; // Accept empty input as valid + } + + // Check if input matches one of the valid formats + var isValid = patterns.some(function (pattern) { + return pattern.test(input); + }); + + if (!isValid) { + return null; // Invalid input, reject + } + + // Determine the current date, year, and century + var now = new Date(); + var currentYear = now.getFullYear(); + var currentCentury = Math.floor(currentYear / 100); + + // Parse input based on its length + var century = ''; + var year = ''; + var month = ''; + var day = ''; + var hour = ''; + var minute = ''; + var second = '00'; // default seconds to '00' + + switch (input.length) { + case 15: // CCYYMMDDhhmm[.SS] + century = input.substr(0, 2); + year = input.substr(2, 2); + month = input.substr(4, 2); + day = input.substr(6, 2); + hour = input.substr(8, 2); + minute = input.substr(10, 2); + second = input.substr(12, 2); + break; + case 13: // YYMMDDhhmm[.SS] + year = input.substr(0, 2); + month = input.substr(2, 2); + day = input.substr(4, 2); + hour = input.substr(6, 2); + minute = input.substr(8, 2); + second = input.substr(10, 2); + break; + case 12: // CCYYMMDDhhmm + century = input.substr(0, 2); + year = input.substr(2, 2); + month = input.substr(4, 2); + day = input.substr(6, 2); + hour = input.substr(8, 2); + minute = input.substr(10, 2); + break; + case 11: // MMDDhhmm[.SS] + month = input.substr(0, 2); + day = input.substr(2, 2); + hour = input.substr(4, 2); + minute = input.substr(6, 2); + second = input.substr(8, 2); + year = String(currentYear).slice(-2); // default to current year + century = String(currentCentury); // default to current century + break; + case 10: // YYMMDDhhmm + year = input.substr(0, 2); + month = input.substr(2, 2); + day = input.substr(4, 2); + hour = input.substr(6, 2); + minute = input.substr(8, 2); + break; + case 8: // MMDDhhmm + month = input.substr(0, 2); + day = input.substr(2, 2); + hour = input.substr(4, 2); + minute = input.substr(6, 2); + year = String(currentYear).slice(-2); // default to current year + century = String(currentCentury); // default to current century + break; + + default: + return null; // Invalid length, reject + } + + // If year is present but no century, infer based on year + if (year && !century) { + century = parseInt(year, 10) >= 69 ? '19' : '20'; // infer century + } + + var fullYear = parseInt(century) * 100 + parseInt(year, 10); + + // Validate ranges for month, day, hour, minute, and second + if ( + month < '01' || + month > '12' || + day < '01' || + day > '31' || + hour < '00' || + hour > '23' || + minute < '00' || + minute > '59' || + second < '00' || + second > '60' + ) { + return null; // Invalid input: reject and keep the old value + } + + // Check day validity for the given month and year (leap year handling) + var daysInMonth = new Date(fullYear, parseInt(month, 10), 0).getDate(); + if (parseInt(day, 10) > daysInMonth) { + return null; // Invalid day for the given month + } + + return input; // Input is valid + }); +}); + +// Define validation extension for the touch -d flag +Blockly.Extensions.register('validate_touch_time_d', function () { + this.getField('change_time_d').setValidator(function (input) { + // Define regex patterns to match different valid formats + var patterns = [ + /^$/, // Empty string pattern + /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])([T\s])(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z)?$/, // YYYY-MM-DDThh:mm:SS[.frac][tz] or YYYY-MM-DD hh:mm:SS[.frac][tz] + /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])([T\s])(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/, // YYYY-MM-DDThh:mm:SS or YYYY-MM-DD hh:mm:SS + /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])([T\s])(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])$/, // YYYY-MM-DDThh:mm or YYYY-MM-DD hh:mm + /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/, // YYYY-MM-DD + /^(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])([T\s])(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])$/, // MM-DDThh:mm or MM-DD hh:mm + /^(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])([T\s])(0[0-9]|1[0-9]|2[0-3])$/ // MM-DDThh or MM-DD hh + ]; + + // Check if input matches one of the valid patterns + var isValid = patterns.some(function (pattern) { + return pattern.test(input); + }); + + if (!isValid) { + return null; // Invalid input, reject + } + + // Normalize the input by replacing space with 'T' + input = input.replace(/\s/, 'T'); + + // Extract components from input + var parts = input.split('T'); + var datePart = parts[0]; + var timePart = (parts[1] || '').split('.')[0]; // Remove fractional seconds part + var fractionPart = (parts[1] || '').split('.')[1] || ''; // Extract fractional seconds + var tzPart = (parts[1] || '').includes('Z') ? 'Z' : ''; + + // Handle date part + var dateParts = datePart.split('-'); + var year = dateParts[0]; + var month = dateParts[1]; + var day = dateParts[2]; + + // Handle time part + var timeParts = timePart.split(':'); + var hour = timeParts[0] || '00'; + var minute = timeParts[1] || '00'; + var second = timeParts[2] || '00'; + + // Validate ranges for year, month, day, hour, minute, and second + if ( + month < '01' || + month > '12' || + day < '01' || + day > '31' || + hour < '00' || + hour > '23' || + minute < '00' || + minute > '59' || + second < '00' || + second > '59' + ) { + return null; // Invalid input: reject + } + + // Check day validity for the given month and year (leap year handling) + var daysInMonth = new Date(year, parseInt(month, 10), 0).getDate(); + if (parseInt(day, 10) > daysInMonth) { + return null; // Invalid day for the given month + } + + // Fractional seconds handling + if (fractionPart.length > 3) { + return null; // Fractional seconds should not exceed 3 digits + } + + // Optional time zone part validation + if (tzPart && tzPart !== 'Z') { + return null; // Invalid time zone designator, should be 'Z' if present + } + + return input; // Input is valid + }); +}); + //*********************************** //EXTENSIONS FOR VALIDATIONS - END //***********************************