|
|
| version 1.336, 2024/12/23 16:59:17 | version 1.343, 2026/04/25 13:38:46 |
|---|---|
| Line 1 | Line 1 |
| /** @file | /** @file |
| Parser: commonly functions. | Parser: commonly functions. |
| Copyright (c) 2000-2024 Art. Lebedev Studio (http://www.artlebedev.com) | Copyright (c) 2000-2026 Art. Lebedev Studio (https://www.artlebedev.com) |
| Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru> | Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru> |
| */ | */ |
| #include "pa_common.h" | #include "pa_common.h" |
| #include "pa_exception.h" | #include "pa_exception.h" |
| #include "pa_hash.h" | #include "pa_hash.h" |
| #include "pa_inline_hash.h" | |
| #include "pa_globals.h" | #include "pa_globals.h" |
| #include "pa_charsets.h" | #include "pa_charsets.h" |
| #include "pa_http.h" | #include "pa_http.h" |
| Line 22 | Line 23 |
| #include <direct.h> | #include <direct.h> |
| #endif | #endif |
| #ifdef _MSC_VER | volatile const char * IDENT_PA_COMMON_C="$Id$" IDENT_PA_COMMON_H IDENT_PA_HASH_H IDENT_PA_INLINE_HASH_H IDENT_PA_ARRAY_H IDENT_PA_STACK_H; |
| #define pa_mkdir(path, mode) _mkdir(path) | |
| #else | |
| #define pa_mkdir(path, mode) mkdir(path, mode) | |
| #endif | |
| volatile const char * IDENT_PA_COMMON_C="$Id$" IDENT_PA_COMMON_H IDENT_PA_HASH_H IDENT_PA_ARRAY_H IDENT_PA_STACK_H; | |
| // some maybe-undefined constants | // some maybe-undefined constants |
| Line 72 const UTF16* pa_utf16_encode(const char* | Line 67 const UTF16* pa_utf16_encode(const char* |
| #ifdef _MSC_VER | #ifdef _MSC_VER |
| #define PA_UTF16_ENC(value) (const wchar_t *)pa_utf16_encode(value, pa_thread_request().charsets.source()) | |
| int pa_stat(const char *pathname, struct stat *buffer){ | int pa_stat(const char *pathname, struct stat *buffer){ |
| const UTF16* utf16name=pa_utf16_encode(pathname, pa_thread_request().charsets.source()); | return _wstat64(PA_UTF16_ENC(pathname), buffer); |
| return _wstat64((const wchar_t *)utf16name, buffer); | |
| } | } |
| int pa_open(const char *pathname, int flags, int mode){ | int pa_open(const char *pathname, int flags, int mode){ |
| const UTF16* utf16name=pa_utf16_encode(pathname, pa_thread_request().charsets.source()); | return _wopen(PA_UTF16_ENC(pathname), flags, mode); |
| return _wopen((const wchar_t *)utf16name, flags, mode); | |
| } | } |
| FILE *pa_fopen(const char *pathname, const char *mode){ | FILE *pa_fopen(const char *pathname, const char *mode){ |
| const UTF16* utf16name=pa_utf16_encode(pathname, pa_thread_request().charsets.source()); | return _wfopen(PA_UTF16_ENC(pathname), PA_UTF16_ENC(mode)); |
| const UTF16* utf16mode=pa_utf16_encode(mode, pa_thread_request().charsets.source()); | } |
| return _wfopen((const wchar_t *)utf16name, (const wchar_t *)utf16mode); | |
| int pa_mkdir(const char *pathname, int){ | |
| return _wmkdir(PA_UTF16_ENC(pathname)); | |
| } | |
| int pa_rmdir(const char *pathname){ | |
| return _wrmdir(PA_UTF16_ENC(pathname)); | |
| } | } |
| int pa_rename(const char *oldpath, const char *newpath){ | |
| return _wrename(PA_UTF16_ENC(oldpath), PA_UTF16_ENC(newpath)); | |
| } | |
| int pa_unlink(const char *pathname){ | |
| return _wunlink(PA_UTF16_ENC(pathname)); | |
| } | |
| #else | |
| #define pa_mkdir mkdir | |
| #define pa_rmdir rmdir | |
| #define pa_rename rename | |
| #define pa_unlink unlink | |
| #endif | #endif |
| /// these options were handled but not checked elsewhere, now check them | /// these options were handled but not checked elsewhere, now check them |
| Line 287 void create_dir_for_file(const String& f | Line 303 void create_dir_for_file(const String& f |
| const char *str=file_spec.taint_cstr(String::L_FILE_SPEC); | const char *str=file_spec.taint_cstr(String::L_FILE_SPEC); |
| if(str[0]){ | if(str[0]){ |
| const char *pos=str+1; | const char *pos=str+1; |
| while((pos=strchr(pos,'/')) && pos[1]) { // to avoid trailing /, see #1166 | while((pos=strpbrk(pos, "/\\")) && pos[1]) { // to avoid trailing /, see #1166 |
| pa_mkdir(pa_strdup(str,pos-str), 0775); | pa_mkdir(pa_strdup(str,pos-str), 0775); |
| pos++; | pos++; |
| } | } |
| Line 441 static void rmdir(const String& file_spe | Line 457 static void rmdir(const String& file_spe |
| #ifdef _MSC_VER | #ifdef _MSC_VER |
| if(!entry_ifdir(dir_spec, true)) | if(!entry_ifdir(dir_spec, true)) |
| break; | break; |
| DWORD attrs=GetFileAttributes(dir_spec); | DWORD attrs=GetFileAttributesW(PA_UTF16_ENC(dir_spec)); |
| if( | if( |
| (attrs==INVALID_FILE_ATTRIBUTES) | (attrs==INVALID_FILE_ATTRIBUTES) |
| || !(attrs & FILE_ATTRIBUTE_DIRECTORY) | || !(attrs & FILE_ATTRIBUTE_DIRECTORY) |
| Line 449 static void rmdir(const String& file_spe | Line 465 static void rmdir(const String& file_spe |
| ) | ) |
| break; | break; |
| #endif | #endif |
| if( rmdir(dir_spec) ) | if(pa_rmdir(dir_spec)) |
| break; | break; |
| }; | }; |
| } | } |
| bool file_delete(const String& file_spec, bool fail_on_problem, bool keep_empty_dirs) { | bool file_delete(const String& file_spec, bool fail_on_problem, bool keep_empty_dirs) { |
| const char* fname=file_spec.taint_cstr(String::L_FILE_SPEC); | const char* fname=file_spec.taint_cstr(String::L_FILE_SPEC); |
| if(unlink(fname)!=0) { | if(pa_unlink(fname)!=0) { |
| if(fail_on_problem) | if(fail_on_problem) |
| throw Exception(errno==EACCES?"file.access":errno==ENOENT?"file.missing":0, | throw Exception(errno==EACCES?"file.access":errno==ENOENT?"file.missing":0, |
| &file_spec, "unlink failed: %s (%d), actual filename '%s'", strerror(errno), errno, fname); | &file_spec, "unlink failed: %s (%d), actual filename '%s'", strerror(errno), errno, fname); |
| Line 476 void file_move(const String& old_spec, c | Line 492 void file_move(const String& old_spec, c |
| create_dir_for_file(new_spec); | create_dir_for_file(new_spec); |
| if(rename(old_spec_cstr, new_spec_cstr)!=0) | if(pa_rename(old_spec_cstr, new_spec_cstr)!=0) |
| throw Exception(errno==EACCES ? "file.access" : errno==ENOENT ? "file.missing" : 0, | throw Exception(errno==EACCES ? "file.access" : errno==ENOENT ? "file.missing" : 0, |
| &old_spec, "rename failed: %s (%d), actual filename '%s' to '%s'", strerror(errno), errno, old_spec_cstr, new_spec_cstr); | &old_spec, "rename failed: %s (%d), actual filename '%s' to '%s'", strerror(errno), errno, old_spec_cstr, new_spec_cstr); |
| Line 682 const char *pa_filename(const char *path | Line 698 const char *pa_filename(const char *path |
| return path; | return path; |
| } | } |
| // format: %[flags][width][.precision]type http://msdn.microsoft.com/ru-ru/library/56e442dc(en-us,VS.80).aspx | |
| // flags: '-', '+', ' ', '#', '0' http://msdn.microsoft.com/ru-ru/library/8aky45ct(en-us,VS.80).aspx | |
| // width, precision: non negative decimal number | |
| enum FormatType { | |
| FormatInvalid, | |
| FormatInt, | |
| FormatUInt, | |
| FormatDouble | |
| }; | |
| FormatType format_type(const char* fmt){ | |
| enum FormatState { | |
| Percent, | |
| Flags, | |
| Width, | |
| Precision, | |
| Done | |
| } state=Percent; | |
| FormatType result=FormatInvalid; | |
| const char* pos=fmt; | |
| while(char c=*(pos++)){ | |
| switch(state){ | |
| case Percent: | |
| if(c=='%'){ | |
| state=Flags; | |
| } else { | |
| return FormatInvalid; // 1st char must be '%' only | |
| } | |
| break; | |
| case Flags: | |
| if(strchr("-+ #0", c)!=0){ | |
| break; | |
| } | |
| // go to the next step | |
| case Width: | |
| if(c=='.'){ | |
| state=Precision; | |
| break; | |
| } | |
| // go to the next step | |
| case Precision: | |
| if(c>='0' && c<='9'){ | |
| if(state == Flags) state=Width; // no more flags | |
| break; | |
| } else if(c=='d' || c=='i'){ | |
| result=FormatInt; | |
| } else if(strchr("feEgG", c)!=0){ | |
| result=FormatDouble; | |
| } else if(strchr("uoxX", c)!=0){ | |
| result=FormatUInt; | |
| } else { | |
| return FormatInvalid; // invalid char | |
| } | |
| state=Done; | |
| break; | |
| case Done: | |
| return FormatInvalid; // no chars allowed after 'type' | |
| } | |
| } | |
| return result; | |
| } | |
| const char* format_double(double value, const char* fmt) { | |
| char local_buf[MAX_NUMBER]; | |
| int size=-1; | |
| if(fmt && strlen(fmt)){ | |
| switch(format_type(fmt)){ | |
| case FormatDouble: | |
| size=snprintf(local_buf, sizeof(local_buf), fmt, value); | |
| break; | |
| case FormatUInt: | |
| if(value >= 0){ // on Apple M1 (uint)<negative value> is 0 | |
| size=snprintf(local_buf, sizeof(local_buf), fmt, clip2uint(value)); | |
| break; | |
| } | |
| case FormatInt: | |
| size=snprintf(local_buf, sizeof(local_buf), fmt, clip2int(value)); | |
| break; | |
| case FormatInvalid: | |
| throw Exception(PARSER_RUNTIME, 0, "Incorrect format string '%s' was specified.", fmt); | |
| } | |
| } else | |
| return pa_itoa(clip2int(value)); | |
| if(size < 0 || size >= MAX_NUMBER-1){ // on win32 we manually reduce max size while printing | |
| throw Exception(PARSER_RUNTIME, 0, "Error occurred white executing snprintf with format string '%s'.", fmt); | |
| } | |
| return pa_strdup(local_buf, (size_t)size); | |
| } | |
| size_t stdout_write(const void *buf, size_t size) { | size_t stdout_write(const void *buf, size_t size) { |
| #ifdef WIN32 | #ifdef WIN32 |
| size_t to_write = size; | size_t to_write = size; |
| Line 797 size_t stdout_write(const void *buf, siz | Line 719 size_t stdout_write(const void *buf, siz |
| } | } |
| enum EscapeState { | enum EscapeState { |
| EscapeRest, | EscapeRest, |
| EscapeFirst, | EscapeFirst, |
| EscapeSecond, | EscapeSecond, |
| EscapeUnicode | EscapeUnicode |
| }; | }; |