Valgrindで見つかったメモリー周辺の問題点を修正
[YACASL2.git] / src / token.c
1 #include <stdio.h>
2 #include <string.h>
3 #include "cerr.h"
4 #include "cmem.h"
5 #include "assemble.h"
6
7
8 /**
9  * 行トークン取得のエラー定義
10  */
11 CERR cerr_linetok[] = {
12     { 104, "label length is too long" },
13     { 105, "no command in the line" },
14 };
15
16 /**
17  * オペランドトークン取得のエラー定義
18  */
19 static CERR cerr_opdtok[] = {
20     { 117, "operand too many in DC" },
21     { 118, "operand length too long" },
22     { 121, "cannot get operand token" },
23     { 123, "unclosed quote" },
24 };
25
26 /**
27  * オペランドトークン取得のエラーを追加
28  */
29 void addcerrlist_tok()
30 {
31     addcerrlist(ARRAYSIZE(cerr_linetok), cerr_linetok);
32     addcerrlist(ARRAYSIZE(cerr_opdtok), cerr_opdtok);
33 }
34
35 /**
36  * 「,」区切りの文字列から、オペランドのトークンを取得
37  */
38 OPD *opdtok(const char *str)
39 {
40     OPD *opd = malloc_chk(sizeof(OPD), "opd");
41     char *p, *q, *r, *sepp;     /* pは文字列全体の先頭位置、qはトークンの先頭位置、rは文字の位置 */
42     int sepc = ',', rcnt = 0;
43     bool quoting = false;
44
45     opd->opdc = 0;
46     if(str == NULL) {
47         return opd;
48     }
49     p = q = r = strdup_chk(str, "opdtok.p");
50     do {
51         /* オペランド数が多すぎる場合はエラー */
52         if(opd->opdc >= OPDSIZE) {
53             setcerr(117, NULL);    /* operand is too many */
54             break;
55         }
56         /* 先頭が「=」の場合 */
57         if(*r == '=') {
58             r++;
59         }
60         /* 「'」の場合 */
61         if(*r == '\'') {
62             /* 「''」以外の場合はquote値を反転 */
63             if(*(r+1) != '\'' && !(q < r && *(r-1) == '\'')) {
64                 quoting = !quoting;
65             }
66             /* 文字列の長さを数える。「'」の場合は数えない */
67             if(*(r+1) != '\'') {
68                 rcnt++;
69             }
70         }
71         if(quoting == true) {
72             /* 閉じ「'」がないまま文字列が終了した場合 */
73             if(*r == '\0') {
74                 setcerr(123, str);    /* unclosed quote */
75                 break;
76             }
77             r++;
78         } else {
79             sepp = r + strcspn(r, ", ");
80             sepc = *sepp;
81             *sepp = '\0';
82             if(*q == '\0') {
83                 setcerr(121, NULL);    /* cannot get operand token */
84                 break;
85             }
86             if(strlen(q) - rcnt > OPDSIZE) {
87                 setcerr(118, NULL);    /* operand length is too long */
88                 break;
89             }
90             opd->opdv[(++opd->opdc)-1] = strdup_chk(q, "opd.opdv[]");
91             q = r = sepp + 1;
92             rcnt = 0;
93         }
94     } while(sepc == ',');
95     FREE(p);
96     return opd;
97 }
98
99 /**
100  * 空白またはタブで区切られた1行から、トークンを取得する
101  */
102 CMDLINE *linetok(const char *line)
103 {
104     char *tokens, *p, *sepp;
105     bool quoting = false;
106     CMDLINE *cmdl = NULL;
107
108     if(line == NULL || strlen(line) == 0) {
109         return NULL;
110     }
111     tokens = strdup_chk(line, "tokens");
112     /* コメントを削除 */
113     for(p = tokens; *p != '\0'; p++) {
114         /* 「'」で囲まれた文字列の処理。「''」は無視 */
115         if(*p == '\'' && *(p+1) != '\'' && !(p > tokens && *(p-1) == '\'')) {
116             quoting = !quoting;
117         } else if(quoting == false && *p == ';') {
118             *p = '\0';
119             break;
120         }
121     }
122     if(*tokens != '\0') {
123         p = tokens;
124         cmdl = malloc_chk(sizeof(CMDLINE), "cmdl");
125         /* ラベルの取得。行の先頭が空白またはタブの場合、ラベルは空 */
126         if((sepp = p + strcspn(p, " \t\n")) == p){
127             cmdl->label = NULL;
128         } else {        /* ラベルを取得 */
129             *sepp = '\0';
130             /* 文字列が長すぎる場合はエラー */
131             if(strlen(p) > LABELSIZE) {
132                 setcerr(104, p);    /* label length is too long */
133             }
134             cmdl->label = strdup_chk(p, "cmdl.label");
135             p = sepp + 1;
136         }
137         /* ラベルと命令の間の空白をスキップ */
138         while(*p == ' ' || *p == '\t') {
139             p++;
140         }
141         /* 命令とオペランドの取得 */
142         if(*p == '\n' || *p == '\0') {        /* 命令がない場合は、終了 */
143             if(cmdl->label != NULL) {         /* ラベルが定義されていて命令がない場合はエラー */
144                 setcerr(105, NULL);    /* no command in the line */
145             }
146         } else {
147             /* 命令の取得 */
148             sepp = p + strcspn(p, " \t\n");
149             *sepp = '\0';
150             cmdl->cmd = strdup_chk(p, "cmdl.cmd");
151             p = sepp + 1;
152             /* 命令とオペランドの間の空白をスキップ */
153             while(*p == ' ' || *p == '\t') {
154                 p++;
155             }
156             /* 改行かタブまでの文字列を取得。
157                「'」で囲まれた文字列に含まれる場合があるため、空白は無視 */
158             if((sepp = p + strcspn(p, "\t\n")) > p) {
159                 *sepp = '\0';
160                 cmdl->opd = opdtok(p);
161             } else {
162                 cmdl->opd = malloc_chk(sizeof(OPD *), "cmdl.opd");
163                 cmdl->opd->opdc = 0;
164             }
165         }
166     }
167     FREE(tokens);
168     return cmdl;
169 }