ادیتور/ویرایشگر تحت کنسول خود را با C بسازید. (۱)

ترجمه ی مطالبی که در viewsourcecode.org نوشته شد.سایت ذکر شده IP ایرانی را تحریم کرده لطفا از پروژه وطنی shecan.ir استفاده کنیدالبته کلا این مطلب ، موضوعات و چیز های بدردنخور را حذف کرده و یا در بخش هایی دیگر توضیح داده ، همچنین من در این پست چیز هایی را توضیح می دهم که پست انگلیسی آن ها را توضیح نداده است :)آماده سازیبرنامه ما تنها نیاز به یک کامپایلر زبان C دارد . ( و همچنین محیط POSIX )(استاندارد POSIX همان استانداردی هست که unix - linux - BSD از آن پیروی می کنند )نصب کامپایلر Cبرای نصب در debian ها ( توزیع های خانواده دبیان ) ترمینال را باز کنید و بنویسیدsudo apt install gcc # or sudo apt install clang # or sudo apt install $c_compiler # or in past versions sudo apt-get install gcc # or ...و خلاصه که نصب کنیددر Windows( توی مطب نگفته بود ، اما کاربران ویندوز ۹۰٪ مواقع با مشکل رو به رو هستند )برای ساخت این ادیتور ما از هدر هایی مانند unistd.h و termios.h استفاده می کنیم ، که در ویندوز نیستند.راه حل هاBash On WindowsCygwinکه من توضیح نمی دهم ، وظیفه بر دوش کابران ویندوز { ویندوزی های بیچاره :( }و اما make یا makefileبنیاد gnu پروژه make را که توسط Stuart Feldman ساخته شده را بازسازی کرده.این برنامه فایل هایی که نیاز به recompile و لینک کردن دوباره ، دارند را تشخیص می دهد ، همچنین اگر این فایل ها به فایلی دیگر وابسته باشند ، وجود داشتن فایل را چک می کند ، و اگر به targetای دیگر آن target را هم اجرا می کند.مسلما است که نوشتن خطوط زیر در ترمینال ، برای هر تغییر بسیار خسته کننده استcc -c editor.c \ -Wall -Werror -Wextra cc editor.o -o myowneditorپس چرا هنگامی که می توانیم از کامند زیر به جایی این همه دستور بالا استفاده کنیم ، این کار را نکنیمmakeفقط لازم هست تا به make بگوییم ، برنامه من را چطور کامپایل کن .(بنده که در ویرگول این پست را می نویسم ، کد هایی با cmake و meson دیدم ، در این دو تا build شما دست بازی ندارید ، اما کار راحتی دارید ، دقیق مثل اسمبلی که دست شما باز هست ، اما همه چیز را خودتان باید بگویید ، بنابراین در make قرار نیست نام پروژه را بگویید )دست خود را به کد آلوده کنیداز نظر من که این ترجمه رو می نویسیم ، برنامه نویس بدون کد همانند زنبور بدون جان هست . اشتباه نکنید! برنامه نویس بدون پروژه همانند زنبور بی عسل هست ، هنگامی که کد ننوشته باشد ، مرده ای بیش نیست { اگر کد ننوشتید ، بفرما کد پایین رو بنویسید که بیجان نباشید دگر}اول از همه فایلی به نام editor.c بسازید و کد زیر را در آن قرار دهید (‌ put کنید )int main(){ return 0; }این هم عکس از کد با vim(توجه : من تابع main را برای افرادی که آشنا نیستند ، توضیح می دهم )تابعی به نام main در کد موجود هست. این تابع جایی هست که برنامه شروع به اجرا شدن می کند ، به عنوان مثال اگر با زبانی مانند پایتون کار کرده باشید ، فرض کنید که تابعی به اسم start در برنامه دارید . وقتی که به زبان تان می گویید، این فایل را بخوان پایتون ، زبان شما ( در اینجا C ) دنبال تابع start می گردد و به جایی خواندن فایل از اول فایل ، تابع start ( در اینجا main ) را اجرا می کند .خوب ، حالیا در ترمینال دستور زیر را بنویسید تا برنامه شما کامپایل شودcc=gcc # or clang , cc $cc editor.c -o editorو با کامند زیر در لینوکس :./editorاجرا اش کنید .( ویندوز رو نمی گویم، چون اگر با ویندوز کار کنید به مشکل می خورید )بعد از اجرا برنامه شما هیچ چیزی نخواهید دید ، اما مقداری که این تابع بر می گرداند ، تاثیر گذار هست ! باور نمی کنید ؟؟ پس می توانید دستور زیر را اجرا کنید. ( در محیط bash در POSIX )echo $?و چه جوابی می گیرید ؟؟؟ بله ۰ یعنی مقداری که تابع برگشت داده ! هنوز هم باور نمی کنید ؟ پس بیاید چیزی که تابع بر می گرداند را تغییر بدهیم و نتیجه را ببینیم . اما قبل از آناو آمده تا Don't repeat Yourself او make یا makefile هست( از کی تا حالا سرتیتر ها هم نقطه {.} می خواهنید؟؟ )(این بخش برای کسانی هست که make یاد ندارند - البته چون که دارای نکاتی طریف هست پیشنهاد می کنم حتی در صورت دانشتن make این بخش را بخوانید )من make را بسیار استفاده می کنم . این یک build system سبک و کارآمد هست ، که بسیار ساده هست ، در حدی که با چند بار خواندن چند makefile می تونید سینتکس این سیستم ساخت را یاد بگیرید! چه عالی :)گول آموزش های موجود در YouTube رو نخورید . به صورت پیش فرض make دنبال چیزی همانند all , default , output نیست. بلکه بر طبق مستندات سایت رسمی gnu ، این نرم افزار اولین target ( بعدا با آن آشنا خواهید شد ) را به عنوان نقطه شروع برنامه و یا target پیش فرض در نظر می گیرد.target: prerequisites recipe recipeاگر شما به جای prerequisites در آن جایی که آموزش دیدید dependency را دیدید ،‌ بدونید که در سایت رسمی gnu این به عنوان prerequisite یاد شده! پس بهتر است که از مستندات سایت رسمی استفاده کنیم و نه آموزش های YouTube .و همچنین recipe که دستور العملی هست که {بر طبق سایت رسمی gnu }این برنامه (make) دستورات و یا recipe هایی که به آن داده شده را هشدار می دهد ( چاپ می کند ) و آن ها را اجرا می کند .پس در بخش recipe ها ، شما می توانید دستورات bash که محیط شل پیش فرض گنو هست و یا shell های دیگر ، را (در بخش recipe ) بنویسید. همانند mkdir - rm -rf و خیلی چیز هایی دیگر. البته make این ها را چک می کند و مثلا اگر از متغییر ای استفاده کنید ، make متغییر را تشخیص داده و مقدار متغیر را با خود آن عوض می کند.در بخش prerequisite ها ، شما می توانید نام فایل هایی که target به آن وابسته هست و یا نام target هایی که این target برای اجرا شدن نیاز دارد آنها اجرا شوند.و در بخش recipe ها هم دستورات محیط shell را می توانید قرار دهید ، البته به همراه دستورات make .تارگت ها را می توانید مانند توابع در نظر بگیرید و target اول را همانند تابع main تصور کنید.وقت ، وقت عمل است خوب بریم make رو بسازیمeditor: editor.c $(CC) editor.c -o editor \ -Wall -Wextra -pedantic \ -std=c99ممکن هست همینجا با دیدن $(CC) تعجب کنید و از ادامه راه منصرف شوید ، اما ساده هست. این یک متغییر در make هست که به صورت پیش فرض ، وجود دارد.آپشن/flag های داده شده با کامپایلر-Wall : هر چیز یک کوکچولو مشکوکی که دیدی رو به ما اعلام کن-Wextra: بعضی از انواع هشدار ها که آپشن اول آن ها را فعال نکرده را ، این یکی فعالشون می کند-std: استاندارد زبان را تعیین می کند ( فکر نکنم نیاز به توضیح باشه ؟ )-padantic: شبیه -Wallاین هم از makefile. و در نهایت ما build می گیریم ، برای اینکار کافی هست دستور make را بنویسید. تصویر پایین گویا ماجرا هست.من رو ببینید ، من گویا ماجرا هستم ، من من مناول با دستور cat محتویات فایل editor.c رو نمایش دادم . و بعد از آن توسط make کد را کامپایل کردم. و در نهایت با دستور echo $? آمدم و وضعیت را فهمیدیم و وضعیت 100 یا همان چیزی که تابع اصلی بر می گرداند بود.و در نهایت دوباره آمدم و make را اجرا کردم ، اما چون این برنامه باهوش هست ، فهمید که من فایلی را تغییر ندادم که بعد بخواهد باز دوباره make بگیرد؟؟؟اما چطور این کار را انجام می دهد ؟؟ در کدام حافظه ثبت می کند?? نگران نباشید ، هیچ چیز عجیبی در کار نیست ، make به طرز عجیبی این کار را انجام می دهد ، اگر آخرین تغییر فایل اجرایی editor نزدیکتر بود ، یعنی عمر editor.c از عمر editor بیشتر بود ، آنگاه make فکر می کند که همه چیز سر جایش هست .چقدر حرف می زنی ،‌ برو سر اصل مطلبممنون از دوستان که به من یاد اوری کردند.دوستان و همراهان گرامی ، لفطا این بخش را کامل بخونید ، بعد انجام بدهید. چون اگر بدون این که کامل بخونید ، این بخش رو انجام بدید ممکن هست بترسید !! (سیستم خراب شود و شما ندانید چی کار کنید)ما به هدر unistd.h نیاز مندیم ، این هدر در ویندوز موجود نیست .فایل editor.c را به کد زیر تغییر دهید :#include <unistd.h> int main(){ char ch; while( read(STDIN_FILENO , &ch , 1) == 1); return 0; }همانطور که در این فایل می بینید . ما تا زمانی که تابع read مقدار ۱ رو بر گرداند ، متغییر ch را برابر با کاراکتر ورودی کابر می کنیم. من تابع read رو بیشتر توضیح خواهم داد.اول از همه باید به شما بگویم STDIN_FILENO یک متغییر ثابت (constant) هست که مقداری برابر با ۰ دارد و در هدر unistd.h موجود هست.این سوال در stackoverflow مورد پسند من هست./* Standard file descriptors. */#define STDIN_FILENO 0 /* Standard input. */#define STDOUT_FILENO 1 /* Standard output. */#define STDERR_FILENO 2 /* Standard error output. */تابع read دارای سه آرگامون | آرگومنت هست.linux manual pagessize_t read(int fd, void *buf, size_t count); read() attempts to read up to count bytes from file descriptor fd into the buffer starting at bufتابع read() تلاش می کند تا بخواند count بایت از توصیف کننده ی فایل fd درون بافری که با buf شروع می شود .به زبان آدمی زاد : این تابع count بایت از fd می خواند و توی buf می گذارد.همانطور که در کد می بینید ، ما یک while با بدنی تهی ( خالی ) داریم که ۱ بایت از ورودی می خواند و مقدارش را در ch می ریزد و اگر مقداری که تابع برگشت داد ، برابر با ۱ نبود از برنامه خارج می شود.الان make بگیرید . و امتحان کنید . اما قبلش یادتون باشد اگر خواستید خارج بشوید باید حالت eof و یا End Of File بسازید که در لینوکس با Ctrl+D و در ونیدوز با Ctrl+Z ساخته می شود.برای خارج شدن Ctrl+D را بگیریدبرای خارج شدن Ctrl+D را بگیرید.برنامه را طوری بسازیم که برای خارج شدن نیاز باشد q رو فشار دهیم#include <unistd.h> int main(){ char ch; while( read(0 , &ch , 1 ) == 1 && ch != 'q'); return 0; }همانطور که در کد هست بخش && ch != 'q' به کد اضافه شده. یعنی اگر ch مقدار q داشت ، شما خارج شو- quitاما مشکلی هست :\ که بعدا با آن رو به رو خواهید شد ( نیم فاصله در اوبونتو چطوری هست ؟؟)خوب اول make و بعد مشکل را خواهید دید.الان ادیتور را باز کنید . و در آن یک چیزی بنویسید . برای خارج شدن نیاز هست تا کلید q را در متن بنویسید ، اما تا قبل از اینکه enter بزنید ، این پارامتر ها به تابع پاس داده نمی شوند.خوب حالا شما متنی را که دارای q هست بنویسید و بعد از q هم چند حرف اضافه بنویسید .خواهید دید که حرف های بعد q به عنوان دستورات ترمینال در نظر گرفته شده و نه به عنوان متنی که در ادیتور نوشتید.با دقت نگاه کنید - شما for quit را که بعد از q آمده را دارید می بینیدنگاه کنید . بعد از q من نوشته بودم for quit و دقیقا همان بخش به عنوان کامند bash در نظر گرفته شده.اما چرا ادیتور به محض فشار دادن q متوقف نشد؟همانطور که گفتم تا قبل از وارد کردن کلید enter هیچ اطلاعاتی به تابع داده نمی شود بنابراین اگر شما بنویسید q تا قبل از فشردن کلید enter هیچ اطلاعاتی به برنامه داده نمی شود . خوب ، حالا ما enter را زدیم و اطلاعاتی یکی یکی به برنامه داده شدند ، برنامه q را دید و متوقف شد. بنابراین برنامه به محض دیدن q متوقف شده و ادیتور چیز های بعد q را نمی خواند ، بنابراین آنها به ترمینال پاس داده می شوند و دستورات bash در نظر گرفته می شوند.چرا ادیتور نمی تواند بدون صبر برای وارد کردن کلید enter اطلاعات را بخواند؟زیرا ترمینال در حالت معمول بر روی حالت cooked mode و یا کانونیکال مود می باشد .برای این کار ما به raw mode نیاز داریم که اگر خدا قسمت کرد در پست های بعد توضیح می دهم .از آنجایی که دیگر کار با ویرگول راحت نیست ( نمی دونم چرا وقتی مطلب طولانی می شود ، لپتاپ من کندتر می شود و به سرعت اول تایپ نمی کند ،هر چه مطلب طولانی تر سرعت تایپ کمتر {البته اگر الان برم تو یک تب دیگر باز سرعت می گیرد ، مشکل از ویرگول هست}) و دیگر خسته شدم و شما هم احتمالا خسته شدید ، بقیه اش برای پست های بعدی موفق باشید :-) قسمت دوم ،ویرگول.آی/اوvirgool.io به امید دیدار دوباره {}خداحافظ :\